效果
計畫
程式碼
using OpenCvSharp.Extensions;
using OpenCvSharp;
using Sdcb.PaddleInference.Native;
using Sdcb.PaddleInference;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Globalization;
namespace PaddleInference_文字檢測
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Bitmap bmp;
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string img = "";
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = fileFilter;
if (ofd.ShowDialog() != DialogResult.OK) return;
pictureBox1.Image = null;
img = ofd.FileName;
bmp = new Bitmap(img);
pictureBox1.Image = new Bitmap(img);
}
int MaxSize = 1536;
float? BoxThreshold = 0.3f;
float? BoxScoreThreahold = 0.7f;
int? DilatedSize = 2;
int MinSize = 3;
float UnclipRatio = 2.0f;
PaddlePredictor predictor;
private unsafe void Form1_Load(object sender, EventArgs e)
{
IntPtr _ptr = PaddleNative.PD_ConfigCreate();
Encoding PaddleEncoding = Environment.OSVersion.Platform == PlatformID.Win32NT ? Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.ANSICodePage) : Encoding.UTF8;
//設定推理模型路徑
String programPath = Application.StartupPath + "\\ch_PP-OCRv3_det\\inference.pdmodel";
String paramsPath = Application.StartupPath + "\\ch_PP-OCRv3_det\\inference.pdiparams";
byte[] programBytes = PaddleEncoding.GetBytes(programPath);
byte[] paramsBytes = PaddleEncoding.GetBytes(paramsPath);
fixed (byte* programPtr = programBytes)
fixed (byte* paramsPtr = paramsBytes)
{
PaddleNative.PD_ConfigSetModel(_ptr, (IntPtr)programPtr, (IntPtr)paramsPtr);
}
predictor = new PaddlePredictor(PaddleNative.PD_PredictorCreate(_ptr));
}
private void button2_Click(object sender, EventArgs e)
{
if (img == "")
{
return;
}
Mat src = Cv2.ImRead(img);
Mat resized = MatResize(src, MaxSize);
Mat padded = MatPadding32(resized);
Mat normalized = Normalize(padded);
OpenCvSharp.Size resizedSize = resized.Size();
using (PaddleTensor input = predictor.GetInputTensor(predictor.InputNames[0]))
{
input.Shape = new[] { 1, 3, normalized.Rows, normalized.Cols };
float[] setData = ExtractMat(normalized);
input.SetData(setData);
}
if (!predictor.Run())
{
throw new Exception("PaddlePredictor(Detector) run failed.");
}
using (PaddleTensor output = predictor.GetOutputTensor(predictor.OutputNames[0]))
{
float[] data = output.GetData<float>();
int[] shape = output.Shape;
Mat pred = new Mat(shape[2], shape[3], MatType.CV_32FC1, data);
Mat cbuf = new Mat();
Mat roi = pred[0, resizedSize.Height, 0, resizedSize.Width];
roi.ConvertTo(cbuf, MatType.CV_8UC1, 255);
Mat dilated = new Mat();
Mat binary = BoxThreshold != null ?
cbuf.Threshold((int)(BoxThreshold * 255), 255, ThresholdTypes.Binary) :
cbuf;
if (DilatedSize != null)
{
Mat ones = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(DilatedSize.Value, DilatedSize.Value));
Cv2.Dilate(binary, dilated, ones);
ones.Dispose();
}
else
{
Cv2.CopyTo(binary, dilated);
}
OpenCvSharp.Point[][] contours = dilated.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxSimple);
OpenCvSharp.Size size = src.Size();
double scaleRate = 1.0 * src.Width / resizedSize.Width;
RotatedRect[] rects = contours
.Where(x => BoxScoreThreahold == null || GetScore(x, pred) > BoxScoreThreahold)
.Select(x => Cv2.MinAreaRect(x))
.Where(x => x.Size.Width > MinSize && x.Size.Height > MinSize)
.Select(rect =>
{
float minEdge = Math.Min(rect.Size.Width, rect.Size.Height);
Size2f newSize = new Size2f(
(rect.Size.Width + UnclipRatio * minEdge) * scaleRate,
(rect.Size.Height + UnclipRatio * minEdge) * scaleRate);
RotatedRect largerRect = new RotatedRect(rect.Center * scaleRate, newSize, rect.Angle);
return largerRect;
})
.OrderBy(v => v.Center.Y)
.ThenBy(v => v.Center.X)
.ToArray();
src.Dispose();
binary.Dispose();
roi.Dispose();
cbuf.Dispose();
pred.Dispose();
dilated.Dispose();
//繪圖
Mat src2 = Cv2.ImRead(img);
for (int i = 0; i < rects.Length; i++)
{
Scalar scalar = Scalar.RandomColor();
List<OpenCvSharp.Point> temp = new List<OpenCvSharp.Point>();
foreach (var item2 in rects[i].Points())
{
temp.Add(new OpenCvSharp.Point(item2.X, item2.Y));
}
List<List<OpenCvSharp.Point>> lltemp = new List<List<OpenCvSharp.Point>>();
lltemp.Add(temp);
Cv2.Polylines(src2, lltemp, true, scalar);
}
if (pictureBox1.Image!=null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = BitmapConverter.ToBitmap(src2);
src2.Dispose();
}
}
private float GetScore(OpenCvSharp.Point[] contour, Mat pred)
{
int width = pred.Width;
int height = pred.Height;
int[] boxX = contour.Select(v => v.X).ToArray();
int[] boxY = contour.Select(v => v.Y).ToArray();
int xmin = Clamp(boxX.Min(), 0, width - 1);
int xmax = Clamp(boxX.Max(), 0, width - 1);
int ymin = Clamp(boxY.Min(), 0, height - 1);
int ymax = Clamp(boxY.Max(), 0, height - 1);
OpenCvSharp.Point[] rootPoints = contour
.Select(v => new OpenCvSharp.Point(v.X - xmin, v.Y - ymin))
.ToArray();
Mat mask = new Mat(ymax - ymin + 1, xmax - xmin + 1, MatType.CV_8UC1, Scalar.Black);
mask.FillPoly(new[] { rootPoints }, new Scalar(1));
Mat croppedMat = pred[ymin, ymax + 1, xmin, xmax + 1];
float score = (float)croppedMat.Mean(mask).Val0;
return score;
}
public int Clamp(int val, int min, int max)
{
if (val < min)
{
return min;
}
elseif (val > max)
{
return max;
}
return val;
}
float[] ExtractMat(Mat src)
{
int rows = src.Rows;
int cols = src.Cols;
float[] array = new float[rows * cols * 3];
GCHandle gCHandle = default(GCHandle);
try
{
gCHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr intPtr = gCHandle.AddrOfPinnedObject();
for (int i = 0; i < src.Channels(); i++)
{
Mat dest = new Mat(rows, cols, MatType.CV_32FC1, intPtr + i * rows * cols * 4, 0L);
Cv2.ExtractChannel(src, dest, i);
dest.Dispose();
}
return array;
}
finally
{
gCHandle.Free();
}
}
private Mat MatResize(Mat src, int? maxSize)
{
if (maxSize == null) return src.Clone();
OpenCvSharp.Size size = src.Size();
int longEdge = Math.Max(size.Width, size.Height);
double scaleRate = 1.0 * maxSize.Value / longEdge;
return scaleRate < 1.0 ?
src.Resize(OpenCvSharp.Size.Zero, scaleRate, scaleRate) :
src.Clone();
}
private Mat MatPadding32(Mat src)
{
OpenCvSharp.Size size = src.Size();
OpenCvSharp.Size newSize = new OpenCvSharp.Size(
32 * Math.Ceiling(1.0 * size.Width / 32),
32 * Math.Ceiling(1.0 * size.Height / 32));
return src.CopyMakeBorder(0, newSize.Height - size.Height, 0, newSize.Width - size.Width, BorderTypes.Constant, Scalar.Black);
}
private Mat Normalize(Mat src)
{
Mat normalized = new Mat();
src.ConvertTo(normalized, MatType.CV_32FC3, 1.0 / 255);
Mat[] bgr = normalized.Split();
float[] scales = new[] { 1 / 0.229f, 1 / 0.224f, 1 / 0.225f };
float[] means = new[] { 0.485f, 0.456f, 0.406f };
for (int i = 0; i < bgr.Length; ++i)
{
bgr[i].ConvertTo(bgr[i], MatType.CV_32FC1, 1.0 * scales[i], (0.0 - means[i]) * scales[i]);
}
normalized.Dispose();
Mat dest = new Mat();
Cv2.Merge(bgr, dest);
foreach (Mat channel in bgr)
{
channel.Dispose();
}
return dest;
}
}
}
參考
https://github.com/sdcb/PaddleSharp