it-swarm.com.ru

Удалите белый фон с изображения и сделайте его прозрачным

Мы пытаемся сделать следующее в Mathematica:
RMagick удаляет белый фон с изображения и делает его прозрачным

Но с реальными фотографиями это выглядит паршиво (как ореол вокруг изображения).

Вот что мы уже пробовали:

unground0[img_] := With[{mask = ChanVeseBinarize[img, TargetColor->{1.,1.,1.}]},
  Rasterize[SetAlphaChannel[img, ImageApply[1-#&, mask]], Background->None]]]

Вот пример того, что это делает.

Исходное изображение:

original image

Изображение с белым фоном заменено без фона (или, для демонстрации, здесь розовый фон):

image with transparent background -- actually a pink background here, to make the halo problem obvious

Есть идеи, как избавиться от этого ореола? Настраивая такие вещи, как LevelPenalty, я могу заставить гало исчезнуть только за счет потери части изображения.

Правка: чтобы я мог сравнить решения для щедрости, пожалуйста, структурируйте ваше решение, как указано выше, а именно автономную функцию с именем unground-что-то, что берет изображение и возвращает изображение с прозрачным фоном. Большое спасибо всем!

80
dreeves

Возможно, в зависимости от качества Edge вам нужно:

img = [email protected]"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]

enter image description here

Редактировать

Stealing a bit from @Szabolcs

img2 = [email protected]"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 -> [email protected]]
GraphicsGrid[{{[email protected], [email protected], [email protected]}}]

enter image description here

Нажмите, чтобы увеличить

Изменить 2

Просто чтобы понять степень ореола и несовершенства фона на изображении:

img = [email protected]"http://i.stack.imgur.com/k7E1F.png";
Join[{img}, MapThread[Binarize, {ColorSeparate[img, "HSB"], {.01, .01, .99}}]]

enter image description here

[email protected][EntropyFilter[img, 1] // ImageAdjust, [email protected]]

enter image description here

44
Dr. belisarius

Эта функция реализует обратную смесь, описанную Марком Рэнсомом, для дополнительного небольшого, но видимого улучшения:

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},

  [email protected]
   Image[Quiet[(c - bc (1 - a))/a, {Power::infy, 
       Infinity::indet}] /. {ComplexInfinity -> 0, Indeterminate -> 0}]
  ]

Это функция удаления фона. Параметр threshold используется для начальной бинаризации изображения, а minSizeCorrection - для настройки предела размера небольших нежелательных компонентов, которые должны быть удалены после бинаризации.

removeWhiteBackground[img_, threshold_: 0.05, minSizeCorrection_: 1] :=
  Module[
  {dim, bigmask, mask, edgemask, alpha},
  dim = ImageDimensions[img];
  bigmask = 
   DeleteSmallComponents[
    [email protected]
     MorphologicalBinarize[[email protected][img, 4 dim], threshold], 
    Round[minSizeCorrection Times @@ dim/5]];
  mask = [email protected]
    ImageResize[ColorConvert[bigmask, "GrayScale"], dim];
  edgemask = 
   ImageResize[
    [email protected]@Dilation[EdgeDetect[bigmask, 2], 6],
     dim];
  alpha = 
   ImageAdd[
    ImageSubtract[
     ImageMultiply[[email protected][img, "GrayScale"], 
      edgemask], ImageMultiply[mask, edgemask]], mask];
  SetAlphaChannel[reverseBlend[img, alpha, 1], alpha]
  ]

Тестирование функции:

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]

Sample

Краткое объяснение того, как это работает:

  1. Выберите ваш любимый метод бинаризации, который дает относительно точные острые края

  2. Примените его к увеличенному изображению, затем уменьшите полученное mask до исходного размера. Это дает нам сглаживание. Большая часть работы выполнена.

  3. Для небольшого улучшения смешайте изображение с фоном, используя яркость его негатива как альфа, затем смешайте полученное изображение с оригиналом в тонкой области по краям (edgemask), чтобы уменьшить видимость белых пикселей по краям. Рассчитывается альфа-канал, соответствующий этим операциям (несколько загадочное выражение ImageMultiply/Add).

  4. Теперь у нас есть оценка альфа-канала, поэтому мы можем сделать обратное смешивание.

Шаги 3 и 4 не так сильно улучшаются, но разница видна.

48
Szabolcs

Я собираюсь говорить в общем, а не конкретно в отношении Mathematica. Я понятия не имею, являются ли эти операции сложными или тривиальными.

Первым шагом является оценка уровня альфа (прозрачности) для пикселей на краю изображения. Прямо сейчас вы используете строгий порог, поэтому альфа либо 0% полностью прозрачна, либо 100% полностью непрозрачна. Вы должны определить диапазон между общим белым фоном и цветами, которые являются неотъемлемой частью изображения, и установить соответствующую пропорцию - если он ближе по цвету к фону, то это низкий альфа, и если он ближе к более темному срезу, то это высокая альфа. После этого вы можете вносить коррективы на основе окружающих альфа-значений - чем больше пиксель окружен прозрачностью, тем больше вероятность, что он сам будет прозрачным.

Когда у вас есть альфа-значения, вам нужно сделать обратное смешивание, чтобы получить правильный цвет. Когда изображение отображается поверх фона, оно смешивается в соответствии с альфа-значением, используя формулу c = bc*(1-a)+fc*a, где bc - цвет фона, а fc - цвет переднего плана. В вашем случае фон белый (255 255 255), а цвет переднего плана неизвестен, поэтому мы обращаемся к формуле: fc = (c - bc*(1-a))/a. Когда a=0 формула требует деления на ноль, но цвет все равно не имеет значения, поэтому просто используйте черный или белый.

22
Mark Ransom

Вот попытка реализации подхода Марка Рэнсома с некоторой помощью генерации маски Велисария:

Найдите границу объекта:

img1 = SetAlphaChannel[img, 1];
erosionamount=2;
mb = [email protected][img, TargetColor -> {1., 1., 1}, 
      "LengthPenalty" -> 10];
Edge = ImageSubtract[Dilation[mb, 2], Erosion[mb, erosionamount]];

ImageApply[{1, 0, 0} &, img, Masking ->Edge]

figure Edge

Установите альфа-значения:

edgealpha = ImageMultiply[ImageFilter[(1 - Mean[Flatten[#]]^5) &, 
   ColorConvert[img, "GrayScale"], 2, Masking -> Edge], Edge];
imagealpha = ImageAdd[edgealpha, Erosion[mb, erosionamount]];
img2 = SetAlphaChannel[img, imagealpha];

Обратное сочетание цветов:

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]

pink background

Заметьте, как на некоторых краях есть белый пух? Сравните это с красным контуром на первом изображении. Нам нужен лучший детектор Edge. Увеличение количества эрозии помогает с пушком, но тогда другие стороны становятся слишком прозрачными, так что есть компромисс по ширине маски края. Это довольно хорошо, хотя, учитывая, что по сути нет операции размытия.

Было бы поучительно запустить алгоритм на различных изображениях, чтобы проверить его надежность, чтобы увидеть, насколько он автоматический.

11
JxB

Просто играть как новичок - удивительно, сколько инструментов доступно.

b = ColorNegate[
    GaussianFilter[MorphologicalBinarize[i, {0.96, 0.999}], 6]];
c = SetAlphaChannel[i, b];
Show[Graphics[Rectangle[], Background -> Orange, 
     PlotRangePadding -> None], c]

10
cormullion

Я совершенно новичок в обработке изображений, но вот что я получаю после игры с новыми морфологическими функциями обработки изображений версии 8:

mask = DeleteSmallComponents[
   [email protected]
    Image[MorphologicalComponents[[email protected], .062, 
      Method -> "Convex"], "Bit"], 10000];
Show[Graphics[Rectangle[], Background -> Red, 
  PlotRangePadding -> None], SetAlphaChannel[img, [email protected]]]

image

9
Alexey Popkov

Я рекомендую использовать Photoshop для этого и сохранить как PNG.

6
angelfilm entertainment

Возможные шаги, которые вы можете предпринять:

  • расширить маску
  • размыть это
  • используя маску, установите прозрачность на расстоянии от белого
  • используя маску, отрегулируйте насыщенность так, чтобы ранее более белые цвета были более насыщенными.
5
Mr.Wizard

Просто замените любой пиксель, "почти близкий к белому", пикселем того же цвета RGB и сигмоидальным градиентом на канале прозрачности. Вы можете применить линейный переход от твердого к прозрачному, но синусоида, сигмоид или танх выглядят более естественно, в зависимости от резкости искомого края, они быстро уходят от носителя к твердому или прозрачному, но не в пошаговом/двоичном виде манера, которая есть у вас сейчас.

Думайте об этом так:

Допустим, каждый R, G, B равен 0,0-1,0, а затем представим белый цвет одним числом: R + G + B = 1,0 * 3 = 3,0.

Извлечение небольшого количества каждого цвета делает его немного "не совсем белым", но если брать немного из всех трех, это отнимает намного больше, чем немного. Допустим, вы разрешаете 10% -ное снижение на любом одном канале: 1,0 * .10 = .1. Теперь распределите эту потерю по всем трем и свяжите ее между 0 и 1 для альфа-канала, если она меньше, чем 0,1, так, чтобы ( потеря = 0,9) => 0 и (потеря = 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];

Для справки:

maxLoss = .1;
Plot[{ 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]),
       Log[maxLoss]/Log[loss],
       loss/maxLoss
     }, {loss, 0, maxLoss}]

Единственная опасность (или выгода?) В этом заключается в том, что это не имеет значения для белых, которые на самом деле являются частью фотографии. Удаляет все белые. Так что, если у вас есть изображение белого автомобиля, в нем будут прозрачные пятна. Но из вашего примера это, кажется, желаемый эффект.

3
Gregory Klopper