If you make the loop greater you will not be sure to find the darkest pixel.
I rewrote it using same approach as Eric Gunnerson does in the article Michael linked to. It improved performance alot. It is hundreds of times faster.
This is really quickly written code and I have not tidied it up, it may even be buggy for certain bitmap format or not work at all. Here goes anyway:
public void FindDarkestPixelFaster(Bitmap bm, out int x, out int y) { Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height); BitmapData bmd = bm.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb); Int32 nSize = bm.Width * bm.Height * 4; Byte[] arrARGB = new Byte[nSize]; System.Runtime.InteropServices.Marshal.Copy(bmd.Scan0, arrARGB, 0, nSize); Color colDarkest = Color.White; Color col; Int32 nIdxDarkest = 0; for (Int32 nIdx = 0; nIdx < arrARGB.Length - 4; nIdx += 4) { col = Color.FromArgb(arrARGB[nIdx + 3], arrARGB[nIdx], arrARGB[nIdx + 1], arrARGB[nIdx + 2]); if (col.GetBrightness() < colDarkest.GetBrightness()) { colDarkest = col; nIdxDarkest = nIdx; if (col == Color.Black) { break; } } } y = nIdxDarkest / 4 / bm.Width; x = (nIdxDarkest / 4) - bm.Width * y; bm.UnlockBits(bmd); }
Here is an example using GetBrightness() of the Color struct for a pixel. Maybe it isn't exactly what you need but slight modifications and I think it should be ok.
public void FindDarkestPixel(Bitmap bm, out int x, out int y) { // White is brightest, find anything less than white. Color colFound = Color.White; Color colCurrent; // Break condition, when black can not get darker Boolean bColorIsBlack = false; // Init x,y x = 0; y = 0;
for (int nCurrentX = 0; nCurrentX < bm.Width && bColorIsBlack == false; nCurrentX++) { for (int nCurrentY = 0; nCurrentY < bm.Height && bColorIsBlack == false; nCurrentY++) { colCurrent = bm.GetPixel(nCurrentX, nCurrentY); if (colCurrent.GetBrightness() < colFound.GetBrightness()) { colFound = colCurrent; x = nCurrentX; y = nCurrentY; // Extra break condition as nothing is darker than black bColorIsBlack = (colFound == Color.Black); } } } }
I do not use the PixelData structure that Eric Gunnerson defined and used. I also didn't really see how he worked with it. It seems as he used pointers, which use must be marked with the unsafe keyword and you must change to compile the project for unsafe code. In this version I also use pointers, it will increase performance a bit as I do not need to copy the data into a managed array.
The first rewrite was because I was interested in comparing performance. Over 100 iterations for c:\windows\zapotec.bmp FindDarkestPixel took more than 4 seconds, FindDarkestPixelFaster took less than 30ms.
public void FindDarkestPixelFaster(Bitmap bm, out int x, out int y) { // The rectangle determines what region of the bitmap to // to lock, lock everything to check all pixels Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
// ReadOnly as I do not need to modify anything // Format32bppPArgb gives pixels in Alpha Red Green Blue // 1 byte/8 bits per pixels BitmapData bmd = bm.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
Int32 nIdxDarkest = 0;
unsafe { // Pointers are unsafe so must all this block in unsafe context // It will work just as the byte array in the earlier example. byte* pPixelData = (byte*)bmd.Scan0;
// This is the size of the pixel data 4 8bit components for each pixel Int32 nSize = bm.Width * bm.Height * 4;
// Init that darkest color as white and found at position 0 // Any other color is darker so it is a safe starting value Color colDarkest = Color.White; Color col;
// Step 4 bytes at a time as every pixel takes up 4 bytes ARGB for (Int32 nIdx = 0; nIdx < nSize - 4; nIdx += 4) { // Could possible just check the maximum R, G or B and divide // with 255 to get same value as GetBrigthness() // In the byte array I found the pixels to be arranged like this // R G B A // 0 1 2 3 I need to get alpha first +3 position and then RGB col = Color.FromArgb(pPixelData[nIdx + 3], pPixelData[nIdx], pPixelData[nIdx + 1], pPixelData[nIdx + 2]); if (col.GetBrightness() < colDarkest.GetBrightness()) { colDarkest = col; nIdxDarkest = nIdx; if (col == Color.Black) { break; } } } } // All picture scanned or a black pixel found, cant get darker // so no need to continue // The position is the starting byte of the darkest pixel // each pixel 4 bytes, each line Width pixels y = nIdxDarkest / 4 / bm.Width; x = (nIdxDarkest / 4) - bm.Width * y; bm.UnlockBits(bmd); }
If you don't mind, could you also explain your coding I've only been doing C# for a couple weeks. And I understood the first OCR function very well because I've seen examples of it alot in VB. I read the article that was linked and it was very helpful. I liked the speed of the grayscale program and I started to make my own OCR derived from stuff in the program in there but I can't seem to access the PixelData variable type I appreciate your help otherwise and your hast to reply =]
That was very helpful and it worked fine :D But it is rather slow :( Is there anyway to speed it up besides making the for loop step with greater values
GetPixel Colour Value
betz
If performance is critical for your pixel-getting routine, you might want to read the article Unsafe Image Processing article by Eric Gunnerson: http://msdn.microsoft.com/library/default.asp url=/library/en-us/dncscol/html/csharp11152001.asp
Michael Blome - Visual C# Documentation Team
Henry N
MonkeyZa
If you make the loop greater you will not be sure to find the darkest pixel.
I rewrote it using same approach as Eric Gunnerson does in the article Michael linked to. It improved performance alot. It is hundreds of times faster.
This is really quickly written code and I have not tidied it up, it may even be buggy for certain bitmap format or not work at all. Here goes anyway:
public void FindDarkestPixelFaster(Bitmap bm, out int x, out int y){
Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
BitmapData bmd = bm.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
Int32 nSize = bm.Width * bm.Height * 4;
Byte[] arrARGB = new Byte[nSize];
System.Runtime.InteropServices.Marshal.Copy(bmd.Scan0, arrARGB, 0, nSize);
Color colDarkest = Color.White;
Color col;
Int32 nIdxDarkest = 0;
for (Int32 nIdx = 0; nIdx < arrARGB.Length - 4; nIdx += 4)
{
col = Color.FromArgb(arrARGB[nIdx + 3], arrARGB[nIdx], arrARGB[nIdx + 1], arrARGB[nIdx + 2]);
if (col.GetBrightness() < colDarkest.GetBrightness())
{
colDarkest = col;
nIdxDarkest = nIdx;
if (col == Color.Black)
{
break;
}
}
}
y = nIdxDarkest / 4 / bm.Width;
x = (nIdxDarkest / 4) - bm.Width * y;
bm.UnlockBits(bmd);
}
JLeBert
Choose properties for the project, right-click on it in the solution explorer.
Go to the Build tab, there is a check box "Allow unsafe code", check it and it should allow the unsafe{} parts.
peterxz
Kamalahasan
Bob Tabor
Here is an example using GetBrightness() of the Color struct for a pixel. Maybe it isn't exactly what you need but slight modifications and I think it should be ok.
public void FindDarkestPixel(Bitmap bm, out int x, out int y){
// White is brightest, find anything less than white.
Color colFound = Color.White;
Color colCurrent;
// Break condition, when black can not get darker
Boolean bColorIsBlack = false;
// Init x,y
x = 0;
y = 0;
for (int nCurrentX = 0; nCurrentX < bm.Width && bColorIsBlack == false; nCurrentX++)
{
for (int nCurrentY = 0; nCurrentY < bm.Height && bColorIsBlack == false; nCurrentY++)
{
colCurrent = bm.GetPixel(nCurrentX, nCurrentY);
if (colCurrent.GetBrightness() < colFound.GetBrightness())
{
colFound = colCurrent;
x = nCurrentX;
y = nCurrentY;
// Extra break condition as nothing is darker than black
bColorIsBlack = (colFound == Color.Black);
}
}
}
}
twiggy
I do not use the PixelData structure that Eric Gunnerson defined and used. I also didn't really see how he worked with it. It seems as he used pointers, which use must be marked with the unsafe keyword and you must change to compile the project for unsafe code. In this version I also use pointers, it will increase performance a bit as I do not need to copy the data into a managed array.
public void FindDarkestPixelFaster(Bitmap bm, out int x, out int y)The first rewrite was because I was interested in comparing performance. Over 100 iterations for c:\windows\zapotec.bmp FindDarkestPixel took more than 4 seconds, FindDarkestPixelFaster took less than 30ms.
{
// The rectangle determines what region of the bitmap to
// to lock, lock everything to check all pixels
Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height); // ReadOnly as I do not need to modify anything
// Format32bppPArgb gives pixels in Alpha Red Green Blue
// 1 byte/8 bits per pixels
BitmapData bmd = bm.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb); Int32 nIdxDarkest = 0; unsafe
{
// Pointers are unsafe so must all this block in unsafe context
// It will work just as the byte array in the earlier example.
byte* pPixelData = (byte*)bmd.Scan0; // This is the size of the pixel data 4 8bit components for each pixel
Int32 nSize = bm.Width * bm.Height * 4; // Init that darkest color as white and found at position 0
// Any other color is darker so it is a safe starting value
Color colDarkest = Color.White;
Color col; // Step 4 bytes at a time as every pixel takes up 4 bytes ARGB
for (Int32 nIdx = 0; nIdx < nSize - 4; nIdx += 4)
{
// Could possible just check the maximum R, G or B and divide
// with 255 to get same value as GetBrigthness()
// In the byte array I found the pixels to be arranged like this
// R G B A
// 0 1 2 3 I need to get alpha first +3 position and then RGB
col = Color.FromArgb(pPixelData[nIdx + 3], pPixelData[nIdx], pPixelData[nIdx + 1], pPixelData[nIdx + 2]);
if (col.GetBrightness() < colDarkest.GetBrightness())
{
colDarkest = col;
nIdxDarkest = nIdx;
if (col == Color.Black)
{
break;
}
}
}
}
// All picture scanned or a black pixel found, cant get darker
// so no need to continue
// The position is the starting byte of the darkest pixel
// each pixel 4 bytes, each line Width pixels
y = nIdxDarkest / 4 / bm.Width;
x = (nIdxDarkest / 4) - bm.Width * y;
bm.UnlockBits(bmd);
}
clinicalcom
I appreciate your help otherwise and your hast to reply =]
Chandan Jhalani