UPDATED:
Been looking around and trying to figure out what alternative there is for windows phone 7.1 for BitmapData. I have Commented out the code in question. I am aware of Lockbits and that its fast in comparison to get set pixels and so on.
As per my understanding, BitmapData Locks the image to memory ready for manipulation.
BmpData.Scan0 acts as a pointer to the memory.
If I were to do this without BitmapData, say Get.Pixel and map it to memory. and manipulate some of image data with Set.Pixel?
P.S: In regards to processing speed; I am not looking to change alot of pixels.
public int Edit(Bitmap BmpIn, byte[] BIn, byte BitsPerByte)
{
int LengthBytes = 1 + 31 / BitsPerByte;
int TextLength = 1 + (8 * BIn.Length - 1) / BitsPerByte;
//BitmapData BmpData = BmpIn.LockBits(new Rectangle(0, 0, BmpIn.Width, BmpIn.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
RGB = new byte[2 + LengthBytes + TextLength];
//Marshal.Copy(BmpData.Scan0, RGB, 0, RGB.Length);
InsertBitsPerByte(BitsPerByte);
SetMasks(BitsPerByte);
InsertLength(LengthBytes, TextLength, BitsPerByte);
InsertBytes(BIn, BitsPerByte);
//Marshal.Copy(RGB, 0, BmpData.Scan0, RGB.Length);
BmpIn.UnlockBits(BmpData);
return TextLength;
}
Any help appreciated.
Thanks
Have a look at WriteableBitmapEx. This will allow you to do pixel manipulation within an image.
Related
I have an image sensor board for embedded development for which I need to capture a stream of images and output them in 8-bit monochrome / grayscale format. The imager output is 12-bit monochrome (which takes 2 bytes per pixel).
In the code, I have an IntPtr to a memory buffer that has the 12-bit image data, from which I have to extract and convert that data down to an 8-bit image. This is represented in memory something like this (with a bright light activating the pixels):
As you can see, every second byte contains the LSB that I want to discard, thereby keeping only the odd-numbered bytes (to put it another way). The best solution I can conceptualize is to iterate through the memory, but that's the rub. I can't get that to work. What I need help with is an algorithm in C# to do this.
Here's a sample image that represents a direct creation of a Bitmap object from the IntPtr as follows:
bitmap = new Bitmap(imageWidth, imageHeight, imageWidth, PixelFormat.Format8bppIndexed, pImage);
// Failed Attempt #1
unsafe
{
IntPtr pImage; // pointer to buffer containing 12-bit image data from imager
int i = 0, imageSize = (imageWidth * imageHeight * 2); // two bytes per pixel
byte[] imageData = new byte[imageSize];
do
{
// Should I bitwise shift?
imageData[i] = (byte)(pImage + i) << 8; // Doesn't compile, need help here!
} while (i++ < imageSize);
}
// Failed Attempt #2
IntPtr pImage; // pointer to buffer containing 12-bit image data from imager
imageSize = imageWidth * imageHeight;
byte[] imageData = new byte[imageSize];
Marshal.Copy(pImage, imageData, 0, imageSize);
// I tried with and without this loop. Neither gives me images.
for (int i = 0; i < imageData.Length; i++)
{
if (0 == i % 2) imageData[i / 2] = imageData[i];
}
Bitmap bitmap;
using (var ms = new MemoryStream(imageData))
{
bitmap = new Bitmap(ms);
}
// This also introduced a memory leak somewhere.
Alternatively, if there's a way to do this with a Bitmap, byte[], MemoryStream, etc. that works, I'm all ears, but everything I've tried has failed.
Here is the algorithm that my coworkers helped formulate. It creates two new (unmanaged) pointers; one 8-bits wide and the other 16-bits.
By stepping through one word at a time and shifting off the last 4 bits of the source, we get a new 8-bit image with only the MSBs. Each buffer has the same number of words, but since the words are different sizes, they progress at different rates as we iterate over them.
unsafe
{
byte* p_bytebuffer = (byte*)pImage;
short* p_shortbuffer = (short*)pImage;
for (int i = 0; i < imageWidth * imageHeight; i++)
{
*p_bytebuffer++ = (byte)(*p_shortbuffer++ >> 4);
}
}
In terms of performance, this appears to be very fast with no perceivable difference in framerate.
Special thanks to #Herohtar for spending a substantial amount of time in chat with me attempting to help me solve this.
I am trying to convert an object to an image.
I grab the object from a usb3 camera using the following:
object RawData = axActiveUSB1.GetImageWindow(0,0,608,608);
This returns a Variant (SAFEARRAY)
After reviewing further, RawData = {byte[1824, 608]}. The image is 608 x 608 so I'm guessing 1824 is 3 times the size due to the RGB component of the image.
The camera's pixel format is BayerRGB8 and according to the API I am using the data type is represented in Bytes:
Camera Pixel Format | Output Format | Data type | Dimensions
Bayer8 | 24-bit RGB | Byte | 0 to SizeX * 3 - 1, 0 to Lines - 1
I can convert it to a bytes array using this code found at Convert any object to a byte[]
private byte[] ObjectToByteArray(Object obj)
{
if (obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
From here, I then do: (all of this code also found or derived from info on stack)
// convert object to bytes
byte[] imgasbytes = ObjectToByteArray(RawData);
// create a bitmap and put data in it to go into the picturebox
var bitmap = new Bitmap(608, 608, PixelFormat.Format24bppRgb);
var bitmap_data = bitmap.LockBits(new Rectangle(0, 0,bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
Marshal.Copy(imgasbytes, 0, bitmap_data.Scan0, imgasbytes.Length );
bitmap.UnlockBits(bitmap_data);
var result = bitmap as Image; // this line not even really necessary
PictureBox.Image = result;
The code works, but I should see this:
But I see this:
I've done this in Python and had similar issues which I was able to resolve, but I'm not as strong in c# and am unable to progress from here. I need to rotate my image 90 degrees, but also I think that my issue relates to incorrectly converting the array. I think that I need to convert my object (SAFEARRAY) to a multidimensional array so that the RGB sits on top of one another. I have looked at many examples on how to do this, but I do not understand how to go about it.
Any feedback is greatly appreciated on what I am doing wrong.
EDIT
I'm looking at this -> Convert RGB8 byte[] to Bitmap
which may be related to my issue.
It looks like the issue was exactly as I described.
In the end, the main issue was that the array needed to be rotated.
I found a solution here ->
Rotate M*N Matrix (90 degrees)
When I rotated the image, It resolved the picture issue that I was seeing above. While my image is inverted now, I understand the issue and as a result, am not seeing the problem any more.
Here is the code in case anyone runs into the same issue
byte[,] newImageAsBytes = new byte[ImageAsBytes.GetLength(1), ImageAsBytes.GetLength(0)];
int newColumn, newRow = 0;
for (int oldColumn = ImageAsBytes.GetLength(1) - 1; oldColumn >= 0; oldColumn--)
{
newColumn = 0;
for (int oldRow = 0; oldRow < ImageAsBytes.GetLength(0); oldRow++)
{
newImageAsBytes[newRow, newColumn] = ImageAsBytes[oldRow, oldColumn];
newColumn++;
}
newRow++;
}
byte[] b = ObjectToByteArray(newImageAsBytes);
var bitmap = new Bitmap(608, 608, PixelFormat.Format24bppRgb); // 608 is my image size and I am working with a camera that uses BayerRGB8
var bitmap_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
Marshal.Copy(b, 0, bitmap_data.Scan0, b.Length);
bitmap.UnlockBits(bitmap_data);
var result = bitmap as Image; // this line not even really necessary
PictureBox.Image = result;
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(C:\Users\112\AppData\Local\Temp\113837.dcm))
{
// obtain the XResolution and YResolution TIFFTAG values
PropertyItem piXRes = bmp.GetPropertyItem(282);
PropertyItem piYRes = bmp.GetPropertyItem(283);
// values are stored as a rational number - numerator/denominator pair
numerator = BitConverter.ToInt32(piXRes.Value, 0);
denominator = BitConverter.ToInt32(piXRes.Value, 4);
float xRes = numerator / denominator;
numerator = BitConverter.ToInt32(piYRes.Value, 0);
denominator = BitConverter.ToInt32(piYRes.Value, 4);
float yRes = numerator / denominator;
// now set the values
byte[] numeratorBytes = new byte[4];
byte[] denominatorBytes = new byte[4];
numeratorBytes = BitConverter.GetBytes(600); // specify resolution in numerator
denominatorBytes = BitConverter.GetBytes(1);
Array.Copy(numeratorBytes, 0, piXRes.Value, 0, 4); // set the XResolution value
Array.Copy(denominatorBytes, 0, piXRes.Value, 4, 4);
Array.Copy(numeratorBytes, 0, piYRes.Value, 0, 4); // set the YResolution value
Array.Copy(denominatorBytes, 0, piYRes.Value, 4, 4);
bmp.SetPropertyItem(piXRes); // finally set the image property resolution
bmp.SetPropertyItem(piYRes);
bmp.SetResolution(600, 600); // now set the bitmap resolution
bmp.Save(#"C:\output.tif"); // save the image
}
I'm getting an "Out of memory" error on the line using (Bitmap bmp = .... How can I solve that?
The "out of memory" is misleading. It really means that the image format can not be determined by .Net.
Sorry but .Net does not support DICOM images. See http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.aspx for info on supported image formats.
With this line...
(Bitmap)Bitmap.FromFile(C:\Users\112\AppData\Local\Temp\113837.dcm)
...you are reading the whole raw data contained in a dicom file. That includes the Dicom Data Elements (fields containing information).
Extracting the image data is much more complicated than this. You should begin looking for some information about the Dicom format.
Other good sources of information to start can be found on Dabsoft and Medical Connections and, of course, on the David Clunie's website.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Convert a image to a monochrome byte array
I have a monochrome bitmap image. I load the image like this:
Image image = Image.FromFile("myMonoChromeImage.bmp");
How can I get a binary array, where 1s represent white pixels and 0s represent black pixels, or vice versa? (The first bit in the array is the top-left pixel and the last bit in array is the bottom-right pixel)
If possible, an efficient approach would be appreciated.
You can use LockBits to get access to the bitmap data and copy the values directly from the bitmap array. GetPixel essentially locks and unlocks the bitmap each time so it's not efficient.
You can extract the data to a byte array and then check the RGBA values to see if they are white (255,255,255,255) or black (0,0,0,255)
The BitmapData class sample shows how to do this. In your case the code would be something like this:
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
So, I am very confused over a quick test that I just ran. I am doing some image processing in C#. Get/SetPixel() have proven to be too slow, so I am using LockBits to get at the raw data.
However, I seem to have hit a situation which I can't figure out. While scanning the image, it seems that each pixel is laid out as Bgra, that is, blue byte, green byte, red byte, and alpha, in that order. I was under the impression that they would be laid out in Argb order. here is a sample of the code that I am using.
BitmapData baseData =
m_baseImage.LockBits(new Rectangle(new Point(0, 0), m_baseImage.Size),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Bitmap test = new Bitmap(m_baseImage.Width, m_baseImage.Height);
byte* ptr = (byte*)baseData.Scan0;
for (int y = 0; y < m_baseImage.Height; ++y)
{
for (int x = 0; x < m_baseImage.Width; ++x)
{
// this works, image is copied correctly
Color c1 = Color.FromArgb(*(ptr + 3), *(ptr + 2), *(ptr + 1), *ptr);
// below does not work! Bytes are reversed.
//Color c1 = Color.FromArgb(*ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3));
test.SetPixel(x, y, c1);
ptr += 4;
}
}
m_baseImage.UnlockBits(baseData);
pictureBox1.Image = m_baseImage;
pictureBox2.Image = test;
The first line which grabs the color of the base image works, the second does not. I am pretty sure that I am missing something very obvious here.
Not only are the colors reversed BGRA, but the rows are reversed as well - the bottom of the image is the first in memory. It's just the way Windows has always worked.
The little-endian explanation seems obvious, but I don't think it's the truth. If you look at the definition of COLORREF in the Windows API, you'll notice that Red is the low order byte and Blue is the higher order; if you stored this as a single integer value, it would be RGB0.
ARGB refers to the byte order in words fetched as words. If you fetch the bytes one at a time, you'll receive em low to hi as IBM PC's are little-endian