I'm working on a processing image software and now i want to change white balance and add some saturation on my photos, so i have to work with each pixel value.
I've tried to transform my BitmapImage to WritableBitmap and then I've tried this code. But if i run it, my image turns to green.
I want to take pixels from that image and then set them to new values.
I've searched many solutions but found none, so maybe someone can help me with my code. Thanks.
public static WriteableBitmap Colors(Bitmap Image)
{
int width = Image.Width;
int height = Image.Width;
byte[,,] pixels = new byte[height, width, 4];
WriteableBitmap wbitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
var target = new Bitmap(Image.Width, Image.Height);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
pixels[i, j, 1] -= 30;
pixels[i, j, 3] -= 30;
}
}
byte[] pixels1d = new byte[height * width * 4];
int index = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
for (int i = 0; i < 4; i++)
pixels1d[index++] = pixels[row, col, i];
}
}
Int32Rect rect = new Int32Rect(0, 0, width, height);
int stride = 4 * width;
wbitmap.WritePixels(rect, pixels1d, stride, 0);
return wbitmap;
}
And then I'm running it like this:
private void run_OnClick(object sender, RoutedEventArgs e)
{
// Contrastt(BitmapImage2Bitmap(bitmap), 10);
// MedianFilter(BitmapImage2Bitmap(bitmap),5);
WriteableBitmap writeableBitmap = new WriteableBitmap(imageX);
BitmapImage colored = ConvertWriteableBitmapToBitmapImage(Colors((BitmapImage2Bitmap(bitmap))));
ImageBrush brush = new ImageBrush();
brush.ImageSource = imageX ;
CanvasProcessedImage.Background = brush;
}
Assuming that you already have a BitmapSource (or an instance of a derived class like BitmapImage) in a source variable, you would first create a BitmapSource with a known pixel format, and then create a WritabelBitmap from that:
// source is a BitmapSource with whatever PixelFormat
var format = PixelFormats.Bgra32;
var temp = new FormatConvertedBitmap(source, format, null, 0); // force BGRA
var bitmap = new WriteableBitmap(temp);
Now you could either directly manipulate the data in the WriteableBitmap's BackBuffer property, or first do it a bit simpler and create a copy of the buffer like this:
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stride = bitmap.BackBufferStride;
var buffer = new byte[stride * height];
bitmap.CopyPixels(buffer, stride, 0);
Then manipulate the buffer and write it back to the WriteableBitmap. Since you have specified the PixelFormat, you know which byte of each 4-byte pixel value is B, G, R and A respectively.
for (int i = 0; i < buffer.Length; i += 4)
{
// Blue
//buffer[i + 0] = ...
// Green
buffer[i + 1] = (byte)Math.Max(buffer[i + 1] - 30, 0);
// Red
buffer[i + 2] = (byte)Math.Max(buffer[i + 2] - 30, 0);
// Alpha
//buffer[i + 3] = ...
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), buffer, stride, 0);
When you have assigned the WriteableBitmap to the Source property of an Image element, or the ImageSource property of an ImageBrush, you can call WritePixels later (and as often as necessary) without needing to reassign it it to the Source/ImageSource property. It is changed "in place".
Related
I'm trying to convert bitmap to 8bpp grayscale with the code below
private Bitmap ConvertPixelformat(ref Bitmap Bmp)
{
Bitmap myBitmap = new Bitmap(Bmp);
// Clone a portion of the Bitmap object.
Rectangle cloneRect = new Rectangle(0, 0, Bmp.Width, Bmp.Height);
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap cloneBitmap = myBitmap.Clone(cloneRect, format);
var pal = cloneBitmap.Palette;
for (i = 0; i < cloneBitmap.Palette.Entries.Length; ++i)
{
var entry = cloneBitmap.Palette.Entries[i];
var gray = (int)(0.30 * entry.R + 0.59 * entry.G + 0.11 * entry.B);
pal.Entries[i] = Color.FromArgb(gray, gray, gray);
}
cloneBitmap.Palette = pal;
cloneBitmap.SetResolution(500.0F, 500.0F);
return cloneBitmap;
}
Checking the properties of the bitmap image shows that the bit depth is correctly set to 8bpp but not as a grayscale rather it is a coloured index 8bpp. Kindly guide on what to do.
Examine the following code:
public static unsafe Bitmap ToGrayscale(Bitmap colorBitmap)
{
int Width = colorBitmap.Width;
int Height = colorBitmap.Height;
Bitmap grayscaleBitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
grayscaleBitmap.SetResolution(colorBitmap.HorizontalResolution,
colorBitmap.VerticalResolution);
///////////////////////////////////////
// Set grayscale palette
///////////////////////////////////////
ColorPalette colorPalette = grayscaleBitmap.Palette;
for (int i = 0; i < colorPalette.Entries.Length; i++)
{
colorPalette.Entries[i] = Color.FromArgb(i, i, i);
}
grayscaleBitmap.Palette = colorPalette;
///////////////////////////////////////
// Set grayscale palette
///////////////////////////////////////
BitmapData bitmapData = grayscaleBitmap.LockBits(
new Rectangle(Point.Empty, grayscaleBitmap.Size),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Byte* pPixel = (Byte*)bitmapData.Scan0;
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color clr = colorBitmap.GetPixel(x, y);
Byte byPixel = (byte)((30 * clr.R + 59 * clr.G + 11 * clr.B) / 100);
pPixel[x] = byPixel;
}
pPixel += bitmapData.Stride;
}
grayscaleBitmap.UnlockBits(bitmapData);
return grayscaleBitmap;
}
this code converts a color image to a grayscale image.
What is the WPF equivalent of the following Java SWT code? I want to create an Image from a list of RGBA values and display on a Canvas.
private Image GetImage()
{
ImageData imageData = new ImageData(imageWidth, imageHeight,32,palette);
int pixelVecLoc=0;
for (int h = 0; h<imageHeight && (pixelVecLoc < currentImagePixelVec.size()); h++)
{
for (int w = 0; w<imageWidth && (pixelVecLoc < currentImagePixelVec.size()); w++)
{
int p = 0;
Pixel pixel = currentImagePixelVec.get(pixelVecLoc);
p = (pixel.Alpha<<24) | (pixel.Red<<16) | (pixel.Green<<8) | pixel.Blue;
imageData.setPixel(w, h, p);
pixelVecLoc++;
}
}
imageData = imageData.scaledTo(imageScaleWidth, imageScaleHeight);
Image image = ImageDescriptor.createFromImageData(imageData).createImage();
return image;
}
Then draw it on a Canvas:
gc.drawImage(image, 0, 0);
This is a short snippet showing how you can create a custom RGBA buffer and write pixel data to it (based on this example):
int width = 512;
int height = 256;
int stride = width * 4 + (width % 4);
int pixelWidth = 4; // RGBA (BGRA)
byte[] imageData = new byte[width * stride]; // raw byte buffer
for (int y = 0; y < height; y++)
{
int yPos = y * stride;
for (int x = 0; x < width; x++)
{
int xPos = yPos + x * pixelWidth;
imageData[xPos + 2] = (byte) (RedValue); // replace *Value with source data
imageData[xPos + 1] = (byte) (GreenValue);
imageData[xPos ] = (byte) (BlueValue);
imageData[xPos + 3] = (byte) (AlphaValue);
}
}
Then use the BitmapSource.Create Method (Int32, Int32, Double, Double, PixelFormat, BitmapPalette, IntPtr, Int32, Int32) method together with a PixelFormats:
BitmapSource bmp =
BitmapSource.Create(
width,
height,
96, // Horizontal DPI
96, // Vertical DPI
PixelFormats.Bgra32, // 32-bit BGRA
null, // no palette
imageData, // byte buffer
imageData.Length, // buffer size
stride); // stride
Note that the byte-order is reverse except the alpha component (BGRA) as shown in the snippet.
To transfer the result to canvas you can first create an Image, set the BitmapSource as Source and finally add that to the canvas:
// create image and set image as source
Image BmpImg = New Image();
BmpImg.Width = width;
BmpImg.Height = height;
BmpImg.Source = bmp;
// add image to canvas
canvas.Children.Add(BmpImg);
Canvas.SetLeft(BmpImg, 0); // to set position (x,y)
Canvas.SetTop(BmpImg, 0);
I am trying to combine 3 grayscale bitmaps into one color bitmap. All three grayscale images are the same size (this is based off of data from the Hubble). My logic is:
Load "blue" image and convert to PixelFormat.Format24bppRgb. Based off of that create a new byte array that is 4 times as large as the blue data array length/3 (so it will be one byte for blue, one byte for green, one byte for red, one byte for alpha per pixel since my system is little endian). Populate the "blue" bytes of the array from the "blue" bytes of the blue image (and in this first loop set the alpha byte to 255). I then load the green and red bitmaps, convert them to PixelFormat.Format24bppRgb, and pull the g/r value and add it to the correct place in the data array. The final data array then has the bgra bytes set correctly from what I can tell.
When I have the data array populated, I have used it to:
Create a PixelFormats.Bgra32 BitmapSource then convert that to a Bitmap.
Create a PixelFormat.Format32bppArgb Bitmap using the Bitmap constructor (width, height, stride, PixelForma, IntPtr)
Create a PixelFormat.Format32bppArgb Bitmap using pointers
All three ways of creating a return bitmap result in the image being "skewed" (sorry, I don't know of a better word).
The actual output (of all three ways of generating the final bitmap) is: Actual output
The desired output is something like (this was done in photoshop so it is slightly different): Desired output
The three file names (_blueFileName, _greenFileName, _redFileName) are set in the constructor and I check to make sure the files exist before creating the class. I can post that code if anyone wants it.
Can anyone tell me what I am doing wrong? I am guessing that is is due to the stride or something like that?
Note: I can't post the links to the images I am using as input as I don't have 10 reputation points. Maybe I could send the links via email or something if someone wants them as well.
Here is my code (with some stuff commented out, the comments describe what happens if each commented out block is used instead):
public Bitmap Merge()
{
// Load original "blue" bitmap.
Bitmap tblueBitmap = (Bitmap)Image.FromFile(_blueFileName);
int width = tblueBitmap.Width;
int height = tblueBitmap.Height;
// Convert to 24 bpp rgb (which is bgr on little endian machines)
Bitmap blueBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(blueBitmap))
{
gr.DrawImage(tblueBitmap, 0, 0, width, height);
}
tblueBitmap.Dispose();
// Lock and copy to byte array.
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly,
blueBitmap.PixelFormat);
int numbBytes = blueData.Stride*blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
Marshal.Copy(blueData.Scan0, blueBytes, 0, numbBytes);
blueBitmap.UnlockBits(blueData);
blueData = null;
blueBitmap.Dispose();
int mult = 4;
byte[] data = new byte[(numbBytes/3)*mult];
int count = 0;
// Copy every third byte starting at 0 to the final data array (data).
for (int i = 0; i < data.Length / mult; i++)
{
// Check for overflow
if (blueBytes.Length <= count*3 + 2)
{
continue;
}
// First pass, set Alpha channel.
data[i * mult + 3] = 255;
// Set blue byte.
data[i*mult] = blueBytes[count*3];
count++;
}
// Cleanup.
blueBytes = null;
int generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tgreenBitmap = (Bitmap)Image.FromFile(_greenFileName);
Bitmap greenBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(greenBitmap))
{
gr.DrawImage(tgreenBitmap, 0, 0, width, height);
}
tgreenBitmap.Dispose();
BitmapData greenData = greenBitmap.LockBits(new Rectangle(0, 0, greenBitmap.Width, greenBitmap.Height), ImageLockMode.ReadOnly,
greenBitmap.PixelFormat);
numbBytes = greenData.Stride * greenBitmap.Height;
byte[] greenBytes = new byte[numbBytes];
Marshal.Copy(greenData.Scan0, greenBytes, 0, numbBytes);
greenBitmap.UnlockBits(greenData);
greenData = null;
greenBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (greenBytes.Length <= count * 3 + 1)
{
continue;
}
// Set green byte
data[i * mult + 1] = greenBytes[count * 3 + 1];
count++;
}
greenBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tredBitmap = (Bitmap)Image.FromFile(_redFileName);
Bitmap redBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(redBitmap))
{
gr.DrawImage(tredBitmap, 0, 0, width, height);
}
tredBitmap.Dispose();
BitmapData redData = redBitmap.LockBits(new Rectangle(0, 0, redBitmap.Width, redBitmap.Height), ImageLockMode.ReadOnly,
redBitmap.PixelFormat);
numbBytes = redData.Stride * redBitmap.Height;
byte[] redBytes = new byte[numbBytes];
Marshal.Copy(redData.Scan0, redBytes, 0, numbBytes);
redBitmap.UnlockBits(redData);
redData = null;
redBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (redBytes.Length <= count * 3+2)
{
count++;
continue;
}
// set red byte
data[i * mult + 2] = redBytes[count * 3 + 2];
count++;
}
redBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
int stride = (width*32 + 7)/8;
var bi = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, data, stride);
// uncomment out below to see what a bitmap source to bitmap does. So far, it is exactly the same as
// the uncommented out lines below.
// ---------------------------------------------------------------------------------------------------
//return BitmapImage2Bitmap(bi);
unsafe
{
fixed (byte* p = data)
{
IntPtr ptr = (IntPtr)p;
// Trying the commented out lines returns the same bitmap as the uncommented out lines.
// ------------------------------------------------------------------------------------
byte* p2 = (byte*)ptr;
Bitmap retBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData fData = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
unsafe
{
for (int i = 0; i < fData.Height; i++)
{
byte* imgPtr = (byte*)(fData.Scan0 + (fData.Stride * i));
for (int x = 0; x < fData.Width; x++)
{
for (int ii = 0; ii < 4; ii++)
{
*imgPtr++ = *p2++;
}
//*imgPtr++ = 255;
}
}
}
retBitmap.UnlockBits(fData);
//Bitmap retBitmap = new Bitmap(width, height, GetStride(width, PixelFormat.Format32bppArgb),
// PixelFormat.Format32bppArgb, ptr);
return retBitmap;
}
}
}
private Bitmap BitmapImage2Bitmap(BitmapSource bitmapSrc)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSrc));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
return new Bitmap(bitmap);
}
}
private int GetStride(int width, PixelFormat pxFormat)
{
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
int validBitsPerLine = width * bitsPerPixel;
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
You are missing the gap between the lines. The Stride value is not the amount of data in a line, it's the distance between the start of one line to the next. There may be a gap at the end of each line to align the next line on an even address boundary.
The Stride value can even be negative, then the image is stored upside down in memory. To get the data without the gaps and to handle all cases you need to copy one line at a time:
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly, blueBitmap.PixelFormat);
int lineBytes = blueBitmap.Width * 3;
int numbBytes = lineBytes * blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
for (int y = 0; y < blueBitmap.Height; y++) {
Marshal.Copy(blueData.Scan0 + y * blueData.Stride, blueBytes, y * lineBytes, lineBytes);
}
blueBitmap.UnlockBits(blueData);
blueBitmap.Dispose();
How can I create a 256x256 color space image using graphics draw? Currently I'm using pointers to loop through each pixel location and setting it. Blue goes from 0...255 on X and Green goes from 0...255 on Y. The image is initialized as so.
Bitmap image = new Bitmap(256, 256);
imageData = image.LockBits(new Rectangle(0, 0, 256, 256),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
for (int row = 0; row < 256; row++)
{
byte* ptr = (byte*)imageData.Scan0 + (row * 768);
for (int col = 0; col < 256; col++)
{
ptr[col * 3] = (byte)col;
ptr[col * 3 + 1] = (byte)(255 - row);
ptr[col * 3 + 2] = 0;
}
}
I have a slider that goes 0...255 on Red. On each scroll, it goes through this loop and updates the image.
for (int row = 0; row < 256; row++)
{
byte* ptr = (byte*)imageData.Scan0 + (row * 768);
for (int col = 0; col < 256; col++)
{
ptr[col * 3 + 2] = (byte)trackBar1.Value;
}
}
I've figured out how to use a ColorMatrix instead for the scrolling part but how can I initialize the image without using pointers or SetPixel?
First of all, add PictureBox control to the Form.
Then, this code will assign different color to each pixel based on the index in the loop and assign the image to the control:
Bitmap image = new Bitmap(pictureBox3.Width, pictureBox3.Height);
SolidBrush brush = new SolidBrush(Color.Empty);
using (Graphics g = Graphics.FromImage(image))
{
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
brush.Color = Color.FromArgb(x, y, 0);
g.FillRectangle(brush, x, y, 1, 1);
}
}
}
pictureBox3.Image = image;
For some reason there's no SetPixel or DrawPixel like I expected, but the FillRectangle will do exactly the same thing when you give it 1x1 dimensions to fill.
Note that it will work fine for small images but the larger the image the slower it will be.
If you don't want to use pointers or SetPixel you'll have to build the gradient in a byte array and then Marshal.Copy it to your bitmap:
int[] b = new int[256*256];
for (int i = 0; i < 256; i++)
for (int j = 0; j < 256; j++)
b[i * 256 + j] = j|i << 8;
Bitmap bmp = new Bitmap(256, 256, PixelFormat.Format32bppRgb);
BitmapData bits = bmp.LockBits(new Rectangle(0, 0, 256, 256),
ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
Marshal.Copy(b, 0, bits.Scan0, b.Length);
This would create you a white image of 256x256
Bitmap image = new Bitmap(256, 256);
using (Graphics g = Graphics.FromImage(image)){
g.FillRectangle(Brushes.White, 0, 0, 256, 256);
}
I have an image with letters in it, the letters are in two colors black and blue, I want to read the blue colored letters from the image.
Can anyone suggest me a method to do this in C#. Iam studying GDI+,but still didn't get any logic to develop this program..
I tried OCRing it, but the issue with common OCRs is that they dont recognize the color difference.
I only want to read the Blue characters....
Any guidance is highly appreciated.
Try this one ;) But that's unsafe code.
void RedAndBlue()
{
OpenFileDialog ofd;
int imageHeight, imageWidth;
if (ofd.ShowDialog() == DialogResult.OK)
{
Image tmp = Image.FromFile(ofd.FileName);
imageHeight = tmp.Height;
imageWidth = tmp.Width;
}
else
{
// error
}
int[,] bluePixelArray = new int[imageWidth, imageHeight];
int[,] redPixelArray = new int[imageWidth, imageHeight];
Rectangle rect = new Rectangle(0, 0, tmp.Width, tmp.Height);
Bitmap temp = new Bitmap(tmp);
BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int remain = bmpData.Stride - bmpData.Width * 3;
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
for (int j = 0; j < bmpData.Height; j++)
{
for (int i = 0; i < bmpData.Width; i++)
{
bluePixelArray[i, j] = ptr[0];
redPixelArray[i, j] = ptr[2];
ptr += 3;
}
ptr += remain;
}
}
temp.UnlockBits(bmpData);
temp.Dispose();
}
Modify the color of the image to gray scaled then use OCR
public Bitmap MakeGrayscale(Bitmap original)
{
//make an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
for (int i = 0; i < original.Width; i++)
{
for (int j = 0; j < original.Height; j++)
{
//get the pixel from the original image
Color originalColor = original.GetPixel(i, j);
//create the grayscale version of the pixel
int grayScale = (int)((originalColor.R * .3) + (originalColor.G * .59)
+ (originalColor.B * .11));
//create the color object
Color newColor = Color.FromArgb(grayScale, grayScale, grayScale);
//set the new image's pixel to the grayscale version
newBitmap.SetPixel(i, j, newColor);
}
}
return newBitmap;
}
You could probably modify the palette to have only black and white on the image