I use the following codes to compress an image file to jpg:
// _rawBitmap = a Bitmap object
ImageCodecInfo encoder = GetEncoder(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
myEncoderParameters.Param[0] = myEncoderParameter;
ImageConverter imageConverter = new ImageConverter();
byte[] b = (byte[])imageConverter.ConvertTo(_rawBitmap, typeof(byte[]));
using (MemoryStream ms = new MemoryStream())
{
ms.Write(b, 0, b.Length);
ms.Seek(0, SeekOrigin.Begin);
rawBitmap.Save(ms, encoder, myEncoderParameters);
bmp = ToBitmap(ms.ToArray());
return (Bitmap)bmp.Clone();
}
but when I try to compress a png file with same way but only change:
ImageCodecInfo encoder = GetEncoder(ImageFormat.Jpeg);
to
ImageCodecInfo encoder = GetEncoder(ImageFormat.Png);
my png file lost transparent data.
so how to compress a PNG file properly?
There are a couple of problems here.
First, you don't need to set those EncoderParams for quality for PNG.
Second, you don't need ImageConverter
Third, you are writing whatever ImageConverter produces to your memory stream, rewinding, and then writing the encoded PNG over the top of it-- it is likely that you have a PNG file with a bunch of garbage at the end of it as a result.
The simplified approach should be:
using (MemoryStream ms = new MemoryStream())
{
rawBitmap.Save(ms, ImageFormat.Png);
}
If you want to load your bitmap back, open it from the stream, but don't close the stream (the stream will be disposed when your returned Bitmap is disposed):
var ms = new MemoryStream();
rawBitmap.Save(ms, ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
return Bitmap.FromStream(ms);
You can use nQuant (https://www.nuget.org/packages/nQuant/)
With it, you convert 32 bit PNGs to high quality 8 bit PNGs
private static int alphaTransparency = 10;
private static int alphaFader = 70;
var quantizer = new WuQuantizer();
using(var bitmap = new Bitmap(sourcePath))
{
using(var quantized = quantizer.QuantizeImage(bitmap, alphaTransparency, alphaFader))
{
quantized.Save(targetPath, ImageFormat.Png);
}
}
Related
I have an array of bitmaps which need compiling into a single, multi-page tiff image, however when saving the bitmap to a MemoryStream object I get the "Parameter is invalid" error message with no other detail.
The problem code:
private static MemoryStream convertToStream(Bitmap b)
{
using (MemoryStream ms = new MemoryStream())
{
b.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);
return ms;
}
}
The bitmaps are in 600x600 DPI with an average of 35 pages (equating to 35 bitmaps)
What I have tried:
I have confirmed the bitmap array contains the expected contents by saving them to disk
Any help is appreciated.
Stack trace:
at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(Stream stream, ImageFormat format)
at ConfigureTiffDPI.Program.convertToStream(Bitmap b) in C:\Users<user>\source\repos\ConfigureTiffDPI\ConfigureTiffDPI\Program.cs:line 37
at ConfigureTiffDPI.Program.CompileBitmaps(Bitmap[] bitmaps) in C:\Users<user>\source\repos\ConfigureTiffDPI\ConfigureTiffDPI\Program.cs:line 44
at ConfigureTiffDPI.Program.Main(String[] args) in C:\Users<user>\source\repos\ConfigureTiffDPI\ConfigureTiffDPI\Program.cs:line 29
[EDIT]
Given the comments I have edited the code as such:
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);
return ms;
However the error still persists
It's not possible to know why calling Save(stream) fails without knowing what the bitmap object contains exactly. But in general, to save multi-page TIFF, you need to call System.Drawing.Image.Save() once, followed by repeated calls to SaveAdd. The approach is the same whether you're saving to disk file or to memory stream.
There's a code sample from Microsoft on this page that saves 3 pages to a TIFF file on disk. I changed the first parameter of multi.Save() from "Multiframe.tiff" to ms, which is the MemoryStream, and the code produced a valid multi-page TIFF in the stream. Here's the modified code:
Bitmap multi;
Bitmap page2;
Bitmap page3;
ImageCodecInfo myImageCodecInfo = null;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
// Create three Bitmap objects. imageFiles is a list of strings holding file names
multi = new Bitmap(imageFiles[0]);
page2 = new Bitmap(imageFiles[1]);
page3 = new Bitmap(imageFiles[2]);
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
if (encoders[j].MimeType == "image/tiff"/*mimeType*/)
{
myImageCodecInfo = encoders[j];
break;
}
myEncoder = Encoder.SaveFlag;
myEncoderParameters = new EncoderParameters(1);
myEncoderParameter = new EncoderParameter(myEncoder, (long)EncoderValue.MultiFrame);
myEncoderParameters.Param[0] = myEncoderParameter;
multi.Save(ms, myImageCodecInfo, myEncoderParameters);
// Save the second page (frame).
myEncoderParameter = new EncoderParameter(myEncoder, (long)EncoderValue.FrameDimensionPage);
myEncoderParameters.Param[0] = myEncoderParameter;
multi.SaveAdd(page2, myEncoderParameters);
// Save the third page (frame).
myEncoderParameter = new EncoderParameter(myEncoder, (long)EncoderValue.FrameDimensionPage);
myEncoderParameters.Param[0] = myEncoderParameter;
multi.SaveAdd(page3, myEncoderParameters);
// Close the multiple-frame file.
myEncoderParameter = new EncoderParameter(myEncoder, (long)EncoderValue.Flush);
myEncoderParameters.Param[0] = myEncoderParameter;
multi.SaveAdd(myEncoderParameters);
Another option to save multi-page TIFF to a stream is to use a professional imaging SDK like LEADTOOLS. (Disclosure: I work for its vendor). The code to load a list of images and save them into a TIFF stream is simply this:
using (RasterCodecs codecs = new RasterCodecs())
foreach (string imageFile in imageFiles) // list of strings holding file names
{
RasterImage imagePage = codecs.Load(imageFile);
ms.Position = 0; //rewind the memory stream to the start
codecs.Save(imagePage, ms, RasterImageFormat.TifLzw, 24, 1, 1, -1, CodecsSavePageMode.Append);
}
Another advantage of using this SDK is the flexibility and added features. For example, to change the compression used for any page in the TIFF, replace RasterImageFormat.TifLzw with a different other value such as RasterImageFormat.TifJpeg411 or RasterImageFormat.CcittGroup4 to save using JPEG or FaxG4 compression, respectively. If you would like to try LEADTOOLS, There's a free evaluation here.
I want to pass compressed bitmap to the Bitmap bmp(variable). This code saves compressed bitmap to the file(compressed) but doesn't change bitmap size which is "in variable", I want to pass it to the variable without creating new file and then reading it. Any ideas? Or maybe I just missed some suitable method?
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 30L);
myEncoderParameters.Param[0] = myEncoderParameter;
Bitmap bmp = CaptureDesktopWithCursor2();
bmp.Save(#"C:\Users\sebb\Desktop\TestPhotoQualityFifty.jpg", jpgEncoder, myEncoderParameters);
byte[] lol = imageToByteArray(bmp);
Have you consider to store (or wrap) your data inside a memorystream instead of use a real file ?
MemoryStream memoryStream = new MemoryStream();
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
See that class provided as example
public class BitmapWrapper
{
private MemoryStream _stream = new MemoryStream () ;
public Bitmap GetBitmap ()
{
return new Bitmap ( _stream ) ;
}
public void SetBitmap ( Bitmap value , ImageFormat format )
{
value.Save ( _stream, format ) ;
}
}
My application is MVC5 C#, I use memorystream to generate images using the following:
using (var memStream = new MemoryStream())
{
const int quality = 90;
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)quality);
objImage.Save(memStream, GetImageCodeInfo("image/png"), encoderParameters);
data = this.File(memStream.GetBuffer(), "image/png");
memStream.Dispose();
}
However I get OutOfMemoryException with some files. I was reading about MemoryTributary but could not find a solution to GetBuffer! Would appreciate your suggestions.
How about leaving all the buffer stuff out?
var memStream = new MemoryStream();
const int quality = 90;
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)quality);
objImage.Save(memStream, GetImageCodeInfo("image/png"), encoderParameters);
memStream.Seek(0, SeekOrigin.Begin);
return File(memStream, "image/png");
The FileStreamResult will dispose the MemoryStream, no need to worry about that.
I've a byte array which contains an image binary data in bitmap format. How do I display it using the PictureBox control in C#?
I went thru a couple of posts listed below but not sure if I need to convert the byte array into something else before sending it to a picturebox. I'd appreciate your help. Thanks!
How to put image in a picture box from Bitmap
Load Picturebox Image From Memory?
This function converts byte array into Bitmap which can be use to set the Image Property of the picturebox.
public static Bitmap ByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}
Sample usage:
pictureBox.Image = ByteToImage(byteArr); // byteArr holds byte array value
byte[] imageSource = **byte array**;
Bitmap image;
using (MemoryStream stream = new MemoryStream(imageSource))
{
image = new Bitmap(stream);
}
pictureBox.Image = image;
using System.IO;
byte[] img = File.ReadAllBytes(openFileDialog1.FileName);
MemoryStream ms = new MemoryStream(img);
pictureBox1.Image = Image.FromStream(ms);
or you can access like this directly,
pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);
You can also convert pictureBox image to byte array like this,
MemoryStream ms = new MemoryStream();
pictureBox1.Image.Save(ms,System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] img = ms.ToArray();
The ImageConverter class in the System.Drawing namespace can do the conversion:
byte[] imageArray = **byte array**
ImageConverter converter = new ImageConverter();
pictureButton.Image = (Image)converter.ConvertFrom(imageArray);
If you want to use BinaryReader to convert then use like this,
FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
byte[] img = br.ReadBytes((int)fs.Length);
Try this for Converting Bitmap Images to array of bytes for jpeg pictures and png file types:
public byte[] UdfConvertPicToByte(Bitmap bitmapImages)
{
using (MemoryStream stream = new MemoryStream())
{
bitmapImages.Compress(Bitmap.CompressFormat.Png, 0, stream);
byte[] bitmapData = stream.ToArray();
bitmap.Compress(Bitmap.CompressFormat.Jpeg, 50, stream);
bitmapData = stream.ToArray();
return bitmapData;
}
}
what is the easiest way to translate a Bitmap & Png to string AND BACK AGAIN. Ive been trying to do some saves through memory streams and such but i cant seem to get it to work!
Appearently i wasnt clear,
what i want, is to be able to translate a Bitmap class, with an image in it.. into a system string. from there i want to be able to throw my string around for a bit, and then translate it back into a Bitmap to be displayed in a PictureBox.
Based on #peters answer I've ended up using this:
string bitmapString = null;
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream, ImageFormat.Png);
byte[] bitmapBytes = memoryStream.GetBuffer();
bitmapString = Convert.ToBase64String(bitmapBytes, Base64FormattingOptions.InsertLineBreaks);
}
and
Image img = null;
byte[] bitmapBytes = Convert.FromBase64String(pictureSourceString);
using (MemoryStream memoryStream = new MemoryStream(bitmapBytes))
{
img = Image.FromStream(memoryStream);
}
From bitmap to string:
MemoryStream memoryStream = new MemoryStream();
bitmap.Save (memoryStream, ImageFormat.Png);
byte[] bitmapBytes = memoryStream.GetBuffer();
string bitmapString = Convert.ToBase64String(bitmapBytes, Base64FormattingOptions.InsertLineBreaks);
From string to image:
byte[] bitmapBytes = Convert.FromBase64String(bitmapString);
MemoryStream memoryStream = new MemoryStream(bitmapBytes);
Image image = Image.FromStream(memoryStream);