简体   繁体   中英

Trying to make naive numpy image processing code faster

I'm trying to transform an image containing colored symbols into pixel art as featured on the right (see image below), where each colored symbol (taking up multiple pixels) would be changed into one pixel of the symbol's color.

Example of what I'm trying to achieve

So far I've written a pretty naive algorithm that just loops through all the pixels, and is pretty sluggish. I believe I could make it faster, for instance using native numpy operations, but I've been unable to find how. Any tips?

(I also started by trying to simply resize the image, but couldn't find a resampling algorithm that would make it work).

def resize(img, new_width):
    width, height = img.shape[:2]
    new_height = height*new_width//width
    new_image = np.zeros((new_width, new_height,4), dtype=np.uint8)
    x_ratio, y_ratio = width//new_width, height//new_height
    for i in range(new_height):
        for j in range(new_width):
            sub_image = img[i*y_ratio:(i+1)*y_ratio, j*x_ratio:(j+1)*x_ratio]
            found = False
            for row in sub_image:
                for pixel in row:
                    if any(pixel!=[0,0,0,0]):
                        new_image[i,j]=pixel
                        break
                if found:
                    break
    return new_image

A larger example

import cv2
import numpy as np

img=cv2.imread('zjZA8.png')
h,w,c=img.shape
new_img=np.zeros((h//7,w//7,c), dtype='uint8')

for k in range(c):
    for i in range(h//7):
        for j in range(w//7):
            new_img[i,j,k]=np.max(img[7*i:7*i+7,7*j:7*j+7,k])
cv2.imwrite('out3.png', new_img)

在此处输入图像描述 Left is result with np.mean, center - source image, right - result with np.max

Please test this code:

img=cv2.imread('zjZA8.png')
h,w,c=img.shape
bgr=[0,0,0]
bgr[0], bgr[1],bgr[2] =cv2.split(img)
for k in range(3):
    bgr[k].shape=(h*w//7, 7)
    bgr[k]=np.mean(bgr[k], axis=1)
    bgr[k].shape=(h//7, 7, w//7)
    bgr[k]=np.mean(bgr[k], axis=1)
    bgr[k].shape=(h//7,w//7)
    bgr[k]=np.uint8(bgr[k])
out=cv2.merge((bgr[0], bgr[1],bgr[2]))
cv2.imshow('mean_image', out)

Modifying my code to use the native np.nonzero operation did the trick. I went down from ~8s to ~0.32s on a 1645x1645 image (with new_width=235), I also tried using numba on top of that. but the overhead ends up making it slower.

def resize(img, new_width):
    height, width = img.shape[:2]
    new_height = height*new_width//width
    new_image = np.ones((new_height, new_width,3), dtype=np.uint8)
    x_ratio, y_ratio = width//new_width, height//new_height
    for i in range(new_height):
        for j in range(new_width):
            sub_image = img[i*y_ratio:(i+1)*y_ratio, j*x_ratio:(j+1)*x_ratio]
            non_zero = np.nonzero(sub_image)
            if non_zero[0].size>0:
                new_image[i, j]=sub_image[non_zero[0][0],non_zero[1][0]][:3]
    return new_image

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM