recortar - Eliminar el fondo blanco de una imagen y hacerlo transparente
quitar fondo blanco a una imagen online gratis (9)
Estamos tratando de hacer lo siguiente en Mathematica:
RMagick elimina el fondo blanco de la imagen y la hace transparente
Pero con las fotos reales termina pésimo (como tener un halo alrededor de la imagen).
Esto es lo que hemos intentado 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 rosado):
Alguna idea para deshacerse de ese halo? Pellizcando cosas como LevelPenalty, solo puedo conseguir que el halo se vaya a expensas de perder parte de la imagen.
EDITAR: para que pueda comparar soluciones para el bounty, por favor, estructure su solución como se indica arriba, es decir, una función autónoma llamada sin tierra, algo que toma una imagen y devuelve una imagen con fondo transparente. Muchas gracias a todos!
Aquí hay un intento de implementar el enfoque de Mark Ransom, con alguna ayuda de la generación de máscaras de belisarius:
Ubique 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 inversa:
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]
Observe cómo algunos de los bordes tienen pelusa blanca? Compare eso con el contorno rojo en la primera imagen. Necesitamos un mejor detector de bordes. Aumentar 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 hay 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.
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 parámetro de threshold
se utiliza para la binarización inicial de la imagen, minSizeCorrection
es para ajustar el límite de tamaño de los componentes pequeños de 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 binariaztion favorito que produce bordes afilados relativamente precisos
Aplíquelo a una imagen en escala ascendente, luego reduzca la escala de la
mask
obtenida al tamaño original. Esto nos da antialiasing. La mayor parte del trabajo está hecho.Para una pequeña mejora, mezcle la imagen en 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. El canal alfa correspondiente a estas operaciones se calcula (la expresión algo misteriosa deImageMultiply/Add
).Ahora tenemos una estimación del canal alfa para que podamos hacer una fusión inversa.
Los pasos 3 y 4 no mejoran tanto, pero la diferencia es visible.
Hablaré genéricamente, no específicamente en referencia a Mathematica. No tengo idea 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á 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 apropiada: si está más cerca del fondo, es alfa baja, y si está más cerca del límite más oscuro, entonces es un alfa alto Después de eso, puede hacer ajustes en función de los valores alfa circundantes: cuanto más un píxel esté rodeado de transparencia, más probabilidades tendrá de ser transparente.
Una vez que tenga los valores alfa, debe realizar una fusión inversa para obtener el color adecuado. Cuando una imagen se muestra sobre un fondo, se mezcla de acuerdo con el valor alfa usando la fórmula c = bc*(1-a)+fc*a
donde bc
es el color de fondo y fc
es el color de primer plano. En su caso, el fondo es blanco (255,255,255) y el color de primer plano es el desconocido, por lo que invertimos la fórmula: fc = (c - bc*(1-a))/a
. Cuando a=0
la fórmula requiere una división por cero, pero el color no importa de todos modos, así que solo use negro o blanco.
Posibles pasos que podrías tomar:
- dilata la máscara
- desenfocarlo
- usando la máscara, establece la transparencia por distancia del blanco
- usando la máscara, ajuste la saturación de modo que los colores anteriormente más blancos estén más saturados.
Recomiendo usar Photoshop para esto y guardar como PNG.
Simplemente jugando como un 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]
Simplemente reemplace cualquier píxel que esté "casi cerca del blanco" con un píxel del mismo color RGB y un gradiente Sigmoid en el canal de transparencia. Puede aplicar la transición lineal de sólido a transparente, pero Sinusoid, Sigmoid o Tanh se ven más naturales, dependiendo de la nitidez del borde que esté buscando, se alejan 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, entonces, representamos blanco como un solo número como R + G + B = 1.0 * 3 = 3.0.
Sacar un poco de cada color lo hace un poco "grisáceo", pero tomar un poco de los 3 es tomarlo mucho más que un poco de uno. Supongamos que permite una reducción del 10% en cualquier canal: 1.0 * .10 = .1, distribuya esta pérdida entre los tres y aúnela entre 0 y 1 para el canal alfa, si es menor que .1, de forma 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 tiene en esto, es que esto no se preocupa por los blancos que en realidad SON parte de la foto. Elimina todos los blancos. De modo que si tiene una imagen del automóvil blanco, terminará teniendo parches transparentes en ella. Pero de su ejemplo, ese parece ser un efecto deseado.
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]]
Tal vez, dependiendo de la calidad de borde que necesita:
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
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 de la extensión del halo y las imperfecciones de 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]