Convert byte array to jpeg in grayscale - c#

I have a simple Byte array representing an image in gray scale (value come from 0 to 255)
There is an example :
Byte[] sample = new Byte[16] {
55 ,0 ,0 ,255,
12 ,255,75 ,255,
255,150,19 ,255,
42 ,255,78 ,255 };
I want to save this in jpeg format but I don't know how I can do this.
I've try 2 ways but it doesn't work. I think my byte array is not right formatted but I don't know how to do.
I have tried this :
TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
Bitmap bmp = (Bitmap)tc.ConvertFrom(sample);
bmp.Save(myPath, System.Drawing.Imaging.ImageFormat.Jpeg);
And this :
MemoryStream str = new MemoryStream(sample);
str.Position = 0;
using (Image image = Image.FromStream(str))
{
image.Save(myPath, System.Drawing.Imaging.ImageFormat.Jpeg);
}

With all your indications, I finally find a way :
public static void SaveByteArryToJpeg(string imagePath, Byte[] data, int width, int height)
{
if (width * height != data.Length)
throw new FormatException("Size does not match");
Bitmap bmp = new Bitmap(width, height);
for (int r = 0; r < height; r++)
{
for (int c = 0; c < width; c++)
{
Byte value = data[r * width + c];
bmp.SetPixel(c, r, Color.FromArgb(value, value, value));
}
}
bmp.Save(imagePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}

BitmapImage photo = byteToImage(byte[] sample)
this functionto convert byte to BitmapImage
public BitmapImage byteToImage(byte[] buffer)
{
using(var ms = new MemoryStream(buffer))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
}
return image;
}
and then byteToImage will return an image, so you can save.
Hopefully this can help you

Related

WPF C# Stride it too low for CopyPixels method

I have to convert a BitmapSource into a BitmapImage because of the Interface I am using. If I display the BitmapSource to an Image view, it works. but when I try and convert the BitmapSource into a BitmapImage, I get this error: "The parameter value cannot be less than '3686400'.\r\nParameter name: buffer" string
My PixelFormat is Bgr24
Here is my code :
void GetFrame(BitmapSource image)
{
try
{
BitmapSourceImageChanged?.Invoke(image);
BitmapImage tempImage = new BitmapImage();
//tempImage = image as BitmapImage;
// Frame = tempImage; var temp = image.Format;
int imagePixelHeight = image.PixelHeight;
int imagePixelWidth = image.PixelWidth;
byte[] pixels = new byte[imagePixelHeight * imagePixelWidth];
int stride = (imagePixelWidth * image.Format.BitsPerPixel +7) / 8;
image.CopyPixels(pixels,stride,0);
Frame = BitmapImageFromArray(pixels, image.PixelWidth, image.PixelHeight);
if (Frame == null)
{
int stop = 0;
}
}
catch (Exception err)
{
Error = err.Message;
}
}
BitmapImage BitmapImageFromArray(byte[] transfer, int imageWidth, int imageHeight)
{
BitmapImage image = new BitmapImage();
try
{
using (
// Convert byte array to BitmapImage()
MemoryStream stream = new MemoryStream(transfer))
{
image.BeginInit();
image.DecodePixelWidth = imageWidth;
image.DecodePixelHeight = imageHeight;
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
}
catch (Exception err)
{
}
return image;
}
My code throws the exception at the CopyPixels method.
Is my stride equation not correct?
Your stride calculation is ok, but not the calculation of the buffer size.
The buffer size is the product of the number of scan lines and the bytes per scan line, i.e. the stride:
int stride = (imagePixelWidth * image.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[imagePixelHeight * stride]; // here
However, your code will still not work, since you can not decode a BitmapImage from a raw pixel buffer. Decoding means to read an encoded bitmap frame, for example a JPG or PNG.
In order to decode a BitmapImage, you would have to create an encoded frame first:
public static BitmapImage BitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
var bitmapImage = bitmapSource as BitmapImage;
if (bitmapImage == null)
{
bitmapImage = new BitmapImage();
var encoder = new BmpBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
}
return bitmapImage;
}
That said, there should actually never be a need to have a method like the above at all. All properties that are relevant for a bitmap in WPF are declared in the BitmapSource class. The additional properties in BitmapImage are only used during creation, but have no relevance afterwards.

Error in Quantization of Image using NQUANT

I recently acquired the NuGet Package Nquant.
I plan to use this to reduce the file size of the bitmap and save it into PNG. But I get this error:
The image you are attempting to quantize does not contain a 32 bit ARGB palette. This image has a bit depth of 8 with 256 colors.
Does anyone here has used Nquant? And have you encountered this error and how did you fix it?
My code for your reference:
var bitmap = new Bitmap(width, jbgsize / height, PixelFormat.Format8bppIndexed);
ColorPalette pal = bitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
var bitmap_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Marshal.Copy(output, 0, bitmap_data.Scan0, output.Length);
bitmap.UnlockBits(bitmap_data);
MemoryStream stream = new MemoryStream();
var quantizer = new WuQuantizer();
using(var bmp = new Bitmap(bitmap))
{
using (var quantized = quantizer.QuantizeImage(bitmap))
{
quantized.Save(stream, ImageFormat.Png);
}
}
byteArray = stream.ToArray();
return byteArray.Concat(output).ToArray();
You can convert your image to Format32bppPArgb and then Quantize it.
This is my working example of that decrease image size for ~3 times.
public static byte[] CompressImageStream(byte[] imageStream)
{
using (var ms = new MemoryStream(imageStream))
using (var original = new Bitmap(ms))
using (var clonedWith32PixelsFormat = new Bitmap(
original.Width,
original.Height,
PixelFormat.Format32bppPArgb))
{
using (Graphics gr = Graphics.FromImage(clonedWith32PixelsFormat))
{
gr.DrawImage(
original,
new Rectangle(0, 0, clonedWith32PixelsFormat.Width, clonedWith32PixelsFormat.Height));
}
using (Image compressedImage = new WuQuantizer().QuantizeImage(clonedWith32PixelsFormat))
{
return ImageToByteArray(compressedImage);
}
}
}
public static byte[] ImageToByteArray(Image image)
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
using (var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
return stream.ToArray();
}
}

GIF image cropping in c# without add-ons

I'm trying to crop gif images in c# without using an add-ons with drawing a rectangle on the image (and I've got it to work with normal image), but .. lets just say the output is less than optimal..
examples:
Origianl Image:
Cropped Image:
and here's the code that I'm using :
public override Image Crop(Rectangle f, bool isStrch, int slashwid, int slashhei)
{
_revert = Image;
GifDecoder();
widmult = Width / (double)slashwid;
heimult = Height / (double)slashhei;
double num = (double)f.X * widmult;
double num2 = (double)f.Y * heimult;
double num3 = (double)f.Width * widmult;
double num4 = (double)f.Height * heimult;
Rectangle srcRect = new Rectangle(isStrch ? ((int)num) : f.X, isStrch ? ((int)num2) : f.Y, isStrch ? ((int)num3) : f.Width, isStrch ? ((int)num4) : f.Height);
CroppedFrames = new Image[Frames.Length];
for (int i = 0; i < Frames.Length; i++)
{
Bitmap image = new Bitmap(Frames[i]);
Bitmap bitmap = new Bitmap(srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), srcRect, GraphicsUnit.Pixel);
}
CroppedFrames[i] = bitmap;
}
GifEncoder(f.Width,f.Height);
return Image;
}
private void GifDecoder()
{
Stream bitmapStream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read);
GifBitmapDecoder gifBitmapDecoder = new GifBitmapDecoder(bitmapStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
Frames = new Image[gifBitmapDecoder.Frames.Count];
for (int i = 0; i < gifBitmapDecoder.Frames.Count; i++)
{
Frames[i] = BitmapFromSource(gifBitmapDecoder.Frames[i]);
}
}
private void GifEncoder(int width,int height)
{
BitmapPalette palette = new BitmapPalette(BitmapImageFromImage(Image),256);
int bytecount = (width * height) / 8;
using (FileStream fs = new FileStream(Path + "output.gif", FileMode.Create))
{
GifBitmapEncoder encoder = new GifBitmapEncoder();
for (int f = 0; f < Frames.Length; f++)
{
byte[] pixels = imageToByteArray(CroppedFrames[f]);
BitmapSource image = BitmapSource.Create(width,height,96,96,System.Windows.Media.PixelFormats.Indexed8,palette,pixels,width);
encoder.Frames.Add(BitmapFrame.Create(image));
}
encoder.Save(fs);
Image = Image.FromStream(fs);
fs.Close();
}
}
private byte[] imageToByteArray(Image imageIn)
{
MemoryStream ms = new MemoryStream();
Bitmap aaa = new Bitmap(imageIn);
aaa.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
(bool isStrch, int slashwid, int slashhei) are for checking if the picturebox is smaller than the image or not to get accurate crops with the rect.

Loaded bitmap differs from saved one in jpeg (c#)

I'm using following methods to load bitmap from jpeg file (JpegToBitmap):
private static Bitmap JpegToBitmap(string fileName)
{
FileStream jpg = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
JpegBitmapDecoder ldDecoder = new JpegBitmapDecoder(jpg, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapFrame lfFrame = ldDecoder.Frames[0];
Bitmap lbmpBitmap = new Bitmap(lfFrame.PixelWidth, lfFrame.PixelHeight);
Rectangle lrRect = new Rectangle(0, 0, lbmpBitmap.Width, lbmpBitmap.Height);
BitmapData lbdData = lbmpBitmap.LockBits(lrRect, ImageLockMode.WriteOnly, (lfFrame.Format.BitsPerPixel == 24 ? PixelFormat.Format24bppRgb : PixelFormat.Format32bppArgb));
lfFrame.CopyPixels(System.Windows.Int32Rect.Empty, lbdData.Scan0, lbdData.Height * lbdData.Stride, lbdData.Stride);
lbmpBitmap.UnlockBits(lbdData);
return lbmpBitmap;
}
And to save bitmap to jpeg file:
private static void SaveToJpeg(Bitmap bitmap, string filePath)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bitmap.Save(filePath, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
Bitmap pixels differs just a little bit, but this difference is significant for me:
Screenshot of values in matrix of colors of bitmap before save
Screenshot of values in same matrix of colors of bitmap after load
This matrixes are just 2-d arrays of Color got by standart bitmap.GetPixel(x,y) in a loop.
public ColorMatrix(Bitmap bitmap, int currHeight, int currWidth)
{
matrix = new Color[8, 8];
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
{
matrix[y, x] = bitmap.GetPixel(x + currWidth, y + currHeight);
}
}
}
So, the question is: How can i load saved bitmap (in jpeg/png or whatever format) correctly?
There are three reasons:
JPEG encoding and decoding processes use floating point arithmetic on integer input.
The Cb and Cr components are often subsampled relative to the Y component during compression.
During the quantization (fancy word for integer division) phase, DCT coefficients are usually discarded.

Create BitmapImage and apply to it a specific Color

I got stuck how to create BitmapImage based on some color value?
For example I have string "Black" so I need black BitmapImage.
How it could be done?
Thank you!
-- UPDATES (I put this code for #StephenChung)
The idea is to do Grid with any opacity and don't apply opacity to its children.
So I create an image by ANY color I need and apply opacity for it.
BitmapSource bs = CreateBitmapSource(GetBackgroundColorValue());
// and here I use method of #StaWho CreateBitmapSource()
ImageBrush ib2 = new ImageBrush(bs);
ib2.Opacity = Opacity;
ib2.Stretch = Stretch.Fill;
RootGrid.Background = ib2;
You can create ImageBrush from BitmapSource:
private BitmapSource CreateBitmapSource(System.Windows.Media.Color color)
{
int width = 128;
int height = width;
int stride = width / 8;
byte[] pixels = new byte[height * stride];
List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>();
colors.Add(color);
BitmapPalette myPalette = new BitmapPalette(colors);
BitmapSource image = BitmapSource.Create(
width,
height,
96,
96,
PixelFormats.Indexed1,
myPalette,
pixels,
stride);
return image;
}
Example:
System.Drawing.Bitmap flag = new System.Drawing.Bitmap(10, 10);
for( int x = 0; x < flag.Height; ++x )
for( int y = 0; y < flag.Width; ++y )
flag.SetPixel(x, y, Color.Black);
for( int x = 0; x < flag.Height; ++x )
flag.SetPixel(x, x, Color.Black);
and the conversion:
private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Png);
ms.Position = 0;
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
return bi;
}
}

Categories

Resources