1

So here is my problem

I've used a scanner to scan an object in greyscale and convert it into a JPEG format to be analyzed by a C# program. The image's pixelformat is 8BppIndexed.

When I import this image into C# and draw a histogram of it, I only see 16 grayscale values, like this:

enter image description here

All the values in between these peaks are 0.

This is what the normal histogram should look like (don't mind the colors, this histogram is made with another tool):

enter image description here

The first histogram (int[]) is formed with this code:

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;
}

To be sure this code is not the problem, I've tried using the AForge.Math.Histogram way and even a for - in - for loop to iterate through all pixels. Each time I get the same result.

Now here is the funny part(s):

  1. When I draw the histogram with any other tool (used 3 others), I get a normal histogram. This tells me that the information is within the image, but my code just can't get it out.
  2. When I scan the exact same object and set the settings to export the image into a .bmp file, c# is able to draw a normal histogram
  3. With another random .jpg image I found on my computer, c# is able to draw a normal histogram.

These points tell me that there is probably something wrong with the way that I import the image into my code, so I tried different ways to import the image:

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

or

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

or

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);

None of which gave a different histogram than the one with just 16 results.

I can not use the .bmp extension in my scanner, because I need to make a great many images and one .bmp image is around 200mb (yea, the images need a high resolution), while the .jpg is only around 30mb. Plus I've already made many .jpg images that can not be remade because the objects that have been scanned no longer exist.

NOTE: I know that using the .jpg extension is a lossy way to compress the images. That is not the current issue.

This is what a histogram, created with the exact same code as the first one, looks like with another random .jpg image from my computer:

enter image description here

Does this sound familiar to anyone? I feel like I've tried everything. Is there another way to solve this problem that I have not yet found?

EDIT

I thought I had found an extremely dirty way to fix my problem, but it does change the histogram:

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;

This is the new histogram:

As you can see, it's not the same as what the histogram should look like. I found out that the problem might be because the image is an 8bppIndexed jpeg image, and jpeg only supports 24bppRgb images. Any solutions?

Steven H
  • 39
  • 10

2 Answers2

1

I think the clue is in the type being "indexed" in your second line. There are probably only 16 colours in the lookup table. Can you post your original scanned image so we can see if there are really more shades in it? If not, try using ImageMagick to count the colours

Like this to get a histogram:

convert yourimage.jpg -format %c histogram:info:-

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

Or count the unique colours like this:

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

Or dump all the pixels like this:

convert yourimage.jpg -colorspace rgb -colors 256 -depth 8 txt:
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • But if there is only 16 colours in the lookup table, why is any other tool able to find all 256 grey scale values? Trying to upload the original image, but after half an hour I'm losing faith it's going to succeed (it's a big image). I'm not great with a windows prompt. How do I get imagemagick to open an image on my desktop? If I just type your line, it says "unable to open image 'original.jpg': no such file or directory @ error/blob.c/OpenBlob/2658." I tried using the command "cd c:\documents and settings\user\desktop", but the error keeps occurring – Steven H Aug 01 '14 at 08:57
  • Start a Command Prompt and just type `cd Desktop`. If you specify a path with spaces in it (like `Documents and Settings`) you must surround the entire path with double quotes. – Mark Setchell Aug 01 '14 at 18:25
  • I don't know why other tools tell you other things, but something is clearly wrong somewhere else you wouldn't be asking your question, so I am trying to help you work out what is going on. – Mark Setchell Aug 01 '14 at 18:28
  • Ok I got it to work. The results are as I expected: every gray value has an amount of pixels. Drawing this Histogram would give the same result as the one I posted in the original post (the 2nd image). Dumping the pixels really got my prompt going (the image is 14648x14288 pixels), since it's showing the value of each pixel, which are also scaling from 0 to 255; I am seeing more than 16 different values. Does this answer the question that you had? – Steven H Aug 04 '14 at 07:30
  • So how many colours are there? `identify -verbose yourimage.jpg | grep -i colors:` – Mark Setchell Aug 04 '14 at 07:31
  • 'grep' is not recognized as an internal or external command, operable program or batch file. – Steven H Aug 04 '14 at 07:41
  • Oh, you are on Windows. Try `identify -verbose yourimage.jpg | more` and look for a line that says `Colors:`. Or `identify -verbose yourimage.jpg | FINDSTR /i colours:` – Mark Setchell Aug 04 '14 at 07:42
  • Ok, it looks like it has 256 colours. You can see the histogram like this and then hopefully work out what is wrong with your image. `convert your image.jpg -format "%c" histogram:info: | more` – Mark Setchell Aug 04 '14 at 08:02
  • Format: JPEG. Mime type: image/jpeg. Class: PseudoClass. Geometry:14648x14288+0+0. Resolution: 2400x2400. Print size: 6.10333x5.95333. Units: PixelsPerInch. Type: Greyscale. Base type: Greyscale. Endianess: Undefined. Colorspace: Gray. Depth: 8-bit. Channel depts: gray: 8-bits. Channel statistics: Gray: min: 0 <0>, max: 255 <1>, mean: 164,295 <0.644293>, Standard deviation: 65.6141 <0.25731>, kurtosis: 0.595628, skweness: -1.24874. Colors: 256 – Steven H Aug 04 '14 at 08:03
  • 1
    Have a look here also - http://stackoverflow.com/questions/4679827/c-sharp-why-bitmap-save-ignores-pixelformat-of-bitmap – Mark Setchell Aug 04 '14 at 08:05
  • Well looking at the histogram shows me that, again, other image processors are able to get the full histogram while my c# code isn't. I'll take a long look at the other post. – Steven H Aug 04 '14 at 08:09
  • I found an extremely dirty way get closer to the solution. The fix is in the original post. – Steven H Aug 04 '14 at 13:58
0

Well, I solved it by opening the JPEG and saving it as bmp with the ImageJ library in java. I've made a .jar file from the code and I use this code to get the bmp into my c# code:

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);

The jar will receive the extension, m_sourceImageFileName and m_addedImageFileName. It will open the sourceImage and save it under the name of m_addedImageFileName I'm using the AForge library to open the image, because this library doesn't lock the image while it's opened, which makes me able to delete the 'home-made' image.

Steven H
  • 39
  • 10