效果
项目
代码
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Onnx_Demo
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string image_path = "";
string startupPath;
string model_path;
DateTime dt1 = DateTime.Now;
DateTime dt2 = DateTime.Now;
Mat image;
Mat result_image;
SessionOptions options;
InferenceSession onnx_session;
Tensor<float> input_tensor;
List<NamedOnnxValue> input_ontainer;
IDisposableReadOnlyCollection<DisposableNamedOnnxValue> result_infer;
DisposableNamedOnnxValue[] results_onnxvalue;
StringBuilder sb = new StringBuilder();
float binaryThreshold = 0.5f;
float polygonThreshold = 0.7f;
float unclipRatio = 1.5f;
int maxCandidates = 1000;
float[] mean = { 0.485f, 0.456f, 0.406f };
float[] std = { 0.229f, 0.224f, 0.225f };
int inpWidth = 736;
int inpHeight = 736;
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);
}
private void Form1_Load(object sender, EventArgs e)
{
startupPath = Application.StartupPath + "\\model\\";
model_path = startupPath + "model_0.88_depoly.onnx";
// 创建输出会话
options = new SessionOptions();
options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
options.AppendExecutionProvider_CPU(0);// 设置为CPU上运行
// 创建推理模型类,读取本地模型文件
onnx_session = new InferenceSession(model_path, options);
// 输入Tensor
input_tensor = new DenseTensor<float>(new[] { 1, 3, inpHeight, inpWidth });
// 创建输入容器
input_ontainer = new List<NamedOnnxValue>();
}
float ContourScore(Mat binary, OpenCvSharp.Point[] contour)
{
Rect rect = Cv2.BoundingRect(contour);
int xmin = Math.Max(rect.X, 0);
int xmax = Math.Min(rect.X + rect.Width, binary.Cols - 1);
int ymin = Math.Max(rect.Y, 0);
int ymax = Math.Min(rect.Y + rect.Height, binary.Rows - 1);
Mat binROI = new Mat(binary, new Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1));
Mat mask = Mat.Zeros(new OpenCvSharp.Size(xmax - xmin + 1, ymax - ymin + 1), MatType.CV_8UC1);
List<OpenCvSharp.Point> roiContour = new List<OpenCvSharp.Point>();
foreach (var item in contour)
{
OpenCvSharp.Point pt = new OpenCvSharp.Point(item.X - xmin, item.Y - ymin);
roiContour.Add(pt);
}
List<List<OpenCvSharp.Point>> roiContours = new List<List<OpenCvSharp.Point>>
{
roiContour
};
Cv2.FillPoly(mask, roiContours, new Scalar(1));
float score = (float)Cv2.Mean(binROI)[0];
return score;
}
void Unclip(List<Point2f> inPoly, List<Point2f> outPoly)
{
float area = (float)Cv2.ContourArea(inPoly);
float length = (float)Cv2.ArcLength(inPoly, true);
float distance = area * unclipRatio / length;
int numPoints = inPoly.Count();
List<List<Point2f>> newLines = new List<List<Point2f>>();
for (int i = 0; i < numPoints; i++)
{
List<Point2f> newLine = new List<Point2f>();
OpenCvSharp.Point pt1 = (OpenCvSharp.Point)inPoly[i];
int index = (i - 1) % numPoints;
if (index <= 0) index = 0;
OpenCvSharp.Point pt2 = (OpenCvSharp.Point)inPoly[index];
OpenCvSharp.Point vec = pt1 - pt2;
Mat mat_vec = new Mat(1, 2, MatType.CV_8U, new int[] { vec.X, vec.Y });
float unclipDis = (float)(distance / Cv2.Norm(mat_vec));
Point2f rotateVec = new Point2f(vec.Y * unclipDis, -vec.X * unclipDis);
newLine.Add(new Point2f(pt1.X + rotateVec.X, pt1.Y + rotateVec.Y));
newLine.Add(new Point2f(pt2.X + rotateVec.X, pt2.Y + rotateVec.Y));
newLines.Add(newLine);
}
int numLines = newLines.Count();
for (int i = 0; i < numLines; i++)
{
Point2f a = newLines[i][0];
Point2f b = newLines[i][1];
Point2f c = newLines[(i + 1) % numLines][0];
Point2f d = newLines[(i + 1) % numLines][1];
Point2f pt;
Point2f v1 = b - a;
Point2f v2 = d - c;
Mat mat_v1 = new Mat(1, 2, MatType.CV_32FC1, new float[] { v1.X, v1.Y });
Mat mat_v2 = new Mat(1, 2, MatType.CV_32FC1, new float[] { v2.X, v2.Y });
float cosAngle = (float)((v1.X * v2.X + v1.Y * v2.Y) / (Cv2.Norm(mat_v1) * Cv2.Norm(mat_v2)));
if (Math.Abs(cosAngle) > 0.7)
{
pt.X = (float)((b.X + c.X) * 0.5);
pt.Y = (float)((b.Y + c.Y) * 0.5);
}
else
{
float denom = a.X * (float)(d.Y - c.Y) + b.X * (float)(c.Y - d.Y) +
d.X * (float)(b.Y - a.Y) + c.X * (float)(a.Y - b.Y);
float num = a.X * (float)(d.Y - c.Y) + c.X * (float)(a.Y - d.Y) + d.X * (float)(c.Y - a.Y);
float s = num / denom;
pt.X = a.X + s * (b.X - a.X);
pt.Y = a.Y + s * (b.Y - a.Y);
}
outPoly.Add(pt);
}
}
private void button2_Click(object sender, EventArgs e)
{
if (image_path == "")
{
return;
}
textBox1.Text = "检测中,请稍等……";
pictureBox2.Image = null;
Application.DoEvents();
//图片
image = new Mat(image_path);
//将图片转为RGB通道
Mat image_rgb = new Mat();
Cv2.CvtColor(image, image_rgb, ColorConversionCodes.BGR2RGB);
Mat resize_image = new Mat();
Cv2.Resize(image_rgb, resize_image, new OpenCvSharp.Size(inpHeight, inpWidth));
//输入Tensor
for (int y = 0; y < resize_image.Height; y++)
{
for (int x = 0; x < resize_image.Width; x++)
{
input_tensor[0, 0, y, x] = (resize_image.At<Vec3b>(y, x)[0] / 255f - mean[0]) / std[0];
input_tensor[0, 1, y, x] = (resize_image.At<Vec3b>(y, x)[1] / 255f - mean[1]) / std[1];
input_tensor[0, 2, y, x] = (resize_image.At<Vec3b>(y, x)[2] / 255f - mean[2]) / std[2];
}
}
//将 input_tensor 放入一个输入参数的容器,并指定名称
input_ontainer.Add(NamedOnnxValue.CreateFromTensor("input", input_tensor));
dt1 = DateTime.Now;
//运行 Inference 并获取结果
result_infer = onnx_session.Run(input_ontainer);
dt2 = DateTime.Now;
//将输出结果转为DisposableNamedOnnxValue数组
results_onnxvalue = result_infer.ToArray();
var result_array = results_onnxvalue[0].AsTensor<float>().ToArray();
Mat binary = new Mat(resize_image.Rows, resize_image.Cols, MatType.CV_32FC1, result_array);
// threshold
Mat threshold = new Mat();
Cv2.Threshold(binary, threshold, binaryThreshold, 255, ThresholdTypes.Binary);
Cv2.ImShow("threshold", threshold);
int h = image.Rows;
int w = image.Cols;
float scaleHeight = (float)(h) / (float)(binary.Size(0));
float scaleWidth = (float)(w) / (float)(binary.Size(1));
threshold.ConvertTo(threshold, MatType.CV_8UC1);
// Find contours
OpenCvSharp.Point[][] contours;
HierarchyIndex[] hierarchly;
Cv2.FindContours(threshold, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
// Candidate number limitation
int numCandidate = Math.Min(contours.Count(), maxCandidates > 0 ? maxCandidates : int.MaxValue);
List<List<Point2f>> results = new List<List<Point2f>>();
for (int i = 0; i < numCandidate; i++)
{
OpenCvSharp.Point[] contour = contours[i];
// Calculate text contour score
if (ContourScore(binary, contour) < polygonThreshold)
continue;
// Rescale
List<OpenCvSharp.Point> contourScaled = new List<OpenCvSharp.Point>();
foreach (var item in contour)
{
contourScaled.Add(new OpenCvSharp.Point((int)(item.X * scaleWidth), (int)(item.Y * scaleHeight)));
}
RotatedRect box = Cv2.MinAreaRect(contourScaled);
// minArea() rect is not normalized, it may return rectangles with angle=-90 or height < width
float angle_threshold = 60; // do not expect vertical text, TODO detection algo property
bool swap_size = false;
if (box.Size.Width < box.Size.Height) // horizontal-wide text area is expected
{
swap_size = true;
}
else if (Math.Abs(box.Angle) >= angle_threshold) // don't work with vertical rectangles
{
swap_size = true;
}
if (swap_size)
{
float temp = box.Size.Width;
box.Size.Width = box.Size.Height;
box.Size.Height = temp;
if (box.Angle < 0)
box.Angle += 90;
else if (box.Angle > 0)
box.Angle -= 90;
}
Point2f[] vertex = new Point2f[4];
vertex = box.Points(); // order: bl, tl, tr, br
List<Point2f> approx = new List<Point2f>();
for (int j = vertex.Length - 1; j >= 0; j--)
{
approx.Add(vertex[j]);
}
List<Point2f> polygon = new List<Point2f>();
Unclip(approx, polygon);
results.Add(approx);
}
result_image = image.Clone();
for (int i = 0; i < results.Count; i++)
{
for (int j = 0; j < 4; j++)
{
Cv2.Circle(result_image
, new OpenCvSharp.Point((int)results[i][j].X, (int)results[i][j].Y)
, 2
, new Scalar(0, 0, 255)
, -1);
if (j < 3)
{
Cv2.Line(result_image
, new OpenCvSharp.Point((int)results[i][j].X, (int)results[i][j].Y)
, new OpenCvSharp.Point((int)results[i][j + 1].X, (int)results[i][j + 1].Y)
, new Scalar(0, 255, 0), 2);
}
else
{
Cv2.Line(result_image
, new OpenCvSharp.Point((int)results[i][j].X, (int)results[i][j].Y)
, new OpenCvSharp.Point((int)results[i][0].X, (int)results[i][0].Y)
, new Scalar(0, 255, 0), 2);
}
}
}
pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
sb.Clear();
sb.AppendLine("推理耗时:" + (dt2 - dt1).TotalMilliseconds + "ms");
sb.AppendLine("------------------------------");
textBox1.Text = sb.ToString();
}
private void pictureBox2_DoubleClick(object sender, EventArgs e)
{
Common.ShowNormalImg(pictureBox2.Image);
}
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
Common.ShowNormalImg(pictureBox1.Image);
}
}
}
下载
可执行程序exe包下载
源码下载