I used the example in openNI library called "SimpleViewer.net" to display images of kinect device with the library openNI.
Now, my aim is to save all the images that I display, I think that the place is:
lock (this)
{
Rectangle rect = new Rectangle(0, 0, this.bitmap.Width, this.bitmap.Height);
BitmapData data = this.bitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
ushort* pDepth = (ushort*)this.depth.DepthMapPtr.ToPointer();
// set pixels
for (int y = 0; y < depthMD.YRes; ++y)
{
byte* pDest = (byte*)data.Scan0.ToPointer() + y * data.Stride;
for (int x = 0; x < depthMD.XRes; ++x, ++pDepth, pDest += 3)
{
byte pixel = (byte)this.histogram[*pDepth];
pDest[0] = 0;
pDest[1] = pixel;
pDest[2] = pixel;
}
}
this.bitmap.UnlockBits(data);
}
before that I unlock the BitmapData....
I have not able to save a bmp image that containing the depth data.....
Thanks in advance
Related
I wrote some code to show an array of bytes as an image. There is an array of bytes in which every element represents a value of 8-bit gray scale image. Zero equals the most black and 255 does the most white pixel. My goal is to convert this w*w-pixel gray-scale image to some thing accepted by pictureBox1.Image.
This is my code:
namespace ShowRawImage
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0, j = 0, w = 256;
byte[] rawIm = new byte[256 * 256];
for(i = 0; i < w; ++i)
{
for (j = 0; j < w; ++j)
{
rawIm[i * w + j] = (byte)j; // BitConverter.GetBytes(j);
}
}
MemoryStream mStream = new MemoryStream();
mStream.Write(rawIm, 0, Convert.ToInt32(rawIm.Length));
Bitmap bm = new Bitmap(mStream, false);// the error occurs here
mStream.Dispose();
pictureBox1.Image = bm;
}
}
}
However I get this error:
Parameter is not valid.
The error snapshot
where is my mistake?
EDIT:
In next step I am going to display 16-bit grayscale images.
The Bitmap(Stream, bool) constructor expects a stream with an actual image format (eg. PNG, GIF, etc.) along with header, palette, and possibly compressed image data.
To create a Bitmap from raw data, you need to use the Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) constructor, but that is also quite inconvenient because you need a pinned raw data that you can pass as scan0.
The best if you just create an 8bpp bitmap with grayscale palette and set the pixels manually:
var bmp = new Bitmap(256, 256, PixelFormat.Format8bppIndexed);
// making it grayscale
var palette = bmp.Palette;
for (int i = 0; i < 255; i++)
palette.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = palette;
Now you can access its raw content as bytes where 0 is black and 255 is white:
var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < bitmapData.Height; y++)
{
for (int x = 0; x < bitmapData.Width; x++)
{
unsafe
{
((byte*) bitmapData.Scan0)[y * bitmapData.Stride + x] = (byte)x;
}
}
}
bmp.UnlockBits(bitmapData);
The result image:
But if you don't want to use unsafe code, or you want to set pixels by colors, you can use this library (disclaimer: written by me) that supports efficient manipulation regardless of the actual PixelFormat. Using that library the last block can be rewritten like this:
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
IWritableBitmapDataRow row = bitmapData.FirstRow;
do
{
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
} while (row.MoveNextRow());
}
Or like this, using Parallel.For (this works only because in your example all rows are the same so the image is a horizontal gradient):
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
Parallel.For(0, bitmapData.Height, y =>
{
var row = bitmapData[y];
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
});
}
As said in the comments - bitmap is not just an array. So to reach your goal you can create bitmap of needed size and set pixels with Bitmap.SetPixel:
Bitmap bm = new Bitmap(w, w);
for(var i = 0; i < w; ++i)
{
for (var j = 0; j < w; ++j)
{
bm.SetPixel(i,j, Color.FromArgb(j, j, j));
}
}
I have an array which consists in PixelData extracted from a Dicom Image.
Here's the code:
byte[] bytes = img.PixelData.GetFrame(0).Data; // img is the Dicom Image
int count = bytes.Length / 2;
ushort[] words = new ushort[count];
for (int i = 0, p = 0; i < count; i++, p += 2)
{
words[i] = BitConverter.ToUInt16(bytes, p);
}
pixels16 = words.ToList(); //pixels16 contains now the PixelData for the Grayscale image
Now, here's my question, how do I render that into a Picturebox??
My code for converting Bitmaps from Format16bppGrayScale to Format8bppIndexed format. PictureBox can easy show this format. (If you want, you can use different palette).
public Bitmap Gray16To8bppIndexed(Bitmap BmpIn)
{
if (BmpIn.PixelFormat != PixelFormat.Format16bppGrayScale)
throw new BadImageFormatException();
byte[] ImageData = new byte[BmpIn.Width * BmpIn.Height * 2];
Rectangle Re = new Rectangle(0, 0, BmpIn.Width, BmpIn.Height);
BitmapData BmpData = BmpIn.LockBits(Re, ImageLockMode.ReadOnly, BmpIn.PixelFormat);
Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length);
BmpIn.UnlockBits(BmpData);
byte[] ImageData2 = new byte[BmpIn.Width * BmpIn.Height];
for (long i = 0; i < ImageData2.LongLength; i++)
ImageData2[i] = ImageData[i * 2 + 1];
ImageData = null;
Bitmap BmpOut = new Bitmap(BmpIn.Width, BmpIn.Height, PixelFormat.Format8bppIndexed);
BmpData = BmpOut.LockBits(Re, ImageLockMode.WriteOnly, BmpOut.PixelFormat);
Marshal.Copy(ImageData2, 0, BmpData.Scan0, ImageData2.Length);
BmpOut.UnlockBits(BmpData);
ImageData2 = null;
BmpData = null;
ColorPalette GrayPalette = BmpOut.Palette;
Color[] GrayColors = GrayPalette.Entries;
for (int i = 0; i < GrayColors.Length; i++)
GrayColors[GrayColors.Length - 1 - i] = Color.FromArgb(i, i, i);
BmpOut.Palette = GrayPalette;
return BmpOut;
}
Well, I don't know the specifics, because it depends on how you really want to go about it (if performance is important, you need to create your own subclass of Bitmap, but otherwise, Bitmap.SetPixel would work fine).
But essentially, you need to shove those pixels into a Bitmap, then set the picture box's image to that bitmap, like:
Bitmap bitmap = new Bitmap(width, height);
for(int y = 0;y < height;y++)
for(int x = 0;x < width;x++)
bitmap.SetPixel(x,y, Color.fromRGB(/* unpack your R,G,B channel of your pixel here */);
pictureBox.Image = bitmap;
You can utilize the AForge .NET Framework, which is a great .NET library for image processing. The built-in .NET Picturebox could not nativley display images with System.Drawing.Imaging.PixelFormat.Format16bppGrayScale, but the AForge library has its own Picturebox control, check this out. It expects a .NET Image.
You can include AForge to your project easily with NuGet:
Install-Package AForge.Controls
Install-Package AForge.Imaging
Or just
Install-Package AForge
Example code below:
//SOME BYTES
//Load here the DICOM image
int width=640, height=480;
int numberOfPixels = width*height;
byte[] source = new byte[2*numberOfPixels];
//With AFORGE
var image = AForge.Imaging.UnmanagedImage.Create(width, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
IntPtr ptrToImage = image.ImageData;
//Copies the bytes from source to the image
//System.Runtime.InteropServices
Marshal.Copy(source, 0, ptrToImage,numberOfPixels);
//WITH .NET
System.Drawing.Bitmap bitmapImage = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var imageData = bitmapImage.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Marshal.Copy(source, 0, imageData.Scan0, numberOfPixels);
bitmapImage.UnlockBits(imageData);
Got this idea from a friend. The inputImage.ImageSource property is a 2D array with grayscale pixel values.
Bitmap grayscaleImage = new Bitmap(inputImage.ImageSource);
for (int x = 0; x < grayscaleImage.Width; x++)
{
for (int y = 0; y < grayscaleImage.Height; y++)
{
byte[,] tempMatrix = inputImage.ImageGrayscale;
byte temp = tempMatrix[x, y];
Color tempColor = Color.FromArgb(255, temp, temp, temp);
grayscaleImage.SetPixel(x, y, tempColor);
}
}
picboxDisplay.Image = grayscaleImage;
Suppose i had two nearly identical images and i wanted to locate and highlight the differences between them and produce the diff image. the routine works but this routine ask to supply color which i do not want. here is my code.
public class ImageTool
{
public static unsafe Bitmap GetDifferenceImage(Bitmap image1, Bitmap image2, Color matchColor)
{
if (image1 == null | image2 == null)
return null;
if (image1.Height != image2.Height || image1.Width != image2.Width)
return null;
Bitmap diffImage = image2.Clone() as Bitmap;
int height = image1.Height;
int width = image1.Width;
BitmapData data1 = image1.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData data2 = image2.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData diffData = diffImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
byte* data1Ptr = (byte*)data1.Scan0;
byte* data2Ptr = (byte*)data2.Scan0;
byte* diffPtr = (byte*)diffData.Scan0;
byte[] swapColor = new byte[3];
swapColor[0] = matchColor.B;
swapColor[1] = matchColor.G;
swapColor[2] = matchColor.R;
int rowPadding = data1.Stride - (image1.Width * 3);
// iterate over height (rows)
for (int i = 0; i < height; i++)
{
// iterate over width (columns)
for (int j = 0; j < width; j++)
{
int same = 0;
byte[] tmp = new byte[3];
// compare pixels and copy new values into temporary array
for (int x = 0; x < 3; x++)
{
tmp[x] = data2Ptr[0];
if (data1Ptr[0] == data2Ptr[0])
{
same++;
}
data1Ptr++; // advance image1 ptr
data2Ptr++; // advance image2 ptr
}
// swap color or add new values
for (int x = 0; x < 3; x++)
{
diffPtr[0] = (same == 3) ? swapColor[x] : tmp[x];
diffPtr++; // advance diff image ptr
}
}
// at the end of each column, skip extra padding
if (rowPadding > 0)
{
data1Ptr += rowPadding;
data2Ptr += rowPadding;
diffPtr += rowPadding;
}
}
image1.UnlockBits(data1);
image2.UnlockBits(data2);
diffImage.UnlockBits(diffData);
return diffImage;
}
}
calling like this way:
Bitmap diff = ImageTool.GetDifferenceImage(image1, image2, Color.Pink);
diff.MakeTransparent(Color.Pink);
diff.Save("C:\\test-diff.png",ImageFormat.Png);
some one just guide me how to change this routine as a result we do not have to pass color when i will call GetDifferenceImage() method.
this way image comparison is best technique if not then guide me how to develop a routine which can be more faster to get the diff image.
after getting the diff image how can i merge the diff image with image1. help me to develop a faster merge routine.
The diff image is black if two images are identical and has an increasing brightness for pixels with larger differences. You can just change the algorithm so that instead of assigning the pixel the swapcolor it assigns it the difference between the two colors.
// iterate over height (rows)
for (int i = 0; i < height; i++)
{
// iterate over width (columns)
for (int j = 0; j < width; j++)
{
// for each channel
for (int x=0; x<3; x++)
{
diffPtr[0] = Abs(data1Ptr[0]-data2Ptr[0]);
data1Ptr++; // advance image1 ptr
data2Ptr++; // advance image2 ptr
diffPtr++; // advance diff image ptr
}
}
// at the end of each column, skip extra padding
if (rowPadding > 0)
{
data1Ptr += rowPadding;
data2Ptr += rowPadding;
diffPtr += rowPadding;
}
}
How you show/merge the diff will depend on what you are going to do with it.
I was wondering if anyone could shed some light on improvements I can do in making this compositing algorithm faster. What is does is takes 3 images splits them up to get the 1st Images Red Channel, 2nd Images Green channel and the 3rd Images Blue channel and composites them together into 1 new image. Now it works but at an excruciatingly slow pace. The reason i think down to the pixel by pixel processing it has to do on all image components.
The process is to :
For all images:
Extract respective R G and B values -> composite into 1 image -> Save new Image.
foreach (Image[] QRE2ImgComp in QRE2IMGArray)
{
Globals.updProgress = "Processing frames: " + k + " of " + QRE2IMGArray.Count + " frames done.";
QRMProgressUpd(EventArgs.Empty);
Image RedLayer = GetRedImage(QRE2ImgComp[0]);
QRE2ImgComp[0] = RedLayer;
Image GreenLayer = GetGreenImage(QRE2ImgComp[1]);
QRE2ImgComp[1] = GreenLayer;
Image BlueLayer = GetBlueImage(QRE2ImgComp[2]);
QRE2ImgComp[2] = BlueLayer;
Bitmap composite = new Bitmap(QRE2ImgComp[0].Height, QRE2ImgComp[0].Width);
Color Rlayer,Glayer,Blayer;
byte R, G, B;
for (int y = 0; y < composite.Height; y++)
{
for (int x = 0; x < composite.Width; x++)
{
//pixelColorAlpha = composite.GetPixel(x, y);
Bitmap Rcomp = new Bitmap(QRE2ImgComp[0]);
Bitmap Gcomp = new Bitmap(QRE2ImgComp[1]);
Bitmap Bcomp = new Bitmap(QRE2ImgComp[2]);
Rlayer = Rcomp.GetPixel(x, y);
Glayer = Gcomp.GetPixel(x, y);
Blayer = Bcomp.GetPixel(x, y);
R = (byte)(Rlayer.R);
G = (byte)(Glayer.G);
B = (byte)(Blayer.B);
composite.SetPixel(x, y, Color.FromArgb((int)R, (int)G, (int)B));
}
}
Globals.updProgress = "Saving frame...";
QRMProgressUpd(EventArgs.Empty);
Image tosave = composite;
Globals.QRFrame = tosave;
tosave.Save("C:\\QRItest\\E" + k + ".png", ImageFormat.Png);
k++;
}
For reference here is the red channel filter method relatively the same for blue and green:
public Image GetRedImage(Image sourceImage)
{
Bitmap bmp = new Bitmap(sourceImage);
Bitmap redBmp = new Bitmap(sourceImage.Width, sourceImage.Height);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
Color pxl = bmp.GetPixel(x, y);
Color redPxl = Color.FromArgb((int)pxl.R, 0, 0);
redBmp.SetPixel(x, y, redPxl);
}
}
Image tout = (Image)redBmp;
return tout;
}
Move these
Bitmap Rcomp = new Bitmap(QRE2ImgComp[0]);
Bitmap Gcomp = new Bitmap(QRE2ImgComp[1]);
Bitmap Bcomp = new Bitmap(QRE2ImgComp[2]);
outside the for-loops!
Other very important points:
avoid using GetPixel - it is VERY SLOW!
Checkout LockBits etc. - this is how pixel-level access is usually done in .NET
Consider using a 3rd-party library (free or commercial)... several have some optimized method built-in to do what you are trying to achieve...
I totally agree with the points Yahia listed in his answer to improve performance. I'd like to add one more point regarding performance. You could use the Parallel class of the .Net Framework to parallelize the execution of your for loops. The following example makes use of the LockBits method and the Parallel class to improve performance (assuming 32 bits per pixel (PixelFormat.Format32bppArgb)):
public unsafe static Bitmap GetBlueImagePerf(Image sourceImage)
{
int width = sourceImage.Width;
int height = sourceImage.Height;
Bitmap bmp = new Bitmap(sourceImage);
Bitmap redBmp = new Bitmap(width, height, bmp.PixelFormat);
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
BitmapData bd2 = redBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
byte* source = (byte*)bd.Scan0.ToPointer();
byte* target = (byte*)bd2.Scan0.ToPointer();
int stride = bd.Stride;
Parallel.For(0, height, (y1) =>
{
byte* s = source + (y1 * stride);
byte* t = target + (y1 * stride);
for (int x = 0; x < width; x++)
{
// use t[1], s[1] to access green channel
// use t[2], s[2] to access red channel
t[0] = s[0];
t += 4; // Add bytes per pixel to current position.
s += 4; // For other pixel formats this value is different.
}
});
bmp.UnlockBits(bd);
redBmp.UnlockBits(bd2);
return redBmp;
}
public unsafe static void DoImageConversion()
{
Bitmap RedLayer = GetRedImagePerf(Image.FromFile("image_path1"));
Bitmap GreenLayer = GetGreenImagePerf(Image.FromFile("image_path2"));
Bitmap BlueLayer = GetBlueImagePerf(Image.FromFile("image_path3"));
Bitmap composite =
new Bitmap(RedLayer.Width, RedLayer.Height, RedLayer.PixelFormat);
BitmapData bd = composite.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
byte* comp = (byte*)bd.Scan0.ToPointer();
BitmapData bdRed = RedLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bdGreen = GreenLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bdBlue = BlueLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* red = (byte*)bdRed.Scan0.ToPointer();
byte* green = (byte*)bdGreen.Scan0.ToPointer();
byte* blue = (byte*)bdBlue.Scan0.ToPointer();
int stride = bdRed.Stride;
Parallel.For(0, bdRed.Height, (y1) =>
{
byte* r = red + (y1 * stride);
byte* g = green + (y1 * stride);
byte* b = blue + (y1 * stride);
byte* c = comp + (y1 * stride);
for (int x = 0; x < bdRed.Width; x++)
{
c[0] = b[0];
c[1] = g[1];
c[2] = r[2];
r += 4; // Add bytes per pixel to current position.
g += 4; // For other pixel formats this value is different.
b += 4; // Use Image.GetPixelFormatSize to get number of bits per pixel
c += 4;
}
});
composite.Save("save_image_path", ImageFormat.Jpeg);
}
Hope, this answer gives you a starting point for improving your code.
I have a class that creates a matrix of Color values by reading a Bitmap. The class directly reads each byte in the pixels inside an unsafe block using the pointer to the image. The purpose of the class is to read the pixel values into memory where I can run filters on them before saving the image as a new file.
I can recreate the image using GDI+'s setPixel() method, however it is too slow for my needs.
I am attempting to save a new image file using the following function:
public void saveImageFromPixels()
{
this.newBitmap = new Bitmap(srcBitmap.Width, srcBitmap.Height);
BitmapData imgData = newBitmap.LockBits(new Rectangle(0, 0, newBitmap.Width, newBitmap.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
int stride = imgData.Stride;
System.IntPtr Scan0 = imgData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - newBitmap.Width * 3;
for (int x = 0; x < newBitmap.Height; ++x)
{
for (int y = 0; y < newBitmap.Width; ++y)
{
p[0] = (byte)(255 - matrix[y][x].B);
p[1] = (byte)(255 - matrix[y][x].G);
p[2] = (byte)(255 - matrix[y][x].R);
p += 3;
}
p += nOffset;
}
}
this.newBitmap.Save(#"C:\images\1-d.jpg");
}
However the result is an empty image (with the proper dimensions). The code that directly accesses the pixels and saves the value as a Color works fine, it is just saving the image I am having problems with.
The following code defines srcBitmap and newBitmap
private Bitmap srcBitmap;
private Bitmap newBitmap;
private List<List<Color>> matrix;
public PixelMatrix(string path)
{
this.srcBitmap = new Bitmap(path);
this.matrix = new List<List<Color>>(srcBitmap.Width);
for (int x = 0; x < srcBitmap.Width; x++)
{
this.matrix.Add(new List<Color>(srcBitmap.Height));
}
}
You need to UnlockBits().