C# 从图像中剪切矩形块
C# cut rectangle blocks from image
我开发了一个屏幕共享应用程序,我想让它尽可能高效,所以我试图只发送屏幕截图之间的差异。
所以,假设我们有这个图像 example:its 一个 32bpprgba 图像,周围有透明部分。
我想将这里的每个块作为一个矩形存储在列表中并设置它们的边界。这听起来可能很复杂,但实际上只需要一点逻辑。
到目前为止,这是我的代码:
private unsafe List<Rectangle> CodeImage(Bitmap bmp)
{
List<Rectangle> rec = new List<Rectangle>();
Bitmap bmpRes = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr scan0 = bmData.Scan0;
int stride = bmData.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
int minX = int.MaxValue; ;
int minY = int.MaxValue;
int maxX = 0;
bool found = false;
for (int y = 0; y < bmp.Height; y++)
{
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < bmp.Width; x++)
{
if (p[3] != 0) //Check if pixel is not transparent;
{
found = true;
if (x < minX)
minX = x;
if (x > maxX)
maxX = x;
if (y < minY)
minY = y;
}
else
{
if (found)
{
int height = getBlockHeight(stride, scan0, maxX, minY);
found = false;
Rectangle temp = new Rectangle(minX, minY, maxX - minX, height);
rec.Add(temp);
y += minY;
break;
}
}
p += 4;//add to the pointer 4 bytes;
}
}
return rec;
}
如您所见,我正在尝试使用高度和宽度扫描图像,当我找到一个像素时,我将它发送到 GetBlockHeight
函数以获取它的高度:
public unsafe int getBlockHeight(int stride, IntPtr scan, int x, int y1)
{
int height = 0; ;
for (int y = y1; y < 1080; y++)
{
byte* p = (byte*)scan.ToPointer();
p += (y * stride) + (x * 4);
if (p[3] != 0) //Check if pixel is not transparent;
{
height++;
}
}
return height;
}
但我只是没有得到结果...我认为这里的逻辑有点问题...任何人都可以照亮我的眼睛吗?我知道这需要一些时间和思考,但我非常感谢任何能提供一点帮助的人。
在您当前的算法中,在成功匹配一个矩形后,您增加 y
及其高度,并 break
超出内部循环。这意味着您只能为每条水平线检测一个矩形的数据。
如果我是你,在回到代码之前,我会考虑以下事项:
- 将完整的图像保存为PNG文件,并查看其大小。真的需要进一步处理吗?
- 这些矩形准确吗?会不会出现无论如何都要不断发送整个屏幕内容的情况?
- 如果您正在为 Windows 进行开发,您可能能够连接到使屏幕上的区域无效的过程,在这种情况下,您不必自己确定这些矩形。我不知道其他操作系统
此外,我个人不会尝试在 "nesty" for
循环中解决矩形检测算法,而是采用如下方法:
public void FindRectangles(Bitmap bitmap, Rectangle searchArea, List<Rectangle> results)
{
// Find the first non-transparent pixel within the search area.
// Ensure that it is the pixel with the lowest y-value possible
Point p;
if (!TryFindFirstNonTransparent(bitmap, searchArea, out p))
{
// No non-transparent pixels in this area
return;
}
// Find its rectangle within the area
Rectangle r = GetRectangle(bitmap, p, searchArea);
results.Add(r);
// No need to search above the rectangle we just found
Rectangle left = new Rectangle(searchArea.Left, r.Top, r.Left - searchArea.Left, searchArea.Bottom - r.Top);
Rectangle right = new Rectangle(r.Right, r.Top, searchArea.Right - r.Right, searchArea.Bottom - r.Top);
Rectangle bottom = new Rectangle(r.Left, r.Bottom, r.Width, searchArea.Bottom - r.Bottom);
FindRectangles(bitmap, left, results);
FindRectangles(bitmap, right, results);
FindRectangles(bitmap, bottom, results);
}
public Rectangle GetRectangle(Bitmap bitmap, Point p, Rectangle searchArea)
{
int right = searchArea.Right;
for (int x = p.X; x < searchArea.Right; x++)
{
if (IsTransparent(x, p.Y))
{
right = x - 1;
break;
}
}
int bottom = searchArea.Bottom;
for (int y = p.Y; y < searchArea.Bottom; y++)
{
if (IsTransparent(p.X, y))
{
bottom = y - 1;
break;
}
}
return new Rectangle(p.X, p.Y, right - p.X, bottom - p.Y);
}
这种方法在完全实现后应该会为您提供一个矩形列表(尽管它偶尔会将一个矩形分成两部分)。
(当然,不是提供 bitmap
,而是将指针传递给带有一些元数据的像素数据)
我开发了一个屏幕共享应用程序,我想让它尽可能高效,所以我试图只发送屏幕截图之间的差异。
所以,假设我们有这个图像 example:its 一个 32bpprgba 图像,周围有透明部分。
我想将这里的每个块作为一个矩形存储在列表中并设置它们的边界。这听起来可能很复杂,但实际上只需要一点逻辑。 到目前为止,这是我的代码:
private unsafe List<Rectangle> CodeImage(Bitmap bmp)
{
List<Rectangle> rec = new List<Rectangle>();
Bitmap bmpRes = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr scan0 = bmData.Scan0;
int stride = bmData.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
int minX = int.MaxValue; ;
int minY = int.MaxValue;
int maxX = 0;
bool found = false;
for (int y = 0; y < bmp.Height; y++)
{
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < bmp.Width; x++)
{
if (p[3] != 0) //Check if pixel is not transparent;
{
found = true;
if (x < minX)
minX = x;
if (x > maxX)
maxX = x;
if (y < minY)
minY = y;
}
else
{
if (found)
{
int height = getBlockHeight(stride, scan0, maxX, minY);
found = false;
Rectangle temp = new Rectangle(minX, minY, maxX - minX, height);
rec.Add(temp);
y += minY;
break;
}
}
p += 4;//add to the pointer 4 bytes;
}
}
return rec;
}
如您所见,我正在尝试使用高度和宽度扫描图像,当我找到一个像素时,我将它发送到 GetBlockHeight
函数以获取它的高度:
public unsafe int getBlockHeight(int stride, IntPtr scan, int x, int y1)
{
int height = 0; ;
for (int y = y1; y < 1080; y++)
{
byte* p = (byte*)scan.ToPointer();
p += (y * stride) + (x * 4);
if (p[3] != 0) //Check if pixel is not transparent;
{
height++;
}
}
return height;
}
但我只是没有得到结果...我认为这里的逻辑有点问题...任何人都可以照亮我的眼睛吗?我知道这需要一些时间和思考,但我非常感谢任何能提供一点帮助的人。
在您当前的算法中,在成功匹配一个矩形后,您增加 y
及其高度,并 break
超出内部循环。这意味着您只能为每条水平线检测一个矩形的数据。
如果我是你,在回到代码之前,我会考虑以下事项:
- 将完整的图像保存为PNG文件,并查看其大小。真的需要进一步处理吗?
- 这些矩形准确吗?会不会出现无论如何都要不断发送整个屏幕内容的情况?
- 如果您正在为 Windows 进行开发,您可能能够连接到使屏幕上的区域无效的过程,在这种情况下,您不必自己确定这些矩形。我不知道其他操作系统
此外,我个人不会尝试在 "nesty" for
循环中解决矩形检测算法,而是采用如下方法:
public void FindRectangles(Bitmap bitmap, Rectangle searchArea, List<Rectangle> results)
{
// Find the first non-transparent pixel within the search area.
// Ensure that it is the pixel with the lowest y-value possible
Point p;
if (!TryFindFirstNonTransparent(bitmap, searchArea, out p))
{
// No non-transparent pixels in this area
return;
}
// Find its rectangle within the area
Rectangle r = GetRectangle(bitmap, p, searchArea);
results.Add(r);
// No need to search above the rectangle we just found
Rectangle left = new Rectangle(searchArea.Left, r.Top, r.Left - searchArea.Left, searchArea.Bottom - r.Top);
Rectangle right = new Rectangle(r.Right, r.Top, searchArea.Right - r.Right, searchArea.Bottom - r.Top);
Rectangle bottom = new Rectangle(r.Left, r.Bottom, r.Width, searchArea.Bottom - r.Bottom);
FindRectangles(bitmap, left, results);
FindRectangles(bitmap, right, results);
FindRectangles(bitmap, bottom, results);
}
public Rectangle GetRectangle(Bitmap bitmap, Point p, Rectangle searchArea)
{
int right = searchArea.Right;
for (int x = p.X; x < searchArea.Right; x++)
{
if (IsTransparent(x, p.Y))
{
right = x - 1;
break;
}
}
int bottom = searchArea.Bottom;
for (int y = p.Y; y < searchArea.Bottom; y++)
{
if (IsTransparent(p.X, y))
{
bottom = y - 1;
break;
}
}
return new Rectangle(p.X, p.Y, right - p.X, bottom - p.Y);
}
这种方法在完全实现后应该会为您提供一个矩形列表(尽管它偶尔会将一个矩形分成两部分)。
(当然,不是提供 bitmap
,而是将指针传递给带有一些元数据的像素数据)