仅对比图像绘制矩形区域无结果?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
相关产品推荐
相关产品推荐

