Bitmap Image from Hex string - extra bytes being added - c#

I have a Hex string that's coming from postscript file.
<< /ImageType 1
/Width 986 /Height 1
/BitsPerComponent 8
/Decode [0 1 0 1 0 1]
/ImageMatrix [986 0 0 -1 0 1]
/DataSource <
803fe0503824160d0784426150b864361d0f8844625138a4562d178c466351b8e4763d1f904864523924964d27944a6552b964b65d2f984c665339a4d66d379c4e6753b9e4f67d3fa05068543a25168d47a4526954ba648202
> /LZWDecode filter >> image } def
Below are the methods that I am using. I have commented out the method for updating color.
public static void ProcessImageColourMapping()
{
string imageDataSource = "803fe0503824160d0784426150b864361d0f8844625138a4562d178c466351b8e4763d1f904864523924964d27944a6552b964b65d2f984c665339a4d66d379c4e6753b9e4f67d3fa05068543a25168d47a4526954ba648202";
string imageDataSourceUpdated = GetUpdatedImage(imageDataSource);
}
public static string GetUpdatedImage(string strImageDataSource)
{
string imageDataSourceUpdated = "";
byte[] imageBytes = StringToByteArray(strImageDataSource);
Bitmap bitmapImage = ByteArrayToBitmap(imageBytes);
//UpdateColour(bitmapImage);
byte[] imageBytesUpdated = BitmapToByteArray(bitmapImage);
imageDataSourceUpdated = ByteArrayToString(imageBytesUpdated);
return imageDataSourceUpdated;
}
public static byte[] StringToByteArray(String imageHexString)
{
int numberOfChars = imageHexString.Length / 2;
byte[] byteArray = new byte[numberOfChars];
using (var sr = new StringReader(imageHexString))
{
for (int i = 0; i < numberOfChars; i++)
byteArray[i] = Convert.ToByte(new string(new char[2] { (char)sr.Read(), (char)sr.Read() }), 16);
}
return byteArray;
}
public static Bitmap ByteArrayToBitmap(byte[] byteArray)
{
int width = 986; //width and height are taken from postscript file for testing a single hex string.
int height = 1;
Bitmap bitmapImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
BitmapData bmpData = bitmapImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
try
{
Marshal.Copy(byteArray, 0, bmpData.Scan0, byteArray.Length);
}
finally
{
bitmapImage.UnlockBits(bmpData);
}
return bitmapImage;
}
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];
try
{
Marshal.Copy(bmpdata.Scan0, bytedata, 0, numbytes);
}
finally
{
bitmap.UnlockBits(bmpdata);
}
return bytedata;
}
public static string ByteArrayToString(byte[] byteArray)
{
StringBuilder hex = new StringBuilder(byteArray.Length * 2);
foreach (byte b in byteArray)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
Issue:
In below code, I am not updating anything for incoming Hex string imageDataSource.
Converting it to byte[] - then to Bitmap - Back to byte[] - and finally back to Hex string.
So, imageDataSourceUpdated should have same value as imageDataSource.
However, when I finally check the value for imageDataSourceUpdated, it comes out as:
803fe0503824160d0784426150b864361d0f8844625138a4562d178c466351b8e4763d1f904864523924964d27944a6552b964b65d2f984c665339a4d66d379c4e6753b9e4f67d3fa05068543a25168d47a4526954ba64820200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.....
So many zeros being appended.
Can please guide what I am missing here.

You are passing some input string, but the width of the image (and so 1/4 of the size of the byte array) is set to 986 in your sample, which would yield the behaviour you observe - you're not actually passing 986 * 4 bytes of data, but the Bitmap does have that many. So you'll get the first X bytes you actually copied to the bitmap, and then all zeros. In other words, it seems your issue is with your sample data, not with the methods themselves - those work just fine.

Related

How can I convert a Bitmap to a 1D byte array and vice versa in C#?

I wrote the following methods,
public static byte[] BitmapToByteArray(Bitmap image)
{
byte[] returns = null;
if (image.PixelFormat == PixelFormat.Format8bppIndexed)
{
BitmapData bitmapData = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite,
image.PixelFormat);
int noOfPixels = image.Width * image.Height;
int colorDepth = Bitmap.GetPixelFormatSize(image.PixelFormat);
int step = colorDepth / 8;
byte[] bytes = new byte[noOfPixels * step];
IntPtr address = bitmapData.Scan0;
Marshal.Copy(address, bytes, 0, bytes.Length);
////////////////////////////////////////////////
///
returns = (byte[])bytes.Clone();
///
////////////////////////////////////////////////
Marshal.Copy(bytes, 0, address, bytes.Length);
image.UnlockBits(bitmapData);
}
else
{
throw new Exception("8bpp indexed image required");
}
return returns;
}
And,
public static Bitmap ByteArrayToBitmap(byte[] bytes, int width, int height, PixelFormat pixelFormat)
{
Bitmap bitmap = new Bitmap(width, height, pixelFormat);
BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
int colorDepth = Bitmap.GetPixelFormatSize(pixelFormat);
int noOfChannels = colorDepth / 8;
IntPtr address = bitmapData.Scan0;
//////////////////////////////////////////////////////////////
//
Marshal.Copy(bytes, 0, address, width * height * noOfChannels);
//
//////////////////////////////////////////////////////////////
bitmap.UnlockBits(bitmapData);
return bitmap;
}
They seem to be not working,
What has been the problem do you think?
N.B.
Driver program,
public class MainClass
{
public static void Main(string [] args)
{
Bitmap inputBmp = (Bitmap)Bitmap.FromFile(#"cameraman.gif");
byte[] bytes = Converter.BitmapToByteArray(inputBmp);//byte[65536]
Bitmap outputBmp = Converter.ByteArrayToBitmap(bytes, inputBmp.Width, inputBmp.Height, PixelFormat.Format8bppIndexed);
PictureDisplayForm f = new PictureDisplayForm(inputBmp, outputBmp);
f.ShowDialog();
}
}
Okay.
I have solved it.
public static byte[] BitmapToByteArray(Bitmap image)
{
byte[] returns = null;
if (image.PixelFormat == PixelFormat.Format8bppIndexed)
{
BitmapData bitmapData = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite,
image.PixelFormat);
int noOfPixels = image.Width * image.Height;
int colorDepth = Bitmap.GetPixelFormatSize(image.PixelFormat);
int step = colorDepth / 8;
byte[] bytes = new byte[noOfPixels * step];
IntPtr address = bitmapData.Scan0;
Marshal.Copy(address, bytes, 0, bytes.Length);
////////////////////////////////////////////////
///
returns = (byte[])bytes.Clone();
///
////////////////////////////////////////////////
Marshal.Copy(bytes, 0, address, bytes.Length);
image.UnlockBits(bitmapData);
}
else
{
throw new Exception("8bpp indexed image required");
}
return returns;
}
public static Bitmap ByteArray1dToBitmap(byte[] bytes, int width, int height)
{
PixelFormat pixelFormat = PixelFormat.Format8bppIndexed;
Bitmap bitmap = new Bitmap(width, height, pixelFormat);
// Set the palette for gray shades
ColorPalette pal = bitmap.Palette;
for (int i = 0; i < pal.Entries.Length; i++)
{
pal.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = pal;
BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, pixelFormat);
int colorDepth = Bitmap.GetPixelFormatSize(pixelFormat);
int noOfChannels = colorDepth / 8;
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
int area = width * height;
int size = area * noOfChannels;
for (int i = 0; i < area; i++)
{
address[i] = bytes[i];//262144 bytes
}
}
//////////////////////////////////////////////////////////////
bitmap.UnlockBits(bitmapData);
return bitmap;
}

libtiff.net writeScanLine returns false

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).

How to create a Byte array that contains a real Image?

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

Image.FromStream() issue C# at the time of image manipulation

I'm working with JPG image type and this line image = Image.FromStream(ms); is giving an error Parameter is not valid.
What is wrong with this code?
private void button3_Click(object sender, EventArgs e)
{
Image partial=null;
Rectangle bounds;
Guid id;
if (diff != null)
{
ImageConverter converter = new ImageConverter();
var data = (byte[])converter.ConvertTo(diff, typeof(byte[]));
UnpackScreenCaptureData(data, out partial, out bounds,out id);
Image imgfirst = (Image)firstImg;
UpdateScreen(ref imgfirst, partial, bounds);
}
}
public static void UnpackScreenCaptureData(byte[] data, out Image image, out Rectangle bounds, out Guid id)
{
// Unpack the data that is transferred over the wire.
// Create byte arrays to hold the unpacked parts.
const int numBytesInInt = sizeof(int);
int idLength = Guid.NewGuid().ToByteArray().Length;
int imgLength = data.Length - 4 * numBytesInInt - idLength;
byte[] topPosData = new byte[numBytesInInt];
byte[] botPosData = new byte[numBytesInInt];
byte[] leftPosData = new byte[numBytesInInt];
byte[] rightPosData = new byte[numBytesInInt];
byte[] imgData = new byte[imgLength];
byte[] idData = new byte[idLength];
// Fill the byte arrays.
Array.Copy(data, 0, topPosData, 0, numBytesInInt);
Array.Copy(data, numBytesInInt, botPosData, 0, numBytesInInt);
Array.Copy(data, 2 * numBytesInInt, leftPosData, 0, numBytesInInt);
Array.Copy(data, 3 * numBytesInInt, rightPosData, 0, numBytesInInt);
Array.Copy(data, 4 * numBytesInInt, imgData, 0, imgLength);
Array.Copy(data, 4 * numBytesInInt + imgLength, idData, 0, idLength);
// Create the bitmap from the byte array.
MemoryStream ms = new MemoryStream(imgData, 0, imgData.Length);
ms.Write(imgData, 0, imgData.Length);
image = Image.FromStream(ms);
....
}
I think you have to reset your MemoryStream to position 0.
You can accomplish this by calling the Seek() method on the memory stream:
MemoryStream ms = new MemoryStream(imgData, 0, imgData.Length);
ms.Write(imgData, 0, imgData.Length);
ms.Seek(0, SeekOrigin.Begin); // Set stream position to 0.
image = Image.FromStream(ms);

Byte Array to Bitmap Image

I made this code to receive an image and convert it to bitmap image but it doesn't work.
Here is the code:
public void ReceiveImage()
{
NetworkStream stream = new NetworkStream(socket);
byte[] data = new byte[4];
stream.read(data,0,data.length,0)
int size = BitConverter.ToInt32(data,0);
data = new byte[size];
stream.read(data,0,data.length)
MemoryStream imagestream = new MemoryStream(data);
Bitmap bmp = new Bitmap(imagestream);
picturebox1.Image = bmp;
}
It gets to:
Bitmap bmp = new Bitmap(imagestream);
And gives me this error:
Parameter is not valid
This is an alternative method
int w= 100;
int h = 200;
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
byte[] imageData = new byte[w*h*ch]; //you image data here
Bitmap bitmap = new Bitmap(w,h,PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData,0,pNative,w*h*ch);
bitmap.UnlockBits(bmData);
You are probably not receiving enough bytes in stream.read(data,0,data.length) since Read does not ensure that it will read data.length bytes. you have to check its return value and continue to read till data.Length bytes are read.
See : Stream.Read Method's return value
int read = 0;
while (read != data.Length)
{
read += stream.Read(data, read, data.Length - read);
}
PS: I am assuming lengths and reads are typos.
I assume you have a table and want to receive the picture from database.
int cout = ds.Tables["TableName"].Rows.Count;
if (cout > 0)
{
if (ds.Tables["TableName"].Rows[cout - 1]["Image"] != DBNull.Value)
{
var data = (byte[])(ds.Tables["TableName"].Rows[cout - 1]["Image"]);
var stream = new MemoryStream(data);
pictureBox1.Image = Image.FromStream(stream);
}
else
{
pictureBox1.Image = null;
}
}
Try this:
int size = BitConverter.ToInt32(data.Reverse().ToArray(),0);

Categories