Merging JPGs with GDI in C# - c#

My scenario:
I have one color background image JPG.
I have one black text on white background JPG.
Both images are the same size (height and width)
I want to overlay the image with black text and white background over the color background image, i.e. the white background becomes transparent to see the color background beneath it.
How can I do this with GDI in C#?
Thanks!

Thanks to GalacticCowboy I was able to come up with this solution:
using (Bitmap background = (Bitmap)Bitmap.FromFile(backgroundPath))
{
using (Bitmap foreground = (Bitmap)Bitmap.FromFile(foregroundPath))
{
// check if heights and widths are the same
if (background.Height == foreground.Height & background.Width == foreground.Width)
{
using (Bitmap mergedImage = new Bitmap(background.Width, background.Height))
{
for (int x = 0; x < mergedImage.Width; x++)
{
for (int y = 0; y < mergedImage.Height; y++)
{
Color backgroundPixel = background.GetPixel(x, y);
Color foregroundPixel = foreground.GetPixel(x, y);
Color mergedPixel = Color.FromArgb(backgroundPixel.ToArgb() & foregroundPixel.ToArgb());
mergedImage.SetPixel(x, y, mergedPixel);
}
}
mergedImage.Save("filepath");
}
}
}
}
Works like a charm. Thanks!

If the images are the same size, iterate over them and "AND" the colors for each pixel. For the white pixels, you should get the color of the other image, and for the black ones you should get black.
If they're not the same size, scale first.
I'm making this up off the top of my head, but something like:
Color destColor = Color.FromArgb(pixel1.ToArgb() & pixel2.ToArgb());

There exist easier and faster way. You should use ImageAttributes when you draw image that must be partially visible.
Image BackImage = Image.FromFile(backgroundPath);
using (Graphics g = Graphics.FromImage(BackImage))
{
using (ForeImage = Image.FromFile(foregroundPath))
{
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorKey(Color.FromArgb(245, 245, 245), Color.FromArgb(255, 255, 255),
ColorAdjustType.Default);
g.DrawImage(ForeImage, new Rectangle(0, 0, BackImage.Width, BackImage.Height),
0, 0, BackImage.Width, BackImage.Height, GraphicsUnit.Pixel, imageAttr);
}
}
SetColorKey method will make color from specified range transparent, so you can make your white bitmap pixels transparent, including all pixels that are affected to jpeg compression artefacts.

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 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;
}

Crop an image while cropping area is not rectangle

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;
}

Replace colors in indexed image using C#

I'm using Graphics.DrawImage to write a index bitmap into another image. The color black in the indexed image should be replaced with a transparent color when written to the "targetBitmap"
How do I do this in a nice performant way ?
var graphics = Graphics.FromImage(targetBitmap);
//I want the black color in "indexBitmap" to be transparent when it's written to "targetBitmap"
graphics.DrawImage(indexedBitmap,...)
Creating a color map and passing it as an "ImageAttributes" argument to DrawImage worked for me
var colorMaps = new[]{
new ColorMap {OldColor = Color.FromArgb(255, 0, 0, 0), NewColor = Color.Transparent}
};
var attr = new ImageAttributes();
attr.SetRemapTable(colorMaps);
How about using SetColorKey?
SetColorKey allows you to choose a transparent background when drawing the image on a graphics object.
But the fact is that this doesn't work when the SetRemapTable() function has been called.
You can make that work also by adding an extra ColorMap in the "colorMaps" array.
This extra ColorMap should have
- OldColor = 'Choose the transparent color'
- NewColor = Color.Transparent
and then call the SetRemapTable with the extended sub.
Below you see a code sample in C# to easily draw an image to a graphics object.
I use this to make a game with graphics.
This void (sub in basic) allows you to draw an image to graphics (eg. FormX.CreateGraphics()).
You can replace certain colors by other colors, and also choose a transparent color.
You can also draw the image with a specified angle (degrees).
public static void DrawImageToGraphics(Graphics gr, Bitmap img, Rectangle DestRect, Color[] OldColors, Color[] NewColors, Color TransparantColor, uint Angle)
{
System.Drawing.Drawing2D.Matrix lmx = new System.Drawing.Drawing2D.Matrix();
lmx.RotateAt(Angle, new PointF((DestRect.Left + DestRect.Right) / 2, (DestRect.Top + DestRect.Bottom) / 2));
gr.Transform = lmx;
System.Drawing.Imaging.ColorMap[] maps = new System.Drawing.Imaging.ColorMap[OldColors.Count() + 1];
for (int i = 0; i < OldColors.Count(); i++)
{
maps[i].OldColor = OldColors[i];
maps[i].NewColor = NewColors[i];
}
maps[OldColors.Count()].OldColor = TransparantColor;
maps[OldColors.Count()].NewColor = Color.Transparent;
System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes();
attr.SetRemapTable(maps);
gr.DrawImage(img, DestRect, 0, 0, img.Width, img.Height, GraphicsUnit.Point, attr);
}

Categories