这就是我在form1构造函数中所做的:
Bitmap bmp2 = new Bitmap(@"e:\result1001.jpg");
CropImageWhiteAreas.ImageTrim(bmp2);
bmp2.Save(@"e:\result1002.jpg");
bmp2.Dispose();
CropImageWhiteAreas类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Test
{
class CropImageWhiteAreas
{
public static Bitmap ImageTrim(Bitmap img)
{
//get image data
BitmapData bd = img.LockBits(new Rectangle(Point.Empty, img.Size),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int[] rgbValues = new int[img.Height * img.Width];
Marshal.Copy(bd.Scan0, rgbValues, 0, rgbValues.Length);
img.UnlockBits(bd);
#region determine bounds
int left = bd.Width;
int top = bd.Height;
int right = 0;
int bottom = 0;
//determine top
for (int i = 0; i < rgbValues.Length; i++)
{
int color = rgbValues[i] & 0xffffff;
if (color != 0xffffff)
{
int r = i / bd.Width;
int c = i % bd.Width;
if (left > c)
{
left = c;
}
if (right < c)
{
right = c;
}
bottom = r;
top = r;
break;
}
}
//determine bottom
for (int i = rgbValues.Length - 1; i >= 0; i--)
{
int color = rgbValues[i] & 0xffffff;
if (color != 0xffffff)
{
int r = i / bd.Width;
int c = i % bd.Width;
if (left > c)
{
left = c;
}
if (right < c)
{
right = c;
}
bottom = r;
break;
}
}
if (bottom > top)
{
for (int r = top + 1; r < bottom; r++)
{
//determine left
for (int c = 0; c < left; c++)
{
int color = rgbValues[r * bd.Width + c] & 0xffffff;
if (color != 0xffffff)
{
if (left > c)
{
left = c;
break;
}
}
}
//determine right
for (int c = bd.Width - 1; c > right; c--)
{
int color = rgbValues[r * bd.Width + c] & 0xffffff;
if (color != 0xffffff)
{
if (right < c)
{
right = c;
break;
}
}
}
}
}
int width = right - left + 1;
int height = bottom - top + 1;
#endregion
//copy image data
int[] imgData = new int[width * height];
for (int r = top; r <= bottom; r++)
{
Array.Copy(rgbValues, r * bd.Width + left, imgData, (r - top) * width, width);
}
//create new image
Bitmap newImage = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData nbd
= newImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(imgData, 0, nbd.Scan0, imgData.Length);
newImage.UnlockBits(nbd);
return newImage;
}
}
}
我也在彼得解决之前尝试过。
两个结果都是(这是我上传图片后的facebook的截图)仍然是周围的白色区域:
你可以在刚刚上传的图像周围看到矩形,看看周围的白色区域是什么意思。
答案 0 :(得分:1)
如果我理解正确,您找到了一个使用LockBits()
的示例代码段,但您不确定它是如何工作的,或者如何修改它以满足您的特定需求。所以我会尝试从这个角度回答。
首先,一个疯狂的猜测(因为你没有包含你在第一个例子中使用的LockBitmap
类的实现):LockBitmap
类是某种类型的helper类,它应该封装调用LockBits()
并使用结果的工作,包括提供GetPixel()
和SetPixel()
的版本,这些版本可能比在{{1}上调用这些方法快得多直接对象(即访问通过调用Bitmap
获得的缓冲区)。
如果是这样,那么修改第一个例子以满足您的需求可能是最好的:
LockBits()
简而言之:将当前像素值复制到局部变量,将该值与public void Change(Bitmap bmp)
{
Bitmap newBitmap = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);
LockBitmap source = new LockBitmap(bmp),
target = new LockBitmap(newBitmap);
source.LockBits();
target.LockBits();
Color white = Color.FromArgb(255, 255, 255, 255);
for (int y = 0; y < source.Height; y++)
{
for (int x = 0; x < source.Width; x++)
{
Color old = source.GetPixel(x, y);
if (old != white)
{
target.SetPixel(x, y, old);
}
}
}
source.UnlockBits();
target.UnlockBits();
newBitmap.Save("d:\\result.png");
}
颜色值进行比较,如果它不相同,请继续复制像素值到新位图。
第二个代码示例的一些变体也应该起作用。第二个代码示例明确地实现了(我已经假设)封装在第一个代码示例使用的white
类中的内容。如果由于某种原因,第一种方法不适合您的需要,您可以按照第二个例子。
在您提供的代码示例中,大部分方法只是处理&#34; grunt work&#34;锁定位图以便可以访问原始数据,然后迭代原始数据。
它根据外部{计算LockBitmap
和oRow
数组偏移量(以&#34;旧行&#34;以及&#34;新行&#34;,我推测)的名称计算{1}}循环,然后通过基于内部nRow
循环计算给定行内的偏移量来访问单个像素数据。
由于你想要做的事情基本相同,但是你不想将图像转换为灰度,而只是想要选择性地将所有非白色像素复制到新的位图,你可以(应该可以)简单地修改身体内部y
循环。例如:
x
以上内容将完全取代内部x
循环的主体。
需要注意的是:请注意,使用byte red = oRow[x * pixelSize + 2],
green = oRow[x * pixelSize + 1],
blue = oRow[x * pixelSize];
if (red != 255 || green != 255 || blue != 255)
{
nRow[x * pixelSize + 2] = red;
nRow[x * pixelSize + 1] = green;
nRow[x * pixelSize] = blue;
}
方法时,了解位图的像素格式至关重要。您显示的示例假定位图采用24 bpp格式。如果您自己的位图采用这种格式,那么您就不需要更改任何内容。但如果它们采用不同的格式,您需要调整代码以适应这种情况。例如,如果您的位图采用32 bpp格式,则需要将正确的x
值传递给LockBits()
方法调用,然后将PixelFormat
设置为LockBits()
而不是pixelSize
正如代码现在所做的那样4
。
修改强>
您已指出要剪裁新图像,使其成为包含所有非白色像素所需的最小尺寸。以上是第一个应该实现的示例:
3
此示例包括锁定原始位图后的初始扫描,以查找任何非白色像素的最小和最大坐标值。完成后,它使用该扫描的结果来确定新位图的尺寸。复制像素时,它会将public void Change(Bitmap bmp)
{
LockBitmap source = new LockBitmap(bmp);
source.LockBits();
Color white = Color.FromArgb(255, 255, 255, 255);
int minX = int.MaxValue, maxX = int.MinValue,
minY = int.MaxValue, maxY = int.MinValue;
// Brute-force scan of the bitmap to find image boundary
for (int y = 0; y < source.Height; y++)
{
for (int x = 0; x < source.Width; x++)
{
if (source.GetPixel(x, y) != white)
{
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}
Bitmap newBitmap = new Bitmap(maxX - minx + 1, maxY - minY + 1, bmp.PixelFormat);
LockBitmap target = new LockBitmap(newBitmap);
target.LockBits();
for (int y = 0; y < target.Height; y++)
{
for (int x = 0; x < target.Width; x++)
{
target.SetPixel(x, y, source.GetPixel(x + minX, y + minY));
}
}
source.UnlockBits();
target.UnlockBits();
newBitmap.Save("d:\\result.png");
}
和x
循环限制为新位图的尺寸,并调整y
和x
值以映射新位置位图到给定像素的原始位置。
请注意,由于初始扫描确定了非白色像素的位置,因此在实际复制像素时无需再次检查。
扫描位图的方法比上述方法更有效。此版本只查看原始位图中的每个像素,跟踪每个坐标的最小值和最大值。我猜这会对你的目的足够快,但是如果你想要更快的东西,你可以改变扫描,以便按顺序扫描每个最小值和最大值:
y
为0的每一行以确定具有非白色像素的第一行。这是最小y
值。y
y
的每一行,找到最大source.Height - 1
值。y
值后,现在扫描y
为0的列,找到分钟x
,然后从x
向后查找最大值{{} {1}}。这样做会涉及更多代码,并且可能更难以阅读和理解,但在大多数情况下会涉及检查更少的像素。
编辑#2:
以下是第二个代码示例的输出示例:
请注意,原始位图的所有白色边框(显示在左侧)都已被裁剪掉,只留下原始位图的最小子集,它可以包含所有非白色像素(如右侧所示)。