实现矩形框坐标相对于图像而非控件的功能

This commit is contained in:
zqm
2025-11-06 17:21:28 +08:00
parent 467e8fbb7d
commit b273de6434
2 changed files with 130 additions and 21 deletions

View File

@@ -147,3 +147,4 @@
1. 创建独立的叠加层图像 专门维护一个与显示图像同尺寸的Image对象作为矩形框的叠加层
2. 分离绘制逻辑 将临时绘制和最终绘制分离临时矩形仍通过Paint事件显示完成的矩形绘制到叠加层
3. 图像合并机制 在Paint事件中先绘制叠加层再绘制临时矩形。
4. 保存的矩形框位置和大小信息应该是相对于图像的,而不是相对于控件的。

View File

@@ -36,6 +36,9 @@ namespace JoyD.Windows.CS
{
InitializeComponent();
// 订阅SizeChanged事件确保控件大小变化时矩形框正确缩放
this.picBoxTemp.SizeChanged += new EventHandler(PicBoxTemp_SizeChanged);
// 设置按钮图标
SetButtonIcon();
@@ -82,6 +85,65 @@ namespace JoyD.Windows.CS
}
}
/// <summary>
/// 将控件坐标转换为图像坐标
/// </summary>
private Point ControlPointToImagePoint(Point controlPoint)
{
if (picBoxTemp.Image == null)
return controlPoint;
// 计算图像在控件中的缩放比例
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
// 计算图像坐标
return new Point(
(int)(controlPoint.X * scaleX),
(int)(controlPoint.Y * scaleY)
);
}
/// <summary>
/// 将图像坐标转换为控件坐标
/// </summary>
private Point ImagePointToControlPoint(Point imagePoint)
{
if (picBoxTemp.Image == null)
return imagePoint;
// 计算图像在控件中的缩放比例
float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width;
float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height;
// 计算控件坐标
return new Point(
(int)(imagePoint.X * scaleX),
(int)(imagePoint.Y * scaleY)
);
}
/// <summary>
/// 将图像矩形转换为控件矩形
/// </summary>
private Rectangle ImageRectangleToControlRectangle(Rectangle imageRectangle)
{
if (picBoxTemp.Image == null)
return imageRectangle;
// 计算图像在控件中的缩放比例
float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width;
float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height;
// 计算控件矩形
return new Rectangle(
(int)(imageRectangle.X * scaleX),
(int)(imageRectangle.Y * scaleY),
(int)(imageRectangle.Width * scaleX),
(int)(imageRectangle.Height * scaleY)
);
}
/// <summary>
/// 鼠标按下事件 - 开始绘制矩形
/// </summary>
@@ -116,11 +178,12 @@ namespace JoyD.Windows.CS
}
/// <summary>
/// 存储区域信息的类,包含矩形框、颜色和序号
/// 存储区域信息的类,包含矩形框(图像坐标)、颜色和序号
/// </summary>
private class RegionInfo
{
public Rectangle Rectangle { get; set; }
// 存储相对于图像的矩形坐标
public Rectangle ImageRectangle { get; set; }
public Color Color { get; set; }
public int Index { get; set; }
}
@@ -128,6 +191,7 @@ namespace JoyD.Windows.CS
/// <summary>
/// 创建或更新叠加层图像(完全重绘)
/// 仅在必要时调用,如图像尺寸改变或需要完全重绘
/// 使用图像坐标绘制矩形
/// </summary>
private void CreateRectangleOverlayImage()
{
@@ -182,6 +246,7 @@ namespace JoyD.Windows.CS
/// <summary>
/// 将单个区域绘制到叠加层图像
/// 用于增量绘制,避免每次都重绘所有矩形
/// 使用图像坐标绘制矩形
/// </summary>
/// <param name="region">要绘制的区域信息</param>
private void DrawRegionToOverlay(RegionInfo region)
@@ -191,16 +256,19 @@ namespace JoyD.Windows.CS
using (Graphics g = Graphics.FromImage(_rectangleOverlayImage))
{
// 设置高质量绘图
g.SmoothingMode = SmoothingMode.AntiAlias;
// 使用每个区域自己的颜色绘制矩形
g.DrawRectangle(new Pen(region.Color, 2), region.Rectangle);
g.DrawRectangle(new Pen(region.Color, 2), region.ImageRectangle);
// 绘制区域序号
using (Font font = new Font("Arial", 12, FontStyle.Bold))
using (SolidBrush brush = new SolidBrush(region.Color))
{
// 在矩形左上角绘制序号
Point textPosition = new Point(region.Rectangle.X + 5, region.Rectangle.Y - 15);
// 确保文本不超出控件边界
Point textPosition = new Point(region.ImageRectangle.X + 5, region.ImageRectangle.Y - 15);
// 确保文本不超出图像边界
if (textPosition.Y < 0)
textPosition.Y = 5;
@@ -212,21 +280,33 @@ namespace JoyD.Windows.CS
/// <summary>
/// 鼠标释放事件 - 完成矩形绘制(优化版)
/// 使用增量绘制,避免每次都重绘所有矩形
/// 确保矩形坐标相对于图像而非控件
/// </summary>
private void PicBoxTemp_MouseUp(object sender, MouseEventArgs e)
{
if (_isDrawing && _isDrawingMode && e.Button == MouseButtons.Left)
if (_isDrawing && _isDrawingMode && e.Button == MouseButtons.Left && picBoxTemp.Image != null)
{
_isDrawing = false;
// 确保矩形有一定大小才添加
if (_currentRectangle.Width > 10 && _currentRectangle.Height > 10)
// 获取相对于图像的矩形坐标
Point imageStartPoint = ControlPointToImagePoint(new Point(_currentRectangle.X, _currentRectangle.Y));
Point imageEndPoint = ControlPointToImagePoint(new Point(_currentRectangle.Right, _currentRectangle.Bottom));
// 确保矩形有一定大小才添加(转换为图像坐标后检查)
int imageWidth = Math.Abs(imageEndPoint.X - imageStartPoint.X);
int imageHeight = Math.Abs(imageEndPoint.Y - imageStartPoint.Y);
if (imageWidth > 5 && imageHeight > 5)
{
// 增加计数器并创建新的区域信息
// 计算图像坐标的矩形(确保左上角为起始点)
int imageX = Math.Min(imageStartPoint.X, imageEndPoint.X);
int imageY = Math.Min(imageStartPoint.Y, imageEndPoint.Y);
// 增加计数器并创建新的区域信息(存储图像坐标)
_regionCounter++;
RegionInfo regionInfo = new RegionInfo
{
Rectangle = _currentRectangle,
ImageRectangle = new Rectangle(imageX, imageY, imageWidth, imageHeight),
Color = _selectedColor,
Index = _regionCounter
};
@@ -238,9 +318,8 @@ namespace JoyD.Windows.CS
// 如果叠加层不存在或尺寸不匹配,需要完全重建
if (_rectangleOverlayImage == null ||
(picBoxTemp.Image != null && (
_rectangleOverlayImage.Width != picBoxTemp.Image.Width ||
_rectangleOverlayImage.Height != picBoxTemp.Image.Height)))
_rectangleOverlayImage.Height != picBoxTemp.Image.Height)
{
needFullRebuild = true;
}
@@ -259,9 +338,9 @@ namespace JoyD.Windows.CS
// 显示绘制完成的提示
ToolStripStatusLabel statusLabel = new ToolStripStatusLabel
{
Text = string.Format("已添加检测区域{0}: X={1}, Y={2}, 宽={3}, 高={4}",
Text = string.Format("已添加检测区域{0}: 图像坐标 - X={1}, Y={2}, 宽={3}, 高={4}",
_regionCounter,
_currentRectangle.X, _currentRectangle.Y, _currentRectangle.Width, _currentRectangle.Height)
imageX, imageY, imageWidth, imageHeight)
};
// 如果有状态栏,可以添加到状态栏显示
}
@@ -273,17 +352,25 @@ namespace JoyD.Windows.CS
/// <summary>
/// 绘制事件 - 显示矩形框(实现图像合并机制)
/// 处理叠加层图像的缩放,确保与控件尺寸匹配
/// </summary>
private void PicBoxTemp_Paint(object sender, PaintEventArgs e)
{
// 图像合并机制:先绘制叠加层
if (_rectangleOverlayImage != null)
// 设置高质量绘图
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// 图像合并机制:先绘制叠加层(根据控件尺寸进行缩放)
if (_rectangleOverlayImage != null && picBoxTemp.Image != null)
{
e.Graphics.DrawImage(_rectangleOverlayImage, Point.Empty);
// 计算缩放后的目标矩形
Rectangle destRect = new Rectangle(0, 0, picBoxTemp.ClientSize.Width, picBoxTemp.ClientSize.Height);
// 绘制缩放后的叠加层
e.Graphics.DrawImage(_rectangleOverlayImage, destRect, 0, 0, _rectangleOverlayImage.Width, _rectangleOverlayImage.Height, GraphicsUnit.Pixel);
}
// 再绘制临时矩形(当前正在绘制的矩形)
if (!_currentRectangle.IsEmpty)
// 再绘制临时矩形(当前正在绘制的矩形,使用控件坐标
if (!_currentRectangle.IsEmpty && _isDrawingMode && _isDrawing)
{
using (Pen dashedPen = new Pen(Color.Blue, 2))
{
@@ -434,6 +521,27 @@ namespace JoyD.Windows.CS
/// <summary>
/// 定时器每秒触发的事件处理方法
/// </summary>
/// <summary>
/// 当图像更新或控件大小变化时,重新创建叠加层图像
/// 确保矩形框正确显示在新的尺寸下
/// </summary>
private void UpdateOverlayForSizeChange()
{
if (picBoxTemp.Image != null && _drawnRectangles.Count > 0)
{
CreateRectangleOverlayImage();
picBoxTemp.Invalidate();
}
}
/// <summary>
/// 当控件大小改变时,更新叠加层以确保矩形框正确缩放
/// </summary>
private void PicBoxTemp_SizeChanged(object sender, EventArgs e)
{
UpdateOverlayForSizeChange();
}
private void Timer_Tick(object sender, EventArgs e)
{
// 这里可以添加每秒需要执行的代码