c#winforms gdi + - 将图像裁剪为其内容

时间:2017-09-03 10:58:54

标签: c# winforms bitmap gdi+ crop

我正在GDI + WinForms中制作一个绘图应用程序,我有一个我想添加的功能,并试图添加,我在网上找不到任何关于它的内容。

我想拍摄一张图片(或位图,并不重要)并将其裁剪到其中所有内容的位置。

让我举个例子:

我这里有一张图片(Picture of Stickman white a lot of white around it) 它周围有相当多的白色(将图像保存到计算机上,看它周围有白色。)我想将图像裁剪到火柴人所在的区域,我希望它看起来像像这样:

(Cropped stickman)

(将其保存到您的计算机上,您可以比较两者)

如果你看第二个,这是我想要制作的,它已经将图像切割成了火柴人!

但是,当然,我自己也做过。

我在网上看了很多寻找解决方案但找不到一个,所以我决定尝试自己做,但是没有用。

这是我试过的:

我有一个带有图片框和按钮的简单表单 - 点击按钮后,它应该将图像裁剪下来。我将图片框的BackColor设为黑色,使图像居中,使图像中不再存在的区域为黑色。

图像存储在名为ImageToChange的位图中。

点击按钮后,它应该裁剪图像 - 所以我做了一个功能,我将从按钮调用

此功能取决于我在网上找到的另一个裁剪图像的功能:

public Bitmap CropImage(Image source, int x, int y, int width, int height)
    {
        Rectangle crop = new Rectangle(x, y, width, height);

        var bmp = new Bitmap(crop.Width, crop.Height);
        using (var gr = Graphics.FromImage(bmp))
        {
            gr.DrawImage(source, new Rectangle(0, 0, bmp.Width, bmp.Height), crop, GraphicsUnit.Pixel);
        }
        return bmp;
    }

上面的函数应该只是裁剪和成像给它的x,y,宽度和高度 - 我没有编写代码,但我可以看到它的作用。

我的CropToContent功能取决于最后的功能。

所以,这是我创建的用于裁剪图像的函数:

public Bitmap CropToContent(Bitmap oldBmp)
    {
        Rectangle currentRect = new Rectangle();

        // Get a base color

        for (int y = 0; y < oldBmp.Height; y++)
        {
            for (int x = 0; x < oldBmp.Width; x++)
            {
                if (oldBmp.GetPixel(x, y) != Color.White)
                {
                    // We need to interpret this!

                    if (!currentRect.Contains(new Point(x, y)))
                    {
                        // This will run if this is out of the current rectangle

                        if (x > (currentRect.X + currentRect.Width)) currentRect.Width += ((currentRect.X + currentRect.Width) + x);
                        if (x < (currentRect.X))
                        {
                            // Move the rectangle over there and extend it's width to make the right the same!
                            int oldRectLeft = currentRect.Left;

                            currentRect.X = x;
                            currentRect.Width += oldRectLeft - x;
                        }

                        if (y > (currentRect.Y + currentRect.Height)) currentRect.Height += ((currentRect.Y + currentRect.Height) + y);

                        if (y < (currentRect.Y + currentRect.Height))
                        {
                            int oldRectTop = currentRect.Top;

                            currentRect.Y = y;
                            currentRect.Height += oldRectTop - y;
                        }
                    }
                }
            }
        }
        return CropImage(oldBmp, currentRect.X, currentRect.Y, currentRect.Width, currentRect.Height);
    }

如您所见,它使用我之前提到的CropImage函数!

此函数有一个矩形,用于裁剪图像的位置 - 随着函数的进行,它会被修改。

该函数循环遍历位图中的所有像素,如果它不是白色,则忽略它 - 如果它是其他任何东西,但它会根据矩形周围的位置做某事,

如果它在矩形的左边,它将移动矩形的X并改变宽度,使矩形的右边仍然相同

如果它在矩形的顶部,它将向上移动矩形的Y并改变高度......

如果它在矩形的右侧,它将改变宽度以匹配。

如果它在矩形的底部,它将改变高度以匹配。

如果它在矩形内部,它根本不在乎。

我不明白为什么这个功能不起作用。

在表单Load后运行此代码:

ImageToChange = Properties.Resources.stickman;

        pictureBox1.Image = ImageToChange;

我将未触动的火柴人放入Properties.Resources.stickman

然后点击它运行的按钮:

ImageToChange = CropToContent(ImageToChange);

        pictureBox1.Image = ImageToChange;

我不明白为什么这不起作用,非常感谢你,如果你读了整篇文章。

2 个答案:

答案 0 :(得分:1)

我对你的意思有点了解并且没有工作&#34;但我认为我发现了这个问题。

错误在您的逻辑中,例如修改矩形右侧的代码行:

if (x > (currentRect.X + currentRect.Width)) currentRect.Width += ((currentRect.X + currentRect.Width) + x);

这会通过添加 x,rectangle.X和rectangle.Width来修改检测到的矩形的宽度,这是错误的。你想要的可能就是:

if (x > (currentRect.X + currentRect.Width)) currentRect.Width = x - currentRect.X;

您需要对逻辑的其余部分进行类似的更改。

答案 1 :(得分:0)

所以,答案终于来了,感谢Hans Passant指出我应该逐步完成它,并密切注意它是一个非常小的图像。

而罗宾克罗姆说有些逻辑是关闭的。

这是最终结果:

public Bitmap CropToContent(Bitmap oldBmp)
    {
        Rectangle currentRect = new Rectangle();
        bool IsFirstOne = true;

        // Get a base color

        for (int y = 0; y < oldBmp.Height; y++)
        {
            for (int x = 0; x < oldBmp.Width; x++)
            {
                Color debug = oldBmp.GetPixel(x, y);
                if (oldBmp.GetPixel(x, y) != Color.FromArgb(255, 255, 255, 255))
                {
                    // We need to interpret this!

                    // Check if it is the first one!

                    if (IsFirstOne)
                    {
                        currentRect.X = x;
                        currentRect.Y = y;
                        currentRect.Width = 1;
                        currentRect.Height = 1;
                        IsFirstOne = false;
                    }
                    else
                    {

                        if (!currentRect.Contains(new Point(x, y)))
                        {
                            // This will run if this is out of the current rectangle

                            if (x > (currentRect.X + currentRect.Width)) currentRect.Width = x - currentRect.X;
                            if (x < (currentRect.X))
                            {
                                // Move the rectangle over there and extend it's width to make the right the same!
                                int oldRectLeft = currentRect.Left;

                                currentRect.X = x;
                                currentRect.Width += oldRectLeft - x;
                            }

                            if (y > (currentRect.Y + currentRect.Height)) currentRect.Height = y - currentRect.Y;

                            if (y < (currentRect.Y + currentRect.Height))
                            {
                                int oldRectTop = currentRect.Top;

                                currentRect.Y = y;
                                currentRect.Height += oldRectTop - y;
                            }
                        }
                    }
                }
            }
        }
        return CropImage(oldBmp, currentRect.X, currentRect.Y, currentRect.Width, currentRect.Height);
    }

我想通过使用较小的图像进行调试时,我必须在第一个像素上设置矩形的起点 - 默认为0,0,如果第一个像素为2,则它将在右侧当然。

矩形延伸一个,现在位于0,0的位置,宽度为1 ...是的......这不正确 - 它需要从第一个像素的位置开始,所以我添加了这一点。

我当然修复了Robin Krom的逻辑,并且该功能在火柴人身上运行得非常好!

所以它从这样开始: The start

结果如下:

The end result

还值得注意的是,由于某种原因,if (oldBmp.GetPixel(x, y) != Color.FromArgb(255, 255, 255, 255))if (oldBmp.GetPixel(x, y) != Color.White)曾经是@shared_task(name="analyze_atom", queue="atom") def analyze_atom(image_urls, targetdir=target_path, studentuid=None): return {} @shared_task(name="summary_up", queue="summary") def summary_up(rets, studentuid, images): return {} chord(analyze_atom.s([image]) for image in images)(summary_up.s(studentuid, images)) 无效。