Estamos tratando de hacer lo siguiente en Mathematica: RMagick eliminar el fondo blanco de la imagen y hacerla transparente .
Pero con fotos reales termina luciendo horrible (como si tuviera un halo alrededor de la imagen).
Esto es lo que hemos probado hasta ahora:
unground0[img_] := With[{mask = ChanVeseBinarize[img, TargetColor->{1.,1.,1.}]},
Rasterize[SetAlphaChannel[img, ImageApply[1-#&, mask]], Background->None]]]
Aquí hay un ejemplo de lo que hace.
Imagen original:
Imagen con el fondo blanco reemplazado sin fondo (o, para fines de demostración aquí, un fondo rosa):
¿Alguna idea para deshacerse de ese halo? Al ajustar cosas como LevelPenalty, solo puedo hacer que el halo desaparezca a expensas de perder parte de la imagen.
EDITAR: Para que pueda comparar soluciones para la recompensa, por favor, estructure su solución como la anterior, es decir, una función autónoma llamada unground, algo que toma una imagen y devuelve una imagen con fondo transparente.
Respuestas:
Quizás, dependiendo de la calidad del borde que necesite:
img = Import@"http://i.stack.imgur.com/k7E1F.png"; mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10] mask1 = Blur[Erosion[ColorNegate[mask], 2], 5] Rasterize[SetAlphaChannel[img, mask1], Background -> None]
Editar
Stealing a bit from @Szabolcs
img2 = Import@"http://i.stack.imgur.com/k7E1F.png"; (*key point:scale up image to smooth the edges*) img = ImageResize[img2, 4 ImageDimensions[img2]]; mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10]; mask1 = Blur[Erosion[ColorNegate[mask], 8], 10]; f[col_] := Rasterize[SetAlphaChannel[img, mask1], Background -> col, ImageSize -> ImageDimensions@img2] GraphicsGrid[{{f@Red, f@Blue, f@Green}}]
Click para agrandar
Editar 2
Solo para tener una idea del alcance del halo y las imperfecciones del fondo en la imagen:
img = Import@"http://i.stack.imgur.com/k7E1F.png"; Join[{img}, MapThread[Binarize, {ColorSeparate[img, "HSB"], {.01, .01, .99}}]]
ColorNegate@ImageAdd[EntropyFilter[img, 1] // ImageAdjust, ColorNegate@img]
fuente
Esta función implementa la combinación inversa descrita por Mark Ransom, para una mejora adicional pequeña pero visible:
reverseBlend[img_Image, alpha_Image, bgcolor_] := With[ {c = ImageData[img], a = ImageData[alpha] + 0.0001, (* this is to minimize ComplexInfinitys and considerably improve performance *) bc = bgcolor}, ImageClip@ Image[Quiet[(c - bc (1 - a))/a, {Power::infy, Infinity::indet}] /. {ComplexInfinity -> 0, Indeterminate -> 0}] ]
Esta es la función de eliminación de fondo. El
threshold
parámetro se utiliza para la binarización inicial de la imagen, elminSizeCorrection
es para ajustar el límite de tamaño de los pequeños componentes basura que se eliminarán después de la binarización.removeWhiteBackground[img_, threshold_: 0.05, minSizeCorrection_: 1] := Module[ {dim, bigmask, mask, edgemask, alpha}, dim = ImageDimensions[img]; bigmask = DeleteSmallComponents[ ColorNegate@ MorphologicalBinarize[ColorNegate@ImageResize[img, 4 dim], threshold], Round[minSizeCorrection Times @@ dim/5]]; mask = ColorNegate@ ImageResize[ColorConvert[bigmask, "GrayScale"], dim]; edgemask = ImageResize[ ImageAdjust@DistanceTransform@Dilation[EdgeDetect[bigmask, 2], 6], dim]; alpha = ImageAdd[ ImageSubtract[ ImageMultiply[ColorNegate@ColorConvert[img, "GrayScale"], edgemask], ImageMultiply[mask, edgemask]], mask]; SetAlphaChannel[reverseBlend[img, alpha, 1], alpha] ]
Probando la función:
img = Import["http://i.stack.imgur.com/k7E1F.png"]; background = ImageCrop[ Import["http://cdn.zmescience.com/wp-content/uploads/2011/06/\ forest2.jpg"], ImageDimensions[img]]; result = removeWhiteBackground[img] ImageCompose[background, result] Rasterize[result, Background -> Red] Rasterize[result, Background -> Black]
Breve explicación de cómo funciona:
Elija su método de binariaización favorito que produzca bordes afilados relativamente precisos
Aplíquelo a una imagen mejorada, luego reduzca la escala obtenida
mask
al tamaño original. Esto nos da antialiasing. La mayor parte del trabajo está hecho.Para una pequeña mejora, combine la imagen con el fondo usando el brillo de su negativo como alfa, luego mezcle la imagen obtenida sobre el original en una región delgada alrededor de los bordes (
edgemask
) para reducir la visibilidad de los píxeles blancos en los bordes. Se calcula el canal alfa correspondiente a estas operaciones (laImageMultiply/Add
expresión algo críptica ).Ahora tenemos una estimación del canal alfa para que podamos hacer una mezcla inversa.
Los pasos 3 y 4 no mejoran mucho, pero la diferencia es visible.
fuente
Voy a hablar de manera genérica, no específicamente en referencia a Mathematica. No tengo idea de si estas operaciones son difíciles o triviales.
El primer paso es estimar un nivel alfa (transparencia) para los píxeles en el borde de la imagen. En este momento, estás usando un umbral estricto, por lo que el alfa es 0% totalmente transparente o 100% totalmente opaco. Debe definir un rango entre el blanco total del fondo y los colores que son indiscutiblemente parte de la imagen, y establecer una proporción adecuada: si el color está más cerca del fondo, es alfa bajo, y si está más cerca del corte más oscuro, entonces es un alfa alto. Después de eso, puede realizar ajustes basados en los valores alfa circundantes: cuanto más un píxel esté rodeado de transparencia, más probable será que sea transparente.
Una vez que tenga los valores alfa, debe hacer una mezcla inversa para obtener el color adecuado. Cuando una imagen se muestra sobre un fondo, se combina de acuerdo con el valor alfa utilizando la fórmula
c = bc*(1-a)+fc*a
dondebc
es el color de fondo yfc
el color de primer plano. En su caso, el fondo es blanco (255,255,255) y el color de primer plano es lo desconocido, por lo que invertimos la fórmula:fc = (c - bc*(1-a))/a
. Cuandoa=0
la fórmula pide una división por cero, pero el color no importa de todos modos, así que use blanco o negro.fuente
Aquí hay un intento de implementar el enfoque de Mark Ransom, con la ayuda de la generación de máscaras de belisarius:
Localice el límite del objeto:
img1 = SetAlphaChannel[img, 1]; erosionamount=2; mb = ColorNegate@ChanVeseBinarize[img, TargetColor -> {1., 1., 1}, "LengthPenalty" -> 10]; edge = ImageSubtract[Dilation[mb, 2], Erosion[mb, erosionamount]]; ImageApply[{1, 0, 0} &, img, Masking ->edge]
Establezca los valores alfa:
edgealpha = ImageMultiply[ImageFilter[(1 - Mean[Flatten[#]]^5) &, ColorConvert[img, "GrayScale"], 2, Masking -> edge], edge]; imagealpha = ImageAdd[edgealpha, Erosion[mb, erosionamount]]; img2 = SetAlphaChannel[img, imagealpha];
Mezcla de color inverso:
img3 = ImageApply[Module[{c, \[Alpha], bc, fc}, bc = {1, 1, 1}; c = {#[[1]], #[[2]], #[[3]]}; \[Alpha] = #[[4]]; If[\[Alpha] > 0, Flatten[{(c - bc (1 - \[Alpha]))/\[Alpha], \[Alpha]}], {0., 0., 0., 0}]] &, img2]; Show[img3, Background -> Pink]
¿Observa cómo algunos de los bordes tienen pelusa blanca? Compare eso con el contorno rojo de la primera imagen. Necesitamos un mejor detector de bordes. El aumento de la cantidad de erosión ayuda con la pelusa, pero luego los otros lados se vuelven demasiado transparentes, por lo que hay una compensación en el ancho de la máscara de borde. Sin embargo, es bastante bueno, considerando que no existe una operación de desenfoque per se.
Sería instructivo ejecutar el algoritmo en una variedad de imágenes para probar su robustez, para ver qué tan automático es.
fuente
Simplemente jugando como principiante, es increíble la cantidad de herramientas disponibles.
b = ColorNegate[ GaussianFilter[MorphologicalBinarize[i, {0.96, 0.999}], 6]]; c = SetAlphaChannel[i, b]; Show[Graphics[Rectangle[], Background -> Orange, PlotRangePadding -> None], c]
fuente
Soy completamente nuevo en el procesamiento de imágenes, pero esto es lo que obtengo después de jugar con las nuevas funciones de procesamiento de imágenes morfológicas de la versión 8:
mask = DeleteSmallComponents[ ColorNegate@ Image[MorphologicalComponents[ColorNegate@img, .062, Method -> "Convex"], "Bit"], 10000]; Show[Graphics[Rectangle[], Background -> Red, PlotRangePadding -> None], SetAlphaChannel[img, ColorNegate@mask]]
fuente
Method -> "Convex"
? No esta documentado.Recomiendo usar Photoshop para esto y guardarlo como PNG.
fuente
Posibles pasos que podría tomar:
fuente
Simplemente reemplace cualquier píxel que esté "casi cerca del blanco" con un píxel del mismo color RGB y un degradado sigmoide en el canal de transparencia. Puede aplicar una transición lineal de sólido a transparente, pero Sinusoide, Sigmoide o Tanh se ven más naturales, dependiendo de la nitidez del borde que esté buscando, se mueven rápidamente del medio a sólido o transparente, pero no en pasos / binarios. manera, que es lo que tienes ahora.
Piénsalo de esta manera:
Digamos que R, G, B son cada uno 0.0-1.0, luego representemos el blanco como un solo número como R + G + B = 1.0 * 3 = 3.0.
Quitar un poco de cada color lo hace un poco "blanquecino", pero quitar un poco de los 3 es quitarlo mucho más que un poco de cualquiera. Digamos que permite una reducción del 10% en cualquier canal: 1.0 * .10 = .1. Ahora distribuya esta pérdida entre los tres y únala entre 0 y 1 para el canal alfa, si es menor que .1, de modo que ( pérdida = 0.9) => 0 y (pérdida = 1.0) => 1:
threshold=.10; maxLoss=1.0*threshold; loss=3.0-(R+G+B); alpha=If[loss>maxLoss,0,loss/maxLoss]; (* linear scaling is used above *) (* or use 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]) to set sigmoid alpha *) (* Log decay: Log[maxLoss]/Log[loss] (for loss and maxLoss <1, when using RGB 0-255, divide by 255 to use this one *) setNewPixel[R,G,B,alpha];
Para referencia:
maxLoss = .1; Plot[{ 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]), Log[maxLoss]/Log[loss], loss/maxLoss }, {loss, 0, maxLoss}]
El único peligro (¿o beneficio?) Que tienes en esto, es que a esto no le importan los blancos que en realidad SON parte de la foto. Elimina todos los blancos. De modo que si tiene una imagen de un automóvil blanco, terminará teniendo parches transparentes. Pero a partir de su ejemplo, ese parece ser un efecto deseado.
fuente