【问题标题】:Importing JPEG 8BppIndexed image shows only 16 greyscale values C#导入 JPEG 8Bpp 索引图像仅显示 16 个灰度值 C#
【发布时间】:2014-08-20 15:47:56
【问题描述】:

这是我的问题

我使用扫描仪以灰度扫描对象并将其转换为 JPEG 格式以供 C# 程序分析。图片的像素格式为 8BppIndexed。

当我将此图像导入 C# 并绘制它的直方图时,我只看到 16 个灰度值,如下所示:

这些峰值之间的所有值都是 0。

这是正常直方图的样子(不要介意颜色,这个直方图是用其他工具制作的):

第一个直方图 (int[]) 由以下代码构成:

public static int[] GetHistogram(Bitmap b)
{
    int[] myHistogram = new int[256];
    for (int i = 0; i < myHistogram.Length; i++)
        myHistogram[i] = 0;
    BitmapData bmData = null;

    try
    {
        //Lock it fixed with 32bpp
        bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        int scanline = bmData.Stride;
        System.IntPtr Scan0 = bmData.Scan0;
        unsafe
        {
            byte* p = (byte*)(void*)Scan0;
            int nWidth = b.Width;
            int nHeight = b.Height;
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    long Temp = 0;
                    Temp += p[0]; //  p[0] - blue, p[1] - green , p[2]-red
                    Temp += p[1];
                    Temp += p[2];
                    Temp = (int)Temp / 3;
                    myHistogram[Temp]++;
                    //we do not need to use any offset, we always can increment by pixelsize when
                    //locking in 32bppArgb - mode
                    p += 4;
                }
            }
        }
        b.UnlockBits(bmData);
    }
    catch
    {
        try
        {
            b.UnlockBits(bmData);
        }
        catch
        {
        }
    }
    return myHistogram;
}

为了确保这段代码不是问题,我尝试使用 AForge.Math.Histogram 方式,甚至使用 for - in - for 循环来遍历所有像素。每次我得到相同的结果。

现在是有趣的部分:

  1. 当我使用任何其他工具(使用其他 3 个工具)绘制直方图时,我得到 一个正常的直方图。这告诉我信息在图像中,但我的代码无法将其取出。
  2. 当我扫描完全相同的对象并设置设置以将图像导出为 .bmp 文件时,c# 能够绘制正常的直方图
  3. 使用我在计算机上找到的另一个随机 .jpg 图像,c# 能够绘制正常的 直方图。

这些点告诉我,我将图像导入代码的方式可能有问题,因此我尝试了不同的方式来导入图像:

Bitmap bmp = (Bitmap)Bitmap.FromFile(path);

Bitmap bmp = AForge.Imaging.Image.FromFile(path);

Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
System.Windows.Media.Imaging.JpegBitmapDecoder decoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(imageStreamSource, System.Windows.Media.Imaging.BitmapCreateOptions.PreservePixelFormat, System.Windows.Media.Imaging.BitmapCacheOption.Default);
System.Windows.Media.Imaging.BitmapSource bitmapSource = decoder.Frames[0];
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = bitmapSource;
image.Stretch = System.Windows.Media.Stretch.None;

MemoryStream ms = new MemoryStream();
var encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(image.Source as System.Windows.Media.Imaging.BitmapSource));
encoder.Save(ms);
ms.Flush();      

System.Drawing.Image myImage = System.Drawing.Image.FromStream(ms);
Bitmap bmp = (Bitmap)Bitmap.FromStream(ms);

没有一个给出的直方图与只有 16 个结果的直方图不同。

我不能在我的扫描仪中使用 .bmp 扩展名,因为我需要制作大量图像,而一张 .bmp 图像大约是 200mb(是的,图像需要高分辨率),而 .jpg 只是大约30mb。另外,我已经制作了很多无法重新制作的 .jpg 图片,因为已扫描的对象已不存在。

注意:我知道使用 .jpg 扩展名是压缩图像的一种有损方式。这不是当前的问题。

这是使用与第一个完全相同的代码创建的直方图,与我计算机中另一个随机 .jpg 图像的样子:

这听起来对任何人来说都很熟悉吗?我觉得我什么都试过了。有没有其他方法可以解决这个我还没有找到的问题?

编辑

我以为我找到了一种非常肮脏的方法来解决我的问题,但它确实改变了直方图:

Bitmap temp = (Bitmap)Bitmap.FromFile(m_sourceImageFileName);
if (temp.PixelFormat == PixelFormat.Format8bppIndexed ||
    temp.PixelFormat == PixelFormat.Format4bppIndexed ||
    temp.PixelFormat == PixelFormat.Format1bppIndexed ||
    temp.PixelFormat == PixelFormat.Indexed)
{
    //Change pixelformat to a format that AForge can work with
    Bitmap tmp = temp.Clone(new Rectangle(0, 0, temp.Width, temp.Height), PixelFormat.Format24bppRgb);

    //This is a super dirty way to make sure the histogram shows more than 16 grey values.
    for (int i = 0; true; i++)
    {
        if (!File.Exists(m_sourceImageFileName + i + ".jpg"))
        {
            tmp.Save(m_sourceImageFileName + i + ".jpg");
            tmp.Dispose();
            temp = AForge.Imaging.Image.FromFile(m_sourceImageFileName + i + ".jpg");
            File.Delete(m_sourceImageFileName + i + ".jpg");
            break;
        }
    }
}
Bitmap properImage = temp;

这是新的直方图:

如您所见,它与直方图的外观不同。 我发现问题可能是因为图像是 8bppIndexed jpeg 图像,而 jpeg 仅支持 24bppRgb 图像。有什么解决办法吗?

【问题讨论】:

    标签: c# image-processing


    【解决方案1】:

    我认为线索在于您的第二行中被“索引”的类型。查找表中可能只有 16 种颜色。您能否发布您的原始扫描图像,以便我们查看其中是否真的有更多的阴影?如果没有,请尝试使用 ImageMagick 计算颜色

    这样得到直方图:

    convert yourimage.jpg -format %c histogram:info:-
    
    convert yourimage.jpg -colorspace rgb -colors 256 -depth 8 -format "%c" histogram:info:
    

    或者像这样计算独特的颜色:

    identify -verbose yourimage.jpg | grep -i colors:
    

    或者像这样转储所有像素:

    convert yourimage.jpg -colorspace rgb -colors 256 -depth 8 txt:
    

    【讨论】:

    • 但是如果查找表中只有 16 种颜色,为什么其他工具能够找到所有 256 个灰度值?试图上传原始图像,但半小时后我失去了信心,它会成功(这是一张大图)。我不擅长使用 Windows 提示符。如何让 imagemagick 在我的桌面上打开图像?如果我只是输入你的行,它会说“无法打开图像'original.jpg':没有这样的文件或目录@error/blob.c/OpenBlob/2658。”我尝试使用命令“cd c:\documents and settings\user\desktop”,但错误不断发生
    • 启动命令提示符并输入cd Desktop。如果您指定的路径中包含空格(例如 Documents and Settings),则必须用双引号将整个路径括起来。
    • 我不知道为什么其他工具会告诉你其他事情,但在其他地方你不会问你的问题显然有问题,所以我试图帮助你弄清楚发生了什么.
    • 好的,我让它工作了。结果如我所料:每个灰度值都有一定数量的像素。绘制此直方图将给出与我在原始帖子中发布的结果(第二张图片)相同的结果。转储像素确实让我得到了提示(图像为 14648x14288 像素),因为它显示了每个像素的值,这些值也从 0 缩放到 255;我看到了超过 16 种不同的值。这是否回答了您的问题?
    【解决方案2】:

    好吧,我通过打开 JPEG 并使用 java 中的 ImageJ 库将其保存为 bmp 来解决它。我从代码中创建了一个 .jar 文件,并使用此代码将 bmp 放入我的 c# 代码中:

    string extension = m_sourceImageFileName.Substring(m_sourceImageFileName.LastIndexOf("."), m_sourceImageFileName.Length - m_sourceImageFileName.LastIndexOf("."));
    int exitcode;
    ProcessStartInfo ProcessInfo;
    Process process;
    
    ProcessInfo = new ProcessStartInfo("java.exe", @"-jar ""C:\Users\stevenh\Documents\Visual Studio 2010\Projects\BlackSpotDetection V2.0\ConvertToBmp\dist\ConvertToBmp.jar"" " + extension + " " + m_sourceImageFileName + " " + m_addedImageName);
    
    ProcessInfo.CreateNoWindow = true;
    ProcessInfo.UseShellExecute = false;
    // redirecting standard output and error
    ProcessInfo.RedirectStandardError = true;
    ProcessInfo.RedirectStandardOutput = true;
    
    process = Process.Start(ProcessInfo);
    
    process.WaitForExit();
    
    //Reading output and error
    string output = process.StandardOutput.ReadToEnd();
    string error = process.StandardError.ReadToEnd();
    
    exitcode = process.ExitCode;
    if (exitcode != 0)
    {
        statusLabel.Text = output;
        MessageBox.Show("Error in external process: converting image to bmp.\n" + error);
        //Exit code '0' denotes success and '1' denotes failure
        return;
    }
    else
        statusLabel.Text = "Awesomeness";
    process.Close();
    
    Bitmap realImage = AForge.Imaging.Image.FromFile(m_addedImageName);
    File.Delete(m_addedImageName);
    

    jar 将接收扩展名 m_sourceImageFileName 和 m_addedImageFileName。它将打开 sourceImage 并将其保存在 m_addedImageFileName 的名称下 我正在使用 AForge 库打开图像,因为该库在打开图像时不会锁定图像,这使我能够删除“自制”图像。

    【讨论】:

      猜你喜欢
      • 2019-02-10
      • 1970-01-01
      • 2020-05-15
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-03
      相关资源
      最近更新 更多