C# cropping bitmaps depending on bitmap location - c#

I have a PDF file containing numerous pages of hand-written surveys. My C# application currently breaks each PDF page down into single Bitmap objects (each PDF page is a Bitmap object) and then uses various APIs to read the hand-written data from each Bitmap object and then enters the extracted data into a database.
My problem is, in order for my API extraction to work, each checkbox and each answer box needs to be in the exact same XY pixel location in every Bitmap. Because these PDF files are scanned images, each PDF page may be a few pixels off in any direction e.g. some PDF pages are a few pixels off to the left, right, top or bottom
Does anybody know if it is possible to crop a Bitmap based on some constant in each Bitmap? For example (please see Bitmap image below), if I could crop each Bitmap starting at the "S" in Secondary School Study at the top left of each page, then each Bitmap would be cropped at the exact same location and this would solve my problem of each checkbox and answer box being in the same XY locations.
Any advice would be appreciated
EDIT: the only possible solution I can think of is looping over each pixel, starting at the top left hand corner until it hits a black pixel (which would be the first "S" in Secondary School Study). Could I then crop the Bitmap from this location?

I came up with a solution which was similar to the one I mentioned above. I scan over each pixel and until it reaches the first pixel in the "S" in Secondary School Study. I use this pixel X Y location to then crop a rectangle of a fixed height and width, starting at that location. I used bm.GetPixel().GetBrightness() to find out when the pixel reached the "S".
Bitmap bm = new Bitmap(#"C:\IronPDFDoc\2.png", true);
bool cropFlag = false;
int cropX = 0;
int cropY = 0;
for (int y = 0; y < 155; y++)
{
for (int x = 0; x < 115; x++)
{
float pixelBrightness = bm.GetPixel(x, y).GetBrightness();
if (pixelBrightness < 0.8 && cropFlag == false)
{
cropFlag = true;
cropX = x;
cropY = y;
}
}
}
Rectangle crop = new Rectangle(cropX, cropY, 648, 915);
Bitmap croppedSurvey = new Bitmap(crop.Width, crop.Height);
using (Graphics g = Graphics.FromImage(croppedSurvey))
{
g.DrawImage(bm, new Rectangle(0, 0, croppedSurvey.Width, croppedSurvey.Height),
crop,
GraphicsUnit.Pixel);
}
croppedSurvey.Save(#"C:\IronPDFDoc\croppedSurvey.png", ImageFormat.Png);

Related

how to extend draw area in Graphics.DrawImage c#

I have a Rectangle (rec) that contains the area in which a smaller image is contained within a larger image. I want to display this smaller image on a Picturebox. However, what I really am doing is using the smaller image as a picture detector for a larger image that is 333x324. So what I want to do is use the coordinates of the smaller image rectangle, and then draw to the Picturebox, starting from lefthand side of the rectangle, going outwards by 333 width and 324 height.
Currently my code works but it only displays the small image that was being used for detection purposes. I want it to display the smaller image + 300 width and + 300 height.
I fiddled with this code for hours and I must be doing something extremely basic wrong. If anyone can help me I would appreciate it so much!
My code for the class:
public static class Worker
{
public static void doWork(object myForm)
{
//infinitely search for maps
for (;;)
{
//match type signature for Threading
var myForm1 = (Form1)myForm;
//capture screen
Bitmap currentBitmap = new Bitmap(CaptureScreen.capture());
//detect map
Detector detector = new Detector();
Rectangle rec = detector.searchBitmap(currentBitmap, 0.1);
//if it actually found something
if(rec.Width != 0)
{
// Create the new bitmap and associated graphics object
Bitmap bmp = new Bitmap(rec.X, rec.Y);
Graphics g = Graphics.FromImage(bmp);
// Draw the specified section of the source bitmap to the new one
g.DrawImage(currentBitmap, 0,0, rec, GraphicsUnit.Pixel);
// send to the picture box &refresh;
myForm1.Invoke(new Action(() =>
{
myForm1.getPicturebox().Image = bmp;
myForm1.getPicturebox().Refresh();
myForm1.Update();
}));
// Clean up
g.Dispose();
bmp.Dispose();
}
//kill
currentBitmap.Dispose();
//do 10 times per second
System.Threading.Thread.Sleep(100);
}
}
}
If I understand correctly, the rec variable contains a rectangle with correct X and Y which identifies a rectangle with Width=333 and Height=324.
So inside the if statement, start by setting the desired size:
rec.Width = 333;
rec.Height = 324;
Then, note that the Bitmap constructor expects the width and height, so change
Bitmap bmp = new Bitmap(rec.X, rec.Y);
to
Bitmap bmp = new Bitmap(rec.Width, rec.Height);
and that's it - the rest of the code can stay the way it is now.

How can I read total number of the pixels in foreground and background of my image in C#?

Basically I have written a code that creates image of a character (randomly generated). I have a problem how to scan the image line by line and read the total number of the foreground (text) and background (white) pixels in my bmp image and display the results to the user. here is part of my code:
Image bmp = new Bitmap(100, 100);
Graphics g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.DrawString(randomString, myFont, new SolidBrush(Color.Black), new PointF(0, 0));
pictureBox1.Image = bmp;
bmp.Save(#"CAPTCHA.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
The basic idea is simple - iterate over all the pixels in the bitmap, and if the pixel is white, increment your background pixel counter. After you're done, you can simply get the total amount of pixels (width * height) and do whatever you want with the background pixel counter value.
A simple (and very slow) code snippet that does this:
var backgroundPixels = 0;
for (int x = 0; x < bmp.Width; x++)
for (int y = 0; y < bmp.Height; y++)
if (bmp.GetPixel(x, y) == Color.White)
backgroundPixels++;
The notion of foreground and background is only there in your head. The resulting bitmap only has an array of pixels, each with a position and colour. You can assign a specific meaning to some color (white in your case) and say that it means the background - but that's it.
A good alternative would be to use a transparent bitmap, where there indeed is a special meaning for what you call background - transparency. In that case, apart from the colour, the pixel also has a notion of the degree of transparency (Color.A), which you can exploit. In that case, you'd do g.Clear(Color.Transparent); instead of using white, and when iterating over the pixels, you'd check if bmp.GetPixel(x, y).A > 0 or whatever threshold you'd have for saying "this is the background". When you want to add the actual background colour, you'd paint this bitmap over a bitmap that's completely white and save that.

How to get image in image?

Just no idea how to do that:
We have one image, and we know constants WIDTH and HEIGHT of one card in this image. I would like to show one image in this image. Next constant is how many cards we have, so CNT_CARDS = 52. I don't want to create each card - only show that. I'm using winforms (C#).
Pseudocode
Load the image.
For each card can apply:
int offsetTop = row * HEIGHT;
int offsetLeft = column * WIDTH;
imageInImage.Location = new Point(offsetLeft, offsetTop);
imageInImage.Size = new Size(WIDTH, HEIGHT);
For example if we want to get Queen of diamond:
int offsetTop = 2 * HEIGHT;
int offsetLeft = 11 * WIDTH;
Create a bitmap for a single card by using Graphics.DrawImage(). A boilerplate sample implementation could look like this:
static Bitmap GetCardImage(Bitmap cards, int cardnum) {
int width = cards.Width / 13;
int height = cards.Height / 4;
int left = width * (cardnum % 13);
int top = height * (cardnum % 4);
var bmp = new Bitmap(width, height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(cards,
new Rectangle(0, 0, width, height),
new Rectangle(left, top, width, height),
GraphicsUnit.Pixel);
}
return bmp;
}
You can further extend this by creating an array of bitmaps so you'll have them readily available when you need to draw them. Something you'd do at the splash screen. Let's assume you added the image with the card faces as a resource named CardFaces:
static Bitmap[] CreateDeckImages() {
var deck = new Bitmap[52];
using (var images = Properties.Resources.CardFaces) {
for (int cardnum = 0; cardnum < deck.Length; ++cardnum) {
deck[cardnum] = GetCardImage(images, cardnum);
}
}
return deck;
}
Untested, ought to be close.
this should be a good start
var bmp = new Bitmap(225, 315);
var OriBmp = new Bitmap(#"c:\gnv4Q.jpg");
var g = Graphics.FromImage(bmp);
g.DrawImage(OriBmp,0,0,new Rectangle(225,315,225,315),GraphicsUnit.Pixel);
bmp.Save(#"c:\test.png");
An alternative would be to split the image into a set of images, one image for each card.
ImageSplitter, is a website which currently has an online image splitting tool that can be used to efficiently split the OP's image into a set of images, one image for each card.
To note, since the image of the OP is 2925 by 1260 pixels, and the cards span 4 rows by 13 columns in the image, the result will be a set of 225 by 315 pixels sized card images downloaded in a zip file by using the online image splitting tool in discussion.
On the online image splitting tool website in discussion, you will currently find the option on the homepage to upload an image. Where you would find that, you may:
1. Click where it says "Click here to upload your image"
2. Click "UPLOAD IMAGE"
3. Once the image is uploaded, on the next screen, choose the "SPLIT IMAGE" tool
4. Then enter the number of Rows and Columns currently found on the tool
- for this image, enter '4' for Rows, and '13' for columns
5. Next, click "SPLIT IMAGE' currently found on the tool
By following steps 1 through 5 above, a set of images, one image for each card, contained in a zip file, will be downloaded in the download folder of the browser used for the online image splitting tool in duscussion.
Link to Visual Guide for the Online Image Splitting Tool

Cropping a cross rectangle from image using c#

What I want to do is basically cropping a rectangle from an image. However, it should satisfy some special cases:
I want to crop an angled rectangle on image.
I don't want to rotate the image and crop a rectangle :)
If cropping exceeds the image size, I don't want to crop an empty background color.
I want to crop from back of the starting point, that will end at starting point when rectangle size completed. I know I couldn't explain well so if I show what I want visually:
The blue dot is the starting point there, and the arrow shows cropping direction. When cropping exceeds image borders, it will go back to the back of the starting point as much as, when the rectangle width and height finished the end of the rectangle will be at starting point.
Besides this is the previous question I asked:
How to crop a cross rectangle from an image using c#?
In this question, I couldn't predict that a problem can occur about image dimensions so I didn't ask for it. But now there is case 3. Except case three, this is exactly same question. How can I do this, any suggestions?
What needs to be done is to add offsets to the matrix alignment. In this case I am taking one extra length of the rectangle from each side (total 9 rectangles) and offsetting the matrix each time.
Notice that it is necessary to place offset 0 (the original crop) last, otherwise you will get the wrong result.
Also note that if you specify a rectangle that is bigger than the rotated picture you will still get empty areas.
public static Bitmap CropRotatedRect(Bitmap source, Rectangle rect, float angle, bool HighQuality)
{
int[] offsets = { -1, 1, 0 }; //place 0 last!
Bitmap result = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
foreach (int x in offsets)
{
foreach (int y in offsets)
{
using (Matrix mat = new Matrix())
{
//create the appropriate filler offset according to x,y
//resulting in offsets (-1,-1), (-1, 0), (-1,1) ... (0,0)
mat.Translate(-rect.Location.X - rect.Width * x, -rect.Location.Y - rect.Height * y);
mat.RotateAt(angle, rect.Location);
g.Transform = mat;
g.DrawImage(source, new Point(0, 0));
}
}
}
}
return result;
}
To recreate your example:
Bitmap source = new Bitmap("C:\\mjexample.jpg");
Bitmap dest = CropRotatedRect(source, new Rectangle(86, 182, 87, 228), -45, true);

export transparent images in c#?

I've edited an bitmap in c# and for every pixel i've changed it to a certain color if a condition was true else i've set the color to Color.Transparent ( the operations were done with getPixel/setPixel ) . I've exported the image in .png format but the image isn't transparent. Any ideas why or how should i do it ?
Regards,
Alexandru Badescu
here is the code :
-- here i load the image and convert to PixelFormat.Format24bppRgb if png
m_Bitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false);
if(openFileDialog.FilterIndex==3) //3 is png
m_Bitmap=ConvertTo24(m_Bitmap);
-- this is for changing the pixels after a certain position in an matrix
for (int i = startX; i < endX; i++)
for (int j = startY; j < endY; j++)
{
if (indexMatrix[i][j] == matrixFillNumber)
m_Bitmap.SetPixel(j, i, selectedColor);
else
m_Bitmap.SetPixel(j, i, Color.Transparent);
}
Its because pixelformat.
Here is a sample code for you:
Bitmap inp = new Bitmap("path of the image to edit");
Bitmap outImg = new Bitmap(inp.Width, inp.Height, PixelFormat.Format32bppArgb);
outImg.SetResolution(inp.HorizontalResolution, inp.VerticalResolution);
Graphics g = Graphics.FromImage(outImg);
g.DrawImage(inp, Point.Empty);
g.Dispose();
inp.Dispose();
////
// Check your condition and set pixel here (outImg.GetPixel, outImg.SetPixel)
////
outImg.Save("out file path", ImageFormat.Png);
outImg.Dispose();
this is the code that requires minimum change to your current code.
But i would recommend you to check out LockBits Method for a better performance:
http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx
I will need more code to verify, but my best guess is that you first write something on the bitmap to clear it (like fill it with White color), then to make the transparent pixels you draw with Color.Transparent on top. This will simply not work, since White (or anything else) with Transparent on top, is still White.
If you have created a bitmap in code, it will be most likely 24 bit and would not support alpha blending/transparent.
provide the code for creating and we should be able to help.

Categories

Resources