0

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

3
  • Did you try Numba? Commented Apr 18, 2020 at 4:11
  • what exactly is your input? You may be better off just translate the characters to pixels one-to-one and then use some other image processing library, e.g. opencv, to resize. Commented Apr 18, 2020 at 4:33
  • An typical image example is given at the bottom. I then convert it to a numpy array using PIL and feed the resulting img to my resize function. I also happen to know the width the final pixelated image should have, thus the width argument that lets me determine the symbol size. The problem is that the size and even the shape of each symbol is arbitrary, so their color is all I can work wiyh. Commented Apr 18, 2020 at 10:51

2 Answers 2

1
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)

enter image description here 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)
Sign up to request clarification or add additional context in comments.

4 Comments

Or use np.mean() instead of np.max().
Very elegant! But a bit slower (1.32s vs 0.32s, see other message for details) than the other solution, so that one is what I'll go with. Thank you very much though, I didn't know about that syntax.
You can probably replace this looping with a call to dilation (= max filter) and resample in OpenCV.
Wow. This is fast! Thank you.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.