画像から白い背景を削除し、透明にします


82

Mathematicaで次のことをしようとしています-RMagickは画像から白い背景を取り除き、それを透明にします。

しかし、実際の写真では、(画像の周りにハローがあるように)見栄えが悪くなります。

これまでに試したことは次のとおりです。

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

これがその機能の例です。

元の画像:

元の画像

白い背景が背景なしで置き換えられた画像(または、ここでのデモンストレーション目的では、ピンクの背景):

背景が透明な画像-ハローの問題を明確にするために、実際にはここではピンクの背景

そのハローを取り除くためのアイデアはありますか?LevelPenaltyのようなものを微調整すると、画像の一部が失われるという犠牲を払ってハローを消すことができるだけです。

編集:バウンティのソリューションを比較できるように、上記のようにソリューションを構成してください。つまり、画像を取得して背景が透明な画像を返す、ungroundという名前の自己完結型関数です。


1
みなさん、これまでの助けに感謝します!stackoverflowで追加できるとすぐに大きな恩恵があります。そして、創設者によって明確にされたスタックオーバーフローの精神に従って、あなたはあなたの答えを決定的なものにするためにお互いから自由に盗むべきです!
2011年

3
最初の500の報奨金、次に「可能であれば、それを改善するために、お互いに自由に借りることをお勧めします!」-闘犬が欲しいですよね?
Mr.Wizard 2011年

@ Mr.Wizard、:)私はそれを補っていませんが、創設者(JeffとJoel)は最初からそれが奨励されていると言っています。アイデアは、トップの答えが本当に完全で決定的なものになることです。(そして明らかに、この場合、私には
下心

2
非常に好奇心旺盛な方のために、これはIKEAの「FREDRIK」コンピューターワークステーション:ikea.com/us/en/catalog/products/60111123
Arnoud Buzing 2011年

1
@ dreeves、tineye.comを使用しました
Arnoud Buzing 2011年

回答:


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 /窓の事なら...
マティアスOdisio

@Matthiasはい、imgはオリジナルからのコピー/貼り付けです。Windows上のMma8.01。
ベリサリウス博士2011年

ああ...おそらく、オプティマイザは小さな算術ノイズのために異なる結果を生成します。とにかく、このパラメータのセットを使用してうまく機能していることをうれしく思います。
Matthias Odisio 2011年

それはうまくいくようには見えません。エッジがぼやけているだけです。
user541686 2011

48

この関数は、Mark Ransomによって記述された逆ブレンドを実装して、小さいながらも目に見える改善を追加します。

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パラメータは、画像の最初の2値化に使用されます。これはminSizeCorrection、2値化後に削除される小さなジャンクコンポーネントのサイズ制限を微調整するためのものです。

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 2011

かなり標準のように見えます。私にとってのハンガリーの姓:)
ベリサリウス博士2011

@belisariusハンガリー語では姓が最初になり、名が最後になるので、実際にはそれはフォアネーム、より正確には名です。
szabolcs 2011

2
ケースの影は、下部の灰色がかったバンドとして2番目の図にまだあります...
Sjoerd C. de Vries 2011

@ SjoerdC.deVriesそれは本当ですが、このタスクではそのようにする必要があると思います...それが影であり、オブジェクトの一部ではないことを示す方法はありません。アマゾンのほとんどの写真は影があるか、つまらないものだったので、私はこれを選びました。
szabolcs 2011

22

特にMathematicaを参照するのではなく、一般的に話すつもりです。これらの操作が難しいのか、些細なことなのかわかりません。

最初のステップは、画像の端にあるピクセルのアルファ(透明度)レベルを推定することです。現在、厳密なしきい値を使用しているため、アルファは0%完全に透明または100%完全に不透明です。背景の完全な白と画像の紛れもない部分である色の間の範囲を定義し、適切な比率を設定する必要があります-背景に色が近い場合は低アルファであり、暗いカットオフに近い場合は高アルファ。その後、周囲のアルファ値に基づいて調整を行うことができます。ピクセルが透明度で囲まれているほど、それ自体が透明になる可能性が高くなります。

アルファ値を取得したら、適切な色を取得するために逆ブレンドを実行する必要があります。画像が背景の上に表示される場合、は背景色、は前景色c = bc*(1-a)+fc*aである式を使用して、アルファ値に従って画像がブレンドされます。あなたの場合、背景は白(255,255,255)で、前景色は不明なので、式を逆にします。場合式がゼロによる除算を要求するが、色は問題とにかくこれだけ黒または白使用していません。bcfcfc = (c - bc*(1-a))/aa=0


3
素晴らしい答え。アルファ推定は、実際には研究分野全体です。たとえば、ai.stanford.edu
〜ruzon /

2
同意した、すばらしい答え。マークありがとう!賞金については(stackoverflowで追加できる場合)、完全に実装されたソリューションのいずれかが最適であると考えています。これまでのベリサリウスの、私は考えています。
2011年

11

ベリサリウスのマスク生成の助けを借りて、MarkRansomのアプローチを実装する試みを次に示します。

オブジェクトの境界を見つけます。

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 2011

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
dreevesは端にあるギザギザの線を取り除こうとしていると思います。
ベリサリウス博士2011年

1
確かに、これはそのハローを減らすのに良い仕事をしますが、ギザギザは取引のブレーカーになるかもしれません。@belisarius、あなたのバージョンはかなり素晴らしいですね!
2011年

@dreevesぼかしの後に距離変換を使用することで、エッジを(私のバージョンでは)改善できると思いますが、それはWiz氏によってすでに指摘されているので、実験は彼に任せます。
ベリサリウス博士2011年

何をしMethod -> "Convex"ますか?文書化されていません。
szabolcs 2011

申し訳ありません!実際には無関係な関数であるMorphologicalComponentsとMorphologicalBinarizeを混同していることに気付きました。
szabolcs 2011

6

これにはPhotoshopを使用し、PNGとして保存することをお勧めします。


5
良い点ですが、フォトショップがこれをうまく行うために使用するアルゴリズムは何ですか?(もちろん、これを自動化する必要があります。各画像のフォトショップで魔法の杖をクリックするのではありません。)
2011年

3
ちなみに、これは指摘するのに役立つと思います(フォトショップが私に思い浮かばなかったかもしれないほど大きなMathematicaオタクだったかもしれません!)。そして、それはフォトショップでさえスクリプト可能であることがわかったので、フォトショップが小さな数学プログラムでは複製できない本当に賢いことをしているなら、これはその意味で可能な限り最良の答えかもしれません。
2011年

5
アドビがソフトウェアに500smakeroosを請求できるのには理由があります;-)。
ティモ

7
おそらく、PhotoShopスクリプトによって生成された画像のバージョンを投稿することができます(手動介入なし:-)参照用に-私たちは何を打ち負かす必要があるかを知っています...
cormullion 2011

5

あなたが取ることができる可能なステップ:

  • マスクを拡張します
  • ぼかす
  • マスクを使用して、白からの距離で透明度を設定します
  • マスクを使用して、以前はより白い色がより飽和するように彩度を調整します。

良い考え; ありがとうございました!このための汎用コードを入手したいと思います。あなたがその時に戻ってきたいのであれば、私たちはおそらく数日以内に大きな報奨金を上げるでしょう(stackoverflowが私たちに許すとき)。実際、それが飛び込む誘惑であるならば、私はここにそうすることを約束します。:)
2011年

@dreeves私にはいいですね。今は時間がありませんが、また戻ろうと思います。
Mr.Wizard 2011年

3

「ほぼ白に近い」ピクセルを、同じRGBカラーのピクセルと透明度チャネルのシグモイドグラデーションに置き換えるだけです。ソリッドから透明への線形遷移を適用できますが、正弦波、シグモイド、またはタンは、探しているエッジの鋭さに応じて、より自然に見えます。これらは、中程度からソリッドまたは透明に急速に移動しますが、段階的/バイナリではありません。マナー、それはあなたが今持っているものです。

このように考えてください:

R、G、Bがそれぞれ0.0〜1.0であるとすると、白を1つの数値としてR + G + B = 1.0 * 3 = 3.0として表します。

各色を少し取り除くと少し「オフホワイト」になりますが、3つすべてを少し取ると、他の色を少し減らすよりもはるかに多くなります。任意の1つのチャネルで10%の削減を許可するとします:1.0 * .10 = .1ここで、この損失を3つすべてに分散し、アルファチャネルの場合は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のアイデアは、それについて賢く、白いピクセルがより広い領域の白の一部である場合、つまり背景の一部である可能性が非常に高い場合を除いて、白いピクセルを透明にしないことだと思います。
2011年

「より大きな領域」の問題は、それが重要である可能性がある一方で、小さな領域は重要ではない可能性があることです。白い車では、側面全体が重要になりますが、白い大きなパッチとしてタグ付けされます。白い背景の2人の間のスペースは小さく、複雑なエッジがありますが、それは行く必要があります。ボルツマンマシンスタイルのAIに一般的な形状を認識させ、白が空間なのかオブジェクトの一部なのかを確認する必要がありますが、まだそこにはありません。
Gregory Klopper 2011

1
また、わずかに異なる視点から2つの画像を撮影し、ステレオイメージングからの次元推定を使用して、オクルージョンが発生する場所に基づいて、どのピクセルが背景であるかを見つけることもできます。
グレゴリークロッパー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.