VS2022+.net4.8+ OpenCvSharp4+Sdcb.PaddleInference
using OpenCvSharp; using Sdcb.PaddleInference; using Sdcb.PaddleInference.Native; using System; using System.Drawing; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace PaddleInference_图片旋转角度检测 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } Bitmap bmp; string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png"; string img = ""; string startupPath = ""; DateTime dt1 = DateTime.Now; DateTime dt2 = DateTime.Now; PaddlePredictor predictor; float rotateThreshold = 0.50f; InputShape defaultShape = new InputShape(3, 224, 224); private unsafe void Form1_Load(object sender, EventArgs e) { startupPath = Application.StartupPath; // 堆代码 duidaima.com IntPtr _ptr = PaddleNative.PD_ConfigCreate(); Encoding PaddleEncoding = Environment.OSVersion.Platform == PlatformID.Win32NT ? Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.ANSICodePage) : Encoding.UTF8; //设置推理模型路径 String programPath = Application.StartupPath + "\\models\\inference.pdmodel"; String paramsPath = Application.StartupPath + "\\models\\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); PaddleNative.PD_ConfigEnableMKLDNN(_ptr); predictor = new PaddlePredictor(PaddleNative.PD_PredictorCreate(_ptr)); } 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); textBox1.Text = ""; } private void button2_Click(object sender, EventArgs e) { if (img == "") { return; } Mat src = OpenCvSharp.Extensions.BitmapConverter.ToMat(new Bitmap(pictureBox1.Image)); Cv2.CvtColor(src, src, ColorConversionCodes.RGBA2RGB);//mat转三通道mat dt1 = DateTime.Now; Mat resized = ResizePadding(src, defaultShape); Mat normalized = Normalize(resized); using (PaddleTensor input = predictor.GetInputTensor(predictor.InputNames[0])) { input.Shape = new[] { 1, 3, normalized.Rows, normalized.Cols }; float[] data = ExtractMat(normalized); input.SetData(data); } normalized.Dispose(); resized.Dispose(); if (!predictor.Run()) { throw new Exception("PaddlePredictor(Classifier) run failed."); } RotationDegree r = RotationDegree._0; using (PaddleTensor output = predictor.GetOutputTensor(predictor.OutputNames[0])) { float[] softmax = output.GetData<float>(); float max = softmax.Max(); int maxIndex = Array.IndexOf(softmax, max); if (max > rotateThreshold) { r = (RotationDegree)maxIndex; } } dt2 = DateTime.Now; StringBuilder sb = new StringBuilder(); sb.AppendLine("图片旋转角度:" + r.ToString()); sb.AppendLine("--------------------"); sb.AppendLine("耗时:" + (dt2 - dt1).TotalMilliseconds + "ms"); textBox1.Text = sb.ToString(); } private float[] ExtractMat(Mat src) { int rows = src.Rows; int cols = src.Cols; float[] result = new float[rows * cols * 3]; GCHandle resultHandle = default; try { resultHandle = GCHandle.Alloc(result, GCHandleType.Pinned); IntPtr resultPtr = resultHandle.AddrOfPinnedObject(); for (int i = 0; i < src.Channels(); ++i) { Mat dest = new Mat(rows, cols, MatType.CV_32FC1, resultPtr + i * rows * cols * sizeof(float)); Cv2.ExtractChannel(src, dest, i); dest.Dispose(); } } finally { resultHandle.Free(); } return result; } private Mat ResizePadding(Mat src, InputShape shape) { OpenCvSharp.Size srcSize = src.Size(); Mat roi = srcSize.Width / srcSize.Height > shape.Width / shape.Height ? src[0, srcSize.Height, 0, (int)Math.Floor(1.0 * srcSize.Height * shape.Width / shape.Height)] : src.Clone(); double scaleRate = 1.0 * shape.Height / srcSize.Height; Mat resized = roi.Resize(new OpenCvSharp.Size(Math.Floor(roi.Width * scaleRate), shape.Height)); if (resized.Width < shape.Width) { Cv2.CopyMakeBorder(resized, resized, 0, 0, 0, shape.Width - resized.Width, BorderTypes.Constant, Scalar.Black); } roi.Dispose(); return resized; } 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[] { 2.0f, 2.0f, 2.0f }; float[] means = new[] { 0.5f, 0.5f, 0.5f }; 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; } private void button3_Click(object sender, EventArgs e) { if (bmp == null) { return; } var mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp); Cv2.CvtColor(mat, mat, ColorConversionCodes.RGBA2RGB); Cv2.Rotate(mat, mat, RotateFlags.Rotate90Clockwise); var bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat); pictureBox1.Image = bitmap; } private void button4_Click(object sender, EventArgs e) { if (bmp == null) { return; } var mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp); Cv2.CvtColor(mat, mat, ColorConversionCodes.RGBA2RGB); Cv2.Rotate(mat, mat, RotateFlags.Rotate180); var bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat); pictureBox1.Image = bitmap; } private void button5_Click(object sender, EventArgs e) { if (bmp == null) { return; } var mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmp); Cv2.CvtColor(mat, mat, ColorConversionCodes.RGBA2RGB); Cv2.Rotate(mat, mat, RotateFlags.Rotate90Counterclockwise); var bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat); pictureBox1.Image = bitmap; } } /// <summary> /// Represents the shape of input data for a rotation detection model. /// </summary> public readonly struct InputShape { /// <summary> /// Initializes a new instance of the <see cref="InputShape"/> struct. /// </summary> /// <param name="channel">The number of color channels in the input image.</param> /// <param name="width">The width of the input image in pixels.</param> /// <param name="height">The height of the input image in pixels.</param> public InputShape(int channel, int width, int height) { Channel = channel; Height = height; Width = width; } /// <summary> /// Gets the number of color channels in the input image. /// </summary> public int Channel { get; } /// <summary> /// Gets the height of the input image in pixels. /// </summary> public int Height { get; } /// <summary> /// Gets the width of the input image in pixels. /// </summary> public int Width { get; } } /// <summary> /// Enum representing the degrees of rotation. /// </summary> public enum RotationDegree { /// <summary> /// Represents the 0-degree rotation angle. /// </summary> _0, /// <summary> /// Represents the 90-degree rotation angle. /// </summary> _90, /// <summary> /// Represents the 180-degree rotation angle. /// </summary> _180, /// <summary> /// Represents the 270-degree rotation angle. /// </summary> _270, } }参考:https://github.com/sdcb/PaddleSharp