<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Detecting Blank Images with C#</title>
	<atom:link href="http://www.chinhdo.com/20080910/detect-blank-images/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.chinhdo.com/20080910/detect-blank-images/</link>
	<description>Chinh's not quite random thoughts on software development, .NET, gadgets, and other things.</description>
	<lastBuildDate>Tue, 09 Mar 2010 13:15:12 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>By: Cy</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-76956</link>
		<dc:creator>Cy</dc:creator>
		<pubDate>Tue, 16 Feb 2010 19:19:25 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-76956</guid>
		<description>Seems on certain images I get the pointer error even with Timex code.

I&#039;ve added try catch with continue inside:
try
                        {
                            count++;

                            byte blue = p[0];
                            byte green = p[1];
                            byte red = p[2];

                            int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();
                            total += pixelValue;
                            double avg = total / count;
                            totalVariance += Math.Pow(pixelValue - avg, 2);
                            stdDev = Math.Sqrt(totalVariance / count);
                            p += bytesPerPixel;
                        }
                        catch
                        {
                            continue;
                        }

seems to have fixed the issue, but I still dont understand why some images will throw an error.

Times: What do you mean changing the hex to dec? all functions really on a byte not decimal value. Can you repost the decimal version?

Thanks</description>
		<content:encoded><![CDATA[<p>Seems on certain images I get the pointer error even with Timex code.</p>
<p>I&#8217;ve added try catch with continue inside:<br />
try<br />
                        {<br />
                            count++;</p>
<p>                            byte blue = p[0];<br />
                            byte green = p[1];<br />
                            byte red = p[2];</p>
<p>                            int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();<br />
                            total += pixelValue;<br />
                            double avg = total / count;<br />
                            totalVariance += Math.Pow(pixelValue &#8211; avg, 2);<br />
                            stdDev = Math.Sqrt(totalVariance / count);<br />
                            p += bytesPerPixel;<br />
                        }<br />
                        catch<br />
                        {<br />
                            continue;<br />
                        }</p>
<p>seems to have fixed the issue, but I still dont understand why some images will throw an error.</p>
<p>Times: What do you mean changing the hex to dec? all functions really on a byte not decimal value. Can you repost the decimal version?</p>
<p>Thanks</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chinh Do</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-63016</link>
		<dc:creator>Chinh Do</dc:creator>
		<pubDate>Tue, 03 Nov 2009 13:06:43 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-63016</guid>
		<description>Geetesh: My guess is that some of the scanned images have scanning artifacts in them that cause the code to think they are not blank. You can try to increase the Standared Deviation threshold. Change the 100000 number to something bigger.</description>
		<content:encoded><![CDATA[<p>Geetesh: My guess is that some of the scanned images have scanning artifacts in them that cause the code to think they are not blank. You can try to increase the Standared Deviation threshold. Change the 100000 number to something bigger.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Geetesh</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-62161</link>
		<dc:creator>Geetesh</dc:creator>
		<pubDate>Wed, 28 Oct 2009 12:17:17 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-62161</guid>
		<description>Thanks for such a nice peice of code.

I am new to C# and need your help Chinch Do or Timex.
Some of the images(blank pages from both sides) when scanned get tested as blank while some are not blank, even though if they are blank.
Can u pls help with it.
If possible can u please give me some explanation on the code u or Timex have given.</description>
		<content:encoded><![CDATA[<p>Thanks for such a nice peice of code.</p>
<p>I am new to C# and need your help Chinch Do or Timex.<br />
Some of the images(blank pages from both sides) when scanned get tested as blank while some are not blank, even though if they are blank.<br />
Can u pls help with it.<br />
If possible can u please give me some explanation on the code u or Timex have given.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chinh Do</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-49849</link>
		<dc:creator>Chinh Do</dc:creator>
		<pubDate>Thu, 09 Jul 2009 22:17:14 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-49849</guid>
		<description>Roshan: I am not aware of something like this in C++ that doesn&#039;t mean that it doesn&#039;t exist. I am sure you can translate the code to C++. Any C++ expert out there want to help us out?

Steve:

Thanks for your note and I think you are right. My algorithm does not produce a standard deviation number in the textbook definition. I think I used this modified &quot;running&quot; standard deviation algorithm to allow for this optimization: once the &quot;running&quot; standard deviation exceeds a certain threshold, I can short circuit the process and exit the loop. I do remember using this optimization but I guess I took it out at the end to keep the published code simple.

Chinh</description>
		<content:encoded><![CDATA[<p>Roshan: I am not aware of something like this in C++ that doesn&#8217;t mean that it doesn&#8217;t exist. I am sure you can translate the code to C++. Any C++ expert out there want to help us out?</p>
<p>Steve:</p>
<p>Thanks for your note and I think you are right. My algorithm does not produce a standard deviation number in the textbook definition. I think I used this modified &#8220;running&#8221; standard deviation algorithm to allow for this optimization: once the &#8220;running&#8221; standard deviation exceeds a certain threshold, I can short circuit the process and exit the loop. I do remember using this optimization but I guess I took it out at the end to keep the published code simple.</p>
<p>Chinh</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: steve</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-49817</link>
		<dc:creator>steve</dc:creator>
		<pubDate>Thu, 09 Jul 2009 15:53:23 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-49817</guid>
		<description>I believe you have an error calculation your variance.
you can compute the variance only after you know your average.
you should go over the pixels again after you know the average and compute the squared difference.</description>
		<content:encoded><![CDATA[<p>I believe you have an error calculation your variance.<br />
you can compute the variance only after you know your average.<br />
you should go over the pixels again after you know the average and compute the squared difference.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Roshan</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-47582</link>
		<dc:creator>Roshan</dc:creator>
		<pubDate>Wed, 24 Jun 2009 13:26:47 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-47582</guid>
		<description>Is there similar code to check black images in C++?</description>
		<content:encoded><![CDATA[<p>Is there similar code to check black images in C++?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Timex</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-45050</link>
		<dc:creator>Timex</dc:creator>
		<pubDate>Wed, 27 May 2009 01:06:29 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-45050</guid>
		<description>Chinh Do, no, thank you! I didn&#039;t feel like digging in to try to figure out how to do that kind of thing. You did all the grunt work!</description>
		<content:encoded><![CDATA[<p>Chinh Do, no, thank you! I didn&#8217;t feel like digging in to try to figure out how to do that kind of thing. You did all the grunt work!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chinh Do</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-45046</link>
		<dc:creator>Chinh Do</dc:creator>
		<pubDate>Wed, 27 May 2009 00:09:29 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-45046</guid>
		<description>Timex: Thanks very much for sharing the code to support various formats. Very nice!</description>
		<content:encoded><![CDATA[<p>Timex: Thanks very much for sharing the code to support various formats. Very nice!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Timex</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-45045</link>
		<dc:creator>Timex</dc:creator>
		<pubDate>Wed, 27 May 2009 00:05:57 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-45045</guid>
		<description>Opps, argh! I should have proofed before posting. The switch case statements should have decimal values, not hex. Or, convert them to the correct hex value (e.g. 24 = 0x18 etc.). Apologies.</description>
		<content:encoded><![CDATA[<p>Opps, argh! I should have proofed before posting. The switch case statements should have decimal values, not hex. Or, convert them to the correct hex value (e.g. 24 = 0&#215;18 etc.). Apologies.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Timex</title>
		<link>http://www.chinhdo.com/20080910/detect-blank-images/comment-page-1/#comment-45044</link>
		<dc:creator>Timex</dc:creator>
		<pubDate>Tue, 26 May 2009 23:55:08 +0000</pubDate>
		<guid isPermaLink="false">http://www.chinhdo.com/20080910/detect-blank-images/#comment-45044</guid>
		<description>Here is what I come up with:

[code]
/// 
/// Gets whether or not a given Bitmap is blank.
/// 
/// The instance of the Bitmap for this method extension.
/// Returns &lt;b&gt;true&lt;/b&gt;if the given Bitmap is blank; otherwise returns &lt;b&gt;false&lt;/b&gt;.
public static bool IsBlank(this Bitmap bitmap) {
  double stdDev = GetStdDev(bitmap);
  int tolerance = 100000;
  return stdDev &lt; tolerance;
}

/// 
/// Gets the bits per pixel (bpp) for the given .
/// 
/// The instance of the  for this method extension.
/// Returns a  representing the bpp for the .
internal static byte GetBitsPerPixel(this Bitmap bitmap) {
  byte bpp = 0x1;

  //return Regex.Match(Regex.Match(bitmap.PixelFormat.ToString(), @&quot;\dbpp&quot;).Value, @&quot;\d+&quot;).Value;
  switch (bitmap.PixelFormat) { 
    case PixelFormat.Format1bppIndexed:
      bpp = 0x1;
      break;
    case PixelFormat.Format4bppIndexed:
      bpp = 0x4;
      break;
    case PixelFormat.Format8bppIndexed:
      bpp = 0x8;
      break;
    case PixelFormat.Format16bppArgb1555:
    case PixelFormat.Format16bppGrayScale:
    case PixelFormat.Format16bppRgb555:
    case PixelFormat.Format16bppRgb565:
      bpp = 0x16;
      break;
    case PixelFormat.Format24bppRgb:        
      bpp = 0x24;
      break;
    case PixelFormat.Canonical:
    case PixelFormat.Format32bppArgb:
    case PixelFormat.Format32bppPArgb:
    case PixelFormat.Format32bppRgb:
      bpp = 0x32;
      break;
    case PixelFormat.Format48bppRgb:
      bpp = 0x48;
      break;
    case PixelFormat.Format64bppArgb:
    case PixelFormat.Format64bppPArgb:          
      bpp = 0x64;
      break;          
  }
  return bpp;
}

/// 
/// Get the standard deviation of pixel values.
/// 
/// The instance of the  for this method extension.
/// Returns the standard deviation of pixel population of the Bitmap.
public static double GetStdDev(this Bitmap bitmap) {
  double total = 0;
  double totalVariance = 0;
  int count = 0;
  double stdDev = 0;

  // First get all the bytes      
  BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
  int stride = bmData.Stride;
  IntPtr Scan0 = bmData.Scan0;
  
  byte bitsPerPixel = GetBitsPerPixel(bitmap);
  byte bytesPerPixel = (byte)(bitsPerPixel / 8);

  unsafe {
    byte* p = (byte*)(void*)Scan0;
    int nOffset = stride - bitmap.Width * bytesPerPixel;
    for (int y = 0; y &lt; bitmap.Height; ++y) {
      for (int x = 0; x &lt; bitmap.Width; ++x) {
        count++;

        byte blue = p[0];
        byte green = p[1];
        byte red = p[2];

        int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();
        total += pixelValue;
        double avg = total / count;
        totalVariance += Math.Pow(pixelValue - avg, 2);
        stdDev = Math.Sqrt(totalVariance / count);
        p += bytesPerPixel;
      }
      p += nOffset;
    }
  }
  bitmap.UnlockBits(bmData);

  return stdDev;
}
[/code]

Note: I wrote this in C# 3.0, so these are extension methods, that way they appear as helper methods for the Bitmap type, like so:

[code]
using(Bitmap = new Bitmap(@&quot;someimage.bmp&quot;)){
  byte bpp = bitmap.GetBitsPerPixel();
  int stddev = bitmap.GetStdDev();
  bool isBlank = bitmap.IsBlank();
}
[/code]

In any event, I believe this should work for non-indexed bitmaps at least. I&#039;m not sure about true indexed bitmaps.

Try it out and see if it works.

Chinh Do: You still get most of the credit, though! :D Much appreciated.</description>
		<content:encoded><![CDATA[<p>Here is what I come up with:</p>
<p>[code]<br />
///<br />
/// Gets whether or not a given Bitmap is blank.<br />
///<br />
/// The instance of the Bitmap for this method extension.<br />
/// Returns <b>true</b>if the given Bitmap is blank; otherwise returns <b>false</b>.<br />
public static bool IsBlank(this Bitmap bitmap) {<br />
  double stdDev = GetStdDev(bitmap);<br />
  int tolerance = 100000;<br />
  return stdDev &lt; tolerance;<br />
}</p>
<p>///<br />
/// Gets the bits per pixel (bpp) for the given .<br />
///<br />
/// The instance of the  for this method extension.<br />
/// Returns a  representing the bpp for the .<br />
internal static byte GetBitsPerPixel(this Bitmap bitmap) {<br />
  byte bpp = 0x1;</p>
<p>  //return Regex.Match(Regex.Match(bitmap.PixelFormat.ToString(), @"\dbpp").Value, @"\d+").Value;<br />
  switch (bitmap.PixelFormat) {<br />
    case PixelFormat.Format1bppIndexed:<br />
      bpp = 0x1;<br />
      break;<br />
    case PixelFormat.Format4bppIndexed:<br />
      bpp = 0x4;<br />
      break;<br />
    case PixelFormat.Format8bppIndexed:<br />
      bpp = 0x8;<br />
      break;<br />
    case PixelFormat.Format16bppArgb1555:<br />
    case PixelFormat.Format16bppGrayScale:<br />
    case PixelFormat.Format16bppRgb555:<br />
    case PixelFormat.Format16bppRgb565:<br />
      bpp = 0x16;<br />
      break;<br />
    case PixelFormat.Format24bppRgb:<br />
      bpp = 0x24;<br />
      break;<br />
    case PixelFormat.Canonical:<br />
    case PixelFormat.Format32bppArgb:<br />
    case PixelFormat.Format32bppPArgb:<br />
    case PixelFormat.Format32bppRgb:<br />
      bpp = 0x32;<br />
      break;<br />
    case PixelFormat.Format48bppRgb:<br />
      bpp = 0x48;<br />
      break;<br />
    case PixelFormat.Format64bppArgb:<br />
    case PixelFormat.Format64bppPArgb:<br />
      bpp = 0x64;<br />
      break;<br />
  }<br />
  return bpp;<br />
}</p>
<p>///<br />
/// Get the standard deviation of pixel values.<br />
///<br />
/// The instance of the  for this method extension.<br />
/// Returns the standard deviation of pixel population of the Bitmap.<br />
public static double GetStdDev(this Bitmap bitmap) {<br />
  double total = 0;<br />
  double totalVariance = 0;<br />
  int count = 0;<br />
  double stdDev = 0;</p>
<p>  // First get all the bytes<br />
  BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);<br />
  int stride = bmData.Stride;<br />
  IntPtr Scan0 = bmData.Scan0;</p>
<p>  byte bitsPerPixel = GetBitsPerPixel(bitmap);<br />
  byte bytesPerPixel = (byte)(bitsPerPixel / 8);</p>
<p>  unsafe {<br />
    byte* p = (byte*)(void*)Scan0;<br />
    int nOffset = stride - bitmap.Width * bytesPerPixel;<br />
    for (int y = 0; y &lt; bitmap.Height; ++y) {<br />
      for (int x = 0; x &lt; bitmap.Width; ++x) {<br />
        count++;</p>
<p>        byte blue = p[0];<br />
        byte green = p[1];<br />
        byte red = p[2];</p>
<p>        int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();<br />
        total += pixelValue;<br />
        double avg = total / count;<br />
        totalVariance += Math.Pow(pixelValue - avg, 2);<br />
        stdDev = Math.Sqrt(totalVariance / count);<br />
        p += bytesPerPixel;<br />
      }<br />
      p += nOffset;<br />
    }<br />
  }<br />
  bitmap.UnlockBits(bmData);</p>
<p>  return stdDev;<br />
}<br />
[/code]</p>
<p>Note: I wrote this in C# 3.0, so these are extension methods, that way they appear as helper methods for the Bitmap type, like so:</p>
<p>[code]<br />
using(Bitmap = new Bitmap(@"someimage.bmp")){<br />
  byte bpp = bitmap.GetBitsPerPixel();<br />
  int stddev = bitmap.GetStdDev();<br />
  bool isBlank = bitmap.IsBlank();<br />
}<br />
[/code]</p>
<p>In any event, I believe this should work for non-indexed bitmaps at least. I&#8217;m not sure about true indexed bitmaps.</p>
<p>Try it out and see if it works.</p>
<p>Chinh Do: You still get most of the credit, though! <img src='http://www.chinhdo.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' />  Much appreciated.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
