Recently I learned how to save an image as bytes (RGB values in a text file), and now I'd like to know how to create a perfectly valid image from array of RGB values.
You can use the approach mentioned by #dasblinkenlight:
int width = 1; // read from file
int height = 1; // read from file
var bitmap = new Bitmap(width, height, PixelFormat.Canonical);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
int red = 0; // read from array
int green = 0; // read from array
int blue = 0; // read from array
bitmap.SetPixel(x, y, Color.FromArgb(0, red, green, blue));
}
Related
I have a C/C++ dll that I use to generate some informations, and, a image from this informations, now, I need to use this dll in C#.
I have implemented a exported function that write all the CIMg::data into a pre-allocated buffer.
So, I tried to convert this pre-allocated buffer into a Bitmap in C#, but, the image has been generated with just some wrong lines...
After this, I tried to implement an alternative version of this functions, that write into a pre-allocated buffer, all the RGB from the image, but, when the system write the image, the image is mirrored.
This is my function to save all the RGB:
std::vector<unsigned char> SystemImage::getData()
{
if(!_generatedImage)
this->createImage();
std::vector<unsigned char> data;
unsigned char red, blue, green;
for (int x = 0; x < _baseImage->width(); x++)
{
for (int y = 0; y < _baseImage->height(); y++)
{
red = (*_baseImage)(y, x, 0, 0);
green = (*_baseImage)(y, x, 0, 1);
blue = (*_baseImage)(y, x, 0, 2);
data.push_back(red);
data.push_back(green);
data.push_back(blue);
}
}
return data;
}
My C# function to handle this information:
public Bitmap CreateImage()
{
const uint bufferSize = 6113880;
byte[] data = new byte[bufferSize];
Bridge.System_Bridge.getRGBData(PSystem, data, bufferSize);
int position = 0;
Bitmap bmp = new Bitmap(1332, 1530);
for (int x = 0; x < 1332; x++)
for (int y = 0; y < 1530; y++)
{
bmp.SetPixel(x, y, Color.FromArgb(data[position], data[position + 1], data[position + 2]));
position += 3;
}
data = null;
return bmp;
}
There is a more better way to make this ?
How I can use the CIMg::Data to make the bitmap ? ( This will reduce a lot of allocated memory!! )
I have a video stream from a camera to an Image in a WPF. I am trying to access the WritableBitMap Image pixel by pixel before displaying it. As a test I am trying to set the whole image to white or black. In both cases however, I get the AccessViolationException error.
I checked other posts and it seems that this error is very wide and not specific to my case. I can't seem to know why I am not getting this working.
So what is the best way to play with the pixels in my case? or why this is not working? Any help is appreciated
private async void UpdateMasterCameraPreview(IdsFrame frame)
{
if (masterImage != null)
frame.Image.CopyTo(masterImage);
else
masterImage = frame.Image.ToWriteableBitmap();
//BitmapImage temp = ConvertWriteableBitmapToBitmapImage(masterImage);
WriteableBitmap temp = masterImage;
// Here I get the exception, on every pixel access
for (int y = 0; y < temp.PixelHeight; y++)
for (int x = 0; x < temp.PixelWidth; x++)
temp.SetPixel(x, y, 255);
masterImage = temp;
masterImage.Lock();
masterImage.AddDirtyRect(new Int32Rect(0, 0, masterImage.PixelWidth, masterImage.PixelHeight));
masterImage.Unlock();
if (OnMasterFrameCaptured != null)
OnMasterFrameCaptured(this, new CameraFrameCapturedArgs(CameraType.Master, masterImage));
}
You have swapped X and Y, i represents height, j represents width, then you shouldcall SetPixel like:
temp.SetPixel(j, i, 255);
On cases like this is better to use meaningful names for variables, like X and Y.
I ended up using the answer of this post. I now can edit raw pixel data of any WriteableBitmap image before sending it to image control in WPF. Below is what I exactly used but here I just convert every frame to some transparency under a condition:
public void ConvertImage(ref WriteableBitmap Wbmp)
{
int width = Wbmp.PixelWidth;
int height = Wbmp.PixelHeight;
int stride = Wbmp.BackBufferStride;
int bytesPerPixel = (Wbmp.Format.BitsPerPixel + 7) / 8;
unsafe
{
byte* pImgData = (byte*)Wbmp.BackBuffer;
// set alpha to transparent for any pixel with red < 0x88 and invert others
int cRowStart = 0;
int cColStart = 0;
for (int row = 0; row < height; row++)
{
cColStart = cRowStart;
for (int col = 0; col < width; col++)
{
byte* bPixel = pImgData + cColStart;
UInt32* iPixel = (UInt32*)bPixel;
if (bPixel[2 /* bgRa */] < 0x44)
{
// set to 50% transparent
bPixel[3 /* bgrA */] = 0x7f;
}
else
{
// invert but maintain alpha
*iPixel = *iPixel ^ 0x00ffffff;
}
cColStart += bytesPerPixel;
}
cRowStart += stride;
}
}
}
And the routine of using it is like this:
masterImage.Lock();
ConvertImage(ref masterImage);
masterImage.AddDirtyRect(new Int32Rect(0, 0, masterImage.PixelWidth, masterImage.PixelHeight));
masterImage.Unlock();
I'm trying to change an image color by using Bitmap.SetPixel method. And I have a problem when using it like this:
Bitmap bmp = new Bitmap(Project1.Properties.Resources.gray_square_button);
int Width = bmp.Width;
int Height = bmp.Height;
Bitmap Nbmp = new Bitmap(bmp);
ColorDialog ColorDialog = new ColorDialog();
ColorDialog.AllowFullOpen = true;
DialogResult result = ColorDialog.ShowDialog();
if (result == DialogResult.OK)
{
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
System.Drawing.Color BackColor = ColorDialog.Color;
System.Drawing.Color p = bmp.GetPixel(x, y);
int a = BackColor.A;
int r = BackColor.R;
int g = BackColor.G;
int b = BackColor.B;
Nbmp.SetPixel(x, y, System.Drawing.Color.FromArgb(a, r, g, b));
}
}
PictureBox1.Image = Nbmp;
}
It will only draw a square with the color that I choose like in this image:
But if I use it like this, using a manual color and the original image color references (p insted of BackColor which is the color defined by the ColorDialog):
if (result == DialogResult.OK)
{
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
System.Drawing.Color BackColor = ColorDialog.Color;
System.Drawing.Color p = bmp.GetPixel(x, y);
int a = p.A;
int r = p.R;
int g = p.G;
int b = p.B;
Nbmp.SetPixel(x, y, System.Drawing.Color.FromArgb(a, r, 0, 0));
}
}
PictureBox1.Image = Nbmp;
}
The color is applied correctly and the image is displayed correctly as well:
What i've tried is changing only 1 value of the RGB color. But then if you choose a color the color will not be the one you selected but a different one based on the one that is modified by the Color of the Color Dialog.
int a = p.A;
int r = BackColor.R;
int g = p.G;
int b = p.B;
Why are the ColorDialog RGB values not allowing the image to be displayed correctly but only a colored square?
This is the original image:
You're replacing the colour of a "shaded" image with a flat selected colour from a ColorDialog. So basically, you're replacing every pixel, regardless of ARGB, with a single ARGB from the dialog, thus completely overwriting any existing image information. You may as well just draw the image from scratch based on the old image's dimensions, without concern for the original image since it's being completely rewritten.
It sounds like what you are probably intending to do is blend the 2 colours together (the original image + the new colour). There are literally hundreds of ways to do this. One that comes to mind is to create an overlay with 50% transparency and apply it over the original image. You could also set the ARGB to an average:
int a = (BackColor.A + p.A) / 2;
int r = (BackColor.R + p.R) / 2;
int g = (BackColor.G + p.G) / 2;
int b = (BackColor.B + p.B) / 2;
This will give you a feel for how to consider the original colours while changing them versus flat out replacing them.
I am trying to create a program that accepts an image, recursively goes through each pixel, normalizes the pixel and re-creates a NEW image that looks the same as the original, but has normalized pixels instead.
public void parseJpeg(String jpegPath)
{
var normalizedRed = 0.0;
var normalizedGreen = 0.0;
var normalizedBlue = 0.0;
Bitmap normalizedImage = null;
var image = new Bitmap(jpegPath);
normalizedImage = new Bitmap(image.Width, image.Height);
for (int x = 0; x < image.Width; ++x)
{
for (int y = 0; y < image.Height; ++y)
{
Color color = image.GetPixel(x, y);
double exponent = 2;
double redDouble = Convert.ToDouble(color.R);
double blueDouble = Convert.ToDouble(color.B);
double greenDouble = Convert.ToDouble(color.G);
double redResult = Math.Pow(redDouble, exponent);
double blueResult = Math.Pow(blueDouble, exponent);
double greenResult = Math.Pow(greenDouble, exponent);
double totalResult = redResult + blueResult + greenResult;
normalizedRed = Convert.ToDouble(color.R) / Math.Sqrt(totalResult);
normalizedGreen = Convert.ToDouble(color.G) / Math.Sqrt(totalResult);
normalizedBlue = Convert.ToDouble(color.B) / Math.Sqrt(totalResult);
Color newCol = Color.FromArgb(Convert.ToInt32(normalizedRed), Convert.ToInt32(normalizedGreen), Convert.ToInt32(normalizedBlue));
normalizedImage.SetPixel(x, y, newCol);
}
}
normalizedImage.Save("C:\\Users\\username\\Desktop\\test1.jpeg");
resultsViewBox.AppendText("Process completed.\n");
}
Using the above code produces all black pixels and I do not understand why. When it normalizes it sets RGB = 1. After normalization, how do I set pixels with the NEW normalized value?
When I perform the below code, I get a black and blue image in my preview, but when I open the file it's blank. This is better than what I was getting before, which was ALL black pixels. This only works on one image though. So I am not sure how much of a step forward it is.
public void parseJpeg(String jpegPath)
{
Bitmap normalizedImage = null;
var image = new Bitmap(jpegPath);
normalizedImage = new Bitmap(image.Width, image.Height);
for (int x = 0; x < image.Width; ++x)
{
for (int y = 0; y < image.Height; ++y)
{
Color color = image.GetPixel(x, y);
float norm = (float)System.Math.Sqrt(color.R * color.R + color.B * color.B + color.G * color.G);
Color newCol = Color.FromArgb(Convert.ToInt32(norm));
normalizedImage.SetPixel(x, y, newCol);
}
}
normalizedImage.Save("C:\\Users\\username\\Desktop\\test1.jpeg");
resultsViewBox.AppendText("Process completed.\n");
}
I found the code for what I was trying to do:
http://www.lukehorvat.com/blog/normalizing-image-brightness-in-csharp/
public void parseJpeg(String jpegPath)
{
var image = new Bitmap(jpegPath);
normalizedImage = new Bitmap(image.Width, image.Height);
for (int x = 0; x < image.Width; ++x)
{
for (int y = 0; y < image.Height; ++y)
{
float pixelBrightness = image.GetPixel(x, y).GetBrightness();
minBrightness = Math.Min(minBrightness, pixelBrightness);
maxBrightness = Math.Max(maxBrightness, pixelBrightness);
}
}
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
Color pixelColor = image.GetPixel(x, y);
float normalizedPixelBrightness = (pixelColor.GetBrightness() - minBrightness) / (maxBrightness - minBrightness);
Color normalizedPixelColor = ColorConverter.ColorFromAhsb(pixelColor.A, pixelColor.GetHue(), pixelColor.GetSaturation(), normalizedPixelBrightness);
normalizedImage.SetPixel(x, y, normalizedPixelColor);
}
}
normalizedImage.Save("C:\\Users\\username\\Desktop\\test1.jpeg");
resultsViewBox.AppendText("Process completed.\n");
}
You are creating a new Bitmap and saving over the file for every pixel in your image. Move the
normalizedImage = new Bitmap(image.Width, image.Height);
line to before your loops, and the
normalizedImage.Save("C:\\Users\\username\\Desktop\\test1.jpeg");
line to after your loops.
Your normalization algorithm does not appear to be correct. Let's say your original color was red (255,0,0) Then your totalResult will be 65025, and your normalizedRed will be 255/sqrt(65025), which is 1, giving you a new normalized color of (1,0,0), which is essentially black.
Just as a note, your code will run a bit faster if you define all the doubles once outside the look and then assign them within the loop rather than defining and deleting each of the 8 doubles each iteration
Instead of messing with the colors you should use the brightness or luminosity factor to achieve normalization. Here is a link to the already answered question that can help you. you can convert each RGB pixel to HSL and minupulate L factor:
How do I normalize an image?
The code that you shared is actually a trim down version of HSL manipulation.
I am trying to create a binary image in C#/WPF using the WriteableBitmap class, with the BlackWhite Format which is 1 bit per pixel.
However, it seems the my image is very distorted when finished. Using different formats (such as brg32) works just fine. The pixel data is stored in a BitArray. The images vary from 1000x1000 to 3000x3000 pixels.
Here is the code I am current using:
unsafe
{
int colorOffset = 0;
int pixelOffset = 0;
byte color = 0;
int pBackBuffer = (int)_image.BackBuffer;
for (int y = 0; y < mapData.Height; y++)
{
for (int x = 0; x < mapData.Width; x++)
{
if (mapData.Data[y * mapData.Height + x])
{
//Set the pixel to white
color += 1;
}
//Shift the pixel position by 1
color = (byte)(color << 1);
//If 8 pixels have been written, write it to the backbuffer
if (++colorOffset == 8)
{
pixelOffset = ((y * mapData.Height) + x) / 8;
*(byte*)(pBackBuffer + pixelOffset) = color;
color = 0;
colorOffset = 0;
}
}
}
//Update the image
_image.AddDirtyRect(new Int32Rect(0, 0, mapData.Width, mapData.Height));
}
As you can see, I am writing 8 pixels / bits , and then copying it to the back buffer. Perhaps someone who has a bit more knowledge in this topic could help. I've also tried directly copying the BitArray to a byte array, then copy the byte array to the backbuffer (and using the WritePixels function as well), both of which have not helped.
Regards,
Dan
It seems like you are not using BackBufferStride property to compute address of the next line of pixels. Also note that you are missing some pixels if map width is not a multiple of 8. I didn't test the code, but i would have written it like this:
unsafe
{
int colorOffset = 0;
int pixelOffset = 0;
byte color = 0;
byte* pBackBuffer = (byte*)_image.BackBuffer;
for(int y = 0; y < mapData.Height; y++)
{
// get a pointer to first pixel in a line y
byte* pixLine = pBackBuffer;
for(int x = 0; x < mapData.Width; x++)
{
// fix #1: offset = y * width + x, not y * height + x
var mapOffset = y * mapData.Width + x;
if (mapData.Data[mapOffset])
{
//Set the pixel to white
color += 1;
}
//Shift the pixel position by 1
color = (byte)(color << 1);
//If 8 pixels have been written, write it to the backbuffer
if(++colorOffset == 8)
{
*pixLine++ = color;
color = 0;
colorOffset = 0;
}
}
// fix #2: copy any pixels left
if(colorOffset != 0)
{
*pixLine++ = color;
colorOffset = 0;
color = 0;
}
// fix #3: next line offset = previous line + stride, they are aligned
pBackBuffer += _image.BackBufferStride;
}
//Update the image
_image.AddDirtyRect(new Int32Rect(0, 0, mapData.Width, mapData.Height));
}