Converting a CImg data struct to .Net bitmap class - c#

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!! )

Related

Image processing : How to perform this function with lockbits

I have a problem. I need to perform this function with lockbits. Please I need help.
public void xPix(Bitmap bmp, int n, Color cx, Color nx)
{
try
{
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x += (n * 2))
{
cx = bmp.GetPixel(x, y);
if (x + n <= bmp.Width - 1) nx = bmp.GetPixel(x + n, y);
bmp.SetPixel(x, y, nx);
if (x + n <= bmp.Width - 1) bmp.SetPixel(x + n, y, cx);
}
}
}
catch { }
}
There were lots of things that didn't make sense to me about your code. I fixed the pieces that were preventing an image from appearing and here is the result. I will explain my changes after the code.
public void xPix(Bitmap bmp, int n, Color cx, Color nx)
{
var img = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte[] bmpBytes = new byte[Math.Abs(img.Stride) * img.Height];
System.Runtime.InteropServices.Marshal.Copy(img.Scan0, bmpBytes, 0, bmpBytes.Length);
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x+=n*2)
{
cx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
if (x + n <= img.Width - 1) nx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
BitConverter.GetBytes(nx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + x * 4);
if (x + n <= img.Width - 1) BitConverter.GetBytes(cx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + (x + n) * 4);
}
}
System.Runtime.InteropServices.Marshal.Copy(bmpBytes, 0, img.Scan0, bmpBytes.Length);
bmp.UnlockBits(img);
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Bitmap bmp = new Bitmap(#"C:\Users\bluem\Downloads\Default.png");
for (int i = 0; i < bmp.Width; i++)
{
xPix(bmp, new Random().Next(20) + 1, System.Drawing.Color.White, System.Drawing.Color.Green);
}
Canvas.Image = bmp;
}
There's no such class as LockBitmap so I replaced it with the result of a call to Bitmap.LockBits directly.
The result of LockBits does not include functions for GetPixel and SetPixel, so I did what one normally does with the result of LockBits (see https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.7.2) and copied the data into a byte array instead.
When accessing the byte data directly, some math must be done to convert the x and y coordinates into a 1-dimensional coordinate within the array of bytes, which I did.
When accessing the byte data directly under the System.Drawing.Imaging.PixelFormat.Format32bppArgb pixel format, multiple bytes must be accessed to convert between byte data and a pixel color, which I did with BitConverter.GetBytes, BitConverter.ToInt32, Color.FromArgb and Color.ToArgb.
I don't think it's a good idea to be changing the Image in the middle of painting it. You should either be drawing the image directly during the Paint event, or changing the image outside the Paint event and allowing the system to draw it. So I used the OnClick of my form to trigger the function instead.
The first random number I got was 0, so I had to add 1 to avoid an endless loop.
The cx and nx parameters never seem to be used as inputs, so I put arbitrary color values in for them. Your x and y variables were not defined/declared anywhere.
If you want faster on-image-action, you can use Marshall.Copy method with Parallel.For
Why dont use GetPixel method? Because every time you call it, your ALL image is loaded to memory. GetPixel get one pixel, and UNLOAD all image. And in every iteration, ALL image is loaded to memory (for example, if u r working on 500x500 pix image, GetPixel will load 500x500 times whole pixels to memory). When you work on images with C# (CV stuff), work on raw bytes from memory.
I will show how to use with Lockbits in Binarization because its easy to explain.
int pixelBPP = Image.GetPixelFormatSize(resultBmp.PixelFormat) / 8;
unsafe
{
BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), ImageLockMode.ReadWrite, resultBmp.PixelFormat);
byte* ptr = (byte*)bmpData.Scan0; //addres of first line
int height = resultBmp.Height;
int width = resultBmp.Width * pixelBPP;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for(int x = 0; x < width; x = x + pixelBPP)
{
byte value = (offset[x] + offset[x + 1] + offset[x + 2]) / 3 > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x] = value;
offset[x + 1] = value;
offset[x + 2] = value;
if (pixelBPP == 4)
{
offset[x + 3] = 255;
}
}
});
resultBmp.UnlockBits(bmpData);
}
Now, example with Marshall.copy:
BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height),
ImageLockMode.ReadWrite,
resultBmp.PixelFormat
);
int bytes = bmpData.Stride * resultBmp.Height;
byte[] pixels = new byte[bytes];
Marshal.Copy(bmpData.Scan0, pixels, 0, bytes); //loading bytes to memory
int height = resultBmp.Height;
int width = resultBmp.Width;
Parallel.For(0, height - 1, y => //seting 2s and 3s
{
int offset = y * stride; //row
for (int x = 0; x < width - 1; x++)
{
int positionOfPixel = x + offset + pixelFormat; //remember about pixel format!
//do what you want with pixel
}
}
});
Marshal.Copy(pixels, 0, bmpData.Scan0, bytes); //copying bytes to bitmap
resultBmp.UnlockBits(bmpData);
Remember, when you warking with RAW bytes very important is to remember about PixelFormat. If you work on RGBA image, you need to set up every channel. (for example offset + x + pixelFormat). I showed it in Binarization example, how to deak with RGBA image with raw data. If lockbits are not fast enough, use Marshall.Copy

Why AccessViolationException occurs when accessing pixels in WriteableBitmap Image?

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 have a function that creates histogram of each Bitmap. How can I create another 3 histograms for R.G.B of each Bitmap?

This is the histogram function I'm using today and if I'm not wrong it's creating an histogram by Gray color.
What i want is another function that will return me 3 histograms of each Bitmap:
The first histogram will be of the Red color of the bitmap the second for the Green color and the last one for the Blue color.
public static long[] GetHistogram(Bitmap b)
{
long[] myHistogram = new long[256];
BitmapData bmData = null;
try
{
//Lock it fixed with 32bpp
bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int scanline = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nWidth = b.Width;
int nHeight = b.Height;
for (int y = 0; y < nHeight; y++)
{
for (int x = 0; x < nWidth; x++)
{
long Temp = 0;
Temp += p[0]; // p[0] - blue, p[1] - green , p[2]-red
Temp += p[1];
Temp += p[2];
Temp = (int)Temp / 3;
myHistogram[Temp]++;
//we do not need to use any offset, we always can increment by pixelsize when
//locking in 32bppArgb - mode
p += 4;
}
}
}
b.UnlockBits(bmData);
}
catch
{
try
{
b.UnlockBits(bmData);
}
catch
{
}
}
return myHistogram;
}
How may I do it ?
In the part where you specify
Temp += p[0]
...
put the three values into separate histograms:
histB[p[0]]++;
histG[p[1]]++;
histR[p[2]]++;
You can use jagged array(array of arrays) where values from p[0],p[1],p[2] may put into the jagged array. and then work with the indexed values of the jagged array.
Hope this helps

How to save an image from RGB pixels array?

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

How can I speed up this histogram class?

This is supposed to calculate the histogram of an 8-bit grayscale image. With a 1024x770 test bitmap, CreateTime ends up at around 890ms. How can I make this go (way, way) faster?
EDIT: I should mention that this doesn't actually compute the histogram yet, it only gets the values out of the bitmap. So I really should have asked, what is the fastest way to retrieve all pixel values from an 8-bit grayscale image?
public class Histogram {
private static int[,] values;
public Histogram(Bitmap b) {
var sw = Stopwatch.StartNew();
values = new int[b.Width, b.Height];
for (int w = 0; w < b.Width; ++w) {
for (int h = 0; h < b.Height; ++h) {
values[w, h] = b.GetPixel(w, h).R;
}
}
sw.Stop();
CreateTime = (sw.ElapsedTicks /
(double)Stopwatch.Frequency) * 1000;
}
public double CreateTime { get; set; }
}
The basic histogram algorithm is something like:
int[] hist = new hist[256];
//at this point dont forget to initialize your vector with 0s.
for(int i = 0; i < height; ++i)
{
for(int j = 0 ; j < widthl ++j)
{
hist[ image[i,j] ]++;
}
}
The algorithm sums how many pixels with value 0 you have, how many with value=1 and so on.
The basic idea is to use the pixel value as the index to the position of the histogram where you will count.
I have one version of this algorithm written for C# using unmanaged code (which is fast) I dont know if is faster than your but feel free to take it and test, here is the code:
public void Histogram(double[] histogram, Rectangle roi)
{
BitmapData data = Util.SetImageToProcess(image, roi);
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
return;
if (histogram.Length < Util.GrayLevels)
return;
histogram.Initialize();
int width = data.Width;
int height = data.Height;
int offset = data.Stride - width;
unsafe
{
byte* ptr = (byte*)data.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptr)
histogram[ptr[0]]++;
ptr += offset;
}
}
image.UnlockBits(data);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}
I hope I could help you.
You'll want to use the Bitmap.LockBits method to access the pixel data. This is a good reference on the process. Essentially, you're going to need to use unsafe code to iterate over the bitmap data.
Here's a copy/pastable version of the function I've come up w/ based on on this thread.
The unsafe code expects the bitmap to be Format24bppRgb, and if it's not, it'll convert the bitmap to that format and operate on the cloned version.
Note that the call to image.Clone() will throw if you pass in a bitmap using an indexed pixel format, such as Format4bppIndexed.
Takes ~200ms to get a histogram from an image 9100x2048 on my dev machine.
private long[] GetHistogram(Bitmap image)
{
var histogram = new long[256];
bool imageWasCloned = false;
if (image.PixelFormat != PixelFormat.Format24bppRgb)
{
//the unsafe code expects Format24bppRgb, so convert the image...
image = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format24bppRgb);
imageWasCloned = true;
}
BitmapData bmd = null;
try
{
bmd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
const int pixelSize = 3; //pixels are 3 bytes each w/ Format24bppRgb
//For info on locking the bitmap bits and finding the
//pixels using unsafe code, see http://www.bobpowell.net/lockingbits.htm
int height = bmd.Height;
int width = bmd.Width;
int rowPadding = bmd.Stride - (width * pixelSize);
unsafe
{
byte* pixelPtr = (byte*)bmd.Scan0;//starts on the first row
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
histogram[(pixelPtr[0] + pixelPtr[1] + pixelPtr[2]) / 3]++;
pixelPtr += pixelSize;//advance to next pixel in the row
}
pixelPtr += rowPadding;//advance ptr to the next pixel row by skipping the padding # the end of each row.
}
}
}
finally
{
if (bmd != null)
image.UnlockBits(bmd);
if (imageWasCloned)
image.Dispose();
}
return histogram;
}

Categories

Resources