using System.IO;
using System.Diagnostics;
using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System;
using System.Windows.Controls; // 对于 WPF 中的 Image 控件
using System.Windows;
using System.Windows.Media.Imaging;
using Point = OpenCvSharp.Point;
using System.Collections.Generic;
using System.Windows.Input;
using System.Windows.Media; // 添加这一行
using System.Windows.Shapes; // 用于 Polyline 和 Ellipse
namespace OpenCvWpfApp
{
public partial class MainWindow : System.Windows.Window
{
private double zoomFactor = 1.0; // 初始缩放因子
private double offsetX = 0; // 添加 offsetX
private double offsetY = 0; // 添加 offsetY
private List<Point> selectedContourPoints = new List<Point>();
private Point[][] contours;
private Mat baseImage;
private Mat drawImage;
private double epsilon = 0.003;
private List<LineSegmentPoint> lineSegments = new List<LineSegmentPoint>(); // 存储线段
private Scalar[] colors; // 颜色数组
public MainWindow()
{
InitializeComponent();
colors = new Scalar[] // 移到构造函数中
{
Scalar.Red,
Scalar.Green,
Scalar.Blue,
Scalar.Yellow,
Scalar.Cyan,
Scalar.Magenta
};
LoadImages();
this.WindowState = WindowState.Maximized; // 窗口启动时最大化
this.ResizeMode = ResizeMode.CanResize; // 允许调整窗口大小
}
private void LoadImages()
{
string baseImagePath = @"D:/BodorCV/SpaceEye/MultiTask/SegLoc/71.bmp";
string maskImagePath = @"D:/BodorCV/SpaceEye/MultiTask/SegLoc/72.bmp";
baseImage = Cv2.ImRead(baseImagePath, ImreadModes.Color);
drawImage = baseImage.Clone();
Mat maskImage = Cv2.ImRead(maskImagePath, ImreadModes.Grayscale);
if (baseImage.Empty() || maskImage.Empty())
{
MessageBox.Show("Failed to load images.");
return;
}
//MessageBox.Show($"Loaded base image size: {baseImage.Size()}");
//MessageBox.Show($"Loaded mask image size: {maskImage.Size()}");
//LogMessage($"Loaded base image size: {baseImage.Size()}");
//LogMessage($"Loaded mask image size: {maskImage.Size()}");
//UpdateContours(baseImage.CvtColor(ColorConversionCodes.BGR2GRAY));
// 使用 Dispatcher 延迟调用 UpdateContours
//避免在控件尚未完全初始化时进行图像处理和显示
this.Dispatcher.BeginInvoke(new Action(() =>
{
UpdateContours(baseImage.CvtColor(ColorConversionCodes.BGR2GRAY));
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
private void epsilonSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (baseImage == null)
return; // 确保图像已加载
epsilon = e.NewValue;
UpdateContours(baseImage.CvtColor(ColorConversionCodes.BGR2GRAY));
}
private void UpdateContours(Mat maskImage)
{
// 清空画布
drawingCanvas.Children.Clear();
// 将 Mat 转换为 BitmapSource
BitmapSource bitmapSource = drawImage.ToBitmapSource();
double canvasWidth = drawingCanvas.ActualWidth;
double canvasHeight = drawingCanvas.ActualHeight;
// 计算显示比例
double scaleX = canvasWidth / bitmapSource.PixelWidth;
double scaleY = canvasHeight / bitmapSource.PixelHeight;
double scale = Math.Min(scaleX, scaleY);
// 计算居中偏移
double offsetX = (canvasWidth - bitmapSource.PixelWidth * scale) / 2;
double offsetY = (canvasHeight - bitmapSource.PixelHeight * scale) / 2;
// 添加底图
Image backgroundImage = new Image
{
Source = bitmapSource,
Width = bitmapSource.PixelWidth * scale,
Height = bitmapSource.PixelHeight * scale,
Stretch = Stretch.Uniform
};
Canvas.SetLeft(backgroundImage, offsetX);
Canvas.SetTop(backgroundImage, offsetY);
drawingCanvas.Children.Add(backgroundImage);
// 查找轮廓
Cv2.FindContours(maskImage, out contours, out HierarchyIndex[] hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
// 基础色数组(红色系、绿色系、蓝色系)
Brush[][] colorSchemes = new Brush[][]
{
new Brush[] { Brushes.Red, Brushes.OrangeRed, Brushes.IndianRed, Brushes.LightCoral }, // 红色系
new Brush[] { Brushes.Green, Brushes.LimeGreen, Brushes.LightGreen, Brushes.DarkGreen }, // 绿色系
new Brush[] { Brushes.Blue, Brushes.CornflowerBlue, Brushes.LightBlue, Brushes.DarkBlue } // 蓝色系
};
// 存储近似点和线段
List<Ellipse> approxPoints = new List<Ellipse>();
List<Polyline> segmentPolylines = new List<Polyline>();
// 绘制轮廓中的点和线
for (int i = 0; i < contours.Length; i++)
{
var contour = contours[i];
PointCollection originalPoints = new PointCollection();
foreach (var point in contour)
{
// 计算并添加点
var scaledPoint = new System.Windows.Point(point.X * scale + offsetX, point.Y * scale + offsetY);
originalPoints.Add(scaledPoint);
}
// 选择基础色
Brush baseColor = colorSchemes[i % colorSchemes.Length][0]; // 使用基础色
// 创建并添加原始轮廓的 Polyline
Polyline originalPolyline = new Polyline
{
Points = originalPoints,
Stroke = baseColor, // 使用基础色
StrokeThickness = 2
};
originalPolyline.MouseLeftButtonDown += OriginalPolyline_MouseLeftButtonDown;//为原始线段添加鼠标点击事件
drawingCanvas.Children.Add(originalPolyline);
// 近似多边形绘制
Point[] approx = Cv2.ApproxPolyDP(contour, epsilon * Cv2.ArcLength(contour, true), true);
PointCollection approxPointsCollection = new PointCollection();
foreach (var point in approx)
{
var scaledPoint = new System.Windows.Point(point.X * scale + offsetX, point.Y * scale + offsetY);
approxPointsCollection.Add(scaledPoint);
}
// 确保近似多边形闭合
if (approxPointsCollection.Count > 0)
{
approxPointsCollection.Add(approxPointsCollection[0]); // 将第一个点添加到最后以闭合多边形
}
// 创建并添加每一段的 Polyline
for (int j = 0; j < approxPointsCollection.Count - 1; j++)
{
// 选择近似线段的颜色(交替深色和浅色)
Brush approxColor = (j % 2 == 0) ? colorSchemes[i % colorSchemes.Length][1] : colorSchemes[i % colorSchemes.Length][2]; // 深色或浅色
// 创建当前线段的 Polyline
Polyline segmentPolyline = new Polyline
{
Points = new PointCollection { approxPointsCollection[j], approxPointsCollection[j + 1] },
Stroke = approxColor, // 使用近似线段的颜色
StrokeThickness = 2
};
// 为当前线段添加鼠标点击事件
segmentPolyline.MouseLeftButtonDown += ApproxPolyline_MouseLeftButtonDown;//为近似线段添加鼠标点击事件
drawingCanvas.Children.Add(segmentPolyline);// 将轮廓添加到画布
segmentPolylines.Add(segmentPolyline); // 存储线段
}
// 绘制近似点(黄色)
foreach (var point in approxPointsCollection)
{
Ellipse pointEllipse = new Ellipse
{
Fill = Brushes.Yellow,
Width = 10, // 点的宽度
Height = 10 // 点的高度
};
Canvas.SetLeft(pointEllipse, point.X - pointEllipse.Width / 2);
Canvas.SetTop(pointEllipse, point.Y - pointEllipse.Height / 2);
// 添加鼠标事件以实现拖动
pointEllipse.MouseLeftButtonDown += (sender, e) =>
{
pointEllipse.CaptureMouse();
System.Windows.Point mousePosition = e.GetPosition(drawingCanvas);
// 使用不同的名称以避免冲突
double dragOffsetX = mousePosition.X - Canvas.GetLeft(pointEllipse);
double dragOffsetY = mousePosition.Y - Canvas.GetTop(pointEllipse);
// 拖动事件
drawingCanvas.MouseMove += (moveSender, moveArgs) =>
{
if (pointEllipse.IsMouseCaptured)
{
System.Windows.Point currentMousePosition = moveArgs.GetPosition(drawingCanvas);
Point newPosition = new Point(currentMousePosition.X - dragOffsetX, currentMousePosition.Y - dragOffsetY);
// 更新点的位置
Canvas.SetLeft(pointEllipse, currentMousePosition.X - dragOffsetX);
Canvas.SetTop(pointEllipse, currentMousePosition.Y - dragOffsetY);
// 更新连接的线段
UpdateSegmentLines(segmentPolylines, approxPointsCollection);
}
};
// 释放鼠标
pointEllipse.MouseLeftButtonUp += (upSender, upArgs) =>
{
pointEllipse.ReleaseMouseCapture();
drawingCanvas.MouseMove -= (moveSender, moveArgs) => { }; // 移除拖动事件
};
};
drawingCanvas.Children.Add(pointEllipse);
approxPoints.Add(pointEllipse); // 存储近似点
}
}
// 添加 MouseMove 事件处理程序
drawingCanvas.MouseMove += DrawingCanvas_MouseMove;
}
// 更新连接线段的位置
private void UpdateSegmentLines(List<Polyline> segmentPolylines, PointCollection approxPoints)
{
for (int i = 0; i < segmentPolylines.Count; i++)
{
var polyline = segmentPolylines[i];
if (i < approxPoints.Count - 1)
{
polyline.Points.Clear();
polyline.Points.Add(approxPoints[i]);
polyline.Points.Add(approxPoints[i + 1]);
}
}
}
private void DrawingCanvas_MouseMove(object sender, MouseEventArgs e)
{
System.Windows.Point mousePosition = e.GetPosition(drawingCanvas);
Point openCvPoint = new Point((int)mousePosition.X, (int)mousePosition.Y);
// 遍历画布中的所有子元素
foreach (var child in drawingCanvas.Children)
{
if (child is Polyline polyline)
{
// 检查鼠标是否在 Polyline 上
if (IsMouseOverPolyline(polyline, openCvPoint))
{
polyline.StrokeThickness = 4; // 增加线条粗细
}
else
{
polyline.StrokeThickness = 2; // 恢复线条粗细
}
}
}
}
// 检查鼠标是否在 Polyline 上
private bool IsMouseOverPolyline(Polyline polyline, OpenCvSharp.Point mousePosition)
{
// 将 OpenCvSharp.Point 转换为 System.Windows.Point
var mousePoint = new System.Windows.Point(mousePosition.X, mousePosition.Y);
// 使用 Geometry 对象来检查点是否在多边形上
var geometry = new PathGeometry();
var figure = new PathFigure { StartPoint = polyline.Points[0] };
for (int i = 1; i < polyline.Points.Count; i++)
{
figure.Segments.Add(new LineSegment(polyline.Points[i], true));
}
geometry.Figures.Add(figure);
// 检查鼠标位置是否在多边形的 Geometry 内
return geometry.FillContains(mousePoint) || IsPointOnPolyline(geometry, mousePosition);
}
// 辅助方法:检查点是否在多边形的边界上
private bool IsPointOnPolyline(Geometry geometry, Point point)
{
// 创建一个 Pen 来定义边界的宽度
var pen = new Pen(Brushes.Black, 1); // 这里的宽度可以根据需要调整
var strokeGeometry = geometry.GetWidenedPathGeometry(pen);
var Point = new System.Windows.Point(point.X, point.Y);
return strokeGeometry.FillContains(Point);
}
// 处理原始轮廓的点击事件
private void OriginalPolyline_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Polyline polyline = sender as Polyline;
if (polyline != null)
{
// 选中效果,例如改变颜色
polyline.Stroke = Brushes.Red; // 改变颜色以表示选中
MessageBox.Show("原始轮廓被选中!");
}
}
// 处理近似轮廓的点击事件
private void ApproxPolyline_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Polyline polyline = sender as Polyline;
if (polyline != null)
{
// 选中效果,例如改变颜色
polyline.Stroke = Brushes.Yellow; // 改变颜色以表示选中
MessageBox.Show("近似轮廓被选中!");
}
}
//获取所有轮廓
private List<Point> GetContourSegment(Point[] contour, Point start, Point end)//存在bug,选中所有轮廓了
{
List<Point> segment = new List<Point>();
bool isCollecting = false;
bool hasWrapped = false; // 添加标志以检测是否已经环绕一周
// 遍历轮廓点,收集从 start 到 end 的所有点
foreach (var point in contour)
{
if (point == start)
{
if (isCollecting && segment.Count > 0 && segment[0] == start)
{
// 如果再次遇到起始点且已开始收集,则停止(防止无限循环)
break;
}
isCollecting = true;
segment.Add(point);
}
else if (point == end && isCollecting)
{
segment.Add(point);
break;
}
else if (isCollecting)
{
segment.Add(point);
}
}
// 如果没有找到结束点,可能是因为轮廓是闭合的,需要从头开始收集
if (segment.Count > 0 && segment[segment.Count - 1] != end)
{
foreach (var point in contour)
{
if (hasWrapped && point == start)
{
// 如果再次到达起始点,则停止(防止无限循环)
break;
}
segment.Add(point);
if (point == end)
{
break;
}
hasWrapped = true; // 标记已经环绕一周
}
}
return segment;
}
//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓缩放操作↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
private void DrawingCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
// 检查是否按下了 Ctrl 键
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
// 获取鼠标相对于 Canvas 的位置
System.Windows.Point mousePosition = e.GetPosition(drawingCanvas);
// 根据滚轮的方向调整缩放因子
if (e.Delta > 0) // 向上滚动
{
zoomFactor *= 1.1; // 放大
}
else // 向下滚动
{
zoomFactor /= 1.1; // 缩小
}
// 更新画布上的内容
UpdateCanvas(mousePosition);
// 防止滚动事件被进一步处理
e.Handled = true;
}
}
private void UpdateCanvas(System.Windows.Point mousePosition)
{
// 假设 drawImage 是一个可绘制的对象,转换为位图
BitmapSource bitmap = drawImage.ToWriteableBitmap();
double imageWidth = bitmap.PixelWidth * zoomFactor; // 计算缩放后的宽度
double imageHeight = bitmap.PixelHeight * zoomFactor; // 计算缩放后的高度
// 计算图像重心
double centerX = imageWidth / 2;
double centerY = imageHeight / 2;
// 创建或更新背景图像
Image backgroundImage = new Image
{
Source = bitmap,
Width = imageWidth,
Height = imageHeight,
Stretch = Stretch.Uniform
};
// 计算新的图像位置以保持重心不变
double newLeft = (drawingCanvas.ActualWidth - imageWidth) / 2 + (centerX - mousePosition.X) * (zoomFactor - 1);
double newTop = (drawingCanvas.ActualHeight - imageHeight) / 2 + (centerY - mousePosition.Y) * (zoomFactor - 1);
// 清空画布并重新绘制图像
drawingCanvas.Children.Clear();
Canvas.SetLeft(backgroundImage, newLeft);
Canvas.SetTop(backgroundImage, newTop);
drawingCanvas.Children.Add(backgroundImage); // 添加图像到画布
// 应用缩放变换
ScaleTransform scaleTransform = new ScaleTransform(zoomFactor, zoomFactor, centerX, centerY);
drawingCanvas.RenderTransform = scaleTransform;
// 重新绘制矢量图形
DrawVectorGraphics();
}
private void DrawVectorGraphics()
{
// 绘制轮廓中的点和线
foreach (var contour in contours)
{
PointCollection originalPoints = new PointCollection();
foreach (var point in contour)
{
// 根据缩放和偏移量计算点的位置
double scaledX = point.X * zoomFactor + offsetX; // 使用 zoomFactor
double scaledY = point.Y * zoomFactor + offsetY; // 使用 zoomFactor
originalPoints.Add(new System.Windows.Point(scaledX, scaledY));
绘制点
//Ellipse ellipse = new Ellipse
//{
// Fill = Brushes.Red,
// Width = 5,
// Height = 5
//};
//Canvas.SetLeft(ellipse, scaledX - 2.5); // 中心对齐
//Canvas.SetTop(ellipse, scaledY - 2.5); // 中心对齐
//drawingCanvas.Children.Add(ellipse);
}
// 创建并添加原始轮廓的 Polyline
Polyline originalPolyline = new Polyline
{
Points = originalPoints,
Stroke = Brushes.Blue,
StrokeThickness = 2
};
drawingCanvas.Children.Add(originalPolyline);
// 近似多边形
Point[] approx = Cv2.ApproxPolyDP(contour, epsilon * Cv2.ArcLength(contour, true), true);
PointCollection approxPoints = new PointCollection();
foreach (var point in approx)
{
// 根据缩放和偏移量计算近似多边形的点的位置
double approxScaledX = point.X * zoomFactor + offsetX; // 使用 zoomFactor
double approxScaledY = point.Y * zoomFactor + offsetY; // 使用 zoomFactor
approxPoints.Add(new System.Windows.Point(approxScaledX, approxScaledY));
// 绘制近似多边形的点
Ellipse approxEllipse = new Ellipse
{
Fill = Brushes.Yellow, // 设置为黄色
Width = 5,
Height = 5
};
Canvas.SetLeft(approxEllipse, approxScaledX - 2.5); // 中心对齐
Canvas.SetTop(approxEllipse, approxScaledY - 2.5); // 中心对齐
drawingCanvas.Children.Add(approxEllipse);
}
// 确保近似多边形闭合
if (approxPoints.Count > 0)
{
approxPoints.Add(approxPoints[0]); // 将第一个点添加到最后以闭合多边形
}
// 创建并添加近似多边形的 Polyline
Polyline approxPolyline = new Polyline
{
Points = approxPoints,
Stroke = Brushes.Green,
StrokeThickness = 2
};
drawingCanvas.Children.Add(approxPolyline);
}
// 其他矢量图形的绘制逻辑...
}
//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑缩放操作↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
private void DrawingCanvas_MouseEnter(object sender, MouseEventArgs e)
{
}
private void DrawingCanvas_MouseLeave(object sender, MouseEventArgs e)
{
}
private double DistanceToSegment(Point p, Point v, Point w)
{
double l2 = Math.Pow(v.X - w.X, 2) + Math.Pow(v.Y - w.Y, 2); // 线段的平方长度
if (l2 == 0) return Math.Sqrt(Math.Pow(p.X - v.X, 2) + Math.Pow(p.Y - v.Y, 2)); // v 和 w 重合
// 投影计算
double t = ((p.X - v.X) * (w.X - v.X) + (p.Y - v.Y) * (w.Y - v.Y)) / l2;
t = Math.Max(0, Math.Min(1, t)); // 限制 t 在 [0, 1] 范围内
Point projection = new Point(v.X + t * (w.X - v.X), v.Y + t * (w.Y - v.Y)); // 计算投影点
return Math.Sqrt(Math.Pow(p.X - projection.X, 2) + Math.Pow(p.Y - projection.Y, 2)); // 返回距离
}
private void LogMessage(string message)
{
string logFilePath = @"C:\Users\011241\Desktop\surplus.txt";
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
public struct LineSegmentPoint
{
public Point P1;
public Point P2;
public Scalar Color; // 颜色属性
public int ContourIndex; // 该线段所属的轮廓索引
public List<Point> ContourPoints; // 包含的轮廓点
public bool IsHighlighted; // 是否被高亮的标志
public LineSegmentPoint(Point p1, Point p2, Scalar color, int contourIndex, List<Point> contourPoints, bool isHighlighted)
{
P1 = p1;
P2 = p2;
Color = color;
ContourIndex = contourIndex; // 记录轮廓索引
ContourPoints = contourPoints; // 记录轮廓点
IsHighlighted = isHighlighted; // 记录是否被高亮
}
}
}
09200930--
最新推荐文章于 2025-05-27 09:41:42 发布