DPI and Color Depth Resulting in Banded Images

Discussions about image processing and document imaging.
Post Reply
robduff
Posts: 7
Joined: Thu May 05, 2011 4:47 pm

DPI and Color Depth Resulting in Banded Images

Post by robduff » Mon Sep 19, 2011 7:12 pm

My requirements specify the image must be 72.009 DPI and have a bit depth of 8-bits, and if an image does not match these requirements, then I am to convert it so it does. I am having difficulty taking a BMP image that has a vertical and horizontal DPI of 71.9836 and setting it to 72.009 without the color depth changing from 8-bits (256 colors) to 24-bit (8-bits per channel). The 24-bit image looks just like the original, but doesn't meet my requirements. If I call ConvertTo8BppQ, then the image can be saved as an 8-bit depth image successfully. However, this causes banding (stair casing) in the image. Could you please advise how I can set an image from 71.9836 DPI (vertical and horizontal) to 72.009 DPI and 8-bit depth without this banding effect? The original image I am changing the DPI for is at an 8-bit depth already - all I am trying to change is the DPI. I have attached to sample files. PARJFK05.BMP is the original file, and the PARJFK05-test.BMP is the resulting "banded" image after getting the original to meet my specifications I was given. Source code is below that I am using.

Code: Select all

        Dim objGdP As GdPicture.GdPictureImaging = Nothing
        objGdP = New GdPicture.GdPictureImaging()
        objGdP.SetLicenseNumber(GdPictureLicense)

        Dim strSrcFile As String = "C:\DevTest\GdPicture\PARJFK05.BMP"
        Dim intImageID As Integer = objGdP.CreateGdPictureImageFromFile(strSrcFile)

        Dim fltHRes As Single = objGdP.GetHorizontalResolution(intImageID)
        Dim fltVRes As Single = objGdP.GetVerticalResolution(intImageID)

        Dim fltDPIResolution As Single = 72.009
        objGdP.SetHorizontalResolution(intImageID, fltDPIResolution)
        objGdP.SetVerticalResolution(intImageID, fltDPIResolution)

        objGdP.ConvertTo8BppQ(intImageID, 256)

        Dim strDstFile As String = "C:\DevTest\GdPicture\PARJFK05-test.BMP"
        If (File.Exists(strDstFile)) Then

            File.Delete(strDstFile)

        End If
        objGdP.SaveAsBMP(intImageID, strDstFile)

        objGdP.ReleaseGdPictureImage(intImageID)
        objGdP.ClearGdPicture()
Attachments
PARJFK05.zip
Images files.
(33.42 KiB) Downloaded 307 times

robduff
Posts: 7
Joined: Thu May 05, 2011 4:47 pm

Re: DPI and Color Depth Resulting in Banded Images

Post by robduff » Fri Sep 30, 2011 3:24 pm

While I was waiting for a response I did my own research on how to fix the DPI issue with BMP files. The solution was two parts. 1) Don't use GdPicture to change the DPI for BMP images - no offense intended. 2) Modify the BMP header to reflect the actual DPI I needed. As it turns out the DPI (dots-per-inch) is really pixels-per-meter in the BMP header. For 72 DPI a setting of 2835 pixels-per-meter was needed. I have listed the C# code I used to resolve my issue. We still use GdPicture, but call the methods below to fix DPI on our incoming BMP files first.

Code: Select all

    #region BMP Header:

    /// <summary>
    /// The default image resolution in pixels-per-meter. In the BMP header 
    /// the horizontal and vertical resolutions are expressed in pixels-per-
    /// meter. The closest value to 72 DPI converting from meters to inches 
    /// is 72.009. 2835 pixels-per-meter / 39.3700787 meters-per-inch = 72.009.
    /// </summary>
    public const int DEFAULT_RESOLUTION_PIXELS_PER_METER = 2835; //-- DPM

    /// <summary>
    /// Dot-per-Inch or DPI. Dots = pixels. In the BMP header the horizontal
    /// and vertical resolutions are expressed in pixels-per-meter. The 
    /// closest value to 72 DPI converting from meters to inches is 72.009.
    /// 2835 pixels-per-meter / 39.3700787 meters-per-inch = 72.009.
    /// </summary>
    public const double DEFAULT_RESOLUTION_PIXELS_PER_INCH = 72.009;    //-- DPI

    /// <summary>
    /// The number of inches in a meter.
    /// </summary>
    public const double INCHES_PER_METER = 39.3700787;

    /// <summary>
    /// BMP file offset where the horizontal resolution is located within the
    /// BMP header. The value is four bytes long.
    /// </summary>
    public const int BMP_HEADER_HRES_OFFSET = 0x26;

    /// <summary>
    /// BMP file offset where the vertical resolution is located within the
    /// BMP header. The value is four bytes long.
    /// </summary>
    public const int BMP_HEADER_VRES_OFFSET = 0x2A;
    
    /// <summary>
    /// Returns the horizontal and vertical resolutions for a given BMP file
    /// via parameters.
    /// </summary>
    /// <param name="strBMPFile">string: The path to the BMP file to examine.</param>
    /// <param name="intHorizontalDPM">int: (Output) The horizontal resolution of the BMP file in pixels-per-meter.</param>
    /// <param name="intVerticalDPM">int: (Output) The vertical resolution of the BMP file in pixels-per-meter.</param>
    /// <returns>bool: True if successful, and false otherwise.</returns>
    public static bool GetBMPResolutionDPM(string strBMPFile, ref int intHorizontalDPM, ref int intVerticalDPM)
    {
        //-- Locals.
        bool blnResult = false;

        try
        {
            //-- Get the resolution in pixels-per-meter.
            FileStream fsBMPFile = new FileStream(strBMPFile, FileMode.Open, FileAccess.Read);
            byte[] lstBytes = { 0, 0, 0, 0 };
            fsBMPFile.Seek(BMP_HEADER_HRES_OFFSET, SeekOrigin.Begin);
            fsBMPFile.Read(lstBytes, 0, 4);
            intHorizontalDPM = BitConverter.ToInt32(lstBytes, 0);
            fsBMPFile.Seek(BMP_HEADER_VRES_OFFSET, SeekOrigin.Begin);
            fsBMPFile.Read(lstBytes, 0, 4);
            intVerticalDPM = BitConverter.ToInt32(lstBytes, 0);

            //-- Close the BMP file.
            fsBMPFile.Close();
            blnResult = true;
        }
        catch (Exception ex)
        {
            cUtil.WriteException(ex, "cUtil.GetBMPResolution");
            blnResult = false;
        }

        return blnResult;
    }

    /// <summary>
    /// Sets the horizontal and vertical resolutions for a given BMP file.
    /// </summary>
    /// <param name="strBMPFile">string: The path to the BMP file to update.</param>
    /// <param name="intHorizontalDPM">int: The horizontal resolution of the BMP file in pixels-per-meter.</param>
    /// <param name="intVerticalDPM">int: The vertical resolution of the BMP file in pixels-per-meter.</param>
    /// <returns>bool: True if successful, and false otherwise.</returns>
    public static bool SetBMPResolutionDPM(string strBMPFile, int intHorizontalDPM, int intVerticalDPM)
    {
        //-- Locals.
        bool blnResult = false;

        try
        {
            //-- Get the resolution in pixels-per-meter.
            FileStream fsBMPFile = new FileStream(strBMPFile, FileMode.Open, FileAccess.ReadWrite);
            byte[] lstBytes = BitConverter.GetBytes(intHorizontalDPM);
            fsBMPFile.Seek(BMP_HEADER_HRES_OFFSET, SeekOrigin.Begin);
            fsBMPFile.Write(lstBytes, 0, 4);
            lstBytes = BitConverter.GetBytes(intVerticalDPM);
            fsBMPFile.Seek(BMP_HEADER_VRES_OFFSET, SeekOrigin.Begin);
            fsBMPFile.Write(lstBytes, 0, 4);

            //-- Close the BMP file.
            fsBMPFile.Close();
            blnResult = true;
        }
        catch (Exception ex)
        {
            cUtil.WriteException(ex, "cUtil.SetBMPResolution");
            blnResult = false;
        }

        return blnResult;
    }

    /// <summary>
    /// This method corrects a BMP file horizontal and vertical resolution
    /// to the values specified if needed.
    /// </summary>
    /// <param name="strBMPFile">string: The path to the BMP file to fix if needed.</param>
    /// <param name="intHorizontalDPM">int: The horizontal resolution of the BMP file in pixels-per-meter.</param>
    /// <param name="intVerticalDPM">int: The vertical resolution of the BMP file in pixels-per-meter.</param>
    /// <returns>bool: True if successful, and false otherwise.</returns>
    public static bool FixBMPResolutionDPM(string strBMPFile, int intHorizontalDPM, int intVerticalDPM)
    {
        //-- Locals.
        bool blnResult = false;

        try
        {
            //-- Get the resolution in pixels-per-meter.
            int intHResDPM = 0;
            int intVResDPM = 0;
            blnResult = GetBMPResolutionDPM(strBMPFile, ref intHResDPM, ref intVResDPM);
            if (blnResult)
            {
                //-- Make sure the resolution matches what is expected.
                if (intHResDPM != intHorizontalDPM || intVResDPM != intVerticalDPM)
                {
                    //-- Fix it.
                    blnResult = SetBMPResolutionDPM(strBMPFile, intHorizontalDPM, intVerticalDPM);
                    if (blnResult == false)
                    {
                        cUtil.WriteAsException("cUtil.FixBMPResolutionDPM: Failed to set the horizontal and vertical resolution for '" + strBMPFile + "' via a call to SetBMPResolutionDPM.");
                        blnResult = false;
                    }
                }
                else
                {
                    //-- All is well.
                    blnResult = true;
                }
            }
            else
            {
                //-- Log an alert, because this should not happen.
                cUtil.WriteAsException("cUtil.FixBMPResolutionDPM: Failed to obtain the horizontal and vertical resolution for '" + strBMPFile + "' via a call to GetBMPResolutionDPM.");
                blnResult = false;
            }
        }
        catch (Exception ex)
        {
            cUtil.WriteException(ex, "cUtil.GetBMPResolution");
            blnResult = false;
        }

        return blnResult;
    }

    #endregion

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest