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.
Related
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;
}
I am loading an image inside a Console application using Image.FromFile.
After that, I am casting it to a Bitmap to be able to use the Bitmap.GetPixel() method.
Surprisingly, while looping through all the pixels, all what I am getting from the GetPixel is 0,0,0 for R,G,B.
To make sure that the image is well read from the file, I added a reference to System.Windows.Forms and I loaded the image inside a PictureBox to see the result, and the image is well seen.
Original Image:
Here's how I am loading the image and showing it into a PictureBox:
Bitmap img = (Bitmap)Image.FromFile("image.png");
PictureBox pb = new PictureBox
{
Image = img
};
Form frm = new Form
{
Controls = { pb }
};
frm.ShowDialog();
Which shows the image as it is:
And after that, I am getting the pixels like:
byte[] input = new byte[784];
for (int x = 0; x < 28; x++)
{
for (int y = 0; y < 28; y++)
{
Color color = img.GetPixel(x, y);
byte r = color.R;
byte g = color.G;
byte b = color.B;
Console.Write($"{color.R},{color.G},{color.B}|||");
input[x * 28 + y] = (byte)((((r + g + b) / 3)));
}
Console.WriteLine();
}
Note that the image size is 28x28px, and I have tried other images and I've got the same result.
What I expected that the results shows the real color values since I've used that method before and it worked perfectly, but now all the results printed to the console are zeros which I am finding it difficult to understand.
Edit:
Since the PictureBox is showing the real image representation, I tried to get the pixels from the PictureBox.Image like:
Color color = ((Bitmap)pb.Image).GetPixel(x, y);
This also didn't work and the results came back as zeros.
What could be the reason behind this and how to fix it?
In your image all color channels of all pixels are 0, so you code actually works fine.
And most of the pixels also have an alpha of 0, so they are transparent. Some are semi-transparent, anti-aliased pixels and a few are fully opaque.
Where transparent or semi-transprent pixels are, the backcolor will shine through.
If you want to recognize the really, non-transparent black pixels you need to test the alpha channel as well.
Here is a function that will create a composite color of a pixel's color and a given background color..:
Color Composite(Color color, Color backcolor)
{
byte r = (byte)(((255 - color.A) * backcolor.R + color.R * color.A) / 256);
byte g = (byte)(((255 - color.A) * backcolor.G + color.G * color.A) / 256);
byte b = (byte)(((255 - color.A) * backcolor.B + color.B * color.A) / 256);
Color c2 = Color.FromArgb(255, r, g, b);
return c2;
}
If you want the brightness of one of your pixels you could create a composite of its color with white. But, given the image you have, you could just as well use color.A directly..
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;
}
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;
}
I have small function which will recolor pixels in a Bitmap from a given color to a new given color.
The problems I have with the code are as follows:
1)
The function gives results which are remapping white pixels which should not be concidered since I have a threshold... (unless I have defined this calculation wrong)
2) When certain colors are given e.g. LimeGreen wierd results are seen in the image returned from the function (I beleive this is due to overflow of the byte type in the addition or subtraction case)
The base image I am using can be found here:
http://www.freeimagehosting.net/uploads/c8745a9de1.png
Results I have obtained can be found here:
freeimagehosting.net/uploads/fa48e5a0eb.png (Called with Color.Magenta as remapColor, Color.Red as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)
freeimagehosting.net/uploads/8faec6a569.png (Called with Color.Magenta as remapColor, Color.Yellow as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)
freeimagehosting.net/uploads/2efd4c04aa.png (Called with Color.Magenta as remapColor, Color.Blue as newColor, Seems like gradient not colored correctly)
freeimagehosting.net/uploads/defdf04e16.png (Called with Color.Magenta as remapColor, Color.Teal as newColor, Seems like white pixels are effected and none of the gradient is calculated correctly)
The function I have for this code is below: UPDATED per suggestions
public unsafe static Bitmap RecolorImage(Bitmap original, Color remapColor, Color newColor)
{
Bitmap result = new Bitmap(original.Width, original.Height);
//lock the original bitmap in memory
BitmapData originalData = original.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
//lock the new bitmap in memory
BitmapData newData = result.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
//set the number of bytes per pixel
int pixelSize = 4;
int rthreshold = 128;
int gthreshold = 128;
int bthreshold = 128;
for (int y = 0; y < original.Height; y++)
{
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < original.Width; x++)
{
//examine the rgb values
byte r = (byte)((oRow[x * pixelSize]));
byte g = (byte)((oRow[x * pixelSize + 1]));
byte b = (byte)((oRow[x * pixelSize + 2]));
byte a = (byte)((oRow[x * pixelSize + 3]));
if (a > 0 &&
Math.Abs(remapColor.R - r) <= rthreshold &&
Math.Abs(remapColor.B - b) <= bthreshold &&
Math.Abs(remapColor.G - g) <= gthreshold
)
{
if (newColor.R == 0)
{
r = 0;
}
else
{
if (newColor.R > remapColor.R)
r = (byte)(r - newColor.R);
else
r = (byte)(r + newColor.R);
}
if (newColor.G == 0)
{
g = 0;
}
else
{
if (newColor.G > remapColor.G)
g = (byte)(g - newColor.G);
else
g = (byte)(g + newColor.G);
}
if (newColor.B == 0)
{
b = 0;
}
else
{
if (newColor.B > remapColor.B)
b = (byte)(b - newColor.B);
else
b = (byte)(b + newColor.B);
}
}
//set the new image's pixel remaped pixel color
nRow[x * pixelSize] = b; //B
nRow[x * pixelSize + 1] = g; //G
nRow[x * pixelSize + 2] = r; //R
nRow[x * pixelSize + 3] = a; //A
}
}
original.UnlockBits(originalData);
result.UnlockBits(newData);
return result;
}
What gives....
Is what I am trying to do possible?
Is it reliable?
Is there just a bug in my code?
Is there a better way to achive this "re-mapable technique" on bitmaps using gradients?
Thank you for your time.
It looks like your threshold test is incorrect. Take the line:
remapColor.R - r <= rthreshold
If the current pixel is white, then r will be 255, and the test will always be true, no matter what remapColor.R and rthreshold are.
I think Math.Abs(remapColor.R - r) might work.
And you're likely correct about your byte values being out of bounds. Fixing the threshold test might stop that from happening. Otherwise, try putting some bounds checking in to see where it's happening.
I have decided that although this may be possible if I study the various materials regarding color spaces and their supporting theories. It seems that this will take a bit more than some quick threshold calculation and normalization to the remapColor.
I am going to propose that instead of performing this type of modification on a raster bitmap image that the graphics be modified in their vector form.
The process should be something like this:
The graphics are created in whatever imaging suite the designer is working in.
They are saved to a vector format e.g. SVG this will allow the customizable paths to be named, traversed and altered programmatically (and for more than color if needed) with SVG Rendering Engine(http://svg.codeplex.com/)
With this solution we can either output the SVG direct to the browser if supported and do the modifications directly on the client or use the server and output as PNG when needed.
I feel that this arrangement will provide us with more flexibility and a more robust solution than what I was initially going to hack together.
Thank you guys for your time!