How to invert an image - c#

I have a png image to be set on a button:
Button btn = new Button();
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(#"C:\temp\dog.png", UriKind.Relative));
btn.Background = brush;
I would like to have it inverted (meaning negative image).
Something like:
btn.Background = Invert(brush);
Thanks

You can use the code below. Note that it currently only works for PixelFormats with 32 bits per pixel, i.e. Brg32, Bgra32, Prgba32.
public static BitmapSource Invert(BitmapSource source)
{
// Calculate stride of source
int stride = (source.PixelWidth * source.Format.BitsPerPixel + 7) / 8;
// Create data array to hold source pixel data
int length = stride * source.PixelHeight;
byte[] data = new byte[length];
// Copy source image pixels to the data array
source.CopyPixels(data, stride, 0);
// Change this loop for other formats
for (int i = 0; i < length; i += 4)
{
data[i] = (byte)(255 - data[i]); //R
data[i + 1] = (byte)(255 - data[i + 1]); //G
data[i + 2] = (byte)(255 - data[i + 2]); //B
//data[i + 3] = (byte)(255 - data[i + 3]); //A
}
// Create a new BitmapSource from the inverted pixel buffer
return BitmapSource.Create(
source.PixelWidth, source.PixelHeight,
source.DpiX, source.DpiY, source.Format,
null, data, stride);
}
You can now use it like this:
brush.ImageSource = Invert(new BitmapImage(new Uri(#"C:\temp\dog.png")));
So
becomes

Related

Store image width alpha-channel in windows clipboard

I'm trying to copy an image to the windows clipboard but windows keeps removing my alpha. This is may latest attempt where i manually create the buffer and copy the BGRA values to the buffer, however still when i read the values later alpha is set to 255. If i copy my image from Paint.NET I get an InteropBitmap with the exact same values but alpha is set correct, so it should be possible. If i save my image to a PNG-file instead of the clipboard alpha is also correct.
var buffer = new byte[width * height * 4];
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
var pixel = surface[x, y];
var i = (y * width + x) * 4;
buffer[i + 0] = pixel.B;
buffer[i + 1] = pixel.G;
buffer[i + 2] = pixel.R;
buffer[i + 3] = pixel.A;
}
}
Clipboard.Clear();
Clipboard.SetImage(InteropBitmap.Create(
bounds.Width, bounds.Height,
96, 96,
PixelFormats.Bgra32,
null,
buffer,
width * 4));

Merge color and shadow images together

I am trying to "blend" two transparent images together: one is for color, the second one is for shadow. Here are two images I have:
And here is the result I am looking for:
Ignore the logo on the last one. I was trying to use this code https://softwarebydefault.com/2013/03/10/bitmap-blending/
but it produces this result:
Can anyone point if I am using correct method? May be I should use some other algorithm to achieve required result?
Rehshing old LockBits code this is the result
of this function:
public Bitmap Multiply(Bitmap bmp1, Bitmap bmp2)
{
Size s1 = bmp1.Size;
Size s2 = bmp2.Size;
if (s1 != s2) return null;
PixelFormat fmt1 = bmp1.PixelFormat;
PixelFormat fmt2 = bmp2.PixelFormat;
PixelFormat fmt = new PixelFormat();
fmt = PixelFormat.Format32bppArgb;
Bitmap bmp3 = new Bitmap(s1.Width, s1.Height, fmt);
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmp1Data = bmp1.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
BitmapData bmp2Data = bmp2.LockBits(rect, ImageLockMode.ReadOnly, fmt2);
BitmapData bmp3Data = bmp3.LockBits(rect, ImageLockMode.ReadWrite, fmt);
byte bpp1 = 4;
byte bpp2 = 4;
byte bpp3 = 4;
if (fmt1 == PixelFormat.Format24bppRgb) bpp1 = 3;
else if (fmt1 == PixelFormat.Format32bppArgb) bpp1 = 4; else return null;
if (fmt2 == PixelFormat.Format24bppRgb) bpp2 = 3;
else if (fmt2 == PixelFormat.Format32bppArgb) bpp2 = 4; else return null;
int size1 = bmp1Data.Stride * bmp1Data.Height;
int size2 = bmp2Data.Stride * bmp2Data.Height;
int size3 = bmp3Data.Stride * bmp3Data.Height;
byte[] data1 = new byte[size1];
byte[] data2 = new byte[size2];
byte[] data3 = new byte[size3];
System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
System.Runtime.InteropServices.Marshal.Copy(bmp2Data.Scan0, data2, 0, size2);
System.Runtime.InteropServices.Marshal.Copy(bmp3Data.Scan0, data3, 0, size3);
for (int y = 0; y < s1.Height; y++)
{
for (int x = 0; x < s1.Width; x++)
{
int index1 = y * bmp1Data.Stride + x * bpp1;
int index2 = y * bmp2Data.Stride + x * bpp2;
int index3 = y * bmp3Data.Stride + x * bpp3;
Color c1, c2;
if (bpp1 == 4)
c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2], data1[index1 + 1], data1[index1 + 0]);
else c1 = Color.FromArgb(255, data1[index1 + 2], data1[index1 + 1], data1[index1 + 0]);
if (bpp2 == 4)
c2 = Color.FromArgb(data2[index2 + 3], data2[index2 + 2], data2[index2 + 1], data2[index2 + 0]);
else c2 = Color.FromArgb(255, data2[index2 + 2], data2[index2 + 1], data2[index2 + 0]);
data3[index3 + 0] = (byte)( c1.B * c2.B / 256);
data3[index3 + 1] = (byte)( c1.G * c2.G / 256);
data3[index3 + 2] = (byte)( c1.R * c2.R / 256);
data3[index3 + 3] = c1.A;
}
}
System.Runtime.InteropServices.Marshal.Copy(data3, 0, bmp3Data.Scan0, data3.Length);
bmp1.UnlockBits(bmp1Data);
bmp2.UnlockBits(bmp2Data);
bmp3.UnlockBits(bmp3Data);
return bmp3;
}
Note that the alpha channel is copied from the 1st bitmap.
Instead of 'Multiply' 'ColorBurn' is also a good candidate. Here is very nice write-up of the math for the various blending modes.
Update: Using Anna's algorithm
data3[index3 + 0] = (byte)(c2.B + (c1.B * (255 - c2.B) / 256f));
data3[index3 + 1] = (byte)(c2.G + (c1.G * (255 - c2.G) / 256f));
data3[index3 + 2] = (byte)(c2.R + (c1.R * (255 - c2.R) / 256f));
results in this lighter image:
To keep the red colors everywhere and still make the result lighter I would apply a little gamma correction with a Color Matrix.
Sorry for ignoring that blog, I actually read it a bit but had my own algorithm to come up with, you can ignore me as well xD.
It is simple, for each pixel:
In the main image (red) find the RGBA values, let's call it ColorM.
In the shade image, find the RGBA values, let's call it ColorS.
For each of R, G, B and A values, calculate ((255 - ColorM) * ColorS) / 255, let's call the final RGBA value ColorOver.
Calculate ColorM + ColorOver and it will give the final image.
Test it if you wanted, seems a bit buggy.

c# How to read Pixel data in byte array efficiently

I am using below code to read the pixel data of image into byte[]. This solution works for me but I need to optimize as it takes a longer time is there any other workaround for same.
private byte[] GetPixelBytes(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentException("FileName cal not be null or Blank");
var bitmapImageSource = new BitmapImage(new System.Uri(fileName));
System.Drawing.Bitmap imgo = new System.Drawing.Bitmap(fileName);
var height = imgo.Height;
var width = imgo.Width;
//System.Drawing.Image.GetPixelFormatSize(imgo.PixelFormat);
var bitsPerPixel = bitmapImageSource.Format.BitsPerPixel;
var bytesPerPixel = (bitmapImageSource.Format.BitsPerPixel + 7) / 8;
int size = (int)(bitmapImageSource.Width * bitmapImageSource.Height * bytesPerPixel);
byte[] pixels = new byte[size];
for (int i = 0; i < imgo.Width; i++)
{
for (int j = 0; j < imgo.Height; j++)
{
System.Drawing.Color pixel = imgo.GetPixel(i, j);
//int offset = y * imgo.Width * 4 + x * 4; (x is the column and y is the row)
int offset = j * imgo.Width * bytesPerPixel + i * bytesPerPixel;
switch (bytesPerPixel)
{
case 4:
pixels[offset + 0] = pixel.B;
pixels[offset + 1] = pixel.G;
pixels[offset + 2] = pixel.R;
pixels[offset + 3] = pixel.A;
break;
case 3:
pixels[offset + 0] = pixel.B;
pixels[offset + 1] = pixel.G;
pixels[offset + 2] = pixel.R;
break;
default:
throw new InvalidCastException("Only 32 and 24 BPP images are supported");
break;
}
}
}
return pixels;
}
I have tried below option to optimize the
var bitmapData = imgo.LockBits(new System.Drawing.Rectangle(0,0,imgo.Width,imgo.Height),ImageLockMode.ReadOnly,imgo.PixelFormat);
var length = bitmapData.Stride * bitmapData.Height;
byte[] bytes = new byte[length];
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
imgo.UnlockBits(bitmapData);
But in this case bytes copied contains different value than what I get with my original function. I am using color Image having below properties.
Dimension: 3072*3072
Width: 3072 Pixel
Height: 3072 Pixel
Resolution: 96 dpi (Both)
Bit depth: 24
Resolved with below code.
private byte[] ReadByte()
{
System.Drawing.Bitmap imgo= new System.Drawing.Bitmap(filename);
var bitmapData = imgo.LockBits(new System.Drawing.Rectangle(0,0,imgo.Width,imgo.Height),ImageLockMode.ReadOnly,imgo.PixelFormat);
var length = bitmapData.Stride * bitmapData.Height;
byte[] bytes = new byte[length];
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
imgo.UnlockBits(bitmapData);
return bytes;
}
private static BitmapSource ByteToImage(byte[] buffer, int width, int height, PixelFormat pixelFormat, string fileName, int bytesPerPixel = 0)
{
var stride = ((width * pixelFormat.BitsPerPixel + 31) / 32) * 4;
switch (bytesPerPixel)
{
case 1:
pixelFormat = PixelFormats.Gray8;
break;
case 3:
pixelFormat = PixelFormats.Rgb24;
break;
case 4:
pixelFormat = PixelFormats.Bgr32;
break;
}
var imago = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
imago.WritePixels(new Int32Rect(0, 0, width, height), buffer, width * bytesPerPixel, 0);
return imago;
}

Color to Bitmap

How do I go about setting a Bitmap with a Color of the pixels. I created a program with LockBits and it is very fast but now I need to set a PictureBox with that image I ran through the LockBits I do not want to use SetPixels My current code is:
Bitmap imageFile = new Bitmap(bmpPath);
BitmapData imageData = imageFile.LockBits(new Rectangle(0, 0, imageFile.Width, imageFile.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
IntPtr Pointer = imageData.Scan0;
int ArraySize = Math.Abs(imageData.Stride) * imageFile.Height;
byte[] PixelArray = new byte[ArraySize];
Marshal.Copy(Pointer, PixelArray, 0, ArraySize);
int PixelAmount = 4; //ArGb
Color ArGBformat;
Bitmap RenderedImage = new Bitmap(imageFile.Width, imageFile.Height);
byte NewAlpha;
byte NewRed;
byte NewGreen;
byte NewBlue;
unsafe
{
for (int y = 0; y < imageData.Height; y++)
{
byte* row = (byte*)imageData.Scan0 + (y * imageData.Stride);
for (int x = 0; x < imageData.Width; x++)
{
int offSet = x * PixelAmount;
// read pixels
byte blue = row[offSet];
byte green = row[offSet + 1];
byte red = row[offSet + 2];
byte alpha = row[offSet + 3];
//Manipulates pixels
NewAlpha = Convert.ToByte(Math.Abs(alpha - _Alpha));
NewRed = Convert.ToByte(Math.Abs(red - _Red));
NewBlue = Convert.ToByte(Math.Abs(blue - _Blue));
NewGreen = Convert.ToByte(Math.Abs(green - _Green));
ArGBformat = Color.FromArgb(NewAlpha, NewRed, NewGreen, NewBlue);
RenderedImage.SetPixel(x, y, ArGBformat); //Slow and want something else
}
}
}
I would like to set my PictureBox1 to the pixels that get ran through the program.
Found the answer. I needed to set the pixels back.
//Sets image
row[offSet] = NewBlue;
row[offSet + 1] = NewGreen;
row[offSet + 2] = NewRed;
row[offSet + 3] = NewAlpha;

Image Distortion with Lock Bits

I'm having a problem with writing to files using lock bits. I'm working on an edge detection software which has a strange distortion effect with most images. I've tried to isolate the problem, and it seems very random. It is not associated with format, but rather the only images that seem to work are pictures made for desktop wallpapers, and I don't really know why. I only switched to writing to files using lockbits recently, so I am sure the problem is with that (there were no problems when I was reading with lockbits and writing with set pixel). Here's a screenshot of the effect:
As you can see, the edge detection works, but the image is distorted horizontally, making the image into a parallelogram.
Here's a code snippet of the method that handles all this (in C#):
private void analyze()
{
//When the analyze button is pressed
percentageInt = float.Parse(textBox1.Text);
float scale = 1;
if (comboBox1.SelectedItem == "Auto")
{
scale = pic.Width / pictureBox1.Width;
}
else if (comboBox1.SelectedItem == "1/2")
{
scale = 2;
}
else if (comboBox1.SelectedItem == "1/4")
{
scale = 4;
}
else if (comboBox1.SelectedItem == "Original")
{
scale = 1;
}
else
{
scale = pic.Width / pictureBox1.Width;
}
int tempWidth = 1;
int tempHeight = 1;
if (scale >= 1)
{
tempWidth = (int)Math.Floor(pic.Width / scale);
tempHeight = (int)Math.Floor(pic.Height / scale);
}
else
{
tempWidth = pic.Width;
tempHeight = pic.Height;
}
width = pic.Width;
height = pic.Height;
edgeData = new Boolean[pic.Width, pic.Height];
img = (Bitmap)resizeImage(pic, new Size(tempWidth, tempHeight));
pic2 = new Bitmap(tempWidth, tempHeight);
Bitmap img2 = (Bitmap)pic2;
Color[] pixels = null;
BitmapData data = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int size = Math.Abs(data.Stride) * img.Height;
Byte[] bytes = new byte[size];
int scaledPercent = (int)(Math.Round(percentageInt * 255));
Debug.WriteLine("percent " + scaledPercent);
unsafe
{
Debug.WriteLine("Woah there, unsafe stuff");
byte* prevLine = (byte*)data.Scan0;
byte* currLine = prevLine + data.Stride;
byte* nextLine = currLine + data.Stride;
for (int y = 1; y < img.Height - 1; y++)
{
byte* pp = prevLine + 3;
byte* cp = currLine + 3;
byte* np = nextLine + 3;
for (int x = 1; x < img.Width - 1; x++)
{
if (IsEdgeOptimized(pp, cp, np, scaledPercent))
{
edgeData[x, y] = true;
//Debug.WriteLine("x " + x + "y " + y);
//img2.SetPixel(x, y, Color.Black);
//bytes[(y * img.Width + x) * 3 + 2] = 255;
}
else
{
bytes[(y * img.Width + x) * 3] = 255;
bytes[(y * img.Width + x) * 3 + 1] = 255;
bytes[(y * img.Width + x) * 3 + 2] = 255;
//img2.SetPixel(x, y, Color.White);
}
pp += 3; cp += 3; np += 3;
}
prevLine = currLine;
currLine = nextLine;
nextLine += data.Stride;
}
}
System.Runtime.InteropServices.Marshal.Copy(bytes, 0, data.Scan0, size);
img.UnlockBits(data);
pictureBox2.Image = img;
} // end analyze
So what is causing the problem, and how can I fix it? If you need more details, feel free to comment.
You're initializing your bytes buffer with stride x height bytes:
int size = Math.Abs(data.Stride) * img.Height;
Byte[] bytes = new byte[size];
But then using the width (instead of stride) when you write to it:
bytes[(y * img.Width + x) * 3] = 255;

Categories