You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅对比图像绘制矩形区域无结果?WinForms图像对比问题求助

问题修复:仅对比PictureBox指定矩形区域图像无结果的问题

问题描述

已将pictureBox2与pictureBox3的SizeMode均设置为StretchImage,在pictureBox2上用鼠标绘制矩形后,希望仅对比pictureBox2和pictureBox3中该矩形区域内的图像,并将对比结果显示在pictureBox1中,但调用CompositeDifferences方法后pictureBox1无任何结果显示。

核心错误分析

  • 坐标转换缺失:SizeMode为StretchImage时,鼠标在控件上绘制的矩形是显示区域坐标,需转换为实际Bitmap的像素坐标,否则对比区域与绘制区域不匹配。
  • 遍历区域错误:CompositeDifferences仅遍历矩形宽高,但未从矩形实际起始位置开始,导致对比Bitmap左上角区域而非绘制区域。
  • 对比逻辑错误:ProcessChanges中两次调用CompositeDifferences都是将其他图像与参考图对比,而非直接对比pictureBox2和pictureBox3的图像。
  • TrackBar赋值错误:trackBar2的ValueChanged事件中错误将m_radarRadius赋值为trackBar1.Value,应为trackBar2.Value。
  • 图像尺寸判断错误:CompositeDifferences中用&&判断图像尺寸,应改为||(宽或高不等即终止)。
  • 多余圆形区域限制:IsInsideCircle判断与矩形区域对比需求冲突,需移除。

修复后的完整代码

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace CompositeDifferences
{
    public partial class Form1 : Form
    {
        private Bitmap m_imageBox2 = null;
        private Bitmap m_imageBox3 = null;
        private Bitmap m_resultImage = null;

        private Rectangle m_drawRect = Rectangle.Empty;
        private bool m_isPainting = false;

        private int m_tolerance = 120;
        private int m_radarRadius = 256;

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            // 加载图像
            m_imageBox2 = (Bitmap)Image.FromFile(@"d:\Comparison\ConvertedBmp.bmp");
            m_imageBox3 = (Bitmap)Image.FromFile(@"d:\Comparison\radar666.bmp");
            // 初始化结果图像为box2的副本,后续仅修改指定区域
            m_resultImage = (Bitmap)m_imageBox2.Clone();

            pictureBox2.Image = m_imageBox2;
            pictureBox3.Image = m_imageBox3;
            pictureBox1.Image = m_resultImage;
        }

        // 将控件上的坐标转换为Bitmap实际像素坐标
        private Point ConvertControlToBitmapCoord(PictureBox pb, Point controlPoint)
        {
            if (pb.Image == null) return Point.Empty;
            // 计算缩放比例
            float scaleX = (float)pb.Image.Width / pb.ClientSize.Width;
            float scaleY = (float)pb.Image.Height / pb.ClientSize.Height;
            return new Point((int)(controlPoint.X * scaleX), (int)(controlPoint.Y * scaleY));
        }

        private unsafe void CompareRectRegion(Bitmap srcImage, Bitmap targetImage, Bitmap resultImage, Rectangle rect)
        {
            // 检查图像尺寸是否一致,以及矩形是否有效
            if (srcImage.Width != targetImage.Width || srcImage.Height != targetImage.Height 
                || rect.Width <= 0 || rect.Height <=0)
                return;

            // 锁定仅需要处理的矩形区域,提升性能
            BitmapData srcData = srcImage.LockBits(rect, ImageLockMode.ReadOnly, srcImage.PixelFormat);
            BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.ReadOnly, targetImage.PixelFormat);
            BitmapData resultData = resultImage.LockBits(rect, ImageLockMode.WriteOnly, resultImage.PixelFormat);

            int stride = srcData.Stride;
            int bytesPerPixel = Image.GetPixelFormatSize(srcImage.PixelFormat) / 8;

            byte* srcScan0 = (byte*)srcData.Scan0;
            byte* targetScan0 = (byte*)targetData.Scan0;
            byte* resultScan0 = (byte*)resultData.Scan0;

            for (int y = 0; y < rect.Height; y++)
            {
                byte* srcRow = srcScan0 + y * stride;
                byte* targetRow = targetScan0 + y * stride;
                byte* resultRow = resultScan0 + y * stride;

                for (int x = 0; x < rect.Width; x++)
                {
                    ColorBgr* srcPixel = (ColorBgr*)(srcRow + x * bytesPerPixel);
                    ColorBgr* targetPixel = (ColorBgr*)(targetRow + x * bytesPerPixel);
                    ColorBgr* resultPixel = (ColorBgr*)(resultRow + x * bytesPerPixel);

                    // 像素差异超过阈值时,将目标像素写入结果图
                    if (srcPixel->GetDistanceSquared(*targetPixel) >= m_tolerance * m_tolerance)
                    {
                        *resultPixel = *targetPixel;
                    }
                }
            }

            srcImage.UnlockBits(srcData);
            targetImage.UnlockBits(targetData);
            resultImage.UnlockBits(resultData);
        }

        private void ProcessComparison()
        {
            if (m_drawRect == Rectangle.Empty || m_imageBox2 == null || m_imageBox3 == null)
                return;

            // 将控件上的绘制矩形转换为Bitmap实际坐标
            Point rectStart = ConvertControlToBitmapCoord(pictureBox2, new Point(m_drawRect.Left, m_drawRect.Top));
            Size rectSize = new Size((int)(m_drawRect.Width * (float)m_imageBox2.Width / pictureBox2.ClientSize.Width),
                                    (int)(m_drawRect.Height * (float)m_imageBox2.Height / pictureBox2.ClientSize.Height));
            Bitmap actualRect = new Rectangle(rectStart, rectSize);

            // 重置结果图像为box2的副本,避免之前的对比残留
            m_resultImage.Dispose();
            m_resultImage = (Bitmap)m_imageBox2.Clone();

            // 对比指定区域
            CompareRectRegion(m_imageBox2, m_imageBox3, m_resultImage, actualRect);

            // 更新显示
            pictureBox1.Image = m_resultImage;
            pictureBox1.Refresh();
        }

        private void trackBar1_ValueChanged(object sender, EventArgs e)
        {
            m_tolerance = trackBar1.Value;
            ProcessComparison();
        }

        private void trackBar2_ValueChanged(object sender, EventArgs e)
        {
            m_radarRadius = trackBar2.Value;
            ProcessComparison();
        }

        private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
        {
            m_drawRect = new Rectangle(e.X, e.Y, 0, 0);
            m_isPainting = true;
        }

        private void pictureBox2_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && m_isPainting)
            {
                int width = e.X - m_drawRect.Left;
                int height = e.Y - m_drawRect.Top;
                // 确保矩形不超出控件范围
                m_drawRect = new Rectangle(
                    Math.Min(m_drawRect.Left, e.X),
                    Math.Min(m_drawRect.Top, e.Y),
                    Math.Abs(width),
                    Math.Abs(height));
                pictureBox2.Invalidate();
            }
        }

        private void pictureBox2_MouseUp(object sender, MouseEventArgs e)
        {
            m_isPainting = false;
            ProcessComparison();
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            if (m_isPainting && m_drawRect != Rectangle.Empty)
            {
                using (Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, m_drawRect);
                }
            }
        }

        private struct ColorBgr : IEquatable<ColorBgr>
        {
            public byte B;
            public byte G;
            public byte R;

            public ColorBgr(byte b, byte g, byte r)
            {
                B = b;
                G = g;
                R = r;
            }

            public double GetDistanceSquared(ColorBgr other)
            {
                double db = other.B - B;
                double dg = other.G - G;
                double dr = other.R - R;
                return db * db + dg * dg + dr * dr;
            }

            public static bool operator ==(ColorBgr left, ColorBgr right)
            {
                return left.Equals(right);
            }

            public static bool operator !=(ColorBgr left, ColorBgr right)
            {
                return !(left == right);
            }

            public bool Equals(ColorBgr other)
            {
                return B == other.B && G == other.G && R == other.R;
            }

            public override bool Equals(object obj)
            {
                return obj is ColorBgr bgr && Equals(bgr);
            }

            public override int GetHashCode()
            {
                return HashCode.Combine(B, G, R);
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者jhon last

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:55:54