using System; using System.Runtime.InteropServices; namespace YOLODetectProcessLib { /// /// cpp dll 中图像信息的数据结构 /// public struct StructImageInfo { public int width; public int height; public int channel; public IntPtr pData; } /// /// cpp dll 中矩形框的数据结构 /// public struct StructMyRect { public int left; public int top; public int width; public int height; } /// /// cpp dll 中提取前处理数据时所需信息的数据结构 /// public struct StructExtractInfo { public int meanValueType; public float meanR; public float meanG; public float meanB; public int scaleValueType; public float scaleR; public float scaleG; public float scaleB; public int normalizationType; public bool reverseInputChannels; public int axisOrder; } /// /// 图像处理过程中的一些辅助的功能函数 /// public class ImageUtils { #region dll import [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ResizeBilinear", CallingConvention = CallingConvention.Cdecl)] public static extern int ResizeBilinear(StructImageInfo origImg, ref StructImageInfo dstImg); [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "FillWithPadValue", CallingConvention = CallingConvention.Cdecl)] public static extern int FillWithPadValue(ref StructImageInfo origImg, IntPtr padValue); [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "CropRect", CallingConvention = CallingConvention.Cdecl)] public static extern int CropRect(StructImageInfo origImg, StructMyRect cropRect, ref StructImageInfo dstImg); [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "PasteRect", CallingConvention = CallingConvention.Cdecl)] public static extern int PasteRect(StructImageInfo srcImg, StructMyRect pasteRect, ref StructImageInfo dstImg); [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsGray", CallingConvention = CallingConvention.Cdecl)] public static extern int ExtractRGB24AsGray(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo); [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsColor", CallingConvention = CallingConvention.Cdecl)] public static extern int ExtractRGB24AsColor(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo); #endregion #region public public static IImage ResizeBilinearWithCpp(IImage image, int dstWidth, int dstHeight) { GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned); IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject(); StructImageInfo origImgInfo = new StructImageInfo { width = image.Width, height = image.Height, channel = image.Channel, pData = pDataOrig, }; RawImage resizedImg = new RawImage(new byte[dstWidth * dstHeight * image.Channel], dstWidth, dstHeight, image.Channel, dstWidth * image.Channel); GCHandle hObjectDst = GCHandle.Alloc(resizedImg.DataBuffer, GCHandleType.Pinned); IntPtr pDataDst = hObjectDst.AddrOfPinnedObject(); StructImageInfo dstImgInfo = new StructImageInfo { width = resizedImg.Width, height = resizedImg.Height, channel = resizedImg.Channel, pData = pDataDst, }; var ret = ResizeBilinear(origImgInfo, ref dstImgInfo); if (hObjectOrig.IsAllocated) { hObjectOrig.Free(); } if (hObjectDst.IsAllocated) { hObjectDst.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: ResizeBilinear.(error code: " + ret + ")."); } return resizedImg; } public static void FillPadValWithCpp(IImage image, byte[] padValue) { GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned); IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject(); StructImageInfo origImgInfo = new StructImageInfo { width = image.Width, height = image.Height, channel = image.Channel, pData = pDataOrig, }; GCHandle hObjectPadValue = GCHandle.Alloc(padValue, GCHandleType.Pinned); IntPtr pDataPadValue = hObjectPadValue.AddrOfPinnedObject(); var ret = FillWithPadValue(ref origImgInfo, pDataPadValue); if (hObjectOrig.IsAllocated) { hObjectOrig.Free(); } if (hObjectPadValue.IsAllocated) { hObjectPadValue.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: FillWithPadValue.(error code: " + ret + ")."); } } public static IImage CropRectWithCpp(IImage image, Rect cropRect) { GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned); IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject(); StructImageInfo origImgInfo = new StructImageInfo { width = image.Width, height = image.Height, channel = image.Channel, pData = pDataOrig, }; RawImage dstImage = new RawImage(new byte[cropRect.Width * cropRect.Height * image.Channel], cropRect.Width, cropRect.Height, image.Channel, cropRect.Width * image.Channel); GCHandle hObjectDst = GCHandle.Alloc(dstImage.DataBuffer, GCHandleType.Pinned); IntPtr pDataDst = hObjectDst.AddrOfPinnedObject(); StructImageInfo dstImgInfo = new StructImageInfo { width = dstImage.Width, height = dstImage.Height, channel = dstImage.Channel, pData = pDataDst, }; StructMyRect structCropRect = new StructMyRect { left = cropRect.Left, top = cropRect.Top, width = cropRect.Width, height = cropRect.Height, }; var ret = CropRect(origImgInfo, structCropRect, ref dstImgInfo); if (hObjectOrig.IsAllocated) { hObjectOrig.Free(); } if (hObjectDst.IsAllocated) { hObjectDst.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: CropRect.(error code: " + ret + ")."); } return dstImage; } public static void CropRectWithCpp(IImage image, Rect cropRect, ref byte[] dstImgDataBuffer) { // 目标图像数据区尺寸是否需要改变? int dstChannel = image.Channel; int dstWidth = cropRect.Width; int dstHeight = cropRect.Height; int dstDataLen = dstWidth * dstHeight * dstChannel; if (dstImgDataBuffer.Length < dstDataLen) { Array.Resize(ref dstImgDataBuffer, dstDataLen); } GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned); IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject(); StructImageInfo origImgInfo = new StructImageInfo { width = image.Width, height = image.Height, channel = image.Channel, pData = pDataOrig, }; GCHandle hObjectDst = GCHandle.Alloc(dstImgDataBuffer, GCHandleType.Pinned); IntPtr pDataDst = hObjectDst.AddrOfPinnedObject(); StructImageInfo dstImgInfo = new StructImageInfo { width = dstWidth, height = dstHeight, channel = dstChannel, pData = pDataDst, }; StructMyRect structCropRect = new StructMyRect { left = cropRect.Left, top = cropRect.Top, width = cropRect.Width, height = cropRect.Height, }; var ret = CropRect(origImgInfo, structCropRect, ref dstImgInfo); if (hObjectOrig.IsAllocated) { hObjectOrig.Free(); } if (hObjectDst.IsAllocated) { hObjectDst.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: CropRect.(error code: " + ret + ")."); } return; } public static void PasteRectWithCpp(IImage dstImg, IImage srcImg, Rect pasteRect) { GCHandle hObjectSrc = GCHandle.Alloc(srcImg.DataBuffer, GCHandleType.Pinned); IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject(); StructImageInfo srcImgInfo = new StructImageInfo { width = srcImg.Width, height = srcImg.Height, channel = srcImg.Channel, pData = pDataSrc, }; GCHandle hObjectDst = GCHandle.Alloc(dstImg.DataBuffer, GCHandleType.Pinned); IntPtr pDataDst = hObjectDst.AddrOfPinnedObject(); StructImageInfo dstImgInfo = new StructImageInfo { width = dstImg.Width, height = dstImg.Height, channel = dstImg.Channel, pData = pDataDst, }; StructMyRect structPasteRect = new StructMyRect { left = pasteRect.Left, top = pasteRect.Top, width = pasteRect.Width, height = pasteRect.Height, }; var ret = PasteRect(srcImgInfo, structPasteRect, ref dstImgInfo); if (hObjectSrc.IsAllocated) { hObjectSrc.Free(); } if (hObjectDst.IsAllocated) { hObjectDst.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: PasteRect.(error code: " + ret + ")."); } } public static IImage ResizeBilinear(IImage image, int dstWidth, int dstHeight) { byte[] dstDataBuffer = new byte[dstWidth * dstHeight * image.Channel]; ResizeBilinear(image, dstWidth, dstHeight, ref dstDataBuffer); return new RawImage(dstDataBuffer, dstWidth, dstHeight, image.Channel, dstWidth * image.Channel); } public static void FillPadVal(IImage image, byte[] padValue) { if (padValue == null) { throw new ArgumentNullException("padValue"); } int channel = image.Channel; byte[] dataBuffer = image.DataBuffer; if (padValue.Length != channel) { throw new ArgumentException("padValue", "Length of the padValue should equal to Channel."); } if (dataBuffer == null) { throw new ArgumentNullException("image.DataBuffer"); } int imageSize = image.Width * image.Height; for (int ni = 0; ni < imageSize; ni++) { Buffer.BlockCopy(padValue, 0, dataBuffer, ni * channel, channel); } } public static IImage CropRect(IImage image, Rect cropRect) { byte[] croppedDataBuffer = new byte[cropRect.Width * cropRect.Height * image.Channel]; CropRect(image, cropRect, ref croppedDataBuffer); return new RawImage(croppedDataBuffer, cropRect.Width, cropRect.Height, image.Channel, cropRect.Width * image.Channel); } public static void PasteRect(IImage dstImg, IImage srcImg, Rect pasteRect) { int dstRectLeft = pasteRect.Left; int dstRectRight = pasteRect.Right; int dstRectTop = pasteRect.Top; int dstRectBottom = pasteRect.Bottom; int dstRectWidth = pasteRect.Width; int dstRectHeight = pasteRect.Height; int width = dstImg.Width; int height = dstImg.Height; int channel = dstImg.Channel; int stride = dstImg.Stride; byte[] dataBuffer = dstImg.DataBuffer; if (dstRectLeft < 0 || dstRectLeft > width - 1) { throw new ArgumentOutOfRangeException("dstRect.Left", "Left of the paste dstRect is out of the image."); } if (dstRectRight < 0 || dstRectRight > width) { throw new ArgumentOutOfRangeException("dstRect.Right", "Right of the paste dstRect is out of the image."); } if (dstRectTop < 0 || dstRectTop > height - 1) { throw new ArgumentOutOfRangeException("dstRect.Top", "Top of the paste dstRect is out of the image."); } if (dstRectBottom < 0 || dstRectBottom > height) { throw new ArgumentOutOfRangeException("dstRect.Bottom", "Right of the paste dstRect is out of the image."); } if (srcImg == null) { throw new ArgumentNullException("srcImg"); } if (srcImg.Channel != channel) { throw new ArgumentException("srcImg.Channel", "The channel of srcImg should be equal to the channel of this image."); } if (srcImg.Width != dstRectWidth) { throw new ArgumentException("srcImg.Width", "The Width of srcImg should be equal to the width of the dstRect."); } if (srcImg.Height != dstRectHeight) { throw new ArgumentException("srcImg.Height", "The Height of srcImg should be equal to the height of the dstRect."); } if (dataBuffer == null) { throw new ArgumentNullException("dstImg.DataBuffer"); } // 如果rect的尺寸就是整幅图的尺寸,就直接整幅图clone即可,不需要一行一行复制 if (dstRectLeft == 0 && dstRectTop == 0 && dstRectWidth == width && dstRectHeight == height) { Buffer.BlockCopy(srcImg.DataBuffer, 0, dataBuffer, 0, width * height * channel); return; } byte[] srcImgDataBuffer = srcImg.DataBuffer; int rectRowCopyLen = dstRectWidth * channel; int rectCopyOffset = dstRectTop * width * channel + dstRectLeft * channel; int rowCopyOffset = width * channel; for (int nh = 0; nh < dstRectHeight; nh++) { Buffer.BlockCopy(srcImgDataBuffer, nh * rectRowCopyLen, dataBuffer, rectCopyOffset + nh * rowCopyOffset, rectRowCopyLen); } return; } public static void ResizeBilinear(IImage image, int dstWidth, int dstHeight, ref byte[] dstDataBuffer) { int width = image.Width; int height = image.Height; int channel = image.Channel; byte[] dataBuffer = image.DataBuffer; int dstDataLen = dstWidth * dstHeight * channel; if (dstDataBuffer.Length < dstDataLen) { Array.Resize(ref dstDataBuffer, dstDataLen); } float scale_x = (float)width / dstWidth; float scale_y = (float)height / dstHeight; int dstStep = dstWidth * channel; int srcStep = width * channel; float fy, fx; int sy, sx; short[] cbufy = new short[2]; short[] cbufx = new short[2]; int nh, nw, nc; for (nh = 0; nh < dstHeight; ++nh) { fy = (float)((nh + 0.5) * scale_y - 0.5); sy = Convert.ToInt32(Math.Floor(fy)); fy -= sy; sy = Math.Min(sy, height - 2); sy = Math.Max(0, sy); cbufy[0] = Convert.ToInt16((1 - fy) * 2048); cbufy[1] = Convert.ToInt16(2048 - cbufy[0]); for (nw = 0; nw < dstWidth; ++nw) { fx = (float)((nw + 0.5) * scale_x - 0.5); sx = Convert.ToInt16(Math.Floor(fx)); fx -= sx; if (sx < 0) { fx = 0; sx = 0; } if (sx >= width - 2) { fx = 0; sx = width - 2; } cbufx[0] = Convert.ToInt16((1 - fx) * 2048); cbufx[1] = Convert.ToInt16(2048 - cbufx[0]); for (nc = 0; nc < channel; ++nc) { dstDataBuffer[nh * dstStep + nw * channel + nc] = Convert.ToByte(( dataBuffer[sy * srcStep + sx * channel + nc] * cbufx[0] * cbufy[0] + dataBuffer[(sy + 1) * srcStep + sx * channel + nc] * cbufx[0] * cbufy[1] + dataBuffer[sy * srcStep + (sx + 1) * channel + nc] * cbufx[1] * cbufy[0] + dataBuffer[(sy + 1) * srcStep + (sx + 1) * channel + nc] * cbufx[1] * cbufy[1]) >> 22); } } } } public static void ResizeBilinearWithCpp(IImage image, int dstWidth, int dstHeight, ref byte[] dstImgDataBuffer) { // 目标图像数据区尺寸是否需要改变? int dstChannel = image.Channel; int dstDataLen = dstWidth * dstHeight * dstChannel; if (dstImgDataBuffer.Length < dstDataLen) { Array.Resize(ref dstImgDataBuffer, dstDataLen); } GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned); IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject(); StructImageInfo origImgInfo = new StructImageInfo { width = image.Width, height = image.Height, channel = image.Channel, pData = pDataOrig, }; GCHandle hObjectDst = GCHandle.Alloc(dstImgDataBuffer, GCHandleType.Pinned); IntPtr pDataDst = hObjectDst.AddrOfPinnedObject(); StructImageInfo dstImgInfo = new StructImageInfo { width = dstWidth, height = dstHeight, channel = dstChannel, pData = pDataDst, }; var ret = ResizeBilinear(origImgInfo, ref dstImgInfo); if (hObjectOrig.IsAllocated) { hObjectOrig.Free(); } if (hObjectDst.IsAllocated) { hObjectDst.Free(); } if (ret != 0) { throw new Exception("Failed at calling cpp func: ResizeBilinear.(error code: " + ret + ")."); } return; } public static void CropRect(IImage image, Rect cropRect, ref byte[] dstDataBuffer) { if (cropRect.IsEmpty()) { return; } int rectLeft = cropRect.Left; int rectRight = cropRect.Right; int rectTop = cropRect.Top; int rectBottom = cropRect.Bottom; int rectWidth = cropRect.Width; int rectHeight = cropRect.Height; int width = image.Width; int height = image.Height; int channel = image.Channel; int stride = image.Stride; byte[] dataBuffer = image.DataBuffer; if (rectLeft < 0 || rectLeft > width - 1) { throw new ArgumentOutOfRangeException("rect.Left", "Left of the crop rect is out of the image."); } if (rectRight < 0 || rectRight > width) { throw new ArgumentOutOfRangeException("rect.Right", "Right of the crop rect is out of the image."); } if (rectTop < 0 || rectTop > height - 1) { throw new ArgumentOutOfRangeException("rect.Top", "Top of the crop rect is out of the image."); } if (rectBottom < 0 || rectBottom > height) { throw new ArgumentOutOfRangeException("rect.Bottom", "Right of the crop rect is out of the image."); } // 目标图像的尺寸是否需要改变 int dstDataLen = rectWidth * rectHeight * channel; if (dstDataBuffer.Length < dstDataLen) { Array.Resize(ref dstDataBuffer, dstDataLen); } // 如果rect的尺寸就是整幅图的尺寸,就直接整幅图clone即可,不需要一行一行复制 if (rectLeft == 0 && rectTop == 0 && rectWidth == width && rectHeight == height) { Buffer.BlockCopy(dataBuffer, 0, dstDataBuffer, 0, image.DataBufferSize); } int rectRowCopyLen = rectWidth * channel; int rectCopyOffset = rectTop * width * channel + rectLeft * channel; for (int nh = 0; nh < rectHeight; nh++) { Buffer.BlockCopy(dataBuffer, rectCopyOffset + nh * stride, dstDataBuffer, nh * rectRowCopyLen, rectRowCopyLen); } } #endregion } }