Видаліть білий фон із зображення та зробіть його прозорим


82

Ми намагаємося зробити наступне в Mathematica - RMagick видаляє білий фон із зображення та робить його прозорим .

Але на реальних фотографіях це виглядає паршиво (як би ореол навколо зображення).

Ось що ми спробували до цього часу:

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

Ось приклад того, що це робить.

Оригінальне зображення:

оригінальне зображення

Зображення з білим тлом замінено на відсутність (або, для демонстрації тут, рожевим фоном):

зображення з прозорим фоном - насправді тут рожевий фон, щоб зробити проблему ореолу очевидною

Будь-які ідеї для позбавлення від цього ореолу? Налаштовуючи такі речі, як LevelPenalty, я можу змусити ореол зникнути лише за рахунок втрати частини зображення.

EDIT: Тож я можу порівняти рішення щодо нагород, будь ласка, структуруйте своє рішення, як зазначено вище, а саме автономну функцію з іменем unnground-something, яка робить зображення і повертає зображення з прозорим фоном.


1
Щиро дякуємо за допомогу поки що всім! Велика винагорода приходить на це, як тільки stackoverflow дозволяє мені додати одну. І відповідно до духу stackoverflow, сформульованого засновниками, ви повинні сміливо красти один одного, щоб зробити свою відповідь остаточною!
dreeves

3
Спочатку 500 нагород, а потім "Я закликаю вас усіх позиково позичати один одного, щоб покращити це, якщо це можливо!" - ти хочеш собачий бій, правда?
Mr.Wizard

@ Mr.Wizard, :) Однак я не вигадую цього, що засновники (Джефф та Джоел) з самого початку говорили, що це заохочується. Ідея полягає в тому, щоб найкраща відповідь була справді повною та остаточною. (І, очевидно, у мене також є приховані мотиви і в цьому випадку!)
dreeves

2
Для надто допитливих - це робоча станція комп’ютера IKEA "FREDRIK": ikea.com/us/en/catalog/products/60111123
Арноуд Базінг,

1
@dreeves, я використовував tineye.com .
Арноуд Базінг,

Відповіді:


45

Можливо, залежно від якості краю, який вам потрібен:

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]

введіть тут опис зображення

Редагувати

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

введіть тут опис зображення

Натисніть, щоб збільшити

Редагувати 2

Просто для того, щоб отримати уявлення про масштаби ореолу та недосконалості фону на зображенні:

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]

введіть тут опис зображення


На жаль, на моїй машині ваш код не дає абсолютно однакової якості результату. Чи було img зображення 500x500, як розміщено у питанні? Якщо так, можливо, у mac / windows ...
Маттіас Одізіо

@Matthias Так, img є копією / вставкою з оригіналу. Mma 8.01 на вікнах.
Доктор Белісаріус,

О ... можливо, оптимізатор видає інший результат завдяки крихітним арифметичним шумам. У будь-якому випадку, я радий, що він працює добре для вас, використовуючи цей набір параметрів.
Маттіас Одізіо

Здається, це не працює. Це просто розмиває краї.
user541686

48

Ця функція реалізує зворотну суміш, описану Марком Ренсомом, для додаткового невеликого, але помітного поліпшення:

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

Це функція видалення фону. thresholdПараметр використовується для початкового бинаризации зображення, minSizeCorrectionдля тонкої настройки граничного розміру дрібних компонентів сміття , щоб бути видалено після бинаризации.

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]
  ]

Тестування функції:

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]

Зразок

Коротке пояснення того, як це працює:

  1. Виберіть свій улюблений метод бінаріації, який дає відносно точні гострі краї

  2. Нанесіть його на масштабоване зображення, а потім зменште отримане maskдо початкового розміру. Це дає нам згладжування. Більша частина роботи виконана.

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

  4. Тепер у нас є оцінка альфа-каналу, щоб ми могли зробити зворотне поєднання.

Кроки 3 та 4 не настільки покращуються, але різниця помітна.


@belisarius справа не в англійській мові, я знаю, що моє ім'я виглядає дуже незвично для більшості :-)
Szabolcs

Виглядає як гарний стандарт. Угорське прізвище для мене :)
Доктор Белісаріус,

@belisarius Насправді це прізвище, а точніше дане ім’я, оскільки в угорській мові прізвище стоїть першим, а дане ім’я прізвищем.
Сабольч

2
Тінь справи все ще присутня на 2-му малюнку у вигляді сіруватої смужки внизу ...
Sjoerd C. de Vries

@ SjoerdC.deVries Це правда, але я думаю, що для цього завдання воно повинно бути таким ... немає можливості сказати, що це тінь, а не частина об'єкта. Більшість зображень на Amazon або мали тіні, або були нудно тривіальними, тому я пішов із цим.
Сабольч

22

Я буду говорити загально, а не конкретно стосовно Математики. Я поняття не маю, важкі ці тривіальні операції.

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

Після отримання альфа-значень потрібно зробити зворотну суміш, щоб отримати належний колір. Коли зображення відображається на фоні, воно змішується відповідно до значення альфа за допомогою формули, c = bc*(1-a)+fc*aде bc- колір тла та fcколір переднього плану. У вашому випадку фон білий (255255255) та колір переднього плану невідомість, тому ми перевернемо формулу fc = (c - bc*(1-a))/a. Коли a=0формула вимагає ділення на нуль, але колір все одно не має значення, тому просто використовуйте чорний або білий.


3
Чудова відповідь. Оцінка альфа насправді є цілим напрямком досліджень, наприклад, ai.stanford.edu/~ruzon/alpha
mpenkov

2
Погоджено, чудова відповідь; дякую Марк! Щодо баунти (коли stackoverflow дозволяє мені додати його), хоча я планую піти з тим, яке повністю реалізоване рішення виглядає найкраще. Поки що у Белісарія, я думаю.
dreeves

11

Ось спробу реалізувати підхід Марка Ренсома, за деякої допомоги від генерації масок Белізарія:

Знайдіть межу об'єкта:

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]

край фігури

Встановіть значення альфа:

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]

рожевий фон

Зверніть увагу, як деякі краї мають білу пухнастість? Порівняйте це з червоним контуром на першому зображенні. Нам потрібен кращий детектор країв. Збільшення кількості ерозії допомагає при розмитті, але тоді інші сторони стають занадто прозорими, тому існує компроміс щодо ширини крайньої маски. Це досить добре, хоча, враховуючи відсутність розмиття як таке.

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


Мда, для мене img2 виглядає краще (див. Нижню частину поверхні таблиці), ніж img3. Можливо, зворотне поєднання кольорів непотрібне?
JxB

10

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

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


9

Я абсолютно новачок у обробці зображень, але ось що я отримую після певної гри з новими морфологічними функціями обробки зображень версії 8:

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

зображення


3
Думаю, дрів намагається позбутися цих нерівних ліній по краях.
Доктор Белісарій,

1
Правда, це робить хорошу роботу, щоб зменшити цей ореол, але нерівність може перервати справу. @belisarius, ваша версія виглядає досить дивовижно!
dreeves

@dreeves Я думаю, що краї можна покращити (у моїй версії), використовуючи перетворення відстані після розмиття, але це вже зазначив містер Віз, тому я залишаю експеримент йому.
Доктор Белісарій

Що робить Method -> "Convex"? Це не задокументовано.
Саболч

Вибачте! Я розумію, що я переплутав MorphologicalComponents та MorphologicalBinarize, які насправді не пов’язані між собою функціями!
Сабольч

6

Я рекомендую використовувати для цього Photoshop і зберігати як PNG.


5
Хороший момент, але який алгоритм використовує Photoshop, щоб зробити це так добре? (І звичайно, ми хочемо це автоматизувати, а не клацати чарівною паличкою у фотошопі для кожного зображення.)
dreeves

3
До речі, я думаю, що це корисно відзначити (я міг би бути таким великим ботаніком Mathematica, що фотошоп міг і не спасти мені на думку!). І виявляється, це навіть можна писати у Photoshop, тому це може бути навіть найкращою з можливих відповідей у ​​цьому сенсі, якщо Photoshop робить щось справді розумне, чого не можна дублювати за допомогою невеликої програми математики.
dreeves

5
Існує причина, по якій Adobe може стягувати 500 програм за їх програмне забезпечення ;-).
Тимо

7
Можливо, ви можете опублікувати версію зображення, згенеровану сценарієм PhotoShop (без втручання вручну :-) для довідки - ми знали б, що нам потрібно перемогти ...
кормульйон

5

Можливі кроки, які ви могли б зробити:

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

Добрі думки; Дякую! Хотілося б отримати для цього загальний код. Ми, ймовірно, зробимо велику винагороду за кілька днів (коли це дозволить stackoverflow), якщо ви захочете повернутися тоді. Насправді, я цим зобов'язуюсь це зробити, якщо це заклик зануритися. :)
dreeves

@dreeves Звучить мені добре; Зараз у мене немає часу, але я спробую до цього повернутися.
Mr.Wizard

3

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

Подумайте про це так:

Скажімо, 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}]

Єдина небезпека (або користь?), Яку ви маєте в цьому, полягає в тому, що це не хвилює білих, які насправді є частиною фотографії. Він видаляє всі білі. Так що якщо у вас є зображення білого автомобіля, в підсумку на ньому з’являться прозорі плями. Але на вашому прикладі це здається бажаним ефектом.


Я думаю, що ідея ChanVeseBinarize полягає в тому, щоб бути розумним щодо цього і не робити білі пікселі прозорими, якщо вони не є частиною більшої зони білого, тобто, дуже ймовірно, будуть частиною фону.
dreeves

Проблема з "більшою площею" полягає в тому, що це може бути важливим, тоді як невелика площа може бути неважливою. На білому автомобілі важлива буде вся сторона, але вона буде позначена як велика пляма білого кольору. Простір між двома людьми на білому тлі був би невеликий і зі складними краями, але для цього потрібно йти. Вам доведеться мати ШІ в стилі Больцмана, щоб він розпізнавав загальні форми і перевіряв, чи є білий простір чи частина об’єкта, але ми ще не там.
Грегорі Клоппер

1
Ви також можете зробити 2 зображення з дещо різних ракурсів, а потім скористатися вирахуванням розмірності зі стереозображення, щоб з’ясувати, які пікселі є фоновими на основі місця оклюзії.
Грегорі Клоппер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.