3

透明度のない約2500のPNG画像を含むフォルダがあります。すべての画像は約500x500です(491 x 433、511 x 499など)。

プログラムですべての画像を元のサイズの10%に縮小し、すべての画像の白い背景を透明色に設定したいと思います。

毎回2500枚の画像のサイズを変更せずにアプリケーションの機能をテストするために、ビリヤードボールの15枚の画像を「テスト」フォルダーとして使用しました。

今私の問題は次のコードにあります、私はほとんど透明な背景で、サイズ変更されてトリミングされたPNGを取得します。問題は、すべての画像ビューア(Irfan View、Paint.Net、GIMP)に左上の白い境界線が表示されることです。

どうすればこの境界線を回避できますか?

これに使用したコードは次のとおりです。

void ResizeI(string[] Paths, string OutPut, Methods m, PointF Values, bool TwoCheck, bool Overwrite, float[] CropVals)
    {
        for (int i = 0; i < Paths.Length; i++)//Paths is the array of all images
        {
            string Path = Paths[i];//current image
            Bitmap Oimg = (Bitmap)Bitmap.FromFile(Path);//original image
            Bitmap img = new Bitmap((int)(Oimg.Width - CropVals[0] - CropVals[1]), (int)(Oimg.Height - CropVals[2] - CropVals[3]));//cropped image
            Graphics ggg = Graphics.FromImage(img);
            ggg.DrawImage(Oimg, new RectangleF(((float)-CropVals[0]), ((float)-CropVals[2]), Oimg.Width - CropVals[1], Oimg.Height - CropVals[3]));
            ggg.Flush(System.Drawing.Drawing2D.FlushIntention.Flush);
            ggg.Dispose();
            PointF scalefactor = GetScaleFactor(img, Values, TwoCheck);//the scale factor equals 0.1 for 10%
            Bitmap newimg = new Bitmap((int)(Math.Ceiling(((float)img.Width) * scalefactor.X)), (int)(Math.Ceiling(((float)img.Height) * scalefactor.Y)));
            System.Drawing.Imaging.ImageFormat curform = img.RawFormat;
            string OutPath = System.IO.Path.Combine(OutPut, System.IO.Path.GetFileName(Path));
            OutPath = CheckPath(OutPath, Overwrite);//Delete if exsits
            Graphics g = Graphics.FromImage(newimg);
            g.InterpolationMode = GetModeFromMethod(m);//Bicubic interpolation
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.ScaleTransform(scalefactor.X, scalefactor.Y);
            g.DrawImage(img, new Rectangle(0, 0, (int)Math.Ceiling(((float)newimg.Width) / scalefactor.X) + 1, (int)Math.Ceiling(((float)newimg.Height) / scalefactor.Y) + 1));
            //g.Flush(System.Drawing.Drawing2D.FlushIntention.Flush);
            newimg.MakeTransparent(Color.White);
            newimg.Save(OutPath, curform);
            g.Dispose();
            img.Dispose();
        }
    }

そして、これが私が言及した白い境界線の例です。画像をダウンロードするか、ドラッグしてその下に黒い背景を配置し、境界線を確認します。

サイズ変更された画像と白い境界線

- 編集 -

代わりにこの関数を書くことができましたnewimg.MakeTransparent(...)

 void SetTransparent(ref Bitmap b)
    {
        for (int i = 0; i < b.Width; i++)
        {
            for (int ii = 0; ii < b.Height; ii++)
            {
                Color cc = b.GetPixel(i, ii);
                int tog = cc.R + cc.G + cc.B;
                float durch = 255f - (((float)tog) / 3f);
                b.SetPixel(i, ii, Color.FromArgb((int)durch, cc.R, cc.G, cc.B));
            }
        }
    }

問題は、私のビリヤードボールが次のようになっていることです。

ビリヤードボールの2番目のバージョン

4

2 に答える 2

4

特定のコードについてはお答えできませんが、何が起こっているのか説明できるかもしれません。

newimg.MakeTransparent(Color.White);

これは 1 つの色を取り、それを透明にします。問題は、ビリヤード ボールのエッジ (オレンジ) と真っ白な背景の間にさまざまな色があることです。これは、エッジのアンチエイリアシングです (ボールの純粋なオレンジ色から背景の純粋な白までの色のブレンドになります)。

純粋な白のみを透明にすることで、オブジェクトの周囲に白っぽい色の「光輪」が残ります。

アルファ マスクとして白い値を使用してこれを処理するより良い方法があるかもしれませんが、.net の画像ライブラリがそれを処理できるかどうかはわかりません (.net の経験が豊富な人に委ねる必要があります)。

ただし、暫定的に、サイズ変更を行う前に透明度を設定すると役立つ場合があります。これは本当の修正ではありませんが、ハロー効果をいくらか減らすかもしれません.

アップデート:

だから、私はこれについてもう少し考えてきました.アルファチャンネルの透明度を自動的に作成するためのプログラムによる解決策があるかどうかは完全にはわかりません.

頭のてっぺんから、これが私が思いついたものです:

  • 左上のピクセルが 100% 透明色であると仮定します (ピクセル X と呼びます)。
  • 透明にしたい背景が1つの単色(パターンに対して)であると仮定します
  • 約 3px のアンチエイリアスを想定

あなたはそれからすることができます...

  • X の隣接ピクセルをチェックします。X の色に一致する X の隣接ピクセルごとに、100% 透明に設定します。
  • x の隣のピクセルが同じでない場合は、相対的な色相を確認できます。
  • そのピクセルから分岐し、周囲のピクセルを確認します。
  • 相対的な色相が特定の割合で変化するか、および/またはピクセルの色が隣接するピクセルの色と同じになるまで、各ピクセル (a、b、c など) にこのマーキングを行います (一定の変動幅があります)。もしそうなら、私たちはオブジェクトの内部に十分入っていると仮定します.
  • 次に、マークしたピクセルを逆方向​​に進み、透明度を調整します...たとえば、c = 0% b = 33% a = 66%

それでも、それは実際に起こるべきことを大幅に単純化しすぎています。パターン化された背景を考慮せずに、多くの仮定を行っており、透明にする必要がある内部領域 (ドーナツ ホールなど) を完全に無視しています。

通常、グラフィックス編集アプリでは、これは背景色のブロックを選択し、その選択範囲のエッジをぼかし、それを最大アルファに変換することによって行われます。

とても興味深い質問/問題です。残念ながら、私はあなたに答えを持っていませんが、好奇心を持ってこのスレッドを見ています!

于 2011-06-04T11:20:08.190 に答える
1

編集しSetTransparentた関数は正しい方向に進んでおり、ほぼそこにいます。

少し変更すると、これを試すことができます:

void SetTransparent(ref Bitmap b)    
{   
    const float selectivity = 20f;  // set it to some number much larger than 1 but less than 255
    for (int i = 0; i < b.Width; i++)
    {        
        for (int ii = 0; ii < b.Height; ii++)
        {
            Color cc = b.GetPixel(i, ii);
            float avgg = (cc.R + cc.G + cc.B) / 3f;
            float durch = Math.Min(255f, (255f - avgg) * selectivity);
            b.SetPixel(i, ii, Color.FromArgb((int)durch, cc.R, cc.G, cc.B));
        }
    }
}

ビリヤード ボールのアルファ値に影響を与えないようにするために、0 に非常に近い色のアルファ値だけを減らす必要があるという考えです。つまり、色が白から遠ざかるにつれて0から255まで急激に上昇する関数です。

@DA が言ったように、これは理想的な結果にはなりません。なぜなら、失われた情報 (オブジェクトのエッジの近くで透明なピクセルと不透明なピクセルが混ざり合っている) が回復できないからです。完全にエイリアスのないアルファ エッジを作成するには、ソース イメージ自体を透過的に生成する必要があります。

于 2011-06-04T20:57:29.633 に答える