330 lines
13 KiB
C#
330 lines
13 KiB
C#
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using Hawkeye.VisionBuilder.Workflow;
|
|
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
|
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements;
|
|
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Origin;
|
|
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Poly;
|
|
using OpenCvSharp;
|
|
using OpenCvSharp.Extensions;
|
|
using Point = System.Drawing.Point;
|
|
using PointF = System.Drawing.PointF;
|
|
using Matrix = Hawkeye.VisionBuilder.Workflow.Matrix;
|
|
using Vector2 = Hawkeye.VisionBuilder.Workflow.Vector2;
|
|
|
|
namespace Hawkeye.VisionBuilder.Features.ImagePreview
|
|
{
|
|
public partial class ImagePreviewControl : UserControl,ICanvas
|
|
{
|
|
private Bitmap _backBuffer;
|
|
public List<IGraphicsElement> GraphicsElements { get; set; }=new List<IGraphicsElement>();
|
|
public EControlMode Mode { get; set; } = EControlMode.Preview;
|
|
|
|
public Hawkeye.VisionBuilder.Workflow.Datatypes.HawkeyeImage Image
|
|
{
|
|
get => _image;
|
|
set
|
|
{
|
|
var needsCentering = _image == null;
|
|
_image = value;
|
|
if(needsCentering) CenterImage();
|
|
}
|
|
}
|
|
|
|
public void CenterImage()
|
|
{
|
|
TransformationMatrix= Workflow.Matrix.Identity;
|
|
var hRatio = Width/ (float)_image.ImageData.Width;
|
|
var vRatio = Height / (float) _image.ImageData.Height;
|
|
TransformationMatrix*=Matrix.CreateScale(Math.Min(hRatio,vRatio));
|
|
var shift = new Vector2(Width,Height)- Vector2.Transform(new Vector2(_image.ImageData.Width, _image.ImageData.Height),
|
|
TransformationMatrix);
|
|
TransformationMatrix *= Matrix.CreateTranslation(shift.X / 2f, shift.Y / 2f, 0);
|
|
|
|
}
|
|
|
|
public ImagePreviewControl()
|
|
{
|
|
InitializeComponent();
|
|
|
|
_backBuffer = new Bitmap(Width, Height);
|
|
|
|
MouseWheel += ZoomPictureBoxWheel_MouseWheel;
|
|
DoubleClick += ZoomPictureBoxWheel_DoubleClick;
|
|
DoubleBuffered = true;
|
|
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
|
}
|
|
|
|
private void ZoomPictureBoxWheel_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
private float _zoom = 1;
|
|
private void ZoomPictureBoxWheel_MouseWheel(object sender, MouseEventArgs e)
|
|
{
|
|
_zoom *= (e.Delta > 0) ? 0.9f : (1f / 0.9f);
|
|
TransformationMatrix = TransformationMatrix
|
|
* Matrix.CreateTranslation(-_lastMousePosition.X, -_lastMousePosition.Y, 0)
|
|
* Matrix.CreateScale((e.Delta > 0) ? 0.9f : (1f / 0.9f))
|
|
* Matrix.CreateTranslation(_lastMousePosition.X, _lastMousePosition.Y, 0);
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
public void FitToScreen()
|
|
{
|
|
if (Image == null) return;
|
|
var width = Image.ImageData.Width;
|
|
var height = Image.ImageData.Height;
|
|
var aspectRatio = (float)width / height;
|
|
var screenAspectRatio = (float)Width / Height;
|
|
|
|
if (aspectRatio > screenAspectRatio)
|
|
{
|
|
_zoom = Width / (float)width;
|
|
TransformationMatrix = Matrix.Identity;
|
|
TransformationMatrix *= Matrix.CreateScale(_zoom);
|
|
var shift = new Vector2(Width, Height) - Vector2.Transform(new Vector2(width, height),
|
|
TransformationMatrix);
|
|
TransformationMatrix *= Matrix.CreateTranslation(shift.X / 2f, shift.Y / 2f, 0);
|
|
}
|
|
else
|
|
{
|
|
_zoom = Height / (float)height;
|
|
TransformationMatrix = Matrix.Identity;
|
|
TransformationMatrix *= Matrix.CreateScale(_zoom);
|
|
var shift = new Vector2(Width, Height) - Vector2.Transform(new Vector2(width, height),
|
|
TransformationMatrix);
|
|
TransformationMatrix *= Matrix.CreateTranslation(shift.X / 2f, shift.Y / 2f, 0);
|
|
}
|
|
Invalidate();
|
|
}
|
|
|
|
private MouseButtons _mouseButton = MouseButtons.None;
|
|
|
|
public Matrix TransformationMatrix { get; set; }=Matrix.Identity;
|
|
|
|
|
|
private Vector2 _lastMousePosition;
|
|
private const float NODE_SIZE = 10;
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
_mouseButton = e.Button;
|
|
_lastMousePosition = new Vector2(e.X, e.Y);
|
|
var threshold = NODE_SIZE / _zoom ;
|
|
|
|
var inverted = Matrix.Invert(TransformationMatrix);
|
|
|
|
var imageCoords = Vector2.Transform(new Vector2(e.X, e.Y), inverted);
|
|
_selectedNode=GraphicsElements.Where(x=>x.Editable).Select(x => x.GetNode(imageCoords, threshold)).FirstOrDefault(x => x != null);
|
|
}
|
|
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
_mouseButton = MouseButtons.None;
|
|
}
|
|
ToolTip _toolTip = new ToolTip();
|
|
|
|
static bool RectangleContainsPoint(int x, int y, int width, int height, int px, int py)
|
|
{
|
|
return px >= x && px <= x + width && py >= y && py <= y + height;
|
|
}
|
|
|
|
protected override void OnMouseLeave(EventArgs e)
|
|
{
|
|
_toolTip.Hide(this);
|
|
}
|
|
|
|
string GetContourInfo(Mat image, int x, int y)
|
|
{
|
|
var contours = image.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxSimple);
|
|
var contour = contours.FirstOrDefault(c => Cv2.PointPolygonTest(c, new Point2f(x, y), false) >= 0);
|
|
if (contour == null) return "No contour";
|
|
var area = Cv2.ContourArea(contour);
|
|
var rect = Cv2.BoundingRect(contour);
|
|
var width = rect.Width;
|
|
var height = rect.Height;
|
|
return $"Area: {area}, Width: {width}, Height: {height}";
|
|
|
|
}
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
// if Ctrl is pressed
|
|
|
|
|
|
|
|
if ((ModifierKeys & Keys.Control) != 0&&_mouseButton == MouseButtons.None)
|
|
{
|
|
if(Image==null||Image.ImageData==null) return;
|
|
var inverted = Matrix.Invert(TransformationMatrix);
|
|
var imageCoords = Vector2.Transform(new Vector2(e.X, e.Y), inverted);
|
|
if(!RectangleContainsPoint(0,0,Image.ImageData.Width,Image.ImageData.Height,(int)imageCoords.X,(int)imageCoords.Y)) return;
|
|
if (Image.ImageData.Type() == MatType.CV_8UC3)
|
|
{
|
|
var pixel = Image.ImageData.Get<Vec3b>((int)imageCoords.Y,(int)imageCoords.X);
|
|
_toolTip.Show($"R:{pixel.Item0} G:{pixel.Item1} B:{pixel.Item2}", this, e.X + 15, e.Y + 15, 5000);
|
|
return;
|
|
}
|
|
|
|
if (Image.ImageData.Type() == MatType.CV_8UC1)
|
|
{
|
|
var pixel = Image.ImageData.Get<byte>((int)imageCoords.Y,(int)imageCoords.X);
|
|
var info= GetContourInfo(Image.ImageData, (int)imageCoords.X, (int)imageCoords.Y);
|
|
_toolTip.Show($"Gray:{pixel}\n"+info, this, e.X+15, e.Y+15,5000);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (Mode == EControlMode.Preview)
|
|
{
|
|
if (_mouseButton == MouseButtons.Middle)
|
|
{
|
|
TransformationMatrix *=
|
|
Matrix.CreateTranslation(e.X - _lastMousePosition.X, e.Y - _lastMousePosition.Y, 0);
|
|
|
|
}
|
|
}
|
|
|
|
_mouseShift = new Vector2(e.X, e.Y) - _lastMousePosition;
|
|
if (_mouseButton == MouseButtons.Left&& _selectedNode != null)
|
|
{
|
|
var inverted = Matrix.Invert(TransformationMatrix);
|
|
var transformed = Vector2.Transform(new Vector2(e.X, e.Y), inverted);
|
|
|
|
|
|
_selectedNode.SetPosition(transformed);
|
|
|
|
|
|
}
|
|
|
|
_lastMousePosition = new Vector2(e.X, e.Y);
|
|
Invalidate();
|
|
}
|
|
|
|
private Graphics _currentGraphics;
|
|
private Workflow.Datatypes.HawkeyeImage? _image;
|
|
private Vector2 _mouseShift;
|
|
private INode _selectedNode;
|
|
|
|
protected override void OnPaint(PaintEventArgs pe)
|
|
{
|
|
if (Image == null)
|
|
{
|
|
pe.Graphics.Clear(Color.Black);
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
using (Graphics g = Graphics.FromImage(_backBuffer))
|
|
{
|
|
_currentGraphics = g;
|
|
|
|
g.Clear(Color.Black);
|
|
var img = Image.ImageData.ToBitmap();
|
|
var pos = Vector2.Transform(new Vector2(0, 0), TransformationMatrix);
|
|
var size = Vector2.Transform(new Vector2(img.Width, img.Height), TransformationMatrix);
|
|
g.DrawImage(img, pos.X, pos.Y, size.X - pos.X, size.Y - pos.Y);
|
|
|
|
|
|
foreach (IGraphicsElement element in GraphicsElements)
|
|
{
|
|
element.Draw(this);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
pe.Graphics.DrawImage(_backBuffer, 0, 0);
|
|
}
|
|
catch
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs pevent)
|
|
{
|
|
//base.OnPaintBackground(pevent);
|
|
}
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
{
|
|
if (Width == 0) return;
|
|
_backBuffer = new Bitmap(Width, Height);
|
|
}
|
|
|
|
|
|
|
|
public void DrawNode(Vector2 location)
|
|
{
|
|
|
|
var imageCoordSpace = Vector2.Transform(location, TransformationMatrix);
|
|
var start = imageCoordSpace - new Vector2(NODE_SIZE/2, NODE_SIZE / 2);
|
|
var end = (imageCoordSpace + new Vector2(NODE_SIZE / 2, NODE_SIZE / 2))-start;
|
|
|
|
|
|
_currentGraphics.FillRectangle(Brushes.White,start.X,start.Y,end.X,end.Y);
|
|
_currentGraphics.DrawRectangle(Pens.Black,start.X,start.Y,end.X,end.Y);
|
|
|
|
}
|
|
|
|
public void DrawArrow(Vector2 location, Vector2 direction)
|
|
{
|
|
var imageCoordSpaceLocation = Vector2.Transform(location, TransformationMatrix);
|
|
var imageCoordSpaceSize = Vector2.Transform(location + direction, TransformationMatrix);
|
|
var arrowPen = new Pen(Color.Aqua, 2);
|
|
arrowPen.EndCap = LineCap.Custom;
|
|
arrowPen.CustomEndCap = new AdjustableArrowCap(5,5,true);
|
|
|
|
_currentGraphics.DrawLine(arrowPen, imageCoordSpaceLocation.X, imageCoordSpaceLocation.Y, imageCoordSpaceSize.X, imageCoordSpaceSize.Y);
|
|
}
|
|
|
|
|
|
public void DrawRectangle(Vector2 start, Vector2 size, bool isGood)
|
|
{
|
|
var imageCoordSpaceLocation = Vector2.Transform(start, TransformationMatrix);
|
|
var imageCoordSpaceSize = Vector2.Transform(start+size, TransformationMatrix)-imageCoordSpaceLocation;
|
|
|
|
var pen = new Pen(isGood ? Color.Green : Color.Red, 2);
|
|
_currentGraphics.DrawRectangle(pen, imageCoordSpaceLocation.X, imageCoordSpaceLocation.Y, imageCoordSpaceSize.X, imageCoordSpaceSize.Y);
|
|
}
|
|
|
|
public void DrawLine(Vector2 start, Vector2 end,LineType type)
|
|
{
|
|
var coordStart = Vector2.Transform(start, TransformationMatrix);
|
|
var coordEnd = Vector2.Transform(end, TransformationMatrix);
|
|
var pen = new Pen(type ==LineType.Good? Color.Green:(type==LineType.Bad? Color.Red:Color.Aqua),2);
|
|
_currentGraphics.DrawLine(pen,coordStart.X,coordStart.Y,coordEnd.X,coordEnd.Y);
|
|
}
|
|
|
|
public void DrawPoly(Vector2[] points, bool isGood)
|
|
{
|
|
var pen = new Pen(isGood ? Color.Green : Color.Red, 3);
|
|
var coords = points.Select(x=> Vector2.Transform(x, TransformationMatrix)).Select(x => new PointF(x.X, x.Y)).ToArray();
|
|
_currentGraphics.DrawPolygon(pen, coords);
|
|
}
|
|
|
|
public void DrawCross(Vector2 location)
|
|
{
|
|
|
|
var startH=Vector2.Transform(location-new Vector2(10,0),TransformationMatrix);
|
|
var endH = Vector2.Transform(location+new Vector2(10,0), TransformationMatrix);
|
|
var startV =Vector2.Transform(location-new Vector2(0,20), TransformationMatrix);
|
|
var endV =Vector2.Transform(location+new Vector2(0,20), TransformationMatrix);
|
|
var pen = new Pen(Color.Aqua, 1);
|
|
_currentGraphics.DrawLine(pen, startH.X, startH.Y, endH.X, endH.Y);
|
|
_currentGraphics.DrawLine(pen, startV.X, startV.Y, endV.X, endV.Y);
|
|
}
|
|
}
|
|
}
|