My problem is that I need to convert an image to a byte array to obtain its pixels.
My image size is 268x188 and when I use the property PixelsFormat it returns Format24bppRgb, so I understand that each pixel contains 3 bytes.
If this is true, the size of the pixels should be 268*188*3 = 151152 bytes, but the byte array that I am creating has a size of 4906 bytes, which is the size of the image file in my computer.
I donĀ“t know if there is another way to obtain these pixels or you can only obtain image file size.
If you want to ignore the header and the compression of the file you can do the following.
var path = ...
using(var image = Image.FromFile(path))
using(var bitmap = new Bitmap(image))
{
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var bytesPerPixel = 4; // bitmapData.PixelFormat (image.PixelFormat and bitmapData.PixelFormat can be different)
var ptr = bitmapData.Scan0;
var imageSize = bitmapData.Width * bitmapData.Height * bytesPerPixel;
var data = new byte[imageSize];
for (int x = 0; x < imageSize; x += bytesPerPixel)
{
for(var y = 0; y < bytesPerPixel; y++)
{
data[x + y] = Marshal.ReadByte(ptr);
ptr += 1;
}
}
bitmap.UnlockBits(bitmapData);
}
To get image pixel try this:
public static byte[] GetImageRaw(Bitmap image)
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
if (image.PixelFormat != PixelFormat.Format24bppRgb)
{
throw new NotSupportedException("Invalid pixel format.");
}
const int PixelSize = 3;
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.ReadWrite,
image.PixelFormat);
try
{
var bytes = new byte[data.Width * data.Height * PixelSize];
for (var y = 0; y < data.Height; ++y)
{
var source = (IntPtr)((long)data.Scan0 + y * data.Stride);
// copy row without padding
Marshal.Copy(source, bytes, y * data.Width * PixelSize, data.Width * PixelSize);
}
return bytes;
}
finally
{
image.UnlockBits(data);
}
}
Take a look at Bitmap.LockBits
I use this code in ASP.NET application. Very simple:
var imagePath = GetFilePathToYourImage();
using (var img = System.IO.File.OpenRead(imagePath))
{
var imageBytes = new byte[img.Length];
img.Read(imageBytes, 0, (int)img.Length);
}
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;
}
Let's say, I have an array of byte containing raw bitmap data without headers.
However the bitmap data is a bit weird, I'm not quite sure but it seems the bitmap data is not correctly aligned if the width is NPOT (Not Power of Two)
I use following codes to construct the bmp from such bitmap data:
public Bitmap GetBitmap(byte[] bitmapData, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppRgb555);
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
for (int i = 0; i < bitmapData.Length; i++)
{
*ptr = bitmapData[i];
ptr++;
if (width % 2 != 0)
{
if ((i + 1) % (width * 2) == 0 && (i + 1) * 2 % width < width - 1)
{
ptr += 2;
}
}
}
}
bitmap.UnlockBits(bmpData);
return bitmap;
}
The code works fine so far. But for some reasons, I need to implement "Import Bitmap", which mean I need to get the "weird" bitmap data from an instance of bitmap.
How do I do this?
Finally, I figure out how to do this.
I decide to copy the data first to an array of byte via Marshal.Copy and then copy it to another array of bytes while skip some point if the width is NPOT:
public byte[] ImportBitmap(Bitmap bitmap)
{
int width = bitmap.Width, height = bitmap.Height;
var bmpArea = new Rectangle(0, 0, width, height);
var bmpData = bitmap.LockBits(bmpArea, ImageLockMode.ReadWrite, PixelFormat.Format16bppRgb555);
var data = new byte[bmpData.Stride * Height];
Marshal.Copy(bmpData.Scan0, data, 0, data.Length);
bitmap.UnlockBits(bmpData);
bitmap.Dispose(); // bitmap is no longer required
var destination = new List<byte>();
int leapPoint = width * 2;
for (int i = 0; i < data.Length; i++)
{
if (width % 2 != 0)
{
// Skip at some point
if (i == leapPoint)
{
// Skip 2 bytes since it's 16 bit pixel
i += 1;
leapPoint += (width * 2) + 2;
continue;
}
}
destination.Add(data[i]);
}
return destination.ToArray();
}
im trying to send screenshots over socket so i use unsafe pointers to send only the differences:
private unsafe Bitmap GetDiffBitmap(Bitmap bmp, Bitmap bmp2)
{
bmpRes = new Bitmap(1920, 1080,bmp.PixelFormat);
bmData = bmp.LockBits(new Rectangle(0, 0, 1920, 1080), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
bmDataRes = bmpRes.LockBits(new Rectangle(0, 0, bmpRes.Width, bmpRes.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
IntPtr scan0 = bmData.Scan0;
IntPtr scan02 = bmData2.Scan0;
IntPtr scan0Res = bmDataRes.Scan0;
int stride = bmData.Stride;
int stride2 = bmData2.Stride;
int strideRes = bmDataRes.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
//for(int y = 0; y < nHeight; y++)
System.Threading.Tasks.Parallel.For(0, nHeight, y =>
{
//define the pointers inside the first loop for parallelizing
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
byte* p2 = (byte*)scan02.ToPointer();
p2 += y * stride2;
byte* pRes = (byte*)scan0Res.ToPointer();
pRes += y * strideRes;
for (int x = 0; x < nWidth; x++)
{
//always get the complete pixel when differences are found
if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
p += 4;
p2 += 4;
pRes += 4;
}
});
bmp.UnlockBits(bmData);
bmp2.UnlockBits(bmData2);
bmpRes.UnlockBits(bmDataRes);
return bmpRes;
}
this is the call on the client:
private void startSend()
{
Bitmap curr;
Bitmap pre = screenshot();
byte []bmpBytes = imageToByteArray(pre);
SendVarData(handler, bmpBytes);// this is the first send of the whole screen
while (true)
{
curr = screenshot();
Bitmap diff = GetDiffBitmap(pre, curr);//generate differences.
bmpBytes = imageToByteArray(diff);
SendVarData(handler, bmpBytes);//sending the diff image.
pre = curr;
}
}
SendVarData is a method which send the bytes array over the socket it is not the problem here-leave it.
this is how i get the data in the server side:
public void startListening()
{
Bitmap merge = new Bitmap(1920, 1080);
Graphics g = Graphics.FromImage(merge);
Bitmap prev = byteArrayToImage(ReceiveVarData(client.Client)) as Bitmap;//getting the first full size image.
theImage.Image = prev;//assisning it to picturebox.
while (true)
{
byte[]data = ReceiveVarData(client.Client);
Bitmap curr = byteArrayToImage(data) as Bitmap;//here is the diffrent image
//merge and apply differences
g.DrawImage(prev, 0, 0,1920, 1080);
g.DrawImage(curr, 0, 0, 1920,1080);
theImage.Image = merge;
count++;
prev = merge;
}
}
my problem is that eventhough i merge the two images with the Graphics.Draw it still(after the first dataReceive) looks like not full... this is actually what i see on the server..
i dont know what's wrong here... can anyone light my eyes? :D
#DmitriTrofimov
if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
else
pRes[0] = 0;
This is what you get when you keep pixel opacity. You should set Alpha to zero on the pixels that are not different and make sure you are working with 32-bit images (refer to PixelFormat parameter of Bitmap constructor).
P.S. Make sure you compress the diff bitmap otherwise it's no use.
I am converting a image to a byte array, then converting the bytes into something my epson printer can print
this piece of code converts the picture into bytes fast
ImageConverter converter = new ImageConverter();
buff = (byte[])converter.ConvertTo(signature, typeof(byte[]));
this one is slow but works
public static byte[] ImageToByte2(Image img)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
Both of these result in a different byte array and i can not figure out why, the memory stream one is the one that works for me but the conversion is slow
Can anyone perhaps hint as to why these bytes arrays are different converting the same picture
ex image converter byte array
ex memorystream conv
string display = BitConverter.ToString(buff);
MessageBox.Show(display);
Bitmap signature;
pictureBox1.Image=signature;
string Base64String = "79,27:79,27:79,26:79,26:79,26:79,27:79,28:77,30:73,33:69,38:66,44:62,52:56,62:48,72:42,79:39,82:39,81:#,#:79,29:79,29:78,28:80,26:82,26:81,30:77,36:75,40:74,44:75,47:76,50:78,51:84,48:92,41:101,33:108,27:110,26:109,26:109,28:109,30:109,31:109,34:107,40:103,49:97,58:93,65:88,71:87,74:#,#:133,38:133,38:133,38:132,40:129,47:124,54:121,59:117,65:114,70:#,#:130,39:130,39:130,39:135,35:145,29:157,24:166,23:#,#:125,52:125,52:122,53:125,52:136,48:147,47:152,48:#,#:114,69:114,69:112,69:117,68:129,65:139,63:147,62:#,#:199,30:199,30:197,31:196,35:195,40:195,45:191,54:182,63:174,72:171,76:173,76:182,72:198,69:216,67:231,66:239,66:240,67:238,68:#,#:";
string[] cord = Base64String.Split(new Char[]{',',':'},StringSplitOptions.RemoveEmptyEntries);
Graphics g;
g = Graphics.FromImage(signature);
g.Clear(Color.White);
Pen mypen = new Pen(new SolidBrush(Color.Black));
mypen.Width = 2;
mypen.EndCap = System.Drawing.Drawing2D.LineCap.Square;
mypen.StartCap = System.Drawing.Drawing2D.LineCap.Square;
mypen.MiterLimit = mypen.Width * 1.25f;
mypen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
for (int i = 0; i < cord.Length; i=i+4)
{
//MessageBox.Show(cord[i]);
Point p;
Point p2;
if (cord[i] != "#" && cord[i + 1] != "#")
{
p = new Point(int.Parse(cord[i]), int.Parse(cord[i + 1]));
if (cord[i + 2] != "#" && cord[i + 3] != "#")
{
p2 = new Point(int.Parse(cord[i + 2]), int.Parse(cord[i + 3]));
}
else
{
p2 = new Point(int.Parse(cord[i]), int.Parse(cord[i + 1]));
}
}
else
{
}
}
g.Dispose();
signature = BitmapTo1Bpp(signature);
public static Bitmap BitmapTo1Bpp(Bitmap img)
{
int w = img.Width;
int h = img.Height;
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
byte[] scan = new byte[(w + 7) / 8];
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (x % 8 == 0) scan[x / 8] = 0;
Color c = img.GetPixel(x, y);
if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
}
Marshal.Copy(scan, 0, (IntPtr)((long)data.Scan0 + data.Stride * y), scan.Length);
}
bmp.UnlockBits(data);
return bmp;
}
Based on the MSDN documentation found here, the ImageConverter function is converting the internal .NET representation of the image object reference to a byte array rather than the image data it contains.
I'm using libtiff.net to make a tiff from a jpeg.
The problem apears when I try to write, because the tiff.writeScanLine returns false, meaning the image isn't written in the tiff.
Why is this happening? And how can I figure out what's wrong?
Here's the code:
private bool creatingTiff()
{
using (Bitmap bmp = new Bitmap(targetFile))
{
using (Tiff tif = Tiff.Open("BitmapTo24BitColorTiff.tif", "w"))
{
byte[] raster = getImageRasterBytes(bmp, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
tif.SetField(TiffTag.COMPRESSION, Compression.OJPEG);
tif.SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR);
tif.SetField(TiffTag.SUBFILETYPE, 0);
tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);
tif.SetField(TiffTag.ORIENTATION, BitMiracle.LibTiff.Classic.Orientation.TOPLEFT);
tif.SetField(TiffTag.XRESOLUTION, bmp.HorizontalResolution);
tif.SetField(TiffTag.YRESOLUTION, bmp.VerticalResolution);
tif.SetField(TiffTag.RESOLUTIONUNIT, ResUnit.INCH);
tif.SetField(TiffTag.BITSPERSAMPLE, 8);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);
tif.SetField(TiffTag.JPEGIFOFFSET, 768);
tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
for (int i = 0, offset = 0; i < bmp.Height; i++)
{
bool b = tif.WriteScanline(raster, offset, i, 0);
Console.WriteLine("write succes: " + b);
offset += stride;
}
}
System.Diagnostics.Process.Start("BitmapTo24BitColorTiff.tif");
return true;
}
}
private static byte[] getImageRasterBytes(Bitmap bmp, System.Drawing.Imaging.PixelFormat format)
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
byte[] bits = null;
try
{
// Lock the managed memory
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
// Declare an array to hold the bytes of the bitmap.
bits = new byte[bmpdata.Stride * bmpdata.Height];
// Copy the values into the array.
System.Runtime.InteropServices.Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);
// Release managed memory
bmp.UnlockBits(bmpdata);
}
catch
{
return null;
}
return bits;
}
private static void convertSamples(byte[] data, int width, int height)
{
int stride = data.Length / height;
const int samplesPerPixel = 3;
for (int y = 0; y < height; y++)
{
int offset = stride * y;
int strideEnd = offset + width * samplesPerPixel;
for (int i = offset; i < strideEnd; i += samplesPerPixel)
{
byte temp = data[i + 2];
data[i + 2] = data[i];
data[i] = temp;
}
}
}
The tif-tags are written, but the image itself isn't. Perhaps if someone can point me in the direction of the library developers blog (BitMiracle), I can direct my problem to them directly.
I think your code has the following errors:
You can not use Compression.OJPEG for new images. Old-JPEGs can only be de-compressed.
You probably should not specify TiffTag.JPEGIFOFFSET value by hand. The library will specify proper value itself.
You are trying to write the whole strip using WriteScanline method. You should use WriteEncodedStrip instead.
It also helps to review warnings emitted by the library (it emits them into console).