字节数组图像裁剪性能问题
Byte array image crop performance issue
有两个指针(字节*)指向 1. B8G8R8A8 像素数据 2. 用于放置裁剪像素数据的字节缓冲区,非常简单的问题。这是我的实现:
private unsafe void Crop(byte* src, byte* dst, Rectangle rSrc, Rectangle rDst)
{
int sSrc = 4 * rSrc.Width, sDst = 4 * rDst.Width; // stride
for (int x = 0; x < rDst.Width; x++) for (int y = 0; y < rDst.Height; y++)
{
dst[(x * 4 + y * (sDst)) + 0] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 0];
dst[(x * 4 + y * (sDst)) + 1] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 1];
dst[(x * 4 + y * (sDst)) + 2] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 2];
dst[(x * 4 + y * (sDst)) + 3] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 3];
}
}
它有效:1920x1080 为 ~ 100 毫秒,但需要 ~ 10 - 15 毫秒。有可能让它更快吗?例如普通副本(没有裁剪)提供更好的性能 8 毫秒(对于相同的分辨率),这里是功能:
private unsafe void Copy(void* dst, void* src, long count)
{
long block; block = count >> 3;
long* pDst = (long*)dst; long* pSrc = (long*)src;
{
for (int i = 0; i < block; i++) { *pDst = *pSrc; pDst++; pSrc++; }
}
}
需要你的想法!
谢谢。
实际上,您可以在 3 毫秒内完成 运行,并且不需要不安全。
void Main()
{
int width;
int height;
int border;
byte[] src;
using (var bm = new Bitmap(@"C:\Data\sample-photo.jpg"))
{
width = bm.Width;
height = bm.Height;
border = width / 10;
src = new byte[width * height * 4];
for (var y = 0; y < bm.Height; y++)
for (var x = 0; x < bm.Width; x++)
{
var pixel = bm.GetPixel(x,y);
var i = GetIndex(width, x,y);
src[i + 1] = pixel.R;
src[i + 2] = pixel.G;
src[i + 3] = pixel.B;
}
}
var sw = Stopwatch.StartNew();
byte[] dst = null;
dst = Crop(src, width, border, width - border, border, height - border);
sw.Stop();
Console.WriteLine(sw.Elapsed);
using (var dstBmp = new Bitmap(width - border * 2, height - border * 2, PixelFormat.Format32bppArgb))
{
for (var y = 0; y < (height - border * 2); y++)
for (var x = 0; x < (width - border * 2); x++)
{
var i = GetIndex(dstBmp.Width, x, y);
var r = dst[i + 1];
var g = dst[i + 2];
var b = dst[i + 3];
dstBmp.SetPixel(x, y, Color.FromArgb(255, r, g, b));
}
dstBmp.Save(@"C:\Data\sample-photo-cropped.jpg");
}
}
byte[] Crop(byte[] src, int srcWidth, int xStart, int xEnd, int yStart, int yEnd)
{
var width = xEnd - xStart;
var height = yEnd - yStart;
var dst = new byte[width * height * 4];
for (var iY = yStart; iY < yEnd; iY++)
{
Array.Copy(src, GetIndex(srcWidth, xStart, iY), dst, GetIndex(width, 0, iY - yStart), width * 4);
}
return dst;
}
int GetIndex(int width, int x, int y)
{
return (x + (width * y)) * 4;
}
输出:
00:00:00.0007131
要使用指针,您可以使用 Marshal
class,它有一堆用于复制内存的重载或 Buffer.MemoryCopy Method
给你
private static unsafe void Crop(byte* src, byte* dst, Rectangle rSrc, Rectangle rDst)
{
int* pDst = (int*)dst, pSrc = (int*)src + (rDst.Y * rSrc.Width + rDst.X);
int srcSkip = rSrc.Width - rDst.Width;
int rowCount = rDst.Height, colCount = rDst.Width;
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++) *pDst++ = *pSrc++;
pSrc += srcSkip;
}
}
有两个指针(字节*)指向 1. B8G8R8A8 像素数据 2. 用于放置裁剪像素数据的字节缓冲区,非常简单的问题。这是我的实现:
private unsafe void Crop(byte* src, byte* dst, Rectangle rSrc, Rectangle rDst)
{
int sSrc = 4 * rSrc.Width, sDst = 4 * rDst.Width; // stride
for (int x = 0; x < rDst.Width; x++) for (int y = 0; y < rDst.Height; y++)
{
dst[(x * 4 + y * (sDst)) + 0] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 0];
dst[(x * 4 + y * (sDst)) + 1] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 1];
dst[(x * 4 + y * (sDst)) + 2] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 2];
dst[(x * 4 + y * (sDst)) + 3] = src[((rDst.X + x) * 4 + (rDst.Y + y) * (sSrc)) + 3];
}
}
它有效:1920x1080 为 ~ 100 毫秒,但需要 ~ 10 - 15 毫秒。有可能让它更快吗?例如普通副本(没有裁剪)提供更好的性能 8 毫秒(对于相同的分辨率),这里是功能:
private unsafe void Copy(void* dst, void* src, long count)
{
long block; block = count >> 3;
long* pDst = (long*)dst; long* pSrc = (long*)src;
{
for (int i = 0; i < block; i++) { *pDst = *pSrc; pDst++; pSrc++; }
}
}
需要你的想法!
谢谢。
实际上,您可以在 3 毫秒内完成 运行,并且不需要不安全。
void Main()
{
int width;
int height;
int border;
byte[] src;
using (var bm = new Bitmap(@"C:\Data\sample-photo.jpg"))
{
width = bm.Width;
height = bm.Height;
border = width / 10;
src = new byte[width * height * 4];
for (var y = 0; y < bm.Height; y++)
for (var x = 0; x < bm.Width; x++)
{
var pixel = bm.GetPixel(x,y);
var i = GetIndex(width, x,y);
src[i + 1] = pixel.R;
src[i + 2] = pixel.G;
src[i + 3] = pixel.B;
}
}
var sw = Stopwatch.StartNew();
byte[] dst = null;
dst = Crop(src, width, border, width - border, border, height - border);
sw.Stop();
Console.WriteLine(sw.Elapsed);
using (var dstBmp = new Bitmap(width - border * 2, height - border * 2, PixelFormat.Format32bppArgb))
{
for (var y = 0; y < (height - border * 2); y++)
for (var x = 0; x < (width - border * 2); x++)
{
var i = GetIndex(dstBmp.Width, x, y);
var r = dst[i + 1];
var g = dst[i + 2];
var b = dst[i + 3];
dstBmp.SetPixel(x, y, Color.FromArgb(255, r, g, b));
}
dstBmp.Save(@"C:\Data\sample-photo-cropped.jpg");
}
}
byte[] Crop(byte[] src, int srcWidth, int xStart, int xEnd, int yStart, int yEnd)
{
var width = xEnd - xStart;
var height = yEnd - yStart;
var dst = new byte[width * height * 4];
for (var iY = yStart; iY < yEnd; iY++)
{
Array.Copy(src, GetIndex(srcWidth, xStart, iY), dst, GetIndex(width, 0, iY - yStart), width * 4);
}
return dst;
}
int GetIndex(int width, int x, int y)
{
return (x + (width * y)) * 4;
}
输出:
00:00:00.0007131
要使用指针,您可以使用 Marshal
class,它有一堆用于复制内存的重载或 Buffer.MemoryCopy Method
给你
private static unsafe void Crop(byte* src, byte* dst, Rectangle rSrc, Rectangle rDst)
{
int* pDst = (int*)dst, pSrc = (int*)src + (rDst.Y * rSrc.Width + rDst.X);
int srcSkip = rSrc.Width - rDst.Width;
int rowCount = rDst.Height, colCount = rDst.Width;
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < colCount; col++) *pDst++ = *pSrc++;
pSrc += srcSkip;
}
}