I loaded a 1 pixel image into a bitmap and then converted it to a byte[]
_Image = "test.jpg";
Bitmap testImage = new Bitmap(_Image);
ImageConverter converter = new ImageConverter();
byte[] byteTestImage = (byte[])converter.ConvertTo(testImage,typeof(byte[]));
The single pixel has RGB values (255, 116, 25). Each of these can be represented by a byte,
so I assumed that byteTestImage would correspond to this. But, byteTestImage is 635 elements in total.
What is the relationship between those bytes and the 1 pixel image?
The file you loaded is a JPG. It has certain additional information (width, height, EXIF data) not just colors. Look at https://en.wikipedia.org/wiki/JPEG
Try opening it in a hex editor. You might even be able to read info about the camera used to take it.
There is no always RGB format for single pixel in Bitmap. It all depends on format. You can have a alfa component, you can have a palette to which martix of pixels refers to and more...
Check out: Bitmap format
Related
I want to create a bitmap that has 16 bits per pixel, but when I specify any of the pixel formats:
PixelFormat.Format16bppArgb1555, PixelFormat.Format16bppGrayScale, PixelFormat.Format16bppRgb555, PixelFormat.Format16bppRgb565
They all throw a System.NotImplementedException with the error message "Not Implemented". I can't allocate 16 bits per pixel manually because Bitmap.SetPixel() only allows you to set a byte to alpha, red, green, or blue (so I need to use marshalling which requires a particular pixel format). Rather than setting a byte for each color value, I'm trying to distribute 16 bits between red, green, and blue. For example 5 bits for red, 5 bits for green, and 5 bits for blue.
Below is an example of one of the 16 bits per pixel pixel formats that throws a System.NotImplementedException:
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppArgb1555);
My system is MacOS.
System.Drawing does not support every pixel format on all platforms. Once I collected the differences (see the Restrictions of Possible Pixel Formats on Different Platforms part). I think MacOS uses the same package as the Linux version.
As Bitmap encoders cannot save images preserving the Format16bppArgb1555 pixel format anyway I suggest you to create a Bitmap instance using the closest compatible format (for example, Format32bppArgb instead of Format16bppArgb1555), and if you really want to transform the colors into the ARGB1555 color space, you can use the Quantize method with PredefinedColorsQuantizer.Argb1555 using the same library I linked for the comparison. You can download it from NuGet.
Disclaimer: I dit not test it on MacOS but it works in Linux (Ubuntu).
Rather than setting a byte for each color value, I'm trying to distribute 16 bits between red, green, and blue. For example 5 bits for red, 5 bits for green, and 5 bits for blue.
Edit: I'm already working on the next version, which will allow using a managed bitmap data with any PixelFormat on any platform. See the public BitmapDataFactory.CreateBitmapData method (not available in a public release yet but you can already play with it if you check out the Development branch).
Update:
I have image bytes (i.e. bytes representing only an image, no header), and I'm testing to see if the format of the image the image bytes decode to is 16 bits per pixel.
Now I see why you need to set pixels in a 16-bit raw format. As in Linux/MacOS the System.Drawing.Bitmap has a limited support the easiest solution if you create managed bitmap data as I mentioned above.
Note: I published a pre-release, which makes the BitmapDataFactory.CreateBitmapData method available without checking out my development branch. And now you can turn your raw bytes of any PixelFormat to a Bitmap like this:
(of course, if pfGuess is not the actual format the result can be messy)
public Bitmap ToBitmap(byte[] rawData, PixelFormat pfGuess, Size size)
{
// the result bitmap will be ARGB32, which works on every platform
Bitmap result = new Bitmap(size.Width, size.Height);
// we create an accessor for the result so we can quickly copy the translated pixels
using var bitmapDataDst = result.GetWritableBitmapData();
// For the managed source any pixel format will work.
// Please note though that unlike System.Drawing.Bitmap images,
// managed bitmap data never has padding bytes at the end of each line.
// So if your rawData has padding (eg. when size has odd width and pf is 16bpp),
// then maybe the best if you include the padding in width.
// See https://learn.microsoft.com/en-us/windows/win32/medfound/image-stride for details
using var bitmapDataSrc = BitmapDataFactory.CreateBitmapData(size, pfGuess);
// Checking if rawData is long enough. RowSize is the width in bytes.
if (rawData.Length < bitmapDataSrc.RowSize * size.Height)
throw new ArgumentException("rawData is too short");
int currentByte = 0;
// iterating through rows
for (int y = 0; y < size.Height; y++)
{
var rowSrc = bitmapDataSrc[y]; // has the specified pixel format
var rowDst = bitmapDataDst[y]; // has ARGB32 pixel format
// writing raw pixels
for (int x = 0; x < bitmapDataSrc.RowSize; x++)
rowSrc.WriteRaw<byte>(x, rawData[currentByte++]);
// copying the pixels interpreted as pfGuess to result
for (int x = 0; x < size.Width; x++)
rowDst[x] = rowSrc[x];
}
// The result bitmap is now populated from the arbitrarily interpreted raw data.
// As it has ARGB32 format you can save it on any platform without any issues.
return result;
}
I have learnt how to export pixel data of an image to byte array, here is my code
void Button2Click(object sender, EventArgs e)
{
Bitmap img = new Bitmap (#"24x30.bmp");
var BitmapData = img.LockBits( new Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadOnly,img.PixelFormat);
var length = BitmapData.Stride * BitmapData.Height;
MessageBox.Show(BitmapData.Width.ToString());
byte[] bytes = new byte[length];
Marshal.Copy(BitmapData.Scan0, bytes, 0, length);
img.UnlockBits(BitmapData);
string test = ByteArrayToBinary(bytes);
}
I convert the bytes to string bit but lets ignore it. What I want to know is, how can I convert the byte of pixel data to an image? Please share the code and the reference.
I have read many references but I don't get it until now.
EDIT:
This summary of my case, i have Stride, Width, Height, and Byte[] of Pixel data. How can i reconstruct it to image again thanks
Same code, but copy the other way. (You can read a dummy image or use another image constructor.)
Obviously, you first step should be to somehow convert your text back to a byte array, but then you'll see that you can't actually create an image from just that data.
As you mentioned in comments already, your dumped binary block is missing all header data. You need the original width, height and pixel format before you can convert the data back to an image.
Also, if this is an indexed image, there will be a colour palette, and you don't save that either.
And finally, you copy all data in the image memory. Images are kept in memory per line of pixels, but these lines are usually rounded to the next multiple of four bytes (except in some indexed pixel formats, I think). This means that unless your image uses four bytes per pixel (32 bits per pixel), the bytes you end up with may contain junk data at the end of each line. You don't trim that off in any way, meaning you not only need the width and height, but the stride as well, before you can reconstruct the image.
As for how to build the image, the method is pretty much the same. You make a new image with the correct dimensions and pixel format, open its backing memory using LockBits but in WriteOnly mode, and copy your data into it.
I posted a full method for that on this site before, so feel free to check it out.
I'm using Microsoft Surface 2.0 SDK with SUR40 PixelSense compatible computer. I need to capture image from it's touch and save it as .bmp. Since Surface SDK comes with RawImageVisualizer example, which displays picture from touch on the screen, I've tried to modify program for writing picture to HDD. The problem is, I get ArgumentException: Parameter is invalid during building Image from byte array captured from touch.
This is how I retrieve byte array with image data from FrameReceivedEventArgs on FrameReceived event:
event.UpdateRawImage(
ImageType.Normalized,
normalizedImage,
0, 0,
InteractiveSurface.PrimarySurfaceDevice.WorkingAreaWidth,
InteractiveSurface.PrimarySurfaceDevice.WorkingAreaHeight);
And that's how I try to write bytes as .bmp to disk:
System.Drawing.Image img;
using (System.Drawing.Image raw = System.Drawing.Image.FromStream(new MemoryStream(normalizedImage)))
{
img = raw.Clone() as System.Drawing.Bitmap;
}
img.Save("C:/img.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
So I get the exception trying to create Image from stream. Nevertheless this byte array works totally fine with Texture2D and SpriteBatch which displays it. How can I fix ArgumentException?
i've just realized, that UpdateRawImage does not return a byte representation of PNG file, but only an array of pixels. So, to build an image from it, one have to write all other parts of file structure to the array: header and color table (if needed). In many cases this can be simply done with one of System.Drawing.Bitmap constructors:
public Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
But I was not so lucky, because UpdateRawImage returns 8bpp grayscale pixels, and PixelFormat enum doesn't support them (the most close is Format16bppGrayScale, but it uses 2 bytes for pixel, not one). So, in this particular situation, there are two obvious solutions. The first is making a new array of pixels, which meets one of PixelFormat standards (that was my choice, because I need 24-bit RGB image, despite it's actually black-white with only 256 shades). The second is writing BMP headers manually (and it's not very difficult due to open specs).
I'm trying to convert a 32bpp screenshot image to an 8bpp (or 4bpp, or 1bpp) format using C#. I've already looked at several stackoverflow answers on similar subjects and most suggest variations using the following code:
public static Bitmap Convert(Bitmap oldbmp)
{
Bitmap newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format8bppIndexed);
Graphics gr = Graphics.FromImage(newbmp);
gr.PageUnit = GraphicsUnit.Pixel;
gr.DrawImageUnscaled(oldbmp, 0, 0);
return newbmp;
}
However, when this executes, I get a the exception: A graphics object cannot be created from an image that has an indexed pixel format. I understand that 8, 4 and 1bpp images have colour table mappings rather than the actual colour pixels themselves (as in 32 or 16bpp images) so I assume I'm missing some conversion step somewhere, but I'm fairly new to C# (coming from a C++ background) and would prefer to be able do this using native C# calls rather than resorting to PInvoking BitBlt and GetDIBits etc. Anybody able to help me solve this? Thanks.
EDIT: I should point out that I need this to be backwardly compatible to .NET framework 2.0
GDI+ in general has very poor support for indexed pixel formats. There is no simple way to convert an image with 65536 or 16 million colors into one that only has 2, 16 or 256. Colors have to be removed from the source image and that is a lossy conversion that can have very poor results. There are multiple algorithms available to accomplish this, none of them are perfect for every kind of image. This is a job for a graphics editor.
There is one trick I found. GDI+ has an image encoder for GIF files. That's a graphics format that has only 256 colors, the encoder must limit the number of colors. It uses a dithering algorithm that's suitable for photos. It does have a knack for generating a grid pattern, you'll be less than thrilled when it does. Use it like this:
public static Image Convert(Bitmap oldbmp) {
using (var ms = new MemoryStream()) {
oldbmp.Save(ms, ImageFormat.Gif);
ms.Position = 0;
return Image.FromStream(ms);
}
}
The returned image has a 8bpp pixel format with the Palette entries calculated by the encoder. You can cast it to Bitmap if necessary. By far the best thing to do is to simply not bother with indexed formats. They date from the stone age of computing back when memory was severely constrained. Or use a professional graphics editor.
AForge library is doing it perfectly using Grayscale.
var bmp8bpp = Grayscale.CommonAlgorithms.BT709.Apply(bmp);
This class is the base class for image grayscaling [...]
The filter accepts 24, 32, 48 and 64 bpp color images and produces 8
(if source is 24 or 32 bpp image) or 16 (if source is 48 or 64 bpp
image) bpp grayscale image.
Negative stride signifies the image is bottom-up (inverted). Just use the absolute of the stride if you dont care. I know that works for 24bpp images, unaware if it works for others.
You can use System.Windows.Media.Imaging in PresentationCore Assembly take a look at here for more information
I want to create an 8-bit indexed image from a regular 32-bit Image object.
Bitmap img = new Bitmap(imgPath); // 32-bit
Bitmap img8bit = new Bitmap(imgW, imgH, Format8bppIndexed); // 8-bit
// copy img to img8bit -- HOW?
img8bit.Save(imgNewPath, ImageFormat.Png);
I cannot use SetPixel to copy it over pixel-by-pixel since Graphics doesn't work with indexed images.
How else?
I found a C# library that converts a bitmap into a palettized (8-bit) image. The technique is fast because it calls GDI32 (the windows graphics system) directly.
To convert to an 8bpp (palettized) image with a greyscale palette, do
System.Drawing.Bitmap b0 = CopyToBpp(b,8);
If you want to convert to an image with a different palette, look at the comments in the source code of CopyToBpp for suggestions. Note that, when you convert to a 1bpp or 8bpp palettized copy, Windows will look at each pixel one by one, and will chose the palette entry that's closest to that pixel. Depending on your source image and choice of palette, you may very well end up with a resulting image that uses only half of the colours available in the palette.
Converting an arbitrary RGBA image to an 8-bit indexed bitmap is a non-trivial operation; you have to do some math to determine the top 256 colors and round the rest (or do dithering, etc).
http://support.microsoft.com/kb/319061 has the details of everything except for a good algorithm, and it should give you an idea of how to get started.