About
I’m using WinForms
. In my form, I have a picturebox
. The picturebox
size mode is set to zoom
. I use the picturebox
to view TIF images. The TIF images are grayscale (Black and White ONLY).
What My App Does
My application finds the first black pixel and the last black pixel in the document and draws a red rectangle around it. In hopes that it would draw the rectangle around the content of the image.
The Problem
Sometimes the TIF documents have spots/dots around the content of the image. This throws my application off. It doesn't know where the content of the image begins and ends. How can I find the content of the TIF documents and draw a rectangle around it if the document has spots/dots?
About the Document
- Black and white only (1 bit depth)
- TIF document
- Document always has letters and numbers
- The letters and numbers are always bigger than the spots/dots
- There can be multiple spots all over the image even in the content
- The background is always white
- The content is always black
- The spots are also black
Download Test Image Links:
• File Dropper: http://www.filedropper.com/test-tifs
• Rapid Share: https://ufile.io/2qiir
What I Found
Upon my research, I found AForge.Imaging library which has many imaging filters that may potentially help me achieve my goal. I'm thinking about removing the spots/dots using the median filter or use the other filters to achieve the desired result.
What I Tried
I tried applying the median filter from AForge library to get rid of the spots but that only got rid of some of the spots. I had to repeat replying the filter multiple times to get rid of MOST of the spots to find the content and it still had a hard time finding the content. That method didn't work too well for me.
Link to AForge Filters: http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm
Code
private void btn_Draw_Click(object sender, EventArgs e)
{
// Wrap the creation of the OpenFileDialog instance in a using statement,
// rather than manually calling the Dispose method to ensure proper disposal
using (OpenFileDialog dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(dlg.FileName);
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
startX = Int32.MaxValue;
startY = Int32.MaxValue;
endX = Int32.MinValue;
endY = Int32.MinValue;
using (Bitmap bmp = new Bitmap(pictureBox1.Image))
{
for (var y = 0; y < yMax; y+=3)
{
for (var x = 0; x < xMax; x+=3)
{
Color col = bmp.GetPixel(x, y);
if(col.ToArgb() == Color.Black.ToArgb())
{
// Finds first black pixel
if (x < startX)
startX = x;
if(y < startY)
startY = y;
// Finds last black pixel
if (x > endX)
endX = x;
if (y > endY)
endY = y;
}
}
}
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//var scaleX = picWidth / (float)xMax;
//var scaleY = picHeight / (float)yMax;
//startX = (int)Math.Round(startX * scaleX);
//startY = (int)Math.Round(startY * scaleY);
//endX = (int)Math.Round(endX * scaleX);
//endY = (int)Math.Round(endY * scaleY);
}
}
}
}
private bool _once = true;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_once)
{
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
Tif document that DOES NOT HAVE any spots and dots around content
Tif document that HAS spots and dots around content
Example Image 1:
Example Image 2
Example Image 3
A solution could be to find areas of black pixels. When a black pixel is found, check the colour of the neighbouring pixels and create an area of black pixels. When the area is big enough, it can be considered as content. The pseudo code below illustrates this. But it is a very resource intensive solution and should at least be optimized.
I have solution for this,
And I suggest "kodak imaging professional". This is the viewer to display multi-page tiff files. with many features like: Annotation, invert color, rotate image... etc., and these are inbuilt functionalities.
Following experiment seems to meet all your requirements.
I put following controls onto Form1
A MenuStrip: Docking=Top, with 2 MenuItems - one to open a file, second to run an algorithm
A progressbar: Docking=Top, to watch performance of loading and algorithm
A panel with Docking=Fill and AutoScroll=true
A picture into the panel, Point(0,0), the rest by default. SizeMode=Normal.
Update
"this.pictureBox1.Size = image.Size;" has been removed. Paint event handler's code changed. PictureBox size mode can be set to Zoom now.
Update 2
I tried to simplify code and increase performance.
I watched the performance with ProgressBar. This one's quite faster. I must also mention that your images are too big.