C# OpenCvSharp DNN 卡证检测矫正
目录
说明
源码地址:https://modelscope.cn/models/iic/cv_resnet_carddetection_scrfd34gkps
在实人认证、文档电子化等场景中需要自动化提取卡证的信息,以便进一步做录入处理。这类场景通常存在两类问题,一是识别卡证类型时易受背景干扰,二是卡证拍摄角度造成的文字畸变影响OCR准确率。鉴于证件类数据的敏感性,我们采用大量合成卡证数据做训练(参见:SyntheticCards), 并改造人脸检测SOTA方法SCRFD(论文地址, 代码地址)训练了卡证检测矫正模型,可以对各类国际常见卡证(如,身份证、护照、驾照等)进行检测、定位及矫正,得到去除背景的正视角卡证图像,便于后续卡证分类或OCR内容提取。
效果
模型
Model Properties
-------------------------
---------------------------------------------------------------
Inputs
-------------------------
name:input.1
tensor:Float[1, 3, 640, 640]
---------------------------------------------------------------
Outputs
-------------------------
name:1401
tensor:Float[1, 25600, 1]
name:1455
tensor:Float[1, 6400, 1]
name:1507
tensor:Float[1, 1600, 1]
name:1408
tensor:Float[1, 25600, 4]
name:1461
tensor:Float[1, 6400, 4]
name:1513
tensor:Float[1, 1600, 4]
name:1415
tensor:Float[1, 25600, 8]
name:1467
tensor:Float[1, 6400, 8]
name:1519
tensor:Float[1, 1600, 8]
---------------------------------------------------------------
项目
代码
using OpenCvSharp;
using OpenCvSharp.Dnn;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace OpenCvSharp_Demo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string startupPath;
string image_path;
private void Form1_Load(object sender, EventArgs e)
{
startupPath = System.Windows.Forms.Application.StartupPath;
image_path = "1.jpg";
pictureBox1.Image = new Bitmap(image_path);
image = new Mat(image_path);
opencv_net = CvDnn.ReadNetFromOnnx("carddetection_scrfd34gkps.onnx");
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = fileFilter;
if (ofd.ShowDialog() != DialogResult.OK) return;
pictureBox1.Image = null;
pictureBox2.Image = null;
textBox1.Text = "";
image_path = ofd.FileName;
pictureBox1.Image = new Bitmap(image_path);
image = new Mat(image_path);
}
Stopwatch stopwatch = new Stopwatch();
Net opencv_net;
Mat BN_image;
Mat image;
Mat result_image;
float[] stride = { 8.0f, 16.0f, 32.0f };
int inpWidth = 640;
int inpHeight = 640;
float confThreshold = 0.5f;
float nmsThreshold = 0.5f;
bool keep_ratio = true;
Mat resize_image(Mat srcimg, ref int newh, ref int neww, ref int top, ref int left)
{
int srch = srcimg.Rows, srcw = srcimg.Cols;
newh = inpHeight;
neww = inpWidth;
Mat dstimg = new Mat();
if (keep_ratio && srch != srcw)
{
float hw_scale = (float)srch / srcw;
if (hw_scale > 1)
{
newh = inpHeight;
neww = (int)(inpWidth / hw_scale);
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
left = (int)((inpWidth - neww) * 0.5);
Cv2.CopyMakeBorder(dstimg, dstimg, 0, 0, left, inpWidth - neww - left, BorderTypes.Constant, 0);
}
else
{
newh = (int)(inpHeight * hw_scale);
neww = inpWidth;
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
top = (int)((inpHeight - newh) * 0.5);
Cv2.CopyMakeBorder(dstimg, dstimg, top, inpHeight - newh - top, 0, 0, BorderTypes.Constant, 0);
}
}
else
{
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
}
return dstimg;
}
unsafe private void button2_Click(object sender, EventArgs e)
{
if (image_path == "")
{
return;
}
stopwatch.Restart();
image = new Mat(image_path);
result_image = image.Clone();
int newh = 0, neww = 0, padh = 0, padw = 0;
Mat img = resize_image(image, ref newh, ref neww, ref padh, ref padw);
Mat blob = new Mat();
BN_image = CvDnn.BlobFromImage(img, 1 / 128.0, new OpenCvSharp.Size(inpWidth, inpHeight), new Scalar(127.5, 127.5, 127.5), true, false);
opencv_net.SetInput(BN_image);
Mat[] outs = new Mat[9] { new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat() };
string[] outBlobNames = opencv_net.GetUnconnectedOutLayersNames();
opencv_net.Forward(outs, outBlobNames);
//generate proposals
List<float> confidences = new List<float>();
List<Rect> boxes = new List<Rect>();
List<List<int>> landmarks = new List<List<int>>();
float ratioh = (float)image.Rows / newh, ratiow = (float)image.Cols / neww;
int n = 0, i = 0, j = 0, k = 0, l = 0;
for (n = 0; n < 3; n++)
{
int num_grid_x = (int)(inpWidth / stride[n]);
int num_grid_y = (int)(inpHeight / stride[n]);
float* pdata_score = (float*)outs[n * 3].Data; //score
float* pdata_bbox = (float*)outs[n * 3 + 1].Data; //bounding box
float* pdata_kps = (float*)outs[n * 3 + 2].Data; //face landmark
for (i = 0; i < num_grid_y; i++)
{
for (j = 0; j < num_grid_x; j++)
{
for (k = 0; k < 4; k++)
{
if (pdata_score[0] > confThreshold)
{
int xmin = (int)(((j - pdata_bbox[0]) * stride[n] - padw) * ratiow);
int ymin = (int)(((i - pdata_bbox[1]) * stride[n] - padh) * ratioh);
int width = (int)((pdata_bbox[2] + pdata_bbox[0]) * stride[n] * ratiow);
int height = (int)((pdata_bbox[3] + pdata_bbox[1]) * stride[n] * ratioh);
confidences.Add(pdata_score[0]);
boxes.Add(new Rect(xmin, ymin, width, height));
List<int> landmark = new List<int>();
for (l = 0; l < 8; l += 2)
{
landmark.Add((int)(((j + pdata_kps[l]) * stride[n] - padw) * ratiow));
landmark.Add((int)(((i + pdata_kps[l + 1]) * stride[n] - padh) * ratioh));
}
landmarks.Add(landmark);
}
pdata_score++;
pdata_bbox += 4;
pdata_kps += 8;
}
}
}
}
//vector<int> indices;
int[] indices;
CvDnn.NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, out indices);
//draw bbox and kps
for (i = 0; i < indices.Length; ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
Cv2.Rectangle(result_image, new OpenCvSharp.Point(box.X, box.Y), new OpenCvSharp.Point(box.X + box.Width, box.Y + box.Height), new Scalar(0, 0, 255), 2);
for (k = 0; k < 8; k += 2)
{
Cv2.Circle(result_image, new OpenCvSharp.Point(landmarks[idx][k], landmarks[idx][k + 1]), 10, new Scalar(0, 255, 0), -1);
}
//Get the label for the class name and its confidence
string label = confidences[idx].ToString("P2");
//Display the label at the top of the bounding box
int baseLine;
OpenCvSharp.Size labelSize = Cv2.GetTextSize(label, HersheyFonts.HersheySimplex, 0.5, 2, out baseLine);
int top = Math.Max(box.Y, labelSize.Height);
Cv2.PutText(result_image, label, new OpenCvSharp.Point(box.X, top - 10), HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 0), 2);
}
stopwatch.Stop();
double costTime = stopwatch.Elapsed.TotalMilliseconds;
textBox1.Text = $"耗时:{costTime:F2}ms";
pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
}
private void button3_Click(object sender, EventArgs e)
{
if (pictureBox2.Image == null)
{
return;
}
Bitmap output = new Bitmap(pictureBox2.Image);
var sdf = new SaveFileDialog();
sdf.Title = "保存";
sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp";
if (sdf.ShowDialog() == DialogResult.OK)
{
switch (sdf.FilterIndex)
{
case 1:
{
output.Save(sdf.FileName, ImageFormat.Jpeg);
break;
}
case 2:
{
output.Save(sdf.FileName, ImageFormat.Png);
break;
}
case 3:
{
output.Save(sdf.FileName, ImageFormat.Bmp);
break;
}
}
MessageBox.Show("保存成功,位置:" + sdf.FileName);
}
}
}
}
using OpenCvSharp;
using OpenCvSharp.Dnn;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace OpenCvSharp_Demo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string startupPath;
string image_path;
private void Form1_Load(object sender, EventArgs e)
{
startupPath = System.Windows.Forms.Application.StartupPath;
image_path = "1.jpg";
pictureBox1.Image = new Bitmap(image_path);
image = new Mat(image_path);
opencv_net = CvDnn.ReadNetFromOnnx("carddetection_scrfd34gkps.onnx");
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = fileFilter;
if (ofd.ShowDialog() != DialogResult.OK) return;
pictureBox1.Image = null;
pictureBox2.Image = null;
textBox1.Text = "";
image_path = ofd.FileName;
pictureBox1.Image = new Bitmap(image_path);
image = new Mat(image_path);
}
Stopwatch stopwatch = new Stopwatch();
Net opencv_net;
Mat BN_image;
Mat image;
Mat result_image;
float[] stride = { 8.0f, 16.0f, 32.0f };
int inpWidth = 640;
int inpHeight = 640;
float confThreshold = 0.5f;
float nmsThreshold = 0.5f;
bool keep_ratio = true;
Mat resize_image(Mat srcimg, ref int newh, ref int neww, ref int top, ref int left)
{
int srch = srcimg.Rows, srcw = srcimg.Cols;
newh = inpHeight;
neww = inpWidth;
Mat dstimg = new Mat();
if (keep_ratio && srch != srcw)
{
float hw_scale = (float)srch / srcw;
if (hw_scale > 1)
{
newh = inpHeight;
neww = (int)(inpWidth / hw_scale);
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
left = (int)((inpWidth - neww) * 0.5);
Cv2.CopyMakeBorder(dstimg, dstimg, 0, 0, left, inpWidth - neww - left, BorderTypes.Constant, 0);
}
else
{
newh = (int)(inpHeight * hw_scale);
neww = inpWidth;
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
top = (int)((inpHeight - newh) * 0.5);
Cv2.CopyMakeBorder(dstimg, dstimg, top, inpHeight - newh - top, 0, 0, BorderTypes.Constant, 0);
}
}
else
{
Cv2.Resize(srcimg, dstimg, new OpenCvSharp.Size(neww, newh));
}
return dstimg;
}
unsafe private void button2_Click(object sender, EventArgs e)
{
if (image_path == "")
{
return;
}
stopwatch.Restart();
image = new Mat(image_path);
result_image = image.Clone();
int newh = 0, neww = 0, padh = 0, padw = 0;
Mat img = resize_image(image, ref newh, ref neww, ref padh, ref padw);
Mat blob = new Mat();
BN_image = CvDnn.BlobFromImage(img, 1 / 128.0, new OpenCvSharp.Size(inpWidth, inpHeight), new Scalar(127.5, 127.5, 127.5), true, false);
opencv_net.SetInput(BN_image);
Mat[] outs = new Mat[9] { new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat(), new Mat() };
string[] outBlobNames = opencv_net.GetUnconnectedOutLayersNames();
opencv_net.Forward(outs, outBlobNames);
//generate proposals
List<float> confidences = new List<float>();
List<Rect> boxes = new List<Rect>();
List<List<int>> landmarks = new List<List<int>>();
float ratioh = (float)image.Rows / newh, ratiow = (float)image.Cols / neww;
int n = 0, i = 0, j = 0, k = 0, l = 0;
for (n = 0; n < 3; n++)
{
int num_grid_x = (int)(inpWidth / stride[n]);
int num_grid_y = (int)(inpHeight / stride[n]);
float* pdata_score = (float*)outs[n * 3].Data; //score
float* pdata_bbox = (float*)outs[n * 3 + 1].Data; //bounding box
float* pdata_kps = (float*)outs[n * 3 + 2].Data; //face landmark
for (i = 0; i < num_grid_y; i++)
{
for (j = 0; j < num_grid_x; j++)
{
for (k = 0; k < 4; k++)
{
if (pdata_score[0] > confThreshold)
{
int xmin = (int)(((j - pdata_bbox[0]) * stride[n] - padw) * ratiow);
int ymin = (int)(((i - pdata_bbox[1]) * stride[n] - padh) * ratioh);
int width = (int)((pdata_bbox[2] + pdata_bbox[0]) * stride[n] * ratiow);
int height = (int)((pdata_bbox[3] + pdata_bbox[1]) * stride[n] * ratioh);
confidences.Add(pdata_score[0]);
boxes.Add(new Rect(xmin, ymin, width, height));
List<int> landmark = new List<int>();
for (l = 0; l < 8; l += 2)
{
landmark.Add((int)(((j + pdata_kps[l]) * stride[n] - padw) * ratiow));
landmark.Add((int)(((i + pdata_kps[l + 1]) * stride[n] - padh) * ratioh));
}
landmarks.Add(landmark);
}
pdata_score++;
pdata_bbox += 4;
pdata_kps += 8;
}
}
}
}
//vector<int> indices;
int[] indices;
CvDnn.NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, out indices);
//draw bbox and kps
for (i = 0; i < indices.Length; ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
Cv2.Rectangle(result_image, new OpenCvSharp.Point(box.X, box.Y), new OpenCvSharp.Point(box.X + box.Width, box.Y + box.Height), new Scalar(0, 0, 255), 2);
for (k = 0; k < 8; k += 2)
{
Cv2.Circle(result_image, new OpenCvSharp.Point(landmarks[idx][k], landmarks[idx][k + 1]), 10, new Scalar(0, 255, 0), -1);
}
//Get the label for the class name and its confidence
string label = confidences[idx].ToString("P2");
//Display the label at the top of the bounding box
int baseLine;
OpenCvSharp.Size labelSize = Cv2.GetTextSize(label, HersheyFonts.HersheySimplex, 0.5, 2, out baseLine);
int top = Math.Max(box.Y, labelSize.Height);
Cv2.PutText(result_image, label, new OpenCvSharp.Point(box.X, top - 10), HersheyFonts.HersheySimplex, 1, new Scalar(0, 255, 0), 2);
}
stopwatch.Stop();
double costTime = stopwatch.Elapsed.TotalMilliseconds;
textBox1.Text = $"耗时:{costTime:F2}ms";
pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
}
private void button3_Click(object sender, EventArgs e)
{
if (pictureBox2.Image == null)
{
return;
}
Bitmap output = new Bitmap(pictureBox2.Image);
var sdf = new SaveFileDialog();
sdf.Title = "保存";
sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp";
if (sdf.ShowDialog() == DialogResult.OK)
{
switch (sdf.FilterIndex)
{
case 1:
{
output.Save(sdf.FileName, ImageFormat.Jpeg);
break;
}
case 2:
{
output.Save(sdf.FileName, ImageFormat.Png);
break;
}
case 3:
{
output.Save(sdf.FileName, ImageFormat.Bmp);
break;
}
}
MessageBox.Show("保存成功,位置:" + sdf.FileName);
}
}
}
}
下载
参考
https://github.com/hpc203/cv_resnet_carddetection_scrfd34gkps-opencv-dnn
原文地址:https://blog.csdn.net/lw112190/article/details/144754559
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!