Sample code of color reduction using a set of color pens. A program of error diffusion (dithering). Dithering can be applied not only to binary image but also to color image. You can change the color and the number of pens.
import numpy as np
import cv2
pallette=[
[0,0,0],
[0,0,255],
[0,255,0],
[0,255,255],
[255,0,0],
[255,0,255],
[255,255,0],
[255,255,255]
]
image=cv2.imread('input.bmp',cv2.IMREAD_COLOR)
image=image.astype(np.float64)
rows,cols,_=image.shape
for y in range(rows):
for x in range(cols):
pixel=[image[y,x,2],image[y,x,1],image[y,x,0]]
minerror=255*255*3*2*2
minpen=pallette[0]
for pen in pallette:
error=(pixel[0]-pen[0])**2+(pixel[1]-pen[1])**2+(pixel[2]-pen[2])**2
if
error<minerror:
minerror=error
minpen=pen
error=[pixel[0]-minpen[0],pixel[1]-minpen[1],pixel[2]-minpen[2]]
image[y,x,2]=minpen[0]
image[y,x,1]=minpen[1]
image[y,x,0]=minpen[2]
if x<cols-1:
image[y,x+1,2]+=7/16*error[0]
image[y,x+1,1]+=7/16*error[1]
image[y,x+1,0]+=7/16*error[2]
if x>0 and y<rows-1:
image[y+1,x-1,2]+=3/16*error[0]
image[y+1,x-1,1]+=3/16*error[1]
image[y+1,x-1,0]+=3/16*error[2]
if y<rows-1:
image[y+1,x,2]+=5/16*error[0]
image[y+1,x,1]+=5/16*error[1]
image[y+1,x,0]+=5/16*error[2]
if x<cols-1 and y<rows-1:
image[y+1,x+1,2]+=1/16*error[0]
image[y+1,x+1,1]+=1/16*error[1]
image[y+1,x+1,0]+=1/16*error[2]
image=np.clip(image,0,255).astype(np.uint8)
cv2.imwrite('output.bmp',image)
Gaussian smoothing the output image is as follows. It is close to the input image.