W Mathematica staramy się wykonać następujące czynności - RMagick usunąć białe tło z obrazu i uczynić go przezroczystym .
Ale w przypadku rzeczywistych zdjęć kończy się to kiepskim wyglądem (jak otoczka wokół obrazu).
Oto, czego próbowaliśmy do tej pory:
unground0[img_] := With[{mask = ChanVeseBinarize[img, TargetColor->{1.,1.,1.}]},
Rasterize[SetAlphaChannel[img, ImageApply[1-#&, mask]], Background->None]]]
Oto przykład tego, co to robi.
Oryginalny obraz:
Obraz z białym tłem zastąpionym bez tła (lub, dla celów demonstracyjnych, z różowym tłem):
Jakieś pomysły na pozbycie się tej aureoli? Poprawiając takie rzeczy jak LevelPenalty, mogę tylko usunąć aureolę kosztem utraty części obrazu.
EDYCJA: Więc mogę porównać rozwiązania dla nagrody, proszę ustrukturyzować twoje rozwiązanie jak powyżej, a mianowicie samodzielną funkcję o nazwie unground - coś, co pobiera obraz i zwraca obraz z przezroczystym tłem.
Odpowiedzi:
Być może w zależności od wymaganej jakości krawędzi:
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]
Edytować
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}}]
Kliknij, aby powiększyć
Edytuj 2
Aby zorientować się w zakresie niedoskonałości halo i tła na obrazie:
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]
źródło
Ta funkcja implementuje odwrotną mieszankę opisaną przez Mark Ransom, dla dodatkowej małej, ale widocznej poprawy:
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}] ]
To jest funkcja usuwania tła.
threshold
Parametrów służy do wstępnego binaryzację obrazu,minSizeCorrection
jest skomplikowany limit wielkości małych elementów śmieci do usunięcia po binaryzacji.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] ]
Testowanie funkcji:
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]
Krótkie wyjaśnienie, jak to działa:
Wybierz swoją ulubioną metodę binariacji, która pozwala uzyskać stosunkowo precyzyjne ostre krawędzie
Zastosuj go do obrazu przeskalowanego w górę, a następnie zmniejsz uzyskany rozmiar
mask
do oryginalnego rozmiaru. To daje nam antyaliasing. Większość pracy jest już wykonana.Aby uzyskać niewielką poprawę, zmieszaj obraz z tłem, używając jasności negatywu jako alfa, a następnie zmieszaj uzyskany obraz z oryginałem w cienkim obszarze wokół krawędzi (
edgemask
), aby zmniejszyć widoczność białych pikseli na krawędziach. Obliczany jest kanał alfa odpowiadający tym operacjom (nieco tajemniczeImageMultiply/Add
wyrażenie).Teraz mamy oszacowanie kanału alfa, więc możemy wykonać mieszanie odwrotne.
Kroki 3 i 4 nie poprawiają się tak bardzo, ale różnica jest widoczna.
źródło
Zamierzam mówić ogólnie, a nie konkretnie w odniesieniu do Mathematica. Nie mam pojęcia, czy te operacje są trudne czy trywialne.
Pierwszym krokiem jest oszacowanie poziomu alfa (przezroczystości) pikseli na krawędzi obrazu. W tej chwili używasz ścisłego progu, więc alfa jest albo całkowicie przezroczysta w 0%, albo w 100% całkowicie nieprzezroczysta. Należy zdefiniować zakres między całkowitą bielą tła a kolorami, które są bezsprzecznie częścią obrazu, i ustawić odpowiednią proporcję - jeśli jest bliżej koloru tła to niskie alfa, a jeśli jest bliżej ciemniejszego odcięcia, to jest wysoka alfa. Następnie możesz wprowadzić poprawki na podstawie otaczających wartości alfa - im bardziej piksel jest otoczony przezroczystością, tym większe prawdopodobieństwo, że sam będzie przezroczysty.
Gdy masz już wartości alfa, musisz wykonać mieszanie odwrotne, aby uzyskać właściwy kolor. Gdy obraz jest wyświetlany na tle, jest on mieszany zgodnie z wartością alfa przy użyciu wzoru,
c = bc*(1-a)+fc*a
gdziebc
jest kolorem tła ifc
kolorem pierwszego planu. W twoim przypadku tło jest białe (255255255) oraz kolor pierwszoplanowy jest nieznana, więc odwrócić wzoru:fc = (c - bc*(1-a))/a
. Kiedya=0
formuła wymaga podzielenia przez zero, ale kolor i tak nie ma znaczenia, po prostu użyj czerni lub bieli.źródło
Oto próba wdrożenia podejścia Marka Ransoma, z pewną pomocą generowania masek belizariusza:
Zlokalizuj granicę obiektu:
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]
Ustaw wartości 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];
Odwrotna mieszanka kolorów:
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]
Zauważ, że niektóre krawędzie mają biały meszek? Porównaj to z czerwonym konturem na pierwszym obrazku. Potrzebujemy lepszego detektora krawędzi. Zwiększenie wielkości erozji pomaga w usuwaniu meszku, ale wtedy inne strony stają się zbyt przezroczyste, więc istnieje kompromis w zakresie szerokości maski krawędzi. Jest jednak całkiem nieźle, biorąc pod uwagę, że nie ma operacji rozmycia jako takiej.
Pouczające byłoby uruchomienie algorytmu na różnych obrazach w celu przetestowania jego solidności i sprawdzenia, jak działa automatycznie.
źródło
Po prostu zabawa jako początkujący - to niesamowite, ile narzędzi jest dostępnych.
b = ColorNegate[ GaussianFilter[MorphologicalBinarize[i, {0.96, 0.999}], 6]]; c = SetAlphaChannel[i, b]; Show[Graphics[Rectangle[], Background -> Orange, PlotRangePadding -> None], c]
źródło
Jestem zupełnie nowy w przetwarzaniu obrazu, ale oto, co otrzymuję po zabawie z nowymi funkcjami morfologicznego przetwarzania obrazu w wersji 8:
mask = DeleteSmallComponents[ ColorNegate@ Image[MorphologicalComponents[ColorNegate@img, .062, Method -> "Convex"], "Bit"], 10000]; Show[Graphics[Rectangle[], Background -> Red, PlotRangePadding -> None], SetAlphaChannel[img, ColorNegate@mask]]
źródło
Method -> "Convex"
? To nie jest udokumentowane.Polecam do tego użyć Photoshopa i zapisać jako PNG.
źródło
Możliwe kroki, które możesz podjąć:
źródło
Po prostu zastąp każdy piksel „prawie zbliżony do białego” pikselem o tym samym kolorze RGB i sigmoidalnym gradiencie na kanale przezroczystości. Możesz zastosować liniowe przejście od bryły do przezroczystości, ale sinusoida, sigmoida lub tanh wyglądają bardziej naturalnie, w zależności od ostrości krawędzi, której szukasz, szybko odchodzą od medium do stałego lub przezroczystego, ale nie w stopniowym / binarnym sposób, który masz teraz.
Pomyśl o tym w ten sposób:
Powiedzmy, że każdy z R, G, B ma wartość 0,0-1,0, a następnie przedstawmy biały jako pojedynczą liczbę jako R + G + B = 1,0 * 3 = 3,0.
Usunięcie odrobiny każdego koloru sprawia, że jest on trochę „złamanej bieli”, ale usunięcie odrobiny wszystkich trzech kolorów jest o wiele bardziej osłabione niż trochę z któregokolwiek. Powiedzmy, że pozwalasz na redukcję o 10% na dowolnym kanale: 1,0 * .10 = 0,1, Teraz rozłóż tę stratę na wszystkie trzy i połącz ją między 0 a 1 dla kanału alfa, jeśli jest mniejsza niż 0,1, czyli ( strata = 0,9) => 0 i (strata = 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];
Na przykład:
maxLoss = .1; Plot[{ 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]), Log[maxLoss]/Log[loss], loss/maxLoss }, {loss, 0, maxLoss}]
Jedynym niebezpieczeństwem (lub korzyścią?), Które w tym tkwi, jest to, że nie obchodzą go białe, które tak naprawdę SĄ częścią zdjęcia. Usuwa wszystkie białka. Więc jeśli masz zdjęcie białego samochodu, skończy się to na przezroczystych plamach. Ale z twojego przykładu wydaje się, że jest to pożądany efekt.
źródło