init
This commit is contained in:
+336
@@ -0,0 +1,336 @@
|
||||
using OpenCvSharp;
|
||||
using ZXing;
|
||||
using ZXing.QrCode;
|
||||
using OpenCvSharp.Extensions;
|
||||
using ZXing.Windows.Compatibility;
|
||||
using ZXing.Common;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
namespace BarcodeReaderExample
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
VideoCapture camera = new VideoCapture("/dev/video0", VideoCaptureAPIs.V4L2);
|
||||
|
||||
if (!camera.IsOpened())
|
||||
throw new Exception("Nie można otworzyć kamery /dev/video0");
|
||||
|
||||
camera.Set(VideoCaptureProperties.FrameWidth, 1280);
|
||||
camera.Set(VideoCaptureProperties.FrameHeight, 720);
|
||||
camera.Set(VideoCaptureProperties.Fps, 5);
|
||||
|
||||
|
||||
// Mat frame = new Mat();
|
||||
Mat hey = new Mat("photo.png");
|
||||
|
||||
|
||||
|
||||
using var rgb = new Mat();
|
||||
|
||||
// OpenCV stores camera frames as BGR.
|
||||
// ZXing's RGBLuminanceSource expects RGB.
|
||||
// Cv2.CvtColor(frame, rgb, ColorConversionCodes.BGR2RGB);
|
||||
|
||||
|
||||
|
||||
Cv2.NamedWindow("Camera preview", WindowFlags.AutoSize);
|
||||
int photocount = 0;
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
// if (!camera.Read(frame) || frame.Empty())
|
||||
// {
|
||||
// Console.Error.WriteLine("Nie udało się odczytać klatki");
|
||||
// break;
|
||||
// }
|
||||
camera.Read(frame);
|
||||
|
||||
|
||||
Result result = GetResult(frame);
|
||||
if (result != null)
|
||||
{
|
||||
List<ResultPoint> points = result.ResultPoints.ToList();
|
||||
float[] pos1 = new float[2] {points[0].X, points[0].Y}; /// lewy dolny róg
|
||||
float[] pos2 = new float[2] {points[2].X, points[2].Y}; ///prawy górny róg
|
||||
float[] pos3 = new float[2] { points[1].X, points[1].Y }; ///lewy górny róg
|
||||
float[] pos4 = new float[2] { points[3].X, points[3].Y }; ///prawy dolny róg
|
||||
|
||||
// Cv2.Circle(frame, (int)pos1[0], (int)pos1[1], 5, Scalar.Red, 2);
|
||||
// Cv2.Circle(frame, (int)pos2[0], (int)pos2[1], 5, Scalar.Green, 2);
|
||||
// Cv2.Circle(frame, (int)pos3[0], (int)pos3[1], 5, Scalar.Blue, 2);
|
||||
|
||||
Cv2.PutText(frame, result.Text, new OpenCvSharp.Point((int)pos1[0], (int)pos1[1] - 10), HersheyFonts.HersheySimplex, 1, Scalar.Red, 2);
|
||||
|
||||
Executeit(pos1, pos2, pos3, pos4);
|
||||
// DrawLine(new PointF(LeftBottomX, LeftBottomY), new PointF(LeftTopX, LeftTopY));
|
||||
// DrawLine(new PointF(RightTopX, RightTopY), new PointF(LeftTopX, LeftTopY));
|
||||
// DrawLine(new PointF(LeftBottomX, LeftBottomY), new PointF(RightBottomX, RightBottomY));
|
||||
// DrawLine(new PointF(RightTopX, RightTopY), new PointF(RightBottomX, RightBottomY));
|
||||
|
||||
|
||||
// Cv2.Circle(frame, (int)(RightBottomX), (int)(RightBottomY), 5, Scalar.Green, 2);
|
||||
|
||||
//Cv2.Rectangle(frame, new Point((int)X, (int)Y), new Point ((int)Width, (int)Height), Scalar.Red, 2);
|
||||
Console.WriteLine(result.Text);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// frame.SaveImage($"photos/photo_{photocount}.png");
|
||||
// photocount++;
|
||||
|
||||
|
||||
Cv2.ImShow("Camera preview", frame);
|
||||
Thread.Sleep(1000 / 24);
|
||||
// Console.WriteLine("tick");
|
||||
int key = Cv2.WaitKey(1);
|
||||
if (key == 27 || key == 'q')
|
||||
break;
|
||||
}
|
||||
|
||||
Cv2.DestroyAllWindows();
|
||||
|
||||
}
|
||||
static Mat frame = new Mat();
|
||||
static void DrawLine(PointF p1, PointF p2)
|
||||
{
|
||||
Cv2.Line(frame, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y, Scalar.Red, 2);
|
||||
|
||||
}
|
||||
static void DrawLine(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
Cv2.Line(frame, (int)x1, (int)y1, (int)x2, (int)y2, Scalar.Red, 2);
|
||||
}
|
||||
|
||||
static void Executeit(float[] pos1, float[] pos2, float[] pos3, float[] pos4)
|
||||
{
|
||||
// Your detected points
|
||||
QrOverlay.Pt[] raw = {
|
||||
new QrOverlay.Pt(pos1[0], pos1[1]),
|
||||
new QrOverlay.Pt(pos2[0], pos2[1]),
|
||||
new QrOverlay.Pt(pos3[0], pos3[1]),
|
||||
new QrOverlay.Pt(pos4[0], pos4[1])
|
||||
};
|
||||
|
||||
// Get the expanded corners (margin = 0.10 → 10% bigger, try 0.08–0.18)
|
||||
QrOverlay.Pt[] box = QrOverlay.GetExpandedCorners(raw, 0.12f);
|
||||
|
||||
// Draw
|
||||
DrawLine(box[0].X, box[0].Y, box[1].X, box[1].Y);
|
||||
DrawLine(box[1].X, box[1].Y, box[2].X, box[2].Y);
|
||||
DrawLine(box[2].X, box[2].Y, box[3].X, box[3].Y);
|
||||
DrawLine(box[3].X, box[3].Y, box[0].X, box[0].Y);
|
||||
}
|
||||
|
||||
|
||||
static Result GetResult(Mat frame)
|
||||
{
|
||||
|
||||
QRCodeReader reader = new QRCodeReader();
|
||||
Result result = reader.decode(MatToBinaryBitmap(frame));
|
||||
return result;
|
||||
}
|
||||
// Sorts any 4 points into: Top-Left, Top-Right, Bottom-Right, Bottom-Left
|
||||
static PointF[] SortQrCorners(PointF[] points)
|
||||
{
|
||||
// 1. Find center
|
||||
float cx = (points[0].X + points[1].X + points[2].X + points[3].X) / 4f;
|
||||
float cy = (points[0].Y + points[1].Y + points[2].Y + points[3].Y) / 4f;
|
||||
|
||||
PointF tl = new PointF(), tr = new PointF(), br = new PointF(), bl = new PointF();
|
||||
|
||||
foreach (var p in points)
|
||||
{
|
||||
if (p.X < cx && p.Y < cy) tl = p; // top-left
|
||||
else if (p.X > cx && p.Y < cy) tr = p; // top-right
|
||||
else if (p.X > cx && p.Y > cy) br = p; // bottom-right
|
||||
else bl = p; // bottom-left
|
||||
}
|
||||
|
||||
return new PointF[] { tl, tr, br, bl };
|
||||
}
|
||||
static BinaryBitmap MatToBinaryBitmap(Mat frame)
|
||||
{
|
||||
if (frame.Empty())
|
||||
throw new ArgumentException("The Mat is empty.", nameof(frame));
|
||||
|
||||
using var rgb = new Mat();
|
||||
|
||||
// OpenCV uses BGR; ZXing RGBLuminanceSource expects RGB.
|
||||
Cv2.CvtColor(
|
||||
frame,
|
||||
rgb,
|
||||
ColorConversionCodes.BGR2RGB
|
||||
);
|
||||
|
||||
// Clone ensures contiguous pixel memory.
|
||||
using var contiguous = rgb.Clone();
|
||||
|
||||
int width = contiguous.Width;
|
||||
int height = contiguous.Height;
|
||||
|
||||
int byteCount = checked(
|
||||
(int)(contiguous.Total() * contiguous.ElemSize())
|
||||
);
|
||||
|
||||
byte[] pixels = new byte[byteCount];
|
||||
|
||||
Marshal.Copy(
|
||||
contiguous.Data,
|
||||
pixels,
|
||||
0,
|
||||
pixels.Length
|
||||
);
|
||||
|
||||
var luminanceSource = new RGBLuminanceSource(
|
||||
pixels,
|
||||
width,
|
||||
height,
|
||||
RGBLuminanceSource.BitmapFormat.RGB24
|
||||
);
|
||||
|
||||
return new BinaryBitmap(
|
||||
new HybridBinarizer(luminanceSource)
|
||||
);
|
||||
}
|
||||
}
|
||||
public static class QrOverlay
|
||||
{
|
||||
public struct Pt
|
||||
{
|
||||
public float X, Y;
|
||||
public Pt(float x, float y) { X = x; Y = y; }
|
||||
}
|
||||
|
||||
// Main function you call every frame
|
||||
public static Pt[] GetExpandedCorners(Pt[] rawPoints, float margin = 0.12f)
|
||||
{
|
||||
// 1. Sort
|
||||
var sorted = Sort(rawPoints);
|
||||
Pt tl = sorted[0], tr = sorted[1], br = sorted[2], bl = sorted[3];
|
||||
|
||||
// 2. Fix weak bottom-right
|
||||
br = new Pt(tr.X + bl.X - tl.X, tr.Y + bl.Y - tl.Y);
|
||||
|
||||
// 3. Canonical square (0..1)
|
||||
Pt[] canonical = {
|
||||
new Pt(0, 0),
|
||||
new Pt(1, 0),
|
||||
new Pt(1, 1),
|
||||
new Pt(0, 1)
|
||||
};
|
||||
|
||||
// 4. Expanded canonical square
|
||||
Pt[] expanded = {
|
||||
new Pt(-margin, -margin),
|
||||
new Pt(1 + margin, -margin),
|
||||
new Pt(1 + margin, 1 + margin),
|
||||
new Pt(-margin, 1 + margin)
|
||||
};
|
||||
|
||||
// 5. Homography: canonical → image
|
||||
float[] H = FindHomography(canonical, new[] { tl, tr, br, bl });
|
||||
|
||||
// 6. Transform the expanded points back to image
|
||||
Pt[] result = new Pt[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
result[i] = Transform(H, expanded[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
static Pt[] Sort(Pt[] pts)
|
||||
{
|
||||
float cx = (pts[0].X + pts[1].X + pts[2].X + pts[3].X) * 0.25f;
|
||||
float cy = (pts[0].Y + pts[1].Y + pts[2].Y + pts[3].Y) * 0.25f;
|
||||
|
||||
Pt tl = default, tr = default, br = default, bl = default;
|
||||
foreach (var p in pts)
|
||||
{
|
||||
if (p.X < cx && p.Y < cy) tl = p;
|
||||
else if (p.X >= cx && p.Y < cy) tr = p;
|
||||
else if (p.X >= cx && p.Y >= cy) br = p;
|
||||
else bl = p;
|
||||
}
|
||||
return new[] { tl, tr, br, bl };
|
||||
}
|
||||
|
||||
static Pt Transform(float[] H, Pt p)
|
||||
{
|
||||
float x = H[0] * p.X + H[1] * p.Y + H[2];
|
||||
float y = H[3] * p.X + H[4] * p.Y + H[5];
|
||||
float w = H[6] * p.X + H[7] * p.Y + H[8];
|
||||
return new Pt(x / w, y / w);
|
||||
}
|
||||
|
||||
// Simple but robust 4-point homography (DLT)
|
||||
static float[] FindHomography(Pt[] src, Pt[] dst)
|
||||
{
|
||||
// Build the 8x9 matrix and solve with Gaussian elimination
|
||||
float[,] A = new float[8, 9];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
float x = src[i].X, y = src[i].Y;
|
||||
float u = dst[i].X, v = dst[i].Y;
|
||||
|
||||
A[i * 2, 0] = x; A[i * 2, 1] = y; A[i * 2, 2] = 1;
|
||||
A[i * 2, 6] = -u * x; A[i * 2, 7] = -u * y; A[i * 2, 8] = -u;
|
||||
|
||||
A[i * 2 + 1, 3] = x; A[i * 2 + 1, 4] = y; A[i * 2 + 1, 5] = 1;
|
||||
A[i * 2 + 1, 6] = -v * x; A[i * 2 + 1, 7] = -v * y; A[i * 2 + 1, 8] = -v;
|
||||
}
|
||||
|
||||
// Gaussian elimination to find null-space (last column)
|
||||
// (simplified solver – works well for QR)
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
// Pivot
|
||||
int pivot = i;
|
||||
for (int j = i + 1; j < 8; j++)
|
||||
if (Math.Abs(A[j, i]) > Math.Abs(A[pivot, i])) pivot = j;
|
||||
|
||||
for (int k = 0; k < 9; k++)
|
||||
{
|
||||
float tmp = A[i, k];
|
||||
A[i, k] = A[pivot, k];
|
||||
A[pivot, k] = tmp;
|
||||
}
|
||||
|
||||
float div = A[i, i];
|
||||
if (Math.Abs(div) < 1e-8f) continue;
|
||||
for (int k = i; k < 9; k++) A[i, k] /= div;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
if (j == i) continue;
|
||||
float f = A[j, i];
|
||||
for (int k = i; k < 9; k++)
|
||||
A[j, k] -= f * A[i, k];
|
||||
}
|
||||
}
|
||||
|
||||
float[] H = new float[9];
|
||||
for (int i = 0; i < 8; i++) H[i] = -A[i, 8];
|
||||
H[8] = 1f;
|
||||
return H;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user