查找图像内容并在其周围绘制矩形 [英] Find Image Content and Draw Rectangle Around It

查看:235
本文介绍了查找图像内容并在其周围绘制矩形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于



我正在使用 WinForms 。在我的表单中,我有一个 picturebox picturebox 大小模式设置为 zoom 。我使用 picturebox 来查看TIF图像。 TIF图像是灰度(黑白只有)。



我的应用程序的功能



我的应用程序找到第一个黑色像素,文档中最后一个黑色像素并在其周围绘制一个红色矩形。希望能够围绕图像的内容绘制矩形。

问题



有时,TIF文档会在内容周围显示点/点的形象。这会抛出我的应用程序。它不知道图像内容开始和结束的位置。如果文档有点/点,我如何才能找到TIF文档的内容并在其周围绘制一个矩形?

关于文档


  • 仅限黑白(1位深度)
  • TIF文档
  • 文档始终有字母和数字

  • 字母和数字始终大于点/点

  • 可以有多个点全部即使在内容中也是如此
  • 背景始终为白色

  • 内容始终为黑色

  • 点也是黑色的
  • >



    •文件吸取器



    以内容为中心的TIF文件



    示例图片1:








    示例图片2






    示例图片3






    解决方案

    以下实验似乎满足您的所有要求。



    我将以下控件放到Form1上



    一个MenuStrip:Docking = Top,带有2个MenuItems - 一个打开一个文件,第二个运行算法



    一个进度条:Docking = Top,to观看加载和算法的性能

    具有Docking = Fill和AutoScroll = true的面板

    面板,点(0,0),其余默认。 SizeMode = Normal。



    更新

     使用系统; 
    使用System.Collections.Generic;
    使用System.Drawing;
    使用System.Windows.Forms;

    namespace WindowsFormsApplication1
    {
    public partial class Form1:Form
    {
    public Form1()
    {
    InitializeComponent() ;
    }

    //打开一个图像文件。
    private void openToolStripMenuItem_Click(object sender,EventArgs e)
    {
    OpenFileDialog dlg = new OpenFileDialog();
    if(dlg.ShowDialog()== System.Windows.Forms.DialogResult.OK)
    {
    this.image = Image.FromFile(dlg.FileName)as Bitmap;
    this.pictureBox1.Image = image;
    this.pictureBox1.Invalidate();
    }
    }

    位图图像;

    //找到TIFF文件内容的顶部,左侧,右侧和底部边界。
    //
    private void findBoundsToolStripMenuItem_Click(object sender,EventArgs e)
    {
    int contentSize = 70;

    this.left = 0;
    this.top = 0;
    this.right = this.pictureBox1.Width - 1;
    this.bottom = this.pictureBox1.Height - 1;

    int h = image.Height;
    int w = image.Width;
    this.progressBar1.Value = 0;
    this.progressBar1.Maximum = 4; (int x = 0; x {b; b
    for(int y = 0; y< h; y ++)
    {

    if(this.image.GetPixel(x,y).ToArgb()== Black)
    {
    int size = this.image.GetBlackRegionSize(x,y) ;
    if(this.image.GetBlackRegionSize(x,y)> contentSize)
    {
    this.top = y;
    goto label10;
    }
    }

    }
    }

    label10:
    this.progressBar1.Increment(1); (int x = 0; x 为(int y = h-1; y> = 0; y--)
    {
    如果(this.image.GetPixel(x,y).ToArgb()== Black)

    if(this.image.GetBlackRegionSize(x,y) > contentSize)
    {
    this.bottom = y;
    goto label11;
    }
    }
    }
    }

    label11:
    this.progressBar1.Increment(1);
    for(int x = 0; x {
    for(int y = 0; y {

    if(this.image.GetPixel(x,y).ToArgb()== Black)
    {
    if(this.image.GetBlackRegionSize(x,y)> contentSize)
    {
    this.left = x;
    goto label12;
    }
    }
    }
    }

    label12:
    this.progressBar1.Increment(1); (int y = 0; y
    for(int x = w - 1; x> = 0; x--)
    {
    如果(this.image.GetPixel(x,y).ToArgb()== Black)

    if(this.image.GetBlackRegionSize(x,y) > contentSize)
    {
    this.right = x;
    goto label13;
    }
    }
    }
    }

    label13:
    this.progressBar1.Increment(1);

    this.pictureBox1.Invalidate();
    }

    内部静态只读int Black = Color.Black.ToArgb();
    internal static readonly int White = Color.White.ToArgb();

    int top;
    int bottom;
    int left;
    int right;

    private void pictureBox1_Paint(object sender,PaintEventArgs e)
    {
    if(pictureBox1.Image == null)
    {
    return;
    }

    int xMax = pictureBox1.Image.Width;
    int yMax = pictureBox1.Image.Height;

    int startX = this.left;
    int startY = this.top;
    int endX = this.right;
    int endY = this.bottom;


    int picWidth = pictureBox1.Size.Width;
    int picHeight = pictureBox1.Size.Height;


    float imageRatio = xMax /(float)yMax; // image W:H比例
    float containerRatio = picWidth /(float)picHeight; //容器W:H比例

    if(imageRatio> = containerRatio)
    {
    //水平图像
    float scaleFactor = picWidth /(float)xMax;
    float scaledHeight = yMax * scaleFactor;
    //计算容器顶部和图像顶部之间的差距
    float fill = Math.Abs​​(picHeight - scaledHeight)/ 2;
    // float fill = 0;

    startX =(int)(startX * scaleFactor);
    endX =(int)(endX * scaleFactor);
    startY =(int)((startY)* scaleFactor + fill);
    endY =(int)((endY)* scaleFactor + fill);
    }
    else
    {
    //垂直图像
    float scaleFactor = picHeight /(float)yMax;
    float scaledWidth = xMax * scaleFactor;
    float fill = Math.Abs​​(picWidth - scaledWidth)/ 2;
    startX =(int)((startX)* scaleFactor + fill);
    endX =(int)((endX)* scaleFactor + filler);
    startY =(int)(startY * scaleFactor);
    endY =(int)(endY * scaleFactor);
    }

    // if(_once)
    // Rectangle ee = new Rectangle(35,183,405,157);
    Rectangle ee = new Rectangle(startX,startY,endX - startX,endY - startY);
    System.Diagnostics.Debug.WriteLine(startX +,+ startY +,+(endX - startX)+,+(endY - startY));
    using(Pen pen = new Pen(Color.Red,2))
    {
    e.Graphics.DrawRectangle(pen,ee);
    }
    // _ once = false;



    静态类BitmapHelper
    {
    内部静态int GetBlackRegionSize(此位图图像,int x,int y)
    {
    int size = 0;
    GetRegionSize(image,new List< Point>(),x,y,0,ref size);
    返回大小;
    }

    //这个常量可以防止StackOverFlow异常。
    //也对性能有影响。
    //它的值必须大于findBoundsToolStripMenuItem_Click(object sender,EventArgs e)方法中定义的contentSize的值。
    const int MAXLEVEL = 100;

    static void GetRegionSize(这个位图图像,列表< Point>列表,int x,int y,int level,ref int size)
    {
    if(x> image.Width || x <0 || y> = image.Height || y< 0 || list.Contains(x,y)|| image.GetPixel(x,y).ToArgb()!= Form1.Black || level> MAXLEVEL)
    {
    return;
    }

    if(size {
    size = level;
    }

    list.Add(new Point(x,y));

    image.GetRegionSize(list,x,y - 1,level + 1,ref size);
    image.GetRegionSize(list,x,y + 1,level + 1,ref size);
    image.GetRegionSize(list,x - 1,y,level + 1,ref size);
    image.GetRegionSize(list,x + 1,y,level + 1,ref size);


    静态bool包含(此List< Point>列表,int x,int y)
    {
    foreach(列表中的点)
    {
    if(point.X == x&& point.Y == y)
    {
    return true;
    }
    }
    返回false;
    }
    }

    }

    this .pictureBox1.Size = image.Size;已被删除。 Paint事件处理程序的代码已更改。 PictureBox大小模式可以设置为Zoom。





    Update 2



    我试图简化代码并提高性能。

     使用系统; 
    使用System.Collections.Generic;
    使用System.Drawing;
    使用System.Windows.Forms;

    namespace WindowsFormsApplication3
    {
    public partial class Form1:Form
    {
    public Form1()
    {
    InitializeComponent() ;
    this.pictureBox1.Paint + = new PaintEventHandler(this.pictureBox1_Paint);
    }

    //打开一个图像文件
    //并运行FindBounds()方法
    private void openToolStripMenuItem_Click(object sender,EventArgs e)
    {
    OpenFileDialog dlg = new OpenFileDialog();
    if(dlg.ShowDialog()== System.Windows.Forms.DialogResult.OK)
    {
    this.image = Image.FromFile(dlg.FileName)as Bitmap;
    FindBounds();
    this.pictureBox1.Image = image;
    this.pictureBox1.Invalidate();
    }
    }

    位图图像;

    //图像中点或点的可能最大边
    int maxSpotOrDotSide = 7;

    //在TIFF文件中查找内容的顶部,左侧,右侧和底部边界。
    private void FindBounds()
    {
    //图像中点或点的最大可能区域
    int maxSpotOrDotArea = maxSpotOrDotSide * maxSpotOrDotSide;

    this.left = 0;
    this.top = 0;
    this.right = this.pictureBox1.Width - 1;
    this.bottom = this.pictureBox1.Height - 1;

    int h = image.Height;
    int w = image.Width;
    int num = w * h;

    //增值员。我测试了更大的值
    //比如x = 2,x = 5并且它提高了性能。
    //但我们必须小心,因为这可能会导致跳过内容。
    int dx = 1; // Incrementer forx
    int dy = 1; // Incrementer fory

    //progressBar1的初始化
    this.progressBar1.Value = 0;
    this.progressBar1.Maximum = num;

    //图片内容
    BlackContent imageContent = null;

    //这里我们将扫描图像的像素
    //从左上角开始,
    //完成在右下角
    for(int y = 0 ; y< h; y + = dx)
    {
    for(int x = 0; x {
    int val = y * w + x;
    this.progressBar1.Value = val>数字? num:val;
    //此块跳过扫描imageContent
    //因此应该提高性能。
    if(imageContent!= null&& imageContent.Contains(x,y))
    {
    x = imageContent.Right;
    继续;
    }

    //在我们检测到第一个黑色像素
    if(this.image.GetPixel(x,y))后,有趣的事情开始发生
    //。 ToArgb()== Black)
    {
    BlackContent content = new BlackContent(x,y);
    //启动Flood-Fill算法
    content.FloodFill(this.image);
    if(content.Area> maxSpotOrDotArea)
    {
    if(imageContent == null)
    {
    imageContent = content;
    }
    imageContent.Include(content.Right,content.Bottom);
    imageContent.Include(content.Left,content.Top);
    }
    else
    {
    //这里最好增加增量值。
    //取决于点/点的大小。
    //它会提高性能。
    if(dx if(dy }
    }

    }
    }

    //一切都完成了。
    this.progressBar1.Value = this.progressBar1.Maximum;

    //如果图像内容被检测到
    //那么我们保存信息
    if(imageContent!= null)
    {
    this.left = imageContent.Left;
    this.top = imageContent.Top;
    this.right = imageContent.Right;
    this.bottom = imageContent.Bottom;
    }

    this.pictureBox1.Invalidate();
    }

    内部静态只读int Black = Color.Black.ToArgb();
    internal static readonly int White = Color.White.ToArgb();

    int top;
    int bottom;
    int left;
    int right;

    private void pictureBox1_Paint(object sender,PaintEventArgs e)
    {
    if(pictureBox1.Image == null)
    {
    return;
    }

    int xMax = pictureBox1.Image.Width;
    int yMax = pictureBox1.Image.Height;

    int startX = this.left;
    int startY = this.top;
    int endX = this.right;
    int endY = this.bottom;


    int picWidth = pictureBox1.Size.Width;
    int picHeight = pictureBox1.Size.Height;


    float imageRatio = xMax /(float)yMax; // image W:H比例
    float containerRatio = picWidth /(float)picHeight; //容器W:H比例

    if(imageRatio> = containerRatio)
    {
    //水平图像
    float scaleFactor = picWidth /(float)xMax;
    float scaledHeight = yMax * scaleFactor;
    //计算容器顶部和图像顶部之间的差距
    float fill = Math.Abs​​(picHeight - scaledHeight)/ 2;
    // float fill = 0;

    startX =(int)(startX * scaleFactor);
    endX =(int)(endX * scaleFactor);
    startY =(int)((startY)* scaleFactor + fill);
    endY =(int)((endY)* scaleFactor + fill);
    }
    else
    {
    //垂直图像
    float scaleFactor = picHeight /(float)yMax;
    float scaledWidth = xMax * scaleFactor;
    float fill = Math.Abs​​(picWidth - scaledWidth)/ 2;
    startX =(int)((startX)* scaleFactor + fill);
    endX =(int)((endX)* scaleFactor + filler);
    startY =(int)(startY * scaleFactor);
    endY =(int)(endY * scaleFactor);
    }

    // if(_once)
    // Rectangle ee = new Rectangle(35,183,405,157);
    Rectangle ee = new Rectangle(startX,startY,endX - startX,endY - startY);
    System.Diagnostics.Debug.WriteLine(startX +,+ startY +,+(endX - startX)+,+(endY - startY));
    using(Pen pen = new Pen(Color.Red,2))
    {
    e.Graphics.DrawRectangle(pen,ee);
    }
    // _ once = false;
    }
    }

    //这个类与System.Drawing.Region类
    类似,只不过它只有矩形。
    //因为我们需要的是在图像周围绘制直线标记
    //该属性必须为
    //使其更快,至少我希望如此。

    class BlackContent
    {
    internal void FloodFill(位图图片)
    {
    FloodFillPrivate(image,this.left + 1,this.top,0 );
    }

    //传奇Flood-Fill算法。
    //通常它会以StackOverFlow异常结束。
    //但是这个类和它的矩形属性
    //必须防止这种灾难。
    //在我的实验中,我没有遇到事故。
    void FloodFillPrivate(位图图像​​,int x,int y,int级别)
    {
    if(x> = image.Width || x< 0 || y> =图像.Height || y <0 || this.Contains(x,y)|| image.GetPixel(x,y).ToArgb()!= Form1.Black)
    {
    return;
    }

    this.Include(x,y);

    FloodFillPrivate(image,x,y - 1,level + 1);
    FloodFillPrivate(image,x,y + 1,level + 1);
    FloodFillPrivate(image,x - 1,y,level + 1);
    FloodFillPrivate(image,x + 1,y,level + 1);
    }

    内部BlackContent(int x,int y)
    {
    this.left = x;
    this.right = x;
    this.top = y;
    this.bottom = y;如果(x {



    $ b this.left = x;
    }
    if(this.right< x)
    {
    this.right = x;
    }
    if(this.bottom< y)
    {
    this.bottom = y;
    }
    if(y {
    this.top = y;



    内部bool包含(int x,int y)
    {
    return!(x ; this.right || y< this.top || y> this.bottom);
    }

    int left;
    internal int Left {get {return this.left; }}
    int top;
    internal int Top {get {return this.top; }}
    int right;
    internal int Right {get {return this.right; }}
    int bottom;
    internal int Bottom {get {return this.bottom; }}

    internal int Area
    {
    get
    {
    return Width * Height;



    内部宽度
    {
    获得
    {
    return(this.right - this.left + 1);



    internal int Height
    {
    get
    {
    return(this.bottom - this.top + 1);
    }
    }
    }

    }



    我看着ProgressBar的表现。这个比较快。
    我还必须提及您的图片太大。


    About

    I’m using WinForms. In my form, I have a picturebox. The picturebox size mode is set to zoom. I use the picturebox to view TIF images. The TIF images are grayscale (Black and White ONLY).

    What My App Does

    My application finds the first black pixel and the last black pixel in the document and draws a red rectangle around it. In hopes that it would draw the rectangle around the content of the image.

    The Problem

    Sometimes the TIF documents have spots/dots around the content of the image. This throws my application off. It doesn't know where the content of the image begins and ends. How can I find the content of the TIF documents and draw a rectangle around it if the document has spots/dots?

    About the Document

    • Black and white only (1 bit depth)
    • TIF document
    • Document always has letters and numbers
    • The letters and numbers are always bigger than the spots/dots
    • There can be multiple spots all over the image even in the content
    • The background is always white
    • The content is always black
    • The spots are also black

    Download Test Image Links:

    • File Dropper: http://www.filedropper.com/test-tifs

    • Rapid Share: https://ufile.io/2qiir


    What I Found

    Upon my research, I found AForge.Imaging library which has many imaging filters that may potentially help me achieve my goal. I'm thinking about removing the spots/dots using the median filter or use the other filters to achieve the desired result.

    What I Tried

    I tried applying the median filter from AForge library to get rid of the spots but that only got rid of some of the spots. I had to repeat replying the filter multiple times to get rid of MOST of the spots to find the content and it still had a hard time finding the content. That method didn't work too well for me.

    Link to AForge Filters: http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm


    Code

        private void btn_Draw_Click(object sender, EventArgs e)
        {
            // Wrap the creation of the OpenFileDialog instance in a using statement,
            // rather than manually calling the Dispose method to ensure proper disposal
            using (OpenFileDialog dlg = new OpenFileDialog())
            {
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    pictureBox1.Image = new Bitmap(dlg.FileName);
    
                    int xMax = pictureBox1.Image.Width;
                    int yMax = pictureBox1.Image.Height;
    
                    startX = Int32.MaxValue;
                    startY = Int32.MaxValue;
                    endX = Int32.MinValue;
                    endY = Int32.MinValue;
    
    
                    using (Bitmap bmp = new Bitmap(pictureBox1.Image))
                    {
                        for (var y = 0;  y < yMax; y+=3)
                        {
                            for (var x = 0; x < xMax; x+=3)
                            {
                                Color col = bmp.GetPixel(x, y);
                                if(col.ToArgb() == Color.Black.ToArgb())
                                {
                                    // Finds first black pixel
                                    if (x < startX)
                                        startX = x;
                                    if(y < startY)
                                        startY = y;
    
                                    // Finds last black pixel
                                    if (x > endX)
                                        endX = x;
                                    if (y > endY)
                                        endY = y;
                                }
                            }
                        }
    
    
                        int picWidth = pictureBox1.Size.Width;
                        int picHeight = pictureBox1.Size.Height;
    
    
                        float imageRatio = xMax / (float)yMax; // image W:H ratio
                        float containerRatio = picWidth / (float)picHeight; // container W:H ratio
    
                        if (imageRatio >= containerRatio)
                        {
                            // horizontal image
                            float scaleFactor = picWidth / (float)xMax;
                            float scaledHeight = yMax * scaleFactor;
                            // calculate gap between top of container and top of image
                            float filler = Math.Abs(picHeight - scaledHeight) / 2;
                            //float filler = 0;
    
                            startX = (int)(startX * scaleFactor);
                            endX = (int)(endX * scaleFactor);
                            startY = (int)((startY) * scaleFactor + filler);
                            endY = (int)((endY) * scaleFactor + filler);
                        }
                        else
                        {
                            // vertical image
                            float scaleFactor = picHeight / (float)yMax;
                            float scaledWidth = xMax * scaleFactor;
                            float filler = Math.Abs(picWidth - scaledWidth) / 2;
                            startX = (int)((startX) * scaleFactor + filler);
                            endX = (int)((endX) * scaleFactor + filler);
                            startY = (int)(startY * scaleFactor);
                            endY = (int)(endY * scaleFactor);
                        }
    
                        //var scaleX = picWidth / (float)xMax;
                        //var scaleY = picHeight / (float)yMax;
    
                        //startX = (int)Math.Round(startX * scaleX);
                        //startY = (int)Math.Round(startY * scaleY);
                        //endX = (int)Math.Round(endX * scaleX);
                        //endY = (int)Math.Round(endY * scaleY);
    
                    }
                }
            }
        }
    
        private bool _once = true;
        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (_once)
            {
                //Rectangle ee = new Rectangle(35, 183, 405, 157);
                Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
                System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
                using (Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, ee);
                }
                //_once = false;
            }
        }
    

    Tif document that DOES NOT HAVE any spots and dots around content

    Tif document that HAS spots and dots around content

    Example Image 1:


    Example Image 2


    :

    Example Image 3


    解决方案

    Following experiment seems to meet all your requirements.

    I put following controls onto Form1

    A MenuStrip: Docking=Top, with 2 MenuItems - one to open a file, second to run an algorithm

    A progressbar: Docking=Top, to watch performance of loading and algorithm

    A panel with Docking=Fill and AutoScroll=true

    A picture into the panel, Point(0,0), the rest by default. SizeMode=Normal.

    Update

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            // Opens an image file.
            private void openToolStripMenuItem_Click(object sender, EventArgs e)
            {
                OpenFileDialog dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    this.image = Image.FromFile(dlg.FileName) as Bitmap;
                    this.pictureBox1.Image = image;
                    this.pictureBox1.Invalidate();
                }
            }
    
            Bitmap image;
    
            // finds top, left, right and bottom bounds of the content in TIFF file.
            // 
            private void findBoundsToolStripMenuItem_Click(object sender, EventArgs e)
            {
                int contentSize = 70;
    
                this.left = 0;
                this.top = 0;
                this.right = this.pictureBox1.Width - 1;
                this.bottom = this.pictureBox1.Height - 1;
    
                int h = image.Height;
                int w = image.Width;
                this.progressBar1.Value = 0;
                this.progressBar1.Maximum = 4;
    
                for (int y = 0; y < h; y++)
                {
                    for (int x = 0; x < w; x++)
                    {
    
                        if (this.image.GetPixel(x, y).ToArgb() == Black)
                        {
                            int size = this.image.GetBlackRegionSize(x, y);
                            if (this.image.GetBlackRegionSize(x, y) > contentSize)
                            {
                                this.top = y;
                                goto label10;
                            }
                        }
    
                    }
                }
    
            label10:
                this.progressBar1.Increment(1);
                for (int y = h - 1; y >= 0; y--)
                {
                    for (int x = 0; x < w; x++)
                    {
    
                        if (this.image.GetPixel(x, y).ToArgb() == Black)
                        {
                            if (this.image.GetBlackRegionSize(x, y) > contentSize)
                            {
                                this.bottom = y;
                                goto label11;
                            }
                        }
                    }
                }
    
            label11:
                this.progressBar1.Increment(1);
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
    
                        if (this.image.GetPixel(x, y).ToArgb() == Black)
                        {
                            if (this.image.GetBlackRegionSize(x, y) > contentSize)
                            {
                                this.left = x;
                                goto label12;
                            }
                        }
                    }
                }
    
            label12:
                this.progressBar1.Increment(1);
                for (int x = w - 1; x >= 0; x--)
                {
                    for (int y = 0; y < h; y++)
                    {
    
                        if (this.image.GetPixel(x, y).ToArgb() == Black)
                        {
                            if (this.image.GetBlackRegionSize(x, y) > contentSize)
                            {
                                this.right = x;
                                goto label13;
                            }
                        }
                    }
                }
    
            label13:
                this.progressBar1.Increment(1);
    
                this.pictureBox1.Invalidate();
            }
    
            internal static readonly int Black = Color.Black.ToArgb();
            internal static readonly int White = Color.White.ToArgb();
    
            int top;
            int bottom;
            int left;
            int right;
    
            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                if (pictureBox1.Image == null)
                {
                    return;
                }
    
                int xMax = pictureBox1.Image.Width;
                int yMax = pictureBox1.Image.Height;
    
                int startX = this.left;
                int startY = this.top;
                int endX = this.right;
                int endY = this.bottom;
    
    
                int picWidth = pictureBox1.Size.Width;
                int picHeight = pictureBox1.Size.Height;
    
    
                float imageRatio = xMax / (float)yMax; // image W:H ratio
                float containerRatio = picWidth / (float)picHeight; // container W:H ratio
    
                if (imageRatio >= containerRatio)
                {
                    // horizontal image
                    float scaleFactor = picWidth / (float)xMax;
                    float scaledHeight = yMax * scaleFactor;
                    // calculate gap between top of container and top of image
                    float filler = Math.Abs(picHeight - scaledHeight) / 2;
                    //float filler = 0;
    
                    startX = (int)(startX * scaleFactor);
                    endX = (int)(endX * scaleFactor);
                    startY = (int)((startY) * scaleFactor + filler);
                    endY = (int)((endY) * scaleFactor + filler);
                }
                else
                {
                    // vertical image
                    float scaleFactor = picHeight / (float)yMax;
                    float scaledWidth = xMax * scaleFactor;
                    float filler = Math.Abs(picWidth - scaledWidth) / 2;
                    startX = (int)((startX) * scaleFactor + filler);
                    endX = (int)((endX) * scaleFactor + filler);
                    startY = (int)(startY * scaleFactor);
                    endY = (int)(endY * scaleFactor);
                }
    
                //if (_once)
                //Rectangle ee = new Rectangle(35, 183, 405, 157);
                Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
                System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
                using (Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, ee);
                }
                //_once = false;
            }
        }
    
        static class BitmapHelper
        {
            internal static int GetBlackRegionSize(this Bitmap image, int x, int y)
            {
                int size = 0;
                GetRegionSize(image, new List<Point>(), x, y, 0, ref size);
                return size;
            }
    
            // this constant prevents StackOverFlow exception.
            // also it has effect on performance.
            // It's value must be greater than the value of contentSize defined in findBoundsToolStripMenuItem_Click(object sender, EventArgs e) method.
            const int MAXLEVEL = 100;
    
            static void GetRegionSize(this Bitmap image, List<Point> list, int x, int y, int level, ref int size)
            {
                if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || list.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black || level > MAXLEVEL)
                {
                    return;
                }
    
                if (size < level)
                {
                    size = level;
                }
    
                list.Add(new Point(x, y));
    
                image.GetRegionSize(list, x, y - 1, level + 1, ref size);
                image.GetRegionSize(list, x, y + 1, level + 1, ref size);
                image.GetRegionSize(list, x - 1, y, level + 1, ref size);
                image.GetRegionSize(list, x + 1, y, level + 1, ref size);
            }
    
            static bool Contains(this List<Point> list, int x, int y)
            {
                foreach (Point point in list)
                {
                    if (point.X == x && point.Y == y)
                    {
                        return true;
                    }
                }
                return false;
            }
        }
    
    }
    

    "this.pictureBox1.Size = image.Size;" has been removed. Paint event handler's code changed. PictureBox size mode can be set to Zoom now.

    Update 2

    I tried to simplify code and increase performance.

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication3
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                this.pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
            }
    
            // Opens an image file
            // and runs "FindBounds()" method
            private void openToolStripMenuItem_Click(object sender, EventArgs e)
            {
                OpenFileDialog dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    this.image = Image.FromFile(dlg.FileName) as Bitmap;
                    FindBounds();
                    this.pictureBox1.Image = image;
                    this.pictureBox1.Invalidate();
                }
            }
    
            Bitmap image;
    
            // Possible maximum side of a spot or a dot in the image
            int maxSpotOrDotSide = 7;
    
            // Finds top, left, right and bottom bounds of the content in TIFF file.
            private void FindBounds()
            {
                // Possible maximum area of a spot or a dot in the image
                int maxSpotOrDotArea = maxSpotOrDotSide * maxSpotOrDotSide;
    
                this.left = 0;
                this.top = 0;
                this.right = this.pictureBox1.Width - 1;
                this.bottom = this.pictureBox1.Height - 1;
    
                int h = image.Height;
                int w = image.Width;
                int num = w * h;
    
                // Incrementers. I tested with greater values
                // like "x = 2", "x = 5" and it increased performance.
                // But we must be carefull as this may cause skipping content.
                int dx = 1; // Incrementer for "x"
                int dy = 1; // Incrementer for "y"
    
                // Initialization of "progressBar1"
                this.progressBar1.Value = 0;
                this.progressBar1.Maximum = num;
    
                // Content of the image
                BlackContent imageContent = null;
    
                // Here we will scan pixels of the image
                // starting from top left corner and 
                // finishing at bottom right
                for (int y = 0; y < h; y += dx)
                {
                    for (int x = 0; x < w; x += dy)
                    {
                        int val = y * w + x;
                        this.progressBar1.Value = val > num ? num : val;
                        // This block skips scanning imageContent
                        // thus should increase performance.
                        if (imageContent != null && imageContent.Contains(x, y))
                        {
                            x = imageContent.Right;
                            continue;
                        }
    
                        // Interesting things begin to happen
                        // after we detect the first Black pixel
                        if (this.image.GetPixel(x, y).ToArgb() == Black)
                        {
                            BlackContent content = new BlackContent(x, y);
                            // Start Flood-Fill algorithm
                            content.FloodFill(this.image);
                            if (content.Area > maxSpotOrDotArea)
                            {
                                if (imageContent == null)
                                {
                                    imageContent = content;
                                }
                                imageContent.Include(content.Right, content.Bottom);
                                imageContent.Include(content.Left, content.Top);
                            }
                            else
                            {
                                // Here it's better we increase values of the incrementers.
                                // Depending on size of spots/dots.
                                // It should increase performance.
                                if (dx < content.Width) dx = content.Width;
                                if (dy < content.Height) dy = content.Height;
                            }
                        }
    
                    }
                }
    
                // Everything is done.
                this.progressBar1.Value = this.progressBar1.Maximum;
    
                // If image content has been detected 
                // then we save the information
                if (imageContent != null)
                {
                    this.left = imageContent.Left;
                    this.top = imageContent.Top;
                    this.right = imageContent.Right;
                    this.bottom = imageContent.Bottom;
                }
    
                this.pictureBox1.Invalidate();
            }
    
            internal static readonly int Black = Color.Black.ToArgb();
            internal static readonly int White = Color.White.ToArgb();
    
            int top;
            int bottom;
            int left;
            int right;
    
            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                if (pictureBox1.Image == null)
                {
                    return;
                }
    
                int xMax = pictureBox1.Image.Width;
                int yMax = pictureBox1.Image.Height;
    
                int startX = this.left;
                int startY = this.top;
                int endX = this.right;
                int endY = this.bottom;
    
    
                int picWidth = pictureBox1.Size.Width;
                int picHeight = pictureBox1.Size.Height;
    
    
                float imageRatio = xMax / (float)yMax; // image W:H ratio
                float containerRatio = picWidth / (float)picHeight; // container W:H ratio
    
                if (imageRatio >= containerRatio)
                {
                    // horizontal image
                    float scaleFactor = picWidth / (float)xMax;
                    float scaledHeight = yMax * scaleFactor;
                    // calculate gap between top of container and top of image
                    float filler = Math.Abs(picHeight - scaledHeight) / 2;
                    //float filler = 0;
    
                    startX = (int)(startX * scaleFactor);
                    endX = (int)(endX * scaleFactor);
                    startY = (int)((startY) * scaleFactor + filler);
                    endY = (int)((endY) * scaleFactor + filler);
                }
                else
                {
                    // vertical image
                    float scaleFactor = picHeight / (float)yMax;
                    float scaledWidth = xMax * scaleFactor;
                    float filler = Math.Abs(picWidth - scaledWidth) / 2;
                    startX = (int)((startX) * scaleFactor + filler);
                    endX = (int)((endX) * scaleFactor + filler);
                    startY = (int)(startY * scaleFactor);
                    endY = (int)(endY * scaleFactor);
                }
    
                //if (_once)
                //Rectangle ee = new Rectangle(35, 183, 405, 157);
                Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
                System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
                using (Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, ee);
                }
                //_once = false;
            }
        }
    
        // This class is similar to System.Drawing.Region class
        // except that its only rectangular. 
        // Because all we need is to draw a rectagnle
        // around the image this property must 
        // make it faster, at least I hope so.
    
        class BlackContent
        {
            internal void FloodFill(Bitmap image)
            {
                FloodFillPrivate(image, this.left + 1, this.top, 0);
            }
    
            // Legendary Flood-Fill algorithm.
            // Quite often it ends up with StackOverFlow exception.
            // But this class and its rectangularity property
            // must prevent this disaster.
            // In my experiments I didn't encounter incidents.
            void FloodFillPrivate(Bitmap image, int x, int y, int level)
            {
                if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || this.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black)
                {
                    return;
                }
    
                this.Include(x, y);
    
                FloodFillPrivate(image, x, y - 1, level + 1);
                FloodFillPrivate(image, x, y + 1, level + 1);
                FloodFillPrivate(image, x - 1, y, level + 1);
                FloodFillPrivate(image, x + 1, y, level + 1);
            }
    
            internal BlackContent(int x, int y)
            {
                this.left = x;
                this.right = x;
                this.top = y;
                this.bottom = y;
            }
    
            internal void Include(int x, int y)
            {
                if (x < this.left)
                {
                    this.left = x;
                }
                if (this.right < x)
                {
                    this.right = x;
                }
                if (this.bottom < y)
                {
                    this.bottom = y;
                }
                if (y < this.top)
                {
                    this.top = y;
                }
            }
    
            internal bool Contains(int x, int y)
            {
                return !(x < this.left || x > this.right || y < this.top || y > this.bottom);
            }
    
            int left;
            internal int Left { get { return this.left; } }
            int top;
            internal int Top { get { return this.top; } }
            int right;
            internal int Right { get { return this.right; } }
            int bottom;
            internal int Bottom { get { return this.bottom; } }
    
            internal int Area
            {
                get
                {
                    return Width * Height;
                }
            }
    
            internal int Width
            {
                get
                {
                    return (this.right - this.left + 1);
                }
            }
    
            internal int Height
            {
                get
                {
                    return (this.bottom - this.top + 1);
                }
            }
        }
    
    }
    

    I watched the performance with ProgressBar. This one's quite faster. I must also mention that your images are too big.

    这篇关于查找图像内容并在其周围绘制矩形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆