web-dev-qa-db-ja.com

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

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-somethingという名前の自己完結型関数です。どうもありがとう、みんな!

81
dreeves

おそらく、必要なEdgeの品質に応じて:

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]

enter image description here

編集

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

enter image description here

拡大するにはクリックしてください

Edit 2

画像のhaloと背景の不完全さの範囲を知るためだけに:

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

enter image description here

ColorNegate@ImageAdd[EntropyFilter[img, 1] // ImageAdjust, ColorNegate@img]

enter image description here

44
Dr. belisarius

この関数は、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]

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

ベリサリウスのマスク生成の助けを借りて、Mark Ransomのアプローチを実装しようとしています。

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

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]

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

エッジの一部に白い毛羽立ちがあることに注意してください。それを最初の画像の赤いアウトラインと比較してください。より良いエッジ検出器が必要です。侵食量を増やすと毛羽立ちが助長されますが、他の側面が透明になりすぎるため、エッジマスクの幅にトレードオフがあります。しかし、それ自体はぼかし操作がないことを考えると、かなり良いです。

さまざまな画像でアルゴリズムを実行してその堅牢性をテストし、その自動性を確認することは有益です。

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[
   ColorNegate@
    Image[MorphologicalComponents[ColorNegate@img, .062, 
      Method -> "Convex"], "Bit"], 10000];
Show[Graphics[Rectangle[], Background -> Red, 
  PlotRangePadding -> None], SetAlphaChannel[img, ColorNegate@mask]]

image

9
Alexey Popkov

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

実行可能なステップ:

  • マスクを拡張する
  • ぼかす
  • マスクを使用して、白からの距離で透明度を設定します
  • マスクを使用して、以前に白だった色がより彩度が高くなるように彩度を調整します。
5
Mr.Wizard

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

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

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

各色を少し取り出すと、少し「オフホワイト」になりますが、3つすべてを少し取ると、どの色よりも少し外れます。任意の1つのチャネルで10%の削減を許可するとしましょう:1.0 * .10 = .1、この損失を3つすべてに分散し、アルファチャネルの0から1の間にバインドします。 loss = 0.9)=> 0 and(loss = 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