Files
qrcode/Program.cs
T
2026-08-12 02:04:31 +02:00

337 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}