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

Мы пытаемся сделать следующее в 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-что-то, что берет изображение и возвращает изображение с прозрачным фоном.

Ответ 1

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

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

Ответ 2

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

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 и Я не так сильно улучшаюсь, но разница видна.

Ответ 3

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

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

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

Ответ 4

Попробуйте применить подход Mark Ransom с некоторой помощью генерации маски belisarius:

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

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

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

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

Ответ 5

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

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

Ответ 6

Я совершенно новичок в обработке изображений, но вот что я получаю после некоторых игр с новыми функциями обработки морфологических изображений версии 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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

Подумайте об этом так:

Скажем, что R, G, B равны 0.0-1.0, а затем обозначают белый как одно число как R + G + B = 1.0 * 3 = 3.0.

Взятие немного каждого цвета делает его немного "не совсем белым", но взятие немного всего 3 делает его намного больше, чем немного от любого. Скажем, что вы разрешаете 10% -ное сокращение на любом одном канале: 1.0 *.10 =.1, Теперь распространите эту потерю по всем трем и привяжите ее между 0 и 1 для альфа-канала, если она меньше .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}]

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