Page 1 of 1
DPI and Color Depth Resulting in Banded Images
Posted: Mon Sep 19, 2011 7:12 pm
by robduff
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()
Re: DPI and Color Depth Resulting in Banded Images
Posted: Fri Sep 30, 2011 3:24 pm
by robduff
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