390 lines
13 KiB
C#
390 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using Inspectron.Hawkeye.Vision.Geometry;
|
|
using OpenCvSharp;
|
|
using OpenCvSharp.Extensions;
|
|
using Point = System.Drawing.Point;
|
|
|
|
namespace Lindt.Candybox.Demo
|
|
{
|
|
public partial class MaskControl : UserControl
|
|
{
|
|
private Bitmap _backBuffer;
|
|
private GrahamConvexHull _hull;
|
|
private List<VectorLine> _vectorLines;
|
|
private EditMode _currentMode = EditMode.Brush;
|
|
private bool _isDrawingLine = false;
|
|
private Point _lineStartPoint;
|
|
private VectorLine _selectedLine;
|
|
private bool _isDraggingLine = false;
|
|
private bool _isDraggingVertex = false;
|
|
private bool _isDraggingStartVertex = false;
|
|
private Point _dragOffset;
|
|
|
|
public MaskControl()
|
|
{
|
|
InitializeComponent();
|
|
_backBuffer = new Bitmap(1800, 1408);
|
|
_hull = new GrahamConvexHull();
|
|
_vectorLines = new List<VectorLine>();
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
public EditMode CurrentMode
|
|
{
|
|
get => _currentMode;
|
|
set
|
|
{
|
|
_currentMode = value;
|
|
// Clear any ongoing operations when mode changes
|
|
_isDrawingLine = false;
|
|
_isDraggingLine = false;
|
|
_isDraggingVertex = false;
|
|
_selectedLine = null;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
public List<VectorLine> VectorLines
|
|
{
|
|
get => _vectorLines;
|
|
set => _vectorLines = value;
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
public Bitmap Mask { get; set; } = new Bitmap(1800, 1408);
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
public Bitmap Original { get; set; } = new Bitmap(1800, 1408);
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
|
{
|
|
//base.OnPaintBackground(e);
|
|
}
|
|
|
|
private bool _isDown = false;
|
|
private Point _lastMouse;
|
|
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
_isDown = true;
|
|
_lastMouse = ConvertCoordinates(new Point(e.X, e.Y));
|
|
|
|
switch (_currentMode)
|
|
{
|
|
case EditMode.DrawLine:
|
|
if (!_isDrawingLine)
|
|
{
|
|
_isDrawingLine = true;
|
|
_lineStartPoint = _lastMouse;
|
|
}
|
|
else
|
|
{
|
|
// Finish drawing line
|
|
var newLine = new VectorLine(_lineStartPoint, _lastMouse);
|
|
_vectorLines.Add(newLine);
|
|
_isDrawingLine = false;
|
|
Invalidate();
|
|
}
|
|
break;
|
|
|
|
case EditMode.RemoveLine:
|
|
var lineToRemove = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse));
|
|
if (lineToRemove != null)
|
|
{
|
|
_vectorLines.Remove(lineToRemove);
|
|
Invalidate();
|
|
}
|
|
break;
|
|
|
|
case EditMode.MoveLine:
|
|
_selectedLine = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse));
|
|
if (_selectedLine != null)
|
|
{
|
|
_isDraggingLine = true;
|
|
_dragOffset = new Point(_lastMouse.X - _selectedLine.StartPoint.X, _lastMouse.Y - _selectedLine.StartPoint.Y);
|
|
}
|
|
break;
|
|
|
|
case EditMode.MoveVertices:
|
|
foreach (var line in _vectorLines)
|
|
{
|
|
if (line.IsPointNearVertex(_lastMouse, out bool isStartVertex))
|
|
{
|
|
_selectedLine = line;
|
|
_isDraggingVertex = true;
|
|
_isDraggingStartVertex = isStartVertex;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
_isDown = false;
|
|
_isDraggingLine = false;
|
|
_isDraggingVertex = false;
|
|
}
|
|
|
|
private Pen _pen = new Pen(Color.White, 30);
|
|
private Pen _penBlack = new Pen(Color.Black, 30);
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
var currentPoint = ConvertCoordinates(new Point(e.X, e.Y));
|
|
|
|
// Handle brush mode (existing functionality)
|
|
if (_currentMode == EditMode.Brush)
|
|
{
|
|
if (_isDown && e.Button == MouseButtons.Left)
|
|
{
|
|
using (Graphics g = Graphics.FromImage(Mask))
|
|
{
|
|
g.DrawLine(_pen, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y);
|
|
g.FillEllipse(Brushes.White, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30);
|
|
}
|
|
}
|
|
if (_isDown && e.Button == MouseButtons.Right)
|
|
{
|
|
using (Graphics g = Graphics.FromImage(Mask))
|
|
{
|
|
g.DrawLine(_penBlack, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y);
|
|
g.FillEllipse(Brushes.Black, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30);
|
|
}
|
|
}
|
|
}
|
|
// Handle line dragging
|
|
else if (_currentMode == EditMode.MoveLine && _isDraggingLine && _selectedLine != null)
|
|
{
|
|
var deltaX = currentPoint.X - _lastMouse.X;
|
|
var deltaY = currentPoint.Y - _lastMouse.Y;
|
|
|
|
_selectedLine.StartPoint = new Point(_selectedLine.StartPoint.X + deltaX, _selectedLine.StartPoint.Y + deltaY);
|
|
_selectedLine.EndPoint = new Point(_selectedLine.EndPoint.X + deltaX, _selectedLine.EndPoint.Y + deltaY);
|
|
}
|
|
// Handle vertex dragging
|
|
else if (_currentMode == EditMode.MoveVertices && _isDraggingVertex && _selectedLine != null)
|
|
{
|
|
if (_isDraggingStartVertex)
|
|
{
|
|
_selectedLine.StartPoint = currentPoint;
|
|
}
|
|
else
|
|
{
|
|
_selectedLine.EndPoint = currentPoint;
|
|
}
|
|
}
|
|
|
|
_lastMouse = currentPoint;
|
|
Invalidate();
|
|
}
|
|
|
|
private Pen _contourPen = new Pen(Color.GreenYellow, 3);
|
|
private Pen _vectorLinePen = new Pen(Color.DeepPink, 12);
|
|
private Brush _vertexBrush = new SolidBrush(Color.Orange);
|
|
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
|
|
|
|
|
|
OnPaintInternal(e);
|
|
}
|
|
|
|
void OnPaintInternal(PaintEventArgs e)
|
|
{
|
|
var mat = Mask.ToMat().CvtColor(ColorConversionCodes.BGR2GRAY);
|
|
var contours = mat.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone);
|
|
LastContours = contours;
|
|
|
|
using (Graphics g = Graphics.FromImage(_backBuffer))
|
|
{
|
|
g.Clear(Color.Transparent);
|
|
g.DrawImage(Original, 0, 0);
|
|
|
|
// Draw contours
|
|
for (int i = 0; i < contours.Length; i++)
|
|
{
|
|
if (contours[i].Length > 1)
|
|
{
|
|
var points = contours[i].Select(x => new System.Drawing.Point(x.X, x.Y)).ToArray();
|
|
g.DrawPolygon(_contourPen, points);
|
|
}
|
|
}
|
|
|
|
// Draw vector lines
|
|
foreach (var line in _vectorLines)
|
|
{
|
|
// Convert coordinates back to display coordinates
|
|
var startDisplay = new Point(line.StartPoint.X, line.StartPoint.Y);
|
|
var endDisplay = new Point(line.EndPoint.X, line.EndPoint.Y);
|
|
|
|
g.DrawLine(_vectorLinePen, startDisplay, endDisplay);
|
|
|
|
// Draw vertices when in move vertices mode
|
|
if (_currentMode == EditMode.MoveVertices)
|
|
{
|
|
g.FillEllipse(_vertexBrush, startDisplay.X - 5, startDisplay.Y - 5, 10, 10);
|
|
g.FillEllipse(_vertexBrush, endDisplay.X - 5, endDisplay.Y - 5, 10, 10);
|
|
}
|
|
}
|
|
|
|
// Draw temporary line when drawing
|
|
if (_currentMode == EditMode.DrawLine && _isDrawingLine)
|
|
{
|
|
var startDisplay = new Point(_lineStartPoint.X, _lineStartPoint.Y);
|
|
var endDisplay = new Point(_lastMouse.X, _lastMouse.Y);
|
|
using (var tempPen = new Pen(Color.DeepPink, 12) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash })
|
|
{
|
|
g.DrawLine(tempPen, startDisplay, endDisplay);
|
|
}
|
|
}
|
|
|
|
// Draw mouse cursor indicator for brush mode
|
|
if (_currentMode == EditMode.Brush)
|
|
{
|
|
var cursorDisplay = new Point(_lastMouse.X, _lastMouse.Y);
|
|
g.DrawEllipse(Pens.Gray, cursorDisplay.X - 15, cursorDisplay.Y - 15, 30, 30);
|
|
}
|
|
}
|
|
|
|
e.Graphics.DrawImage(_backBuffer, 0, 0, Width, Height);
|
|
}
|
|
|
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
|
public OpenCvSharp.Point[][] LastContours { get; set; }
|
|
|
|
Point ConvertCoordinates(Point coords)
|
|
{
|
|
return new Point(coords.X * 2, coords.Y * 2);
|
|
}
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
{
|
|
base.OnResize(e);
|
|
}
|
|
}
|
|
public enum EditMode
|
|
{
|
|
Brush,
|
|
DrawLine,
|
|
RemoveLine,
|
|
MoveLine,
|
|
MoveVertices
|
|
}
|
|
|
|
public class VectorLine
|
|
{
|
|
public Point StartPoint { get; set; }
|
|
public Point EndPoint { get; set; }
|
|
public bool Selected { get; set; }
|
|
|
|
public VectorLine()
|
|
{
|
|
}
|
|
|
|
public VectorLine Scale(float sX, float sY)
|
|
{
|
|
return new VectorLine(new Point((int)(StartPoint.X * sX), (int)(StartPoint.Y * sY)),
|
|
new Point((int)(EndPoint.X * sX), (int)(EndPoint.Y * sY)));
|
|
}
|
|
|
|
public VectorLine Translate(int tX, int tY)
|
|
{
|
|
return new VectorLine(new Point(StartPoint.X + tX, StartPoint.Y + tY),
|
|
new Point(EndPoint.X + tX, EndPoint.Y + tY));
|
|
}
|
|
|
|
public VectorLine Translate(Point point)
|
|
{
|
|
return Translate(point.X, point.Y);
|
|
}
|
|
|
|
public VectorLine Sub(int tX, int tY)
|
|
{
|
|
return new VectorLine(new Point(StartPoint.X - tX, StartPoint.Y - tY),
|
|
new Point(EndPoint.X - tX, EndPoint.Y - tY));
|
|
}
|
|
|
|
public VectorLine Sub(Point point)
|
|
{
|
|
return Sub(point.X, point.Y);
|
|
}
|
|
|
|
|
|
public VectorLine(Point start, Point end)
|
|
{
|
|
StartPoint = start;
|
|
EndPoint = end;
|
|
Selected = false;
|
|
}
|
|
|
|
public bool IsPointOnLine(Point point, int tolerance = 10)
|
|
{
|
|
// Calculate distance from point to line segment
|
|
double A = point.X - StartPoint.X;
|
|
double B = point.Y - StartPoint.Y;
|
|
double C = EndPoint.X - StartPoint.X;
|
|
double D = EndPoint.Y - StartPoint.Y;
|
|
|
|
double dot = A * C + B * D;
|
|
double lenSq = C * C + D * D;
|
|
|
|
if (lenSq == 0) return Math.Sqrt(A * A + B * B) <= tolerance;
|
|
|
|
double param = dot / lenSq;
|
|
|
|
double xx, yy;
|
|
if (param < 0)
|
|
{
|
|
xx = StartPoint.X;
|
|
yy = StartPoint.Y;
|
|
}
|
|
else if (param > 1)
|
|
{
|
|
xx = EndPoint.X;
|
|
yy = EndPoint.Y;
|
|
}
|
|
else
|
|
{
|
|
xx = StartPoint.X + param * C;
|
|
yy = StartPoint.Y + param * D;
|
|
}
|
|
|
|
double dx = point.X - xx;
|
|
double dy = point.Y - yy;
|
|
return Math.Sqrt(dx * dx + dy * dy) <= tolerance;
|
|
}
|
|
|
|
public bool IsPointNearVertex(Point point, out bool isStartVertex, int tolerance = 15)
|
|
{
|
|
isStartVertex = false;
|
|
double distToStart = Math.Sqrt(Math.Pow(point.X - StartPoint.X, 2) + Math.Pow(point.Y - StartPoint.Y, 2));
|
|
double distToEnd = Math.Sqrt(Math.Pow(point.X - EndPoint.X, 2) + Math.Pow(point.Y - EndPoint.Y, 2));
|
|
|
|
if (distToStart <= tolerance)
|
|
{
|
|
isStartVertex = true;
|
|
return true;
|
|
}
|
|
if (distToEnd <= tolerance)
|
|
{
|
|
isStartVertex = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|