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
}
}