プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

C#で画像にぼかしをいれる


ブログにスクリーンショットを載せるときに、映り込ませたくないものがあるときに一部分にぼかしを入れるための機能を作りました。
C#で画像処理ソフト、これでとりあえず入れ込みたい機能はすべて盛り込んだので完成です。
f:id:s51517765:20200809104218p:plain
ぼかしのアルゴリズムは画質の向上のために数々考案されていますが、もっとも簡単にRGBの色要素を範囲内で平均化するという方法をとりました。

選択範囲をstartPointからendPointの対角線で指定される長方形とし、ぼかしのサイズをblur_sizeすると、各RGBの平均は

for (int y1 = 0; y1 < blur_size; y1++)
{
    for (int x1 = 0; x1 < blur_size; x1++)
    {
        red += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x + x1, Math.Min(startPoint.Y, endPoint.Y) + y + y1).R;
        green += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x + x1, Math.Min(startPoint.Y, endPoint.Y) + y + y1).G;
        blue += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x + x1, Math.Min(startPoint.Y, endPoint.Y) + y + y1).B;
    }
}
red /= (int)Math.Pow(blur_size, 2);
green /= (int)Math.Pow(blur_size, 2);
blue /= (int)Math.Pow(blur_size, 2);

のようになります。

このように算出したRGBで一つの範囲を塗りつぶします。

SolidBrush brush = new SolidBrush(Color.FromArgb(100, red, green, blue));
brush.Color = Color.FromArgb(red, green, blue);
g.FillRectangle(brush, Math.Min(startPoint.X, endPoint.X) + x, Math.Min(startPoint.Y, endPoint.Y) + y, blur_size, blur_size);

このほかに考慮すべきは、startPointendPointの位置関係で、左上から右下に向けて選択されるとは限らないので、左上の点はMath.Min(startPoint.X, endPoint.X) , Math.Min(startPoint.Y, endPoint.Y) のように選択します。

また、選択範囲がキャンバスの外に出てしまうと配列外参照の例外が発生してしまうので、

if (endPoint.X > FinalPictureWidth) endPoint.X = FinalPictureWidth;
else if (endPoint.X < 0) endPoint.X = 0;

のように、キャンバスの範囲内に修正します。ここで、開始点はキャンバスの中でないとイベントが発生しないのでノーチェックでOKです。

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    if (FlagMask == 2)
    {
        //Lineを消す
        if (backupImage != null) g.DrawImage(backupImage, 0, 0);

        //ImageオブジェクトのGraphicsオブジェクトを作成する
        g = Graphics.FromImage(canvas);
        int blur_size = (int)numericUpDownBlurSize.Value;

        if (endPoint.X > FinalPictureWidth) endPoint.X = FinalPictureWidth;
        else if (endPoint.X < 0) endPoint.X = 0;
        if (endPoint.Y > FinalPictureHeight) endPoint.Y = FinalPictureHeight;
        else if (endPoint.Y < 0) endPoint.Y = 0;

        int size_x = Math.Abs(startPoint.X - endPoint.X);
        int size_y = Math.Abs(startPoint.Y - endPoint.Y);
        for (int x = 0; x < size_x - blur_size; x += blur_size)
        {
            for (int y = 0; y < size_y - blur_size; y += blur_size)
            {
                int red = 0;
                int green = 0;
                int blue = 0;
                for (int y1 = 0; y1 < blur_size; y1++)
                {
                    for (int x1 = 0; x1 < blur_size; x1++)
                    {
                        red += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x, Math.Min(startPoint.Y, endPoint.Y) + y).R;
                        green += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x, Math.Min(startPoint.Y, endPoint.Y) + y).G;
                        blue += canvas.GetPixel(Math.Min(startPoint.X, endPoint.X) + x, Math.Min(startPoint.Y, endPoint.Y) + y).B;
                    }
                }
                red /= (int)Math.Pow(blur_size, 2);
                green /= (int)Math.Pow(blur_size, 2);
                blue /= (int)Math.Pow(blur_size, 2);
                SolidBrush brush = new SolidBrush(Color.FromArgb(100, red, green, blue));

                brush.Color = Color.FromArgb(red, green, blue);
                g.FillRectangle(brush, Math.Min(startPoint.X, endPoint.X) + x, Math.Min(startPoint.Y, endPoint.Y) + y, blur_size, blur_size);
            }
        }
        pictureBox1.Image = canvas;
    }
}

github.com

関連記事

s51517765.hatenadiary.jp
s51517765.hatenadiary.jp
s51517765.hatenadiary.jp