I have a program which gets every pixel in an image and outputs them into a text document. The problem is that it just throws them in there are as numbers and it looks rather ugly.
e.i. " 37 37 37 36" I wish to display them much like the GetPixel function does.
e.i. [A=255, R=4, G=255, B=131]. My current code is...
Bitmap bmp = new Bitmap("Space big.jpg");
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; //Sets pointer
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
StringBuilder Pix = new StringBuilder(" ");
Stopwatch Timer = new Stopwatch();
pictureBox1.Image = bmp;
StringBuilder EachPixel = new StringBuilder("");
Timer.Start();
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
// compute the proper offset into the array for these co-ords
var pixel = rgbValues[i + j * Math.Abs(bmpData.Stride)];
Pix.Append(" ");
Pix.Append(pixel);
}
}
Related
I am trying to use Format16bppGrayScale, but I keep getting an error: "a generic error occurred in gdi+". I have a monochromatic camera that is giving me a 16-bit value and I want to store it in Format16bppGrayScale so I can keep my image from the camera. the 16-bit value is coming in an array that is two array indexes per one 16 bit value
public Bitmap ByteToImage1(byte[] blob)
{
Bitmap bmpRGB = new Bitmap(KeepWidth, KeepHeight, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Rectangle rect = new Rectangle(0, 0, KeepWidth, KeepHeight);
BitmapData bmpData = bmpRGB.LockBits(rect, ImageLockMode.WriteOnly, bmpRGB.PixelFormat);
int padding = bmpData.Stride - 3 * KeepWidth;
unsafe
{
int i = 0;
byte* ptr = (byte*)bmpData.Scan0;
for( int y= 0; y < KeepHeight; y++)
{
for(int x = 0; x < KeepWidth; x++)
{
ptr[1] = blob[i+1];
ptr[0] = blob[i];
i = i + 2;
ptr += 2;
}
ptr += padding;
}
}
bmpRGB.UnlockBits(bmpData);
return bmpRGB;
}
Update: Karsten's code
private Bitmap GenerateDummy16bitImage(byte[] temp)
{
int i2 = 0;
Bitmap b16bpp = new Bitmap(KeepWidth, KeepHeight, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, KeepWidth, KeepHeight);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * KeepHeight;
var bitmapBytes = new short[KeepWidth * KeepHeight];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < KeepWidth; x++)
{
for (int y = 0; y < KeepHeight; y++)
{
var i = ((y * KeepWidth) + x); // 16bpp
// Generate the next random pixel color value.
var value = (short)temp[i2]; //(short)random.Next(5);
bitmapBytes[i] = value; // GRAY
i2++;
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
return b16bpp;
}
hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.
Okay, I've created two programs. One that uses GetPixels and another that uses LockBits. My GetPixels program is as follows...
The stripe picture referred is a 200x200 jpg
Stopwatch GetTime = new Stopwatch();
Bitmap img = new Bitmap("stripe.jpg");
GetTime.Start();
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color pixel = img.GetPixel(i, j);
output += " " + pixel;
}
}
GetTime.Stop();
Now this one reads out about 20 secs to process this image and output all the pixels. Great, but my LockBits one should, theoretically be faster. My code for the LockBits is...
Bitmap bmp = new Bitmap("stripe.jpg");
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, Pixels); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; //Scans the first line of data
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
string Pix = " ";
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
Stopwatch Timer = new Stopwatch();
pictureBox1.Image = bmp;
Timer.Start();
for (int p = 0; p < DataBytes; p++)
{
Pix += " " + rgbValues[p];
}
Timer.Stop();
and the time on that is 37secs. Now I dont understand why my time is longer for the Lockbits than it is for the GetPixels.
Also my output files don't match up in terms of where they are listed. It is almost as if they are out of order.
This is a big problem to tackle so thank you all in advance for reading and trying to solve my problem.
You have a few problems that I can see. The biggest issue is that your image has a width of 200, but in memory, its stride is 600 (for me - probably similar for you). This means you are writing out a lot more data, because you don't ignore the 400 padding pixels per row.
Other issues:
You're timing string concatenation only. By the time you start your timer, the lockbits stuff has finished.
Your string concatenation would be faster using StringBuilder.
You are locking the bitmap for read/write access, when you only need read. No discernible effect on performance for me, with this image, but still might as well change it to ReadOnly.
Most of your comments are unnecessary at best (// creates array)- some are misleading (//Scans the first line of data - no, it returns a pointer to the data which has already been loaded).
The following code completes in only a few milliseconds on my machine.
Bitmap bmp = new Bitmap(#"d:\stripe.jpg");
//pictureBox1.Image = bmp;
Stopwatch Timer = new Stopwatch();
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
bmpRec, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr Pointer = bmpData.Scan0;
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[DataBytes];
Marshal.Copy(Pointer, rgbValues, 0, DataBytes);
bmp.UnlockBits(bmpData);
StringBuilder pix = new StringBuilder(" ");
Timer.Start();
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
// compute the proper offset into the array for these co-ords
var pixel = rgbValues[i + j*Math.Abs(bmpData.Stride)];
pix.Append(" ");
pix.Append(pixel);
}
}
Timer.Stop();
Console.WriteLine(Timer.Elapsed);
Please see my code below.
I want to create a Byte array with data that I can convert into a real image. When I try to run this code I get an argumentException. What do I need to do in the For loop in order to create a legitimate Byte array that will hold data of an image? I don't want to use a real image and convert it to byte array, I want to create an image form random numbers.
Random Rnd = new Random();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Byte[] ByteArray = new Byte[1000];
for (int i = 0; i < 1000; i++)
{
ByteArray[i] = Convert.ToByte(Rnd.Next(9));
}
ImageConverter Convertor = new ImageConverter();
BitmapImage image = (BitmapImage)Convertor.ConvertFrom(ByteArray);
MyImage.Source = image;
}
Notice please that I don't want to work with WinForms types or libraries like system.drawing / bitmap - I only want to use WPF technology.
This is the solution you are looking for, using only WPF technology.
Note that the constant value of 16 used in the stride parameter calculation comes directly from the fact that I am using a 16-bit pixel format.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
Byte[] ByteArray = new Byte[(int)MyImage.Width * (int)MyImage.Height * 3];
rnd.NextBytes(ByteArray);
var image = BitmapSource.Create((int) MyImage.Width, (int) MyImage.Height, 72, 72,
PixelFormats.Bgr565, null, ByteArray, (4*((int)MyImage.Width * 16 + 31)/32));
MyImage.Source = image;
}
This just might do the trick for you:
private static Bitmap GenBitmap(int width, int height) {
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
Random rnd = new Random();
int imageByteSize = width * height * ch;
byte[] imageData = new byte[imageByteSize]; //your image data buffer
rnd.NextBytes(imageData); //Fill with random bytes;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageByteSize);
bitmap.UnlockBits(bmData);
return bitmap;
}
I'm not sure how Converter.ConvertFrom works but I prefer to do my bitmaps the lower-level way with Bitmap.LockBits() and a little Marshal.Copy().
See this method:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
static Bitmap CreateRandomBitmap(Size size)
{
// Create a new bitmap for the size requested.
var bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
// Lock the entire bitmap for write-only acccess.
var rect = new Rectangle(0, 0, size.Width, size.Height);
var bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * size.Height;
var bitmapBytes = new byte[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < size.Width; x++)
{
for (int y = 0; y < size.Height; y++)
{
// Get the index of the byte for this pixel (x/y).
var i = ((y * size.Width) + x) * 4; // 32bpp
// Generate the next random pixel color value.
var value = (byte)random.Next(9);
bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);
// Unlock the bitmap, we're all done.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
Then you can do something like this:
public void Run()
{
using(var bitmap = CreateRandomBitmap(new Size(64, 64)))
{
bitmap.Save("random.png", ImageFormat.Png);
}
}
You can't use random bytes to create an image, because each type of image (bmp, jpeg, png) is constructed with a certain format. The code wouldn't know how to interpret random bytes.
http://en.wikipedia.org/wiki/Image_file_formats
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;