如何使用LockBits将位图中非黑色的像素着色为黄色
本文关键字:像素 黄色 黑色 LockBits 何使用 位图 | 更新日期: 2023-09-27 18:23:51
使用GetPixel和SetPixel很容易,但速度很慢,所以我尝试使用LockBits。
我有这个方法,我做了很久以前比较两个图像:
public static Bitmap FastComparison(Bitmap bmp1,Bitmap bmp2)
{
tolerancenumeric = 15;
int tolerance = tolerancenumeric * tolerancenumeric +
tolerancenumeric * tolerancenumeric +
tolerancenumeric * tolerancenumeric; //dr * dr + dg * dg + db * db;
bmp3 = new Bitmap(512,512);
PixelFormat pxf = PixelFormat.Format24bppRgb;
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadWrite, pxf);
BitmapData bmpData2 = bmp2.LockBits(rect, ImageLockMode.ReadWrite, pxf);
BitmapData bmpData3 = bmp3.LockBits(rect, ImageLockMode.ReadWrite, pxf);
IntPtr ptr1 = bmpData1.Scan0;
IntPtr ptr2 = bmpData2.Scan0;
IntPtr ptr3 = bmpData3.Scan0;
int numBytes = bmpData1.Stride * bmp1.Height;
byte[] rgbValues1 = new byte[numBytes];
Marshal.Copy(ptr1, rgbValues1, 0, numBytes);
bmp1.UnlockBits(bmpData1);
byte[] rgbValues2 = new byte[numBytes];
Marshal.Copy(ptr2, rgbValues2, 0, numBytes);
bmp2.UnlockBits(bmpData2);
for (int counter = 0; counter < rgbValues1.Length; counter += 3)
{
int dr, dg, db;
dr = (int)rgbValues2[counter] - (int)rgbValues1[counter];
dg = (int)rgbValues2[counter + 1] - (int)rgbValues1[counter + 1];
db = (int)rgbValues2[counter + 2] - (int)rgbValues1[counter + 2];
int error = dr * dr + dg * dg + db * db;
int y, x;
y = (counter / 3) / 512;
x = (counter - y * 512 * 3)/3;
if ((x == 479) && (y == 474))
{
Byte r1, g1, b1, r2, g2, b2;
r1 = rgbValues1[counter];
b1 = rgbValues1[counter+1];
g1 = rgbValues1[counter+2];
r2 = rgbValues2[counter];
b2 = rgbValues2[counter+1];
g2 = rgbValues2[counter+2];
}
if (error < tolerance)
{
rgbValues1[counter] = 0;
rgbValues1[counter + 1] = 0;
rgbValues1[counter + 2] = 0;
}
}
Marshal.Copy(rgbValues1, 0, ptr3, numBytes);
bmp3.UnlockBits(bmpData3);
return bmp3;
}
但现在我也想使用LockBits,但只使用一个图像,并将所有非黑色像素涂成黄色。
我开始了新的方法:
public Bitmap ChangeColors(Bitmap bmp1)
{
bmpColors = new Bitmap(512, 512);
PixelFormat pxf = PixelFormat.Format24bppRgb;
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadWrite, pxf);
IntPtr ptr1 = bmpData1.Scan0;
int numBytes = bmpData1.Stride * bmp1.Height;
byte[] rgbValues1 = new byte[numBytes];
Marshal.Copy(ptr1, rgbValues1, 0, numBytes);
bmp1.UnlockBits(bmpData1);
for (int counter = 0; counter < rgbValues1.Length; counter += 3)
{
int y, x;
y = (counter / 3) / 512;
x = (counter - y * 512 * 3) / 3;
Byte r1, g1, b1;
r1 = rgbValues1[counter];
b1 = rgbValues1[counter + 1];
g1 = rgbValues1[counter + 2];
}
return bmpColors;
}
但不确定如何制作,因此位图bmpColors将是原始的,但所有像素都不是黄色中的黑色。
简单地测试字节并相应地设置它们如何?
Byte r1, g1, b1;
r1 = rgbValues1[counter]; // should be + 2 !!
b1 = rgbValues1[counter + 1]; // should be + 0 !!
g1 = rgbValues1[counter + 2]; // should be + 1 !!
if (r1 + b1 + g1 == 0 )
{
r1 = 255;
g1 = 255;
}
当然,这是假设一个真正的黑色和一个基本的黄色对你来说是可以的。。
如果你需要更多的控制,你需要更多像这样的代码
if (r1 + b1 + g1 < threshold)
对于黑色色调,以及可能的黄色:
r1 = myYellow_R;
g1 = myYellow_G;
b1 = myYellow_B;
BTW:您需要检查这些指数;上次我查看LockBits数组中的数据时被反转了:不是RGB
(更不用说RBG
了),而是BGR
!(对于32bpp BGRA
!)
由于您使用的是旧方法,请确保您的像素格式是可以的;如果是32bpp
,则需要进行一些简单的修改。
以下内容应该适用于具有未索引的8字节颜色的格式,这就是您正在使用的:
public static class BitmapHelper
{
//http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Advanced/PixelFormat.cs
//http://msdn.microsoft.com/en-us/magazine/cc534995.aspx#id0070035
private static void GetPixelFormatData(this PixelFormat pixelFormat, out byte bitsPerPixel, out byte bitsPerChannel, out bool hasAlpha, out bool premultiplyAlpha)
{
switch (pixelFormat)
{
//Specifies that the format is 24 bits per pixel; 8 bits each are used for the red, green, and blue components.
case PixelFormat.Format24bppRgb:
/// Specifies that pixel format is 24 bits per pixel. The
/// color information specifies 16777216 shades of color of which 8 bits are red, 8
/// bits are green and 8 bits are blue.
bitsPerPixel = 24;
bitsPerChannel = 8;
hasAlpha = false;
premultiplyAlpha = false;
break;
//Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components.
case PixelFormat.Format32bppArgb:
bitsPerPixel = 32;
bitsPerChannel = 8;
hasAlpha = true;
premultiplyAlpha = false;
break;
//Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied, according to the alpha component.
case PixelFormat.Format32bppPArgb:
bitsPerPixel = 32;
bitsPerChannel = 8;
hasAlpha = true;
premultiplyAlpha = true;
break;
//Specifies that the format is 32 bits per pixel; 8 bits each are used for the red, green, and blue components. The remaining 8 bits are not used.
case PixelFormat.Format32bppRgb:
bitsPerPixel = 32;
bitsPerChannel = 8;
hasAlpha = false;
premultiplyAlpha = false;
break;
//Specifies that the format is 48 bits per pixel; 16 bits each are used for the red, green, and blue components.
case PixelFormat.Format48bppRgb:
bitsPerPixel = 48;
bitsPerChannel = 16;
hasAlpha = false;
premultiplyAlpha = false;
break;
//Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components.
case PixelFormat.Format64bppArgb:
bitsPerPixel = 64;
bitsPerChannel = 16;
hasAlpha = true;
premultiplyAlpha = false;
break;
//Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied according to the alpha component.
case PixelFormat.Format64bppPArgb:
bitsPerPixel = 64;
bitsPerChannel = 16;
hasAlpha = true;
premultiplyAlpha = true;
break;
default:
throw new ArgumentException("Unsupported Pixel Format " + pixelFormat.ToString());
}
}
// Derived by experimentation.
const int BlueIndex = 0;
const int GreenIndex = 1;
const int RedIndex = 2;
const int AlphaIndex = 3;
public delegate bool TransformColorFunc(ref byte r, ref byte g, ref byte b, ref byte a);
public static void PaintSafe(Bitmap bmp, TransformColorFunc filter)
{
BitmapData bData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
try
{
byte bitsPerPixel;
byte bitsPerChannel;
bool hasAlpha;
bool premultiplyAlpha;
bmp.PixelFormat.GetPixelFormatData(out bitsPerPixel, out bitsPerChannel, out hasAlpha, out premultiplyAlpha);
if (bitsPerChannel != 8)
throw new ArgumentException();
if ((bitsPerPixel % 8) != 0)
throw new ArgumentException();
if ((!hasAlpha && bitsPerPixel < 24) || (hasAlpha && bitsPerPixel < 32))
throw new ArgumentException();
int size = bData.Stride * bData.Height;
byte[] data = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);
for (int iPixel = 0; iPixel < size; iPixel += bitsPerPixel / 8)
{
// Format is b, g, r, [a if present.]
byte b = data[iPixel + BlueIndex];
byte g = data[iPixel + GreenIndex];
byte r = data[iPixel + RedIndex];
byte a;
if (hasAlpha)
a = data[iPixel + AlphaIndex];
else
a = 255;
if (filter(ref r, ref g, ref b, ref a))
{
// Format is b, g, r, [a if present.]
data[iPixel + BlueIndex] = b;
data[iPixel + GreenIndex] = g;
data[iPixel + RedIndex] = r;
if (hasAlpha)
data[iPixel + AlphaIndex] = a;
}
}
System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);
}
finally
{
bmp.UnlockBits(bData);
}
}
}
然后称之为:
static bool TransformNonBlackToYellow(ref byte r, ref byte g, ref byte b, ref byte a)
{
if (r != 0 || g != 0 || b != 0)
{
r = 255;
g = 255;
b = 0;
a = 255;
return true;
}
return false;
}
BitmapHelper.PaintSafe(bitmap, TransformNonBlackToYellow);
注意,我没有让我的过滤器函数采用Color,因为从Argb值构建Color结构可能会非常慢。