Crop an image while cropping area is not rectangle - c#

How can I crop a specific area from an image while the chosen area is not a perfect rectangle? In this image, how can I crop the area other than red into different pieces?

To isolate non-rectangular areas of an image, and/or areas based upon color, a powerful method is to use the alpha/transparency property of 32 bit images. For example, the image on the left is an original, 24 bit image (no alpha), and on the right is the result of converting that image to 32 bit, and setting the alpha = 0 for all pixels that are white or red in the original image. Effectively, this can make your image non-rectangular.
The code for this is fairly simple. The method below takes an input bitmap (24 bit), generates a blank 32 bit image, and transfers all pixels from the source to destination, setting the alpha to 0 for all white or red pixels.
public Bitmap ModifyAlpha(Bitmap bmap)
{
Bitmap bmap32 = new Bitmap(bmap.Width, bmap.Height, PixelFormat.Format32bppArgb);
Color theColor = new Color();
Color newColor = new Color();
for (int i = 0; i < bmap.Width; i++)
{
for (int j = 0; j < bmap.Height; j++)
{
// Get the color of the pixel at (i,j)
theColor = bmap.GetPixel(i, j);
// Set the pixel color/range you want to make transparent
if ((theColor.R > 250 && theColor.G > 250 && theColor.B > 250) ||
(theColor.R > 250))
{
newColor = Color.FromArgb(0, theColor.R, theColor.G, theColor.B);
bmap32.SetPixel(i, j, newColor);
} else
{
bmap32.SetPixel(i, j, theColor);
}
}
}
return bmap32;
}

Related

Why when reading pixels from one image and set the pixels over another image not all the pixels are set on the second image?

This is the method i'm using to read and set the pixels :
public void ReadSetPixels(Bitmap image1, Bitmap image2)
{
int tolerance = 64;
for (int x = 0; x < image1.Width; x++)
{
for (int y = 0; y < image1.Height; y++)
{
Color pixelColor = image1.GetPixel(x, y);
// just average R, G, and B values to get gray. Then invert by 255.
int invertedGrayValue = 255 - (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
if (invertedGrayValue > tolerance) { invertedGrayValue = 255; }
// this keeps the original pixel color but sets the alpha value
image1.SetPixel(x, y, Color.FromArgb(invertedGrayValue, pixelColor));
}
}
// composite image1 on top of image2
using (Graphics g = Graphics.FromImage(image2))
{
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(image1, new Point(0, 0));
}
image2.Save(#"d:\mynewbmp.bmp");
image1.Dispose();
image2.Dispose();
}
This is how i'm using it in form1 :
RadarPixels rp = new RadarPixels();
rp.ReadSetPixels(new Bitmap(#"d:\resized1.png"),
new Bitmap(#"d:\clean_radar_image11.jpg"));
The image resized1 is size 512x512 Bit depth 32
This is the image :
resized image
This is the base image that the resized1 should be over it.
This image is size 512x512 72 dpi Bit depth 24
base image
The result is the image mynewbmp.bmp :
result
UPDATE :
i found the problem.
i found the problem but not the solution. the problem is that the setpixel set it in the wrong position. it is the right position by the code 0,0 when making new Point(0, 0) but it's not what i want. iwant that the clouds the readed pixels to be around the center of the base image clean_radar_image11 i changed it to format bmp and all the clouds are show just not in the right position.
This image i created now show the image on the left where the clouds pixels in the position at 0,0 on the right side is how the clouds pixels should be positioned. the right image is just example to show how the clouds should be positioned on the left image.
example of how the clouds pixels should be set in position
A possible problem is that the images dpi does not match. Try calling SetResolution for both images
image1.SetResolution(96, 96);
image2.SetResolution(96, 96);
You should be able to set any resolution, as long as they are the same, but 96 tend to be the default on windows.
You might also try using DrawImageUnscaled, since that should draw the image according to physical/pixel size.
maybe image2's format is .jpg image,its have not alpha value

How to draw what is inside a contour in an image but leave everything else blank?

I have a colour image of type Image<Hsv, Byte>, and another image of type Image<Gray, Byte> of the same size that is all black with some all-white shapes. From the black and white image, I found the contours of the shapes using findContours(). What I want is to create a new image or modify the original colour image I have to show only what corresponds to inside the contours, with everything else being transparent, without having to check pixel by pixel values of the two images (did this, it takes too long). Any possible way to do this?
For example, I have the original image, the black and white image, and the final product I need.
I'm completely new to emgucv, so, I'm not saying this is the best approach; but it seems to work.
Create a new draw surface
Draw the original image
Change the white pixels in the mask image to transparent pixels
Draw the transparent mask on top of the original image
The result image looks like your desired outcome.
void Main()
{
var path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
"images");
var original = new Image<Bgr, Byte>(Path.Combine(path, "vz7Oo1W.png"));
var mask = new Image<Bgr, Byte>(Path.Combine(path, "vIQUvUU.png"));
var bitmap = new Bitmap(original.Width, original.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(original.Bitmap, 0, 0);
g.DrawImage(MakeTransparent(mask.Bitmap), 0, 0);
}
bitmap.Save(Path.Combine(path, "new.png"));
}
public static Bitmap MakeTransparent(Bitmap image)
{
Bitmap b = new Bitmap(image);
var tolerance = 10;
for (int i = b.Size.Width - 1; i >= 0; i--)
{
for (int j = b.Size.Height - 1; j >= 0; j--)
{
var col = b.GetPixel(i, j);
col.Dump();
if (255 - col.R < tolerance &&
255 - col.G < tolerance &&
255 - col.B < tolerance)
{
b.SetPixel(i, j, Color.Transparent);
}
}
}
return b;
}

How to detect where the image content ends?

I receive images of the same size but with different amounts of information. Examples below (red borders are mine). The background is always white.
I am trying to detect where the information on the image ends - at what pixel height (and crop accordingly). In other words, find the first non-white pixel from the bottom.
Is there a better way to do this other than extract BitmapData out of Image object and loop through all the pixels?
Just to add a suggestion having looked over your images and your solution (below) and your method is fine but you may be able to improve efficiency.
The more you know about your image the better; you're confident the background is always white (according to your post, the code is a more generic utility but the following suggestion can still work); can you be confident on the furthest point in a non-white pixel will be found if the row is not empty?
For example; in your two pictures the furthest in non-white pixel on a row is about 60px in. If this is universally true for your data then you don't need to scan the whole line of the image, which would make your for loop:
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < 60; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
}
(You could make it a parameter on the function so you can easily control it if needed).
Imagine for example that the first non-white row was 80px down. To find it currently you do 640 x 300 = 192,000 checks. If you could confidently say that you would know a row was blank within 100 pixels (an over-estimate based on the data presented) then this would be 100 * 300 = 30,000 checks per image.
If you always knew that the first 10 pixels of the image were always blank you could shave a little bit more off (say 3000 checks).
Musing on a setup where you knew that the first non-white pixel was between 10 and 60 pixels in (range of 50) you could find it at row 80 in 50 x 300 = 15,000 checks which is a good reduction.
Of course the downside about assumptions is that if things change your assumptions may not be valid, but if the data is going to remain fairly constant then it may be worthwhile, especially if you do this for a lot of images.
I've ended up using the following code to trim the image. Hopefully someone finds this useful.
class Program {
static void Main(string[] args) {
Image full = Image.FromFile("foo.png");
Image cropped = full.TrimOnBottom();
}
}
public static class ImageUtilities {
public static Image TrimOnBottom(this Image image, Color? backgroundColor = null, int margin = 30) {
var bitmap = (Bitmap)image;
int foundContentOnRow = -1;
// handle empty optional parameter
var backColor = backgroundColor ?? Color.White;
// scan the image from the bottom up, left to right
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < bitmap.Width; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
// exit loop if content found
if (foundContentOnRow > -1) {
break;
}
}
if (foundContentOnRow > -1) {
int proposedHeight = foundContentOnRow + margin;
// only trim if proposed height smaller than existing image
if (proposedHeight < bitmap.Height) {
return CropImage(image, bitmap.Width, proposedHeight);
}
}
return image;
}
private static Image CropImage(Image image, int width, int height) {
Rectangle cropArea = new Rectangle(0, 0, width, height);
Bitmap bitmap = new Bitmap(image);
return bitmap.Clone(cropArea, bitmap.PixelFormat);
}
}

How to change color qr generating to another color of black like green red etc

Problem
How to change color of generating qr to red or green or blue(another color of black)
I working in visual studio 2015 windows form application
I generating qr using message toolkit library and it working good without any problem
only i face problem how to change color qr code using message toolkit
my code
Last update
i try to replace color to red but result convert all image to red
actually i need to display qr but with another color
MessagingToolkit.QRCode.Codec.QRCodeEncoder encoder = new MessagingToolkit.QRCode.Codec.QRCodeEncoder();
encoder.QRCodeScale = 8;
Bitmap bmp = ChangeColor(encoder.Encode(textBox1.Text), Color.Red);
pictureBox1.Image = bmp;
bmp.Save(sv.FileName, ImageFormat.Jpeg);
public static Bitmap ChangeColor(Bitmap scrBitmap, Color color)
{
//You can change your new color here. Red,Green,LawnGreen any..
Color newColor = color;
Color actualColor;
//make an empty bitmap the same size as scrBitmap
Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height);
for (int i = 0; i < scrBitmap.Width; i++)
{
for (int j = 0; j < scrBitmap.Height; j++)
{
//get the pixel from the scrBitmap image
actualColor = scrBitmap.GetPixel(i, j);
// > 150 because.. Images edges can be of low pixel colr. if we set all pixel color to new then there will be no smoothness left.
if (actualColor.A > 150)
newBitmap.SetPixel(i, j, newColor);
else
newBitmap.SetPixel(i, j, actualColor);
}
}
return newBitmap;
}
Result wrong result
The problem in your example code is that you are using the alpha channel to decide whether or not to change a pixel. But since the generated image is fully opaque, every pixel will have an alpha value of 255.
The gradient at the edges is achieved by using appropriate RGB colors to achieve a grayscale between black and white. If you just changed your code to use actualColor.R instead of A, it would actually change the colors of the black parts to red correctly (but remove most of the smooth gradients).
To keep the gradients, you should properly tint the image instead of using a hard if-condition at each pixel. Properly tinting means that you multiply each RGB channel of the source color with each respective RGB channel of the target color.
However, since you wish to tint the dark parts only and not the white ones, we need to invert the source image colors first. Since black is represented as RGB (0,0,0), multiplying it with the target color won't change anything - multiplying anything by 0 is still 0. White on the other hand uses RGB (255,255,255), so it will completely use the target color (without changing it in any way). If it was anything in between black and white, you'd get the respective mix of the source and destination color.
The multiplication (and dealing with colors in general) works better if each color channel is represented as floating point number in the range from 0 to 1, so we divide the original value of source and target by 255 before the multiplication and multiply it by 255 afterwards to pass it to the Color.FromARGB(int,int,int) function.
I made the following code example a little more verbose than needed, so things become more clear.
public static Bitmap ChangeColor(Bitmap scrBitmap, Color newColor)
{
// make an empty bitmap the same size as scrBitmap
Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height);
for (int i = 0; i < scrBitmap.Width; i++) {
for (int j = 0; j < scrBitmap.Height; j++) {
// get the pixel from the scrBitmap image
var actualColor = scrBitmap.GetPixel(i, j);
// invert colors, since we want to tint the dark parts and not the bright ones
var invertedOriginalR = 255 - actualColor.R;
var invertedOriginalG = 255 - actualColor.G;
var invertedOriginalB = 255 - actualColor.B;
// multiply source by destination color (as float channels)
int r = (invertedOriginalR / 255) * (newColor.R / 255) * 255;
int g = (invertedOriginalG / 255) * (newColor.G / 255) * 255;
int b = (invertedOriginalB / 255) * (newColor.B / 255) * 255;
var tintedColor = Color.FromArgb(r, g, b);
newBitmap.SetPixel(i, j, tintedColor);
}
}
return newBitmap;
}

Using Alpha in image manipulation

I'm trying to get an image to display with one of the colors replaced with a white alpha so that I can layer it on top of other images. I've got it so that I can change colors easily enough, but changing it to be transparent is eluding me. Here's my code, using C# and WPF.
private void SetAlpha(string location)
{
//bmp is a bitmap source that I load from an image
bmp = new BitmapImage(new Uri(location));
int[] pixels = new int[(int)bmp.Width * (int)bmp.Height];
//still not sure what 'stride' is. Got this part from a tutorial
int stride = (bmp.PixelWidth * bmp.Format.BitsPerPixel + 7)/8;
bmp.CopyPixels(pixels, stride, 0);
int oldColor = pixels[0];
int red = 255;
int green = 255;
int blue = 255;
int alpha = 0;
int color = (alpha << 24) + (red << 16) + (green << 8) + blue;
for (int i = 0; i < (int)bmp.Width * (int)bmp.Height; i++)
{
if (pixels[i] == oldColor)
{
pixels[i] = color;
}
}
//remake the bitmap source with these pixels
bmp = BitmapSource.Create(bmp.PixelWidth, bmp.PixelHeight, bmp.DpiX, bmp.DpiY, bmp.Format, bmp.Palette, pixels, stride);
}
}
I've got two images that I'm testing this with. Image1 is like what I am going to be working on, no transparency in the original image. Image2 already has transparency. I thought it would be easy to just grab the value from image2 (0x00ffffff) but that just makes it white and covers up any images behind.
Both images are png, and the format for both is Bgr32.
Does anyone know how to get the image to be transparent?
How about using Bgra32?
Also make sure you understand how the color is represented in memory and what alpha means.

Categories