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 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; } } }