效果
项目
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;
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,
}
}
Demo下载