From c6e42c650315f3b961695f013093959ce168731d Mon Sep 17 00:00:00 2001 From: Junxuan HUANG Date: Thu, 10 Sep 2026 17:03:37 +0800 Subject: [PATCH 1/3] Add MyPPOcr and enhance MewUI,ContentViewer --- examples/SystemDrawing.WinForms/MainForm.resx | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 examples/SystemDrawing.WinForms/MainForm.resx diff --git a/examples/SystemDrawing.WinForms/MainForm.resx b/examples/SystemDrawing.WinForms/MainForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/examples/SystemDrawing.WinForms/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From c176a31df704f05441d6ecea6f5666ed94a279b9 Mon Sep 17 00:00:00 2001 From: Junxuan HUANG Date: Fri, 11 Sep 2026 10:36:33 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=9C=A8=20PaddleOcrAll.cs=20=E4=B8=AD?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E9=9D=99=E6=80=81=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=20BytesPerPixel=EF=BC=88=E9=BB=98=E8=AE=A4=203=EF=BC=8C?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=E6=97=B6=E6=A0=A1=E9=AA=8C=E5=BF=85=E9=A1=BB?= =?UTF-8?q?=E4=B8=BA=203=20=E6=88=96=204=EF=BC=89=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=B0=86=E9=A1=B9=E7=9B=AE=E4=B8=AD=E6=89=80=E6=9C=89=E4=B8=8E?= =?UTF-8?q?"=E8=BE=93=E5=85=A5=E5=83=8F=E7=B4=A0=E5=AD=97=E8=8A=82"?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E7=A1=AC=E7=BC=96=E7=A0=81=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=9B=BF=E6=8D=A2=EF=BC=8C=E9=AA=8C=E8=AF=81=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=80=9A=E8=BF=87=E3=80=82=20=E4=BC=98=E5=8C=96WinFor?= =?UTF-8?q?ms=20RunPipline=EF=BC=8C=E6=94=B9=E7=94=A8=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E5=86=85=E5=AD=98=EF=BC=8C=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/SystemDrawing.WinForms/MainForm.cs | 54 +++++++++++++++---- .../SystemDrawing.WinForms.csproj | 1 + src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs | 9 ++-- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs | 9 ++-- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs | 9 ++-- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs | 9 ++-- src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs | 2 +- src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs | 17 +++--- src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs | 23 +++++--- src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs | 28 ++++++++-- src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs | 2 +- src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs | 16 +++--- src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs | 7 +-- 13 files changed, 131 insertions(+), 55 deletions(-) diff --git a/examples/SystemDrawing.WinForms/MainForm.cs b/examples/SystemDrawing.WinForms/MainForm.cs index 690e90b..3a3e06f 100644 --- a/examples/SystemDrawing.WinForms/MainForm.cs +++ b/examples/SystemDrawing.WinForms/MainForm.cs @@ -1,3 +1,4 @@ +using Sdcb.SimdPaddleOCR; using System; using System.Diagnostics; using System.Drawing; @@ -7,7 +8,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; -using Sdcb.SimdPaddleOCR; +using System.Xml.Linq; namespace SystemDrawing.WinForms; @@ -71,6 +72,10 @@ private void BuildLayout() AddPathRow(3, "REC 模型", _recPath, "模型|*.onnx|所有文件|*.*", false); AddPathRow(4, "字典", _dictPath, "字典|*.txt|所有文件|*.*", false); + CheckBox bppCheckBox = new() { Text = "颜色通道数(默认选中为3通道BGR, 不选为4通道BGRA)", Checked = true, Size = new Size(400, 30), Anchor = AnchorStyles.Left, Margin = new Padding(0, 7, 6, 4) }; + bppCheckBox.CheckedChanged += (_, _) => PaddleOcrAll.BytesPerPixel = bppCheckBox.Checked ? 3 : 4; + _paths.Controls.Add(bppCheckBox, 1, 5); + TableLayoutPanel root = new() { Dock = DockStyle.Fill, Padding = new Padding(12), RowCount = 3, ColumnCount = 1 }; root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); root.RowStyles.Add(new RowStyle(SizeType.Absolute, 34)); root.Controls.Add(_paths, 0, 0); @@ -145,15 +150,33 @@ private async Task RunOcrAsync() finally { _run.Enabled = _ocr is not null; _paths.Enabled = true; } } - private OcrResult RunPipeline(string imagePath) + unsafe private OcrResult RunPipeline(string imagePath) { + //改用直接访问内存,提升性能 using Bitmap bitmap = new(imagePath); - byte[] bgr = ToBgr(bitmap, out int stride); PaddleOcrResult result = _ocr!.Run(bgr, bitmap.Width, bitmap.Height, stride); Bitmap annotated = new(bitmap); - using Graphics graphics = Graphics.FromImage(annotated); using Pen pen = new(Color.LimeGreen, 3); using Brush brush = new SolidBrush(Color.Red); + var w = bitmap.Width; + var h = bitmap.Height; + var stride = w * PaddleOcrAll.BytesPerPixel; + var data = bitmap.LockBits( + new Rectangle(0, 0, w, h), + ImageLockMode.ReadOnly, + PaddleOcrAll.BytesPerPixel == 4 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb); + PaddleOcrResult result = _ocr!.Run(new ReadOnlySpan((void*)data.Scan0, stride * h), w, h, stride); + bitmap.UnlockBits(data); + + Bitmap annotated = new(bitmap); + using Graphics graphics = Graphics.FromImage(annotated); + using Pen pen = new(Color.LimeGreen, 3); + using Brush brush = new SolidBrush(Color.Red); foreach (PaddleOcrLine line in result.Lines) { - Point[] points = [new((int)line.Box.X1, (int)line.Box.Y1), new((int)line.Box.X2, (int)line.Box.Y2), new((int)line.Box.X3, (int)line.Box.Y3), new((int)line.Box.X4, (int)line.Box.Y4)]; - graphics.DrawPolygon(pen, points); graphics.DrawString(line.Text, Font, brush, points[0]); + Point[] points = [ + new((int)line.Box.X1,(int)line.Box.Y1), + new((int)line.Box.X2, (int)line.Box.Y2), + new((int)line.Box.X3, (int)line.Box.Y3), + new((int)line.Box.X4, (int)line.Box.Y4)]; + graphics.DrawPolygon(pen, points); + //graphics.DrawString(line.Text, Font, brush, points[0]); } return new OcrResult(string.Join(Environment.NewLine, result.Lines.Select(line => line.Text)), result.DetectedCount, annotated); } @@ -162,10 +185,19 @@ private OcrResult RunPipeline(string imagePath) private static byte[] ToBgr(Bitmap bitmap, out int stride) { - stride = checked(bitmap.Width * 3); byte[] bgr = new byte[checked(stride * bitmap.Height)]; Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); + stride = checked(bitmap.Width * 3); + byte[] bgr = new byte[checked(stride * bitmap.Height)]; + Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); BitmapData data = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - try { for (int y = 0; y < bitmap.Height; y++) Marshal.Copy(new IntPtr(data.Scan0.ToInt64() + y * (long)data.Stride), bgr, y * stride, stride); } - finally { bitmap.UnlockBits(data); } + try + { + for (int y = 0; y < bitmap.Height; y++) + Marshal.Copy(new IntPtr(data.Scan0.ToInt64() + y * (long)data.Stride), bgr, y * stride, stride); + } + finally + { + bitmap.UnlockBits(data); + } return bgr; } @@ -201,6 +233,8 @@ private static string FindRepositoryRoot() private sealed class OcrResult { public OcrResult(string text, int count, Bitmap annotated) { Text = text; Count = count; Annotated = annotated; } - public string Text { get; } public int Count { get; } public Bitmap Annotated { get; } + public string Text { get; } + public int Count { get; } + public Bitmap Annotated { get; } } } diff --git a/examples/SystemDrawing.WinForms/SystemDrawing.WinForms.csproj b/examples/SystemDrawing.WinForms/SystemDrawing.WinForms.csproj index dbdaa44..e5e3cd1 100644 --- a/examples/SystemDrawing.WinForms/SystemDrawing.WinForms.csproj +++ b/examples/SystemDrawing.WinForms/SystemDrawing.WinForms.csproj @@ -10,6 +10,7 @@ true app.manifest x64 + True diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs index a907273..080140a 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs @@ -69,7 +69,7 @@ private static unsafe void SampleCubicAvx(byte* source, int stride, Vector256 wy = CubicWeightVectorAvx(ty); Vector256 wx0 = Avx2.Permute4x64(wx, 0x00), wx1 = Avx2.Permute4x64(wx, 0x55); Vector256 wx2 = Avx2.Permute4x64(wx, 0xAA), wx3 = Avx2.Permute4x64(wx, 0xFF); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * 3; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; Vector256 acc = AccumulateRowAvx(row, wx0, wx1, wx2, wx3, Avx2.Permute4x64(wy, 0x00), Vector256.Zero); acc = AccumulateRowAvx(row + stride, wx0, wx1, wx2, wx3, Avx2.Permute4x64(wy, 0x55), acc); @@ -84,10 +84,11 @@ private static unsafe Vector256 AccumulateRowAvx(byte* row, Vector256 wx0, Vector256 wx1, Vector256 wx2, Vector256 wx3, Vector256 wy, Vector256 acc) { + int bpp = PaddleOcrAll.BytesPerPixel; acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row), wx0), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 3), wx1), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 6), wx2), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 9), wx3), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + bpp), wx1), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 2 * bpp), wx2), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 3 * bpp), wx3), wy)); return acc; } diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs index db376bb..e9fba29 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs @@ -74,7 +74,7 @@ private static unsafe void SampleCubicAvx512(byte* source, int stride, Vector512 wx1 = Vector512.Create(wx.GetElement(1)); Vector512 wx2 = Vector512.Create(wx.GetElement(2)); Vector512 wx3 = Vector512.Create(wx.GetElement(3)); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * 3; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; Vector512 acc = AccumulateRowAvx512(row, wx0, wx1, wx2, wx3, Vector512.Create(wy.GetElement(0)), Vector512.Zero); acc = AccumulateRowAvx512(row + stride, wx0, wx1, wx2, wx3, Vector512.Create(wy.GetElement(1)), acc); @@ -89,10 +89,11 @@ private static unsafe Vector512 AccumulateRowAvx512(byte* row, Vector512 wx0, Vector512 wx1, Vector512 wx2, Vector512 wx3, Vector512 wy, Vector512 acc) { + int bpp = PaddleOcrAll.BytesPerPixel; acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row), wx0), wy)); - acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 3), wx1), wy)); - acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 6), wx2), wy)); - acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 9), wx3), wy)); + acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + bpp), wx1), wy)); + acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 2 * bpp), wx2), wy)); + acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 3 * bpp), wx3), wy)); return acc; } diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs index f2a2c5e..4e57e53 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs @@ -34,7 +34,7 @@ internal static unsafe void ProcessPixel(byte* sourcePtr, int sourceWidth, int s throw new InvalidDataException("Invalid perspective transform."); int destinationX = rotateVertical ? unrotatedHeight - 1 - y : x; int destinationY = rotateVertical ? x : y; - int destination = checked((destinationY * outputWidth + destinationX) * 3); + int destination = checked((destinationY * outputWidth + destinationX) * PaddleOcrAll.BytesPerPixel); SampleCubicScalar(sourcePtr, sourceWidth, sourceHeight, sourceStride, sx, sy, cropPtr, destination); } @@ -58,14 +58,15 @@ internal static unsafe void SampleCubicScalar(byte* source, int width, int heigh CubicWeight(y - (yBase + 1)), CubicWeight(y - (yBase + 2)), ]; + int bpp = PaddleOcrAll.BytesPerPixel; double value0 = 0, value1 = 0, value2 = 0; if (xBase >= 1 && xBase < width - 2 && yBase >= 1 && yBase < height - 2) { for (int ky = 0; ky < 4; ky++) { double wy = yWeights[ky]; - int sourceOffset = (yBase + ky - 1) * stride + (xBase - 1) * 3; - for (int kx = 0; kx < 4; kx++, sourceOffset += 3) + int sourceOffset = (yBase + ky - 1) * stride + (xBase - 1) * bpp; + for (int kx = 0; kx < 4; kx++, sourceOffset += bpp) { double wx = xWeights[kx]; value0 += source[sourceOffset] * wx * wy; @@ -84,7 +85,7 @@ internal static unsafe void SampleCubicScalar(byte* source, int width, int heigh { double wx = xWeights[kx]; int sx = Clamp(xBase + kx - 1, width); - int sourceOffset = sy * stride + sx * 3; + int sourceOffset = sy * stride + sx * bpp; value0 += source[sourceOffset] * wx * wy; value1 += source[sourceOffset + 1] * wx * wy; value2 += source[sourceOffset + 2] * wx * wy; diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs index cd7a53b..4295305 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs @@ -69,7 +69,7 @@ private static unsafe void SampleCubicVector(byte* source, int stride, Vector wy = CubicWeightVector(new Vector(y) - tap); Vector wx0 = new(wx.GetElement(0)), wx1 = new(wx.GetElement(1)); Vector wx2 = new(wx.GetElement(2)), wx3 = new(wx.GetElement(3)); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * 3; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; Vector acc = AccumulateRowVector(row, wx0, wx1, wx2, wx3, new Vector(wy.GetElement(0)), Vector.Zero); acc = AccumulateRowVector(row + stride, wx0, wx1, wx2, wx3, new Vector(wy.GetElement(1)), acc); @@ -84,10 +84,11 @@ private static unsafe Vector AccumulateRowVector(byte* row, Vector wx0, Vector wx1, Vector wx2, Vector wx3, Vector wy, Vector acc) { + int bpp = PaddleOcrAll.BytesPerPixel; acc += LoadPixelVector(row) * wx0 * wy; - acc += LoadPixelVector(row + 3) * wx1 * wy; - acc += LoadPixelVector(row + 6) * wx2 * wy; - acc += LoadPixelVector(row + 9) * wx3 * wy; + acc += LoadPixelVector(row + bpp) * wx1 * wy; + acc += LoadPixelVector(row + 2 * bpp) * wx2 * wy; + acc += LoadPixelVector(row + 3 * bpp) * wx3 * wy; return acc; } diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs index 1aeb582..b058d6c 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs @@ -65,7 +65,7 @@ private static unsafe void SampleMappedPixel(byte* sourcePtr, int sourceWidth, i { int destinationX = rotateVertical ? unrotatedHeight - 1 - y : x; int destinationY = rotateVertical ? x : y; - int destination = checked((destinationY * outputWidth + destinationX) * 3); + int destination = checked((destinationY * outputWidth + destinationX) * PaddleOcrAll.BytesPerPixel); SampleCubic(sourcePtr, sourceWidth, sourceHeight, sourceStride, pixelX, pixelY, cropPtr, destination); } diff --git a/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs b/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs index be2b283..e24fe64 100644 --- a/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs +++ b/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs @@ -21,7 +21,7 @@ public static (int Width, int Height, int ByteCount) GetSize(in PaddleOcrDetecti throw new InvalidDataException("Invalid detection quadrilateral."); int width = transform.RotateVertical ? transform.UnrotatedHeight : transform.UnrotatedWidth; int height = transform.RotateVertical ? transform.UnrotatedWidth : transform.UnrotatedHeight; - long bytes = checked((long)width * height * 3); + long bytes = checked((long)width * height * PaddleOcrAll.BytesPerPixel); return (width, height, checked((int)bytes)); } @@ -82,15 +82,17 @@ private static unsafe void ExtractCore(ReadOnlySpan source, int sourceWidt public static unsafe void Rotate180(Span pixels, int width, int height) { int count = checked(width * height); + int bpp = PaddleOcrAll.BytesPerPixel; fixed (byte* data = pixels) { for (int left = 0; left < count / 2; left++) { int right = count - 1 - left; - byte* l = data + left * 3, r = data + right * 3; - byte value = l[0]; l[0] = r[0]; r[0] = value; - value = l[1]; l[1] = r[1]; r[1] = value; - value = l[2]; l[2] = r[2]; r[2] = value; + byte* l = data + left * bpp, r = data + right * bpp; + for (int i = 0; i < bpp; i++) + { + byte value = l[i]; l[i] = r[i]; r[i] = value; + } } } } @@ -153,8 +155,9 @@ private static void ValidateSource(ReadOnlySpan source, int width, int hei { if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width)); if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height)); - if (stride < checked(width * 3)) throw new ArgumentException("Source stride is too small."); - long required = checked((long)(height - 1) * stride + width * 3L); + int bpp = PaddleOcrAll.BytesPerPixel; + if (stride < checked(width * bpp)) throw new ArgumentException("Source stride is too small."); + long required = checked((long)(height - 1) * stride + width * (long)bpp); if (required > source.Length) throw new ArgumentException("Source buffer is too small."); } } diff --git a/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs b/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs index 24349a2..0b76760 100644 --- a/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs +++ b/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs @@ -191,11 +191,15 @@ private static unsafe void BuildHorizontalRow(byte* source, int sourceStride, in int sourceY, int destinationWidth, int[] offsets, short[] coefficients, int[] destination) { byte* row = source + sourceY * sourceStride; + int sourceBpp = PaddleOcrAll.BytesPerPixel; for (int x = 0; x < destinationWidth; x++) { int sx = offsets[x], sx1 = Math.Min(sx + 1, sourceWidth - 1); short coefficient0 = coefficients[x * 2], coefficient1 = coefficients[x * 2 + 1]; - int sourceOffset = sx * 3, sourceOffset1 = sx1 * 3, destinationOffset = x * 3; + // Source pixels are BGR (3) or BGRA (4) bytes; the intermediate + // horizontal row buffer always carries 3 channels per pixel so + // downstream NCHW packing stays 3-plane regardless of input. + int sourceOffset = sx * sourceBpp, sourceOffset1 = sx1 * sourceBpp, destinationOffset = x * 3; destination[destinationOffset] = row[sourceOffset] * coefficient0 + row[sourceOffset1] * coefficient1; destination[destinationOffset + 1] = row[sourceOffset + 1] * coefficient0 + row[sourceOffset1 + 1] * coefficient1; destination[destinationOffset + 2] = row[sourceOffset + 2] * coefficient0 + row[sourceOffset1 + 2] * coefficient1; @@ -378,6 +382,7 @@ private static unsafe void NormalizeBgrResize(ReadOnlySpan resized, int re int resizedHeight, int outputWidth, Span output) { int plane = checked(resizedHeight * outputWidth); + int bpp = PaddleOcrAll.BytesPerPixel; fixed (byte* sourcePtr = resized) fixed (float* outputPtr = output) { @@ -385,7 +390,7 @@ private static unsafe void NormalizeBgrResize(ReadOnlySpan resized, int re { for (int ox = 0; ox < resizedWidth; ox++) { - int sourceOffset = (oy * resizedWidth + ox) * 3; + int sourceOffset = (oy * resizedWidth + ox) * bpp; int destination = oy * outputWidth + ox; outputPtr[destination] = RecNormalized[sourcePtr[sourceOffset]]; outputPtr[plane + destination] = RecNormalized[sourcePtr[sourceOffset + 1]]; @@ -404,10 +409,13 @@ private static unsafe void ResizeBgrInterLinear(ReadOnlySpan source, int s int sourceHeight, int sourceStride, int destinationWidth, int destinationHeight, Span destination) { - int required = checked(destinationWidth * destinationHeight * 3); + int bpp = PaddleOcrAll.BytesPerPixel; + int required = checked(destinationWidth * destinationHeight * bpp); if (destination.Length < required) throw new ArgumentException("Destination buffer is too small."); int[] xOffsets = PooledArrays.Rent(destinationWidth); short[] xCoefficients = PooledArrays.Rent(checked(destinationWidth * 2)); + // Intermediate horizontal rows always carry 3 channels per pixel so + // downstream NCHW packing stays 3-plane regardless of source bpp. int[] row0 = PooledArrays.Rent(checked(destinationWidth * 3)); int[] row1 = PooledArrays.Rent(checked(destinationWidth * 3)); try @@ -427,11 +435,11 @@ private static unsafe void ResizeBgrInterLinear(ReadOnlySpan source, int s BuildHorizontalRow(sourcePtr, sourceStride, sourceWidth, sy1, destinationWidth, xOffsets, xCoefficients, row1); - int destinationOffset = oy * destinationWidth * 3; + int destinationOffset = oy * destinationWidth * bpp; for (int ox = 0; ox < destinationWidth; ox++) { int rowOffset = ox * 3; - int pixelOffset = destinationOffset + rowOffset; + int pixelOffset = destinationOffset + ox * bpp; int h0 = row0[rowOffset], h1 = row1[rowOffset]; int value = (((h0 >> 4) * beta0 >> 16) + ((h1 >> 4) * beta1 >> 16) + 2) >> 2; destinationPtr[pixelOffset] = (byte)MathCompat.Clamp(value, 0, 255); @@ -458,8 +466,9 @@ private static void ValidateSource(ReadOnlySpan source, int width, int hei { if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width)); if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height)); - if (stride < checked(width * 3)) throw new ArgumentException("Source stride is too small."); - long required = checked((long)(height - 1) * stride + width * 3L); + int bpp = PaddleOcrAll.BytesPerPixel; + if (stride < checked(width * bpp)) throw new ArgumentException("Source stride is too small."); + long required = checked((long)(height - 1) * stride + width * (long)bpp); if (required > source.Length) throw new ArgumentException("Source buffer is too small."); } diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs index a087c12..03ce6da 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs @@ -9,8 +9,9 @@ namespace Sdcb.SimdPaddleOCR; /// -/// Complete pure managed PP-OCR pipeline. It accepts packed BGR8 memory and -/// deliberately has no image-decoding or file-system dependency. +/// Complete pure managed PP-OCR pipeline. It accepts packed BGR or BGRA memory +/// (see ) and deliberately has no image-decoding or +/// file-system dependency. /// /// Thread-safe: the detector, classifier and recognizer each hold one shared /// compiled model and pool per-call inference requests, while every run rents @@ -22,6 +23,27 @@ public sealed class PaddleOcrAll : IDisposable { private static bool s_profileEnabled; private static readonly long[] s_profileTicks = new long[6]; + private static int bytesPerPixel = 3; + + /// + /// Bytes per source pixel supplied to and to the + /// detector/classifier/recognizer entry points. Set to 3 for packed + /// BGR memory (default) or 4 for packed BGRA memory. The alpha + /// channel is ignored throughout the pipeline; choosing 4 lets + /// callers hand off BGRA memory directly without an external pack step. + /// + /// Configure once at startup. Mutating this property while concurrent + /// calls are in flight is not advised. + /// + /// + /// Value must be 3 or 4. + public static int BytesPerPixel + { + get => bytesPerPixel; + set => bytesPerPixel = value is 3 or 4 + ? value + : throw new ArgumentOutOfRangeException(nameof(value), "BytesPerPixel must be 3 or 4."); + } private readonly PaddleOcrDetector _detector; private readonly PaddleOcrClassifier? _classifier; private readonly PaddleOcrRecognizer _recognizer; @@ -134,7 +156,7 @@ public PaddleOcrResult Run(ReadOnlySpan source, int sourceWidth, int sourc int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrAll)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * 3); + if (sourceStride == 0) sourceStride = checked(sourceWidth * BytesPerPixel); long stageStart = s_profileEnabled ? Stopwatch.GetTimestamp() : 0; PaddleOcrDetectionResult detection = _detector.Detect(source, sourceWidth, sourceHeight, sourceStride); if (s_profileEnabled) AddProfile(0, stageStart); diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs index 9046009..6c5e95c 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs @@ -56,7 +56,7 @@ public PaddleOcrClassificationResult Classify(ReadOnlySpan source, int sou int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrClassifier)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * 3); + if (sourceStride == 0) sourceStride = checked(sourceWidth * PaddleOcrAll.BytesPerPixel); if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) throw new InvalidOperationException("Source image exceeds MaxImagePixels."); bool profile = PipelineProfiler.Enabled; diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs index d5fb7c9..8abc831 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs @@ -5,7 +5,8 @@ namespace Sdcb.SimdPaddleOCR; -/// Pure managed DB detector. The caller supplies packed BGR bytes. +/// Pure managed DB detector. The caller supplies packed BGR or BGRA +/// bytes (see ). public sealed class PaddleOcrDetector : IDisposable { private static bool s_profileEnabled; @@ -101,7 +102,8 @@ public PaddleOcrDetectionResult Detect(ReadOnlySpan source, int sourceWidt int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrDetector)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * 3); + int bpp = PaddleOcrAll.BytesPerPixel; + if (sourceStride == 0) sourceStride = checked(sourceWidth * bpp); int originalWidth = sourceWidth, originalHeight = sourceHeight; if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) throw new InvalidOperationException("Source image exceeds MaxImagePixels."); @@ -111,18 +113,18 @@ public PaddleOcrDetectionResult Detect(ReadOnlySpan source, int sourceWidt { int paddedWidth = Math.Max(32, sourceWidth); int paddedHeight = Math.Max(32, sourceHeight); - paddedSource = PooledArrays.Rent(checked(paddedWidth * paddedHeight * 3)); - Span padded = paddedSource.AsSpan(0, checked(paddedWidth * paddedHeight * 3)); + paddedSource = PooledArrays.Rent(checked(paddedWidth * paddedHeight * bpp)); + Span padded = paddedSource.AsSpan(0, checked(paddedWidth * paddedHeight * bpp)); padded.Clear(); for (int y = 0; y < sourceHeight; y++) { - source.Slice(y * sourceStride, checked(sourceWidth * 3)) - .CopyTo(padded.Slice(y * paddedWidth * 3, checked(sourceWidth * 3))); + source.Slice(y * sourceStride, checked(sourceWidth * bpp)) + .CopyTo(padded.Slice(y * paddedWidth * bpp, checked(sourceWidth * bpp))); } source = padded; sourceWidth = paddedWidth; sourceHeight = paddedHeight; - sourceStride = checked(paddedWidth * 3); + sourceStride = checked(paddedWidth * bpp); } try diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs index b797d3f..2a0f784 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs @@ -6,7 +6,8 @@ namespace Sdcb.SimdPaddleOCR; -/// Pure managed PP-OCR CTC recognizer accepting BGR crop memory. +/// Pure managed PP-OCR CTC recognizer accepting BGR or BGRA crop +/// memory (see ). public sealed class PaddleOcrRecognizer : IDisposable { private readonly Model _model; @@ -127,7 +128,7 @@ public PaddleOcrRecognitionResult Recognize(ReadOnlySpan source, int sourc int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrRecognizer)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * 3); + if (sourceStride == 0) sourceStride = checked(sourceWidth * PaddleOcrAll.BytesPerPixel); if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) throw new InvalidOperationException("Source image exceeds MaxImagePixels."); bool profile = PipelineProfiler.Enabled; @@ -262,7 +263,7 @@ internal void RecognizeBatch(byte[] cropBuffer, int[] offsets, int[] cropBytes, throw new InvalidOperationException("Source image exceeds MaxImagePixels."); resizedWidths[k] = PPOCRPreprocess.RecBgrToNchw( cropBuffer.AsSpan(offsets[line], cropBytes[line]), sourceWidth, sourceHeight, - checked(sourceWidth * 3), targetWidth, + checked(sourceWidth * PaddleOcrAll.BytesPerPixel), targetWidth, input.Slice(k * sampleLength, sampleLength), session.ResizeWorkspace); } if (profile) PipelineProfiler.Add(PipelineProfiler.RecPreprocess, started); From 1b468e83419bb8b22d2bc502118b0952df82c0db Mon Sep 17 00:00:00 2001 From: Junxuan HUANG Date: Fri, 11 Sep 2026 12:28:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E4=BD=BF?= =?UTF-8?q?=E7=94=A8internal=20static=20int=20Warp.BytesPerPixel=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs | 9 ++--- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs | 4 +- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs | 9 ++--- src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs | 9 ++--- src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs | 39 +++++++++++-------- src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs | 6 +-- src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs | 8 ++-- src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs | 7 ++-- src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs | 2 +- src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs | 4 +- src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs | 6 +-- 11 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs index 080140a..09d852f 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx.cs @@ -69,7 +69,7 @@ private static unsafe void SampleCubicAvx(byte* source, int stride, Vector256 wy = CubicWeightVectorAvx(ty); Vector256 wx0 = Avx2.Permute4x64(wx, 0x00), wx1 = Avx2.Permute4x64(wx, 0x55); Vector256 wx2 = Avx2.Permute4x64(wx, 0xAA), wx3 = Avx2.Permute4x64(wx, 0xFF); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * BytesPerPixel; Vector256 acc = AccumulateRowAvx(row, wx0, wx1, wx2, wx3, Avx2.Permute4x64(wy, 0x00), Vector256.Zero); acc = AccumulateRowAvx(row + stride, wx0, wx1, wx2, wx3, Avx2.Permute4x64(wy, 0x55), acc); @@ -84,11 +84,10 @@ private static unsafe Vector256 AccumulateRowAvx(byte* row, Vector256 wx0, Vector256 wx1, Vector256 wx2, Vector256 wx3, Vector256 wy, Vector256 acc) { - int bpp = PaddleOcrAll.BytesPerPixel; acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row), wx0), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + bpp), wx1), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 2 * bpp), wx2), wy)); - acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 3 * bpp), wx3), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + BytesPerPixel), wx1), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 2 * BytesPerPixel), wx2), wy)); + acc = Avx.Add(acc, Avx.Multiply(Avx.Multiply(LoadPixelAvx(row + 3 * BytesPerPixel), wx3), wy)); return acc; } diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs index e9fba29..bfe322e 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Avx512.cs @@ -74,7 +74,7 @@ private static unsafe void SampleCubicAvx512(byte* source, int stride, Vector512 wx1 = Vector512.Create(wx.GetElement(1)); Vector512 wx2 = Vector512.Create(wx.GetElement(2)); Vector512 wx3 = Vector512.Create(wx.GetElement(3)); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; Vector512 acc = AccumulateRowAvx512(row, wx0, wx1, wx2, wx3, Vector512.Create(wy.GetElement(0)), Vector512.Zero); acc = AccumulateRowAvx512(row + stride, wx0, wx1, wx2, wx3, Vector512.Create(wy.GetElement(1)), acc); @@ -89,7 +89,7 @@ private static unsafe Vector512 AccumulateRowAvx512(byte* row, Vector512 wx0, Vector512 wx1, Vector512 wx2, Vector512 wx3, Vector512 wy, Vector512 acc) { - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row), wx0), wy)); acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + bpp), wx1), wy)); acc = Avx512F.Add(acc, Avx512F.Multiply(Avx512F.Multiply(LoadPixelAvx512(row + 2 * bpp), wx2), wy)); diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs index 4e57e53..3e745ef 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Scalar.cs @@ -34,7 +34,7 @@ internal static unsafe void ProcessPixel(byte* sourcePtr, int sourceWidth, int s throw new InvalidDataException("Invalid perspective transform."); int destinationX = rotateVertical ? unrotatedHeight - 1 - y : x; int destinationY = rotateVertical ? x : y; - int destination = checked((destinationY * outputWidth + destinationX) * PaddleOcrAll.BytesPerPixel); + int destination = checked((destinationY * outputWidth + destinationX) * BytesPerPixel); SampleCubicScalar(sourcePtr, sourceWidth, sourceHeight, sourceStride, sx, sy, cropPtr, destination); } @@ -58,15 +58,14 @@ internal static unsafe void SampleCubicScalar(byte* source, int width, int heigh CubicWeight(y - (yBase + 1)), CubicWeight(y - (yBase + 2)), ]; - int bpp = PaddleOcrAll.BytesPerPixel; double value0 = 0, value1 = 0, value2 = 0; if (xBase >= 1 && xBase < width - 2 && yBase >= 1 && yBase < height - 2) { for (int ky = 0; ky < 4; ky++) { double wy = yWeights[ky]; - int sourceOffset = (yBase + ky - 1) * stride + (xBase - 1) * bpp; - for (int kx = 0; kx < 4; kx++, sourceOffset += bpp) + int sourceOffset = (yBase + ky - 1) * stride + (xBase - 1) * BytesPerPixel; + for (int kx = 0; kx < 4; kx++, sourceOffset += BytesPerPixel) { double wx = xWeights[kx]; value0 += source[sourceOffset] * wx * wy; @@ -85,7 +84,7 @@ internal static unsafe void SampleCubicScalar(byte* source, int width, int heigh { double wx = xWeights[kx]; int sx = Clamp(xBase + kx - 1, width); - int sourceOffset = sy * stride + sx * bpp; + int sourceOffset = sy * stride + sx * BytesPerPixel; value0 += source[sourceOffset] * wx * wy; value1 += source[sourceOffset + 1] * wx * wy; value2 += source[sourceOffset + 2] * wx * wy; diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs index 4295305..d790220 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.Vector.cs @@ -69,7 +69,7 @@ private static unsafe void SampleCubicVector(byte* source, int stride, Vector wy = CubicWeightVector(new Vector(y) - tap); Vector wx0 = new(wx.GetElement(0)), wx1 = new(wx.GetElement(1)); Vector wx2 = new(wx.GetElement(2)), wx3 = new(wx.GetElement(3)); - byte* row = source + (yBase - 1) * stride + (xBase - 1) * PaddleOcrAll.BytesPerPixel; + byte* row = source + (yBase - 1) * stride + (xBase - 1) * BytesPerPixel; Vector acc = AccumulateRowVector(row, wx0, wx1, wx2, wx3, new Vector(wy.GetElement(0)), Vector.Zero); acc = AccumulateRowVector(row + stride, wx0, wx1, wx2, wx3, new Vector(wy.GetElement(1)), acc); @@ -84,11 +84,10 @@ private static unsafe Vector AccumulateRowVector(byte* row, Vector wx0, Vector wx1, Vector wx2, Vector wx3, Vector wy, Vector acc) { - int bpp = PaddleOcrAll.BytesPerPixel; acc += LoadPixelVector(row) * wx0 * wy; - acc += LoadPixelVector(row + bpp) * wx1 * wy; - acc += LoadPixelVector(row + 2 * bpp) * wx2 * wy; - acc += LoadPixelVector(row + 3 * bpp) * wx3 * wy; + acc += LoadPixelVector(row + BytesPerPixel) * wx1 * wy; + acc += LoadPixelVector(row + 2 * BytesPerPixel) * wx2 * wy; + acc += LoadPixelVector(row + 3 * BytesPerPixel) * wx3 * wy; return acc; } diff --git a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs index b058d6c..c8514e3 100644 --- a/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs +++ b/src/Sdcb.SimdPaddleOCR/Kernels/Warp.cs @@ -8,13 +8,18 @@ namespace Sdcb.SimdPaddleOCR.Kernels; internal static partial class Warp { + /// + /// Bytes per source pixel supplied to the bitmap source buffer. + /// + internal static int BytesPerPixel = 3; + [MethodImpl(MethodImplCompat.AggressiveOptimization)] internal static unsafe void MapRow(byte* sourcePtr, int sourceWidth, int sourceHeight, - int sourceStride, byte* cropPtr, int outputWidth, int unrotatedWidth, int unrotatedHeight, - bool rotateVertical, double a, double b, double c, double d, double e, double f, - double g, double h, int y, double v, ref int x) + int sourceStride, byte* cropPtr, int outputWidth, int unrotatedWidth, int unrotatedHeight, + bool rotateVertical, double a, double b, double c, double d, double e, double f, + double g, double h, int y, double v, ref int x) { - #if !NETSTANDARD2_0 +#if !NETSTANDARD2_0 if (Avx512F.IsSupported && unrotatedWidth >= 8) MapRowAvx512(sourcePtr, sourceWidth, sourceHeight, sourceStride, cropPtr, outputWidth, unrotatedWidth, unrotatedHeight, rotateVertical, a, b, c, d, e, f, g, h, y, v, ref x); @@ -22,11 +27,11 @@ internal static unsafe void MapRow(byte* sourcePtr, int sourceWidth, int sourceH MapRowAvx(sourcePtr, sourceWidth, sourceHeight, sourceStride, cropPtr, outputWidth, unrotatedWidth, unrotatedHeight, rotateVertical, a, b, c, d, e, f, g, h, y, v, ref x); else - #endif - if (Vector.IsHardwareAccelerated && Vector.Count >= 2 && - unrotatedWidth >= Vector.Count) - MapRowVector(sourcePtr, sourceWidth, sourceHeight, sourceStride, cropPtr, outputWidth, - unrotatedWidth, unrotatedHeight, rotateVertical, a, b, c, d, e, f, g, h, y, v, ref x); +#endif + if (Vector.IsHardwareAccelerated && Vector.Count >= 2 && + unrotatedWidth >= Vector.Count) + MapRowVector(sourcePtr, sourceWidth, sourceHeight, sourceStride, cropPtr, outputWidth, + unrotatedWidth, unrotatedHeight, rotateVertical, a, b, c, d, e, f, g, h, y, v, ref x); } [MethodImpl(MethodImplCompat.AggressiveOptimization)] @@ -36,7 +41,7 @@ internal static unsafe void SampleCubic(byte* source, int width, int height, int int xBase = (int)Math.Floor(x), yBase = (int)Math.Floor(y); if (xBase >= 1 && xBase < width - 3 && yBase >= 1 && yBase < height - 2) { - #if !NETSTANDARD2_0 +#if !NETSTANDARD2_0 if (Avx512F.IsSupported) { SampleCubicAvx512(source, stride, x, y, xBase, yBase, destination, destinationOffset); @@ -48,12 +53,12 @@ internal static unsafe void SampleCubic(byte* source, int width, int height, int return; } else - #endif - if (Vector.IsHardwareAccelerated && Vector.Count == 4) - { - SampleCubicVector(source, stride, x, y, xBase, yBase, destination, destinationOffset); - return; - } +#endif + if (Vector.IsHardwareAccelerated && Vector.Count == 4) + { + SampleCubicVector(source, stride, x, y, xBase, yBase, destination, destinationOffset); + return; + } } SampleCubicScalar(source, width, height, stride, x, y, destination, destinationOffset); } @@ -65,7 +70,7 @@ private static unsafe void SampleMappedPixel(byte* sourcePtr, int sourceWidth, i { int destinationX = rotateVertical ? unrotatedHeight - 1 - y : x; int destinationY = rotateVertical ? x : y; - int destination = checked((destinationY * outputWidth + destinationX) * PaddleOcrAll.BytesPerPixel); + int destination = checked((destinationY * outputWidth + destinationX) * BytesPerPixel); SampleCubic(sourcePtr, sourceWidth, sourceHeight, sourceStride, pixelX, pixelY, cropPtr, destination); } diff --git a/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs b/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs index e24fe64..ea07204 100644 --- a/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs +++ b/src/Sdcb.SimdPaddleOCR/PPOCRCrop.cs @@ -21,7 +21,7 @@ public static (int Width, int Height, int ByteCount) GetSize(in PaddleOcrDetecti throw new InvalidDataException("Invalid detection quadrilateral."); int width = transform.RotateVertical ? transform.UnrotatedHeight : transform.UnrotatedWidth; int height = transform.RotateVertical ? transform.UnrotatedWidth : transform.UnrotatedHeight; - long bytes = checked((long)width * height * PaddleOcrAll.BytesPerPixel); + long bytes = checked((long)width * height * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel); return (width, height, checked((int)bytes)); } @@ -82,7 +82,7 @@ private static unsafe void ExtractCore(ReadOnlySpan source, int sourceWidt public static unsafe void Rotate180(Span pixels, int width, int height) { int count = checked(width * height); - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; fixed (byte* data = pixels) { for (int left = 0; left < count / 2; left++) @@ -155,7 +155,7 @@ private static void ValidateSource(ReadOnlySpan source, int width, int hei { if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width)); if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height)); - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; if (stride < checked(width * bpp)) throw new ArgumentException("Source stride is too small."); long required = checked((long)(height - 1) * stride + width * (long)bpp); if (required > source.Length) throw new ArgumentException("Source buffer is too small."); diff --git a/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs b/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs index 0b76760..322faf4 100644 --- a/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs +++ b/src/Sdcb.SimdPaddleOCR/PPOCRPreprocess.cs @@ -191,7 +191,7 @@ private static unsafe void BuildHorizontalRow(byte* source, int sourceStride, in int sourceY, int destinationWidth, int[] offsets, short[] coefficients, int[] destination) { byte* row = source + sourceY * sourceStride; - int sourceBpp = PaddleOcrAll.BytesPerPixel; + int sourceBpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; for (int x = 0; x < destinationWidth; x++) { int sx = offsets[x], sx1 = Math.Min(sx + 1, sourceWidth - 1); @@ -382,7 +382,7 @@ private static unsafe void NormalizeBgrResize(ReadOnlySpan resized, int re int resizedHeight, int outputWidth, Span output) { int plane = checked(resizedHeight * outputWidth); - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; fixed (byte* sourcePtr = resized) fixed (float* outputPtr = output) { @@ -409,7 +409,7 @@ private static unsafe void ResizeBgrInterLinear(ReadOnlySpan source, int s int sourceHeight, int sourceStride, int destinationWidth, int destinationHeight, Span destination) { - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; int required = checked(destinationWidth * destinationHeight * bpp); if (destination.Length < required) throw new ArgumentException("Destination buffer is too small."); int[] xOffsets = PooledArrays.Rent(destinationWidth); @@ -466,7 +466,7 @@ private static void ValidateSource(ReadOnlySpan source, int width, int hei { if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width)); if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height)); - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; if (stride < checked(width * bpp)) throw new ArgumentException("Source stride is too small."); long required = checked((long)(height - 1) * stride + width * (long)bpp); if (required > source.Length) throw new ArgumentException("Source buffer is too small."); diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs index 03ce6da..0391c02 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrAll.cs @@ -23,7 +23,6 @@ public sealed class PaddleOcrAll : IDisposable { private static bool s_profileEnabled; private static readonly long[] s_profileTicks = new long[6]; - private static int bytesPerPixel = 3; /// /// Bytes per source pixel supplied to and to the @@ -39,8 +38,8 @@ public sealed class PaddleOcrAll : IDisposable /// Value must be 3 or 4. public static int BytesPerPixel { - get => bytesPerPixel; - set => bytesPerPixel = value is 3 or 4 + get => Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; + set => Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel = value is 3 or 4 ? value : throw new ArgumentOutOfRangeException(nameof(value), "BytesPerPixel must be 3 or 4."); } @@ -156,7 +155,7 @@ public PaddleOcrResult Run(ReadOnlySpan source, int sourceWidth, int sourc int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrAll)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * BytesPerPixel); + if (sourceStride == 0) sourceStride = checked(sourceWidth * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel); long stageStart = s_profileEnabled ? Stopwatch.GetTimestamp() : 0; PaddleOcrDetectionResult detection = _detector.Detect(source, sourceWidth, sourceHeight, sourceStride); if (s_profileEnabled) AddProfile(0, stageStart); diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs index 6c5e95c..f89a678 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrClassifier.cs @@ -56,7 +56,7 @@ public PaddleOcrClassificationResult Classify(ReadOnlySpan source, int sou int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrClassifier)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * PaddleOcrAll.BytesPerPixel); + if (sourceStride == 0) sourceStride = checked(sourceWidth * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel); if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) throw new InvalidOperationException("Source image exceeds MaxImagePixels."); bool profile = PipelineProfiler.Enabled; diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs index 8abc831..937a169 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrDetector.cs @@ -6,7 +6,7 @@ namespace Sdcb.SimdPaddleOCR; /// Pure managed DB detector. The caller supplies packed BGR or BGRA -/// bytes (see ). +/// bytes (see ). public sealed class PaddleOcrDetector : IDisposable { private static bool s_profileEnabled; @@ -102,7 +102,7 @@ public PaddleOcrDetectionResult Detect(ReadOnlySpan source, int sourceWidt int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrDetector)); - int bpp = PaddleOcrAll.BytesPerPixel; + int bpp = Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel; if (sourceStride == 0) sourceStride = checked(sourceWidth * bpp); int originalWidth = sourceWidth, originalHeight = sourceHeight; if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) diff --git a/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs b/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs index 2a0f784..f8f07ab 100644 --- a/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs +++ b/src/Sdcb.SimdPaddleOCR/PaddleOcrRecognizer.cs @@ -7,7 +7,7 @@ namespace Sdcb.SimdPaddleOCR; /// Pure managed PP-OCR CTC recognizer accepting BGR or BGRA crop -/// memory (see ). +/// memory (see ). public sealed class PaddleOcrRecognizer : IDisposable { private readonly Model _model; @@ -128,7 +128,7 @@ public PaddleOcrRecognitionResult Recognize(ReadOnlySpan source, int sourc int sourceStride = 0) { if (_disposed) throw new ObjectDisposedException(nameof(PaddleOcrRecognizer)); - if (sourceStride == 0) sourceStride = checked(sourceWidth * PaddleOcrAll.BytesPerPixel); + if (sourceStride == 0) sourceStride = checked(sourceWidth * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel); if ((long)sourceWidth * sourceHeight > _options.MaxImagePixels) throw new InvalidOperationException("Source image exceeds MaxImagePixels."); bool profile = PipelineProfiler.Enabled; @@ -263,7 +263,7 @@ internal void RecognizeBatch(byte[] cropBuffer, int[] offsets, int[] cropBytes, throw new InvalidOperationException("Source image exceeds MaxImagePixels."); resizedWidths[k] = PPOCRPreprocess.RecBgrToNchw( cropBuffer.AsSpan(offsets[line], cropBytes[line]), sourceWidth, sourceHeight, - checked(sourceWidth * PaddleOcrAll.BytesPerPixel), targetWidth, + checked(sourceWidth * Sdcb.SimdPaddleOCR.Kernels.Warp.BytesPerPixel), targetWidth, input.Slice(k * sampleLength, sampleLength), session.ResizeWorkspace); } if (profile) PipelineProfiler.Add(PipelineProfiler.RecPreprocess, started);