我尝试加载JPEG文件并删除图像中的所有黑白像素
C#代码:
...
m_SrcImage = new Bitmap(imagePath);
Rectangle r = new Rectangle(0, 0, m_SrcImage.Width, m_SrcImage.Height);
BitmapData bd = m_SrcImage.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
//Load Colors
int[] colours = new int[m_SrcImage.Width * m_SrcImage.Height];
Marshal.Copy(bd.Scan0, colours, 0, colours.Length);
m_SrcImage.UnlockBits(bd);
int len = colours.Length;
List<Color> result = new List<Color>(len);
for (int i = 0; i < len; ++i)
{
uint w = ((uint)colours[i]) & 0x00FFFFFF; //Delete alpha-channel
if (w != 0x00000000 && w != 0x00FFFFFF) //Check pixel is not black or white
{
w |= 0xFF000000; //Return alpha channel
result.Add(Color.FromArgb((int)w));
}
}
...
之后我尝试通过此代码在List中找到唯一的颜色
result.Sort((a, b) =>
{
return a.R != b.R ? a.R - b.R :
a.G != b.G ? a.G - b.G :
a.B != b.B ? a.B - b.B :
0;
});
List<Color> uniqueColors = new List<Color>( result.Count);
Color rgbTemp = result[0];
for (int i = 0; i < len; ++i)
{
if (rgbTemp == result[i])
{
continue;
}
uniqueColors.Add(rgbTemp);
rgbTemp = result[i];
}
uniqueColors.Add(rgbTemp);
此代码在同一图像上的不同机器上产生不同的结果!
例如,在this image上产生:
您可以download here的最低测试项目。它只是打开选定的图像并生成具有独特颜色的txt文件。
还有一个事实。在不同的机器上读取一些像素的方式不同我将txt文件与notepad ++进行比较,它显示某些像素具有不同的RGB组件。每个组件的差异为1,例如
我已阅读此帖
stackoverflow.com/questions/2419598/why-might-different-computers-calculate-different-arithmetic-results-in-vb-net
(对不起,我没有足够的raiting正常链接)。
...但是没有关于如何修复它的信息。
在VS 2015 Commumity Edition中使用OS Windows 7的机器上编译了.NET 4 Client profile的项目。
答案 0 :(得分:3)
Wikipedia has this to say about the accuracy requirements for JPEG Decoders:
JPEG标准中的编码说明无法修复输出压缩图像所需的精度。然而,JPEG标准(和类似的MPEG标准)包括对解码的一些精确要求,包括解码过程的所有部分(可变长度解码,逆DCT,去量化,输出的重新归一化);参考算法的输出不得超过:
- 每个像素组件最多有一位差异
- 每个8×8像素块的低均方误差
- 每个8×8像素块的平均误差非常低
- 整个图像的均方误差非常低
- 整个图像的平均误差极低
(我的重点)
简而言之,这里只有两种不同的解码器实现,并且它们在精度要求内产生不同的图像(如您所观察到的,1位=组件值+/- 1)。
没有使用相同的(非内置)jpeg解码器,这是可以预料的。如果你需要具有完全相同的输出,那么你可能需要切换到一个不同的解码器,无论你在哪个.NET版本或Windows上运行它都是相同的。我猜测GDI +是罪魁祸首,因为自Windows XP以来,它经历了更大的变化。
答案 1 :(得分:0)
我通过将Libjpeg.NET添加到项目并编写此代码来解决我的问题:
private Bitmap JpegToBitmap(JpegImage jpeg)
{
int width = jpeg.Width;
int height = jpeg.Height;
// Read the image into the memory buffer
int[] raster = new int[height * width];
for(int i = 0; i < height; ++i)
{
byte[] temp = jpeg.GetRow(i).ToBytes();
for (int j = 0; j < temp.Length; j += 3)
{
int offset = i*width + j / 3;
raster[offset] = 0;
raster[offset] |= (((int)temp[j+2]) << 16);
raster[offset] |= (((int)temp[j+1]) << 8);
raster[offset] |= (int)temp[j];
}
}
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte[] bits = new byte[bmpdata.Stride * bmpdata.Height];
for (int y = 0; y < bmp.Height; y++)
{
int rasterOffset = y * bmp.Width;
int bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride;
for (int x = 0; x < bmp.Width; x++)
{
int rgba = raster[rasterOffset++];
bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff);
bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff);
bits[bitsOffset++] = (byte)(rgba & 0xff);
}
}
System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length);
bmp.UnlockBits(bmpdata);
return bmp;
}
所以,这对我来说已经足够了。