Necesidad de hacer una versión cómica de dibujos animados de una imagen con Python y OpenCV

Estoy tratando de hacer una función que haga que cualquier imagen se vea como una tira cómica de cartooney. Aquí está mi código hasta ahora:

import numpy import cv2 __author__ = "Michael Beyeler" __license__ = "GNU GPL 3.0 or later" class Cartoonizer: def __init__(self): self.numDownSamples = 1 self.numBilateralFilters = 7 def render(self, img_rgb): # downsample image using Gaussian pyramid img_color = img_rgb for _ in range(self.numDownSamples): img_color = cv2.pyrDown(img_color) # repeatedly apply small bilateral filter instead of applying # one large filter for _ in range(self.numBilateralFilters): img_color = cv2.bilateralFilter(img_color, 9, 9, 7) # upsample image to original size for _ in range(self.numDownSamples): img_color = cv2.pyrUp(img_color) # convert to grayscale and apply bilateral blur img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) for _ in range(self.numBilateralFilters): img_gray_blur = cv2.bilateralFilter(img_gray, 9, 9, 7) # detect and enhance edges img_edge = cv2.adaptiveThreshold(img_gray_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 5) # convert back to color so that it can be bit-ANDed with color image img_edge = cv2.cvtColor(img_edge, cv2.COLOR_GRAY2RGB) #Ensure that img_color and img_edge are the same size, otherwise bitwise_and will not work height = min(len(img_color), len(img_edge)) width = min(len(img_color[0]), len(img_edge[0])) img_color = img_color[0:height, 0:width] img_edge = img_edge[0:height, 0:width] return cv2.bitwise_and(img_color, img_edge) 

Lo tomé de aquí, conservé mi licencia y la modifiqué ligeramente: http://www.askaswiss.com/2016/01/how-to-create-cartoon-effect-opencv-python.html

Lo que tengo originalmente es esto: Imagen original

Esto es lo que mi script produce: Salida de mi script

Y esto es lo que necesito: La imagen que intento replicar.

Lo que he notado hasta ahora es:

  1. Mi código para difuminar la imagen tiene demasiados colores, necesito una transición menos suave de los colores claros a los oscuros.
  2. La imagen de destino tiene bordes suaves, que son líneas, cuando mi código crea mucho ruido (puntos negros “solitarios”) y divide líneas. He intentado cambiar algunos parámetros, he añadido un par de filtros aleatorios, pero realmente no tengo ni idea de qué hacer a continuación.

Cualquier ayuda es muy apreciada.

No tengo el código Python, está escrito en MATLAB (usando DIPimage 3 ). Pero creo que podría obtener algunas ideas de ello. Esto es lo que hace:

1- s es una versión ligeramente suavizada de la imagen de entrada img , y se usará para crear las líneas. Para el alisado utilizo una difusión trivial no lineal. Esto conserva (incluso realza) los bordes. Es similar al filtro bilateral.

2- Usando s , primero aplico el operador laplaciano (este usa derivados gaussianos, el parámetro 1.5 es el sigma para el gaussiano). Esto es similar a una diferencia de gaussianos. Su llamada cv2.adaptiveThreshold hace el equivalente a gaussf(img,2)-img . Mi laplaciano hace algo similar a gaussf(img,2)-gaussf(img,1) (una diferencia de gaussianos). Es decir, hay algo menos de detalle en esta salida que en la de cv2.adaptiveThreshold .

3- El laplaciano se aplicó a una imagen en color, por lo que produce una salida de color. Convierto esto a un valor de gris tomando el elemento de color máximo. Luego recorte y extiendo esto, esencialmente haciendo la otra mitad de lo que hace cv2.adaptiveThreshold , excepto que la salida no es binaria, sino que sigue siendo un valor de gris. Es decir, hay líneas más oscuras y claras. Más importante aún, las líneas no parecen pixeladas porque hay un cambio gradual de la oscuridad a la luz en los bordes de cada línea. Tuve que ajustar estos parámetros un poco para obtener un buen resultado. Ahora es una imagen que es 1, donde no habrá líneas, y más baja (más oscura) donde habrá líneas.

4- Ahora aplico un camino de cierre a l . Este es un operador morfológico bastante especializado, puede que tenga que hacer un esfuerzo para encontrar una implementación. Elimina las líneas oscuras en l que son muy cortas. Esto básicamente elimina el problema que tenías con los puntos. Estoy seguro de que hay otras maneras de resolver el problema de puntos.

5- Para poner color entre las líneas queremos suavizar y cuantificar la imagen original. Sobrescribo s con una versión más fuerte de img , a la que aplico la cuantificación de color utilizando un algoritmo que describí en otra respuesta . Esta cuantización deja solo 10 colores distintos. Aplico un poco de suavizado para evitar la transición demasiado nítida entre los colores.

6- Finalmente, la imagen de color s las líneas de imagen l se multiplican juntas. Donde estaba 1, nada cambia. Donde l tenía valores más bajos, s se volverá más oscuro. Esto efectivamente dibuja las líneas en la imagen. Es un efecto más agradable que el bitwise y el operador que usa.

 img = readim('https://i.stack.imgur.com/Zq1f4.jpg'); % Simplify using non-linear diffusion s = colordiffusion(img,2); % Find lines -- the positive response of the Laplace operator l = laplace(s,1.5); l = tensorfun('immax',l); l = stretch(clip(l,0.4,4),0,100,1,0); % Remove short lines l = pathopening(l,8,'closing','constrained'); % Simplify color image using diffusion and k-means clustering s = colordiffusion(gaussf(img),5); s = quantize(s,10,'minvariance'); s = gaussf(s); % Paint lines on simplified image out = s * l; % Color diffusion: function out = colordiffusion(out,iterations) sigma = 0.8; K = 10; for ii = 1:iterations grey = colorspace(out,'grey'); nabla_out = gradientvector(grey,sigma); D = exp(-(norm(nabla_out)/K)^2); out = out + divergence(D * nabla_out); end end 

salida de código