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 ) ;
}
}
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'm working on a Outlook add-in that's converts attachments. When trying to convert an Tiff attachment to a Bitmap I get:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException. Oveloaded method System.Windows.Media.Imaging.TiffBitmapDecoder.TiffBitmapDecoder has invallid arguments.
The code:
const string PR_ATTACH_DATA_BIN = "http://schemas.microsoft.com/mapi/proptag/0x37010102";
// Retrieve the attachment as a byte array
var attachmentData = myOutlookAttachement.PropertyAccessor.GetProperty(PR_ATTACH_DATA_BIN);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(attachmentData, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
Bitmap bmp = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Thanks to Alex K I found the answer:
const string PR_ATTACH_DATA_BIN = "http://schemas.microsoft.com/mapi/proptag/0x37010102";
// Retrieve the attachment as a byte array
var attachmentData = myOutlookAttachement.PropertyAccessor.GetProperty(PR_ATTACH_DATA_BIN);
Stream stream = new MemoryStream(attachmentData);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
Bitmap bmp = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
I'm trying to import png file as Bitmap and save it as same type(png) but the hash is not the same.
Is it possible to save the bitmap with the same file to get the same hash as before?
What i tried is:
private static void VaryQualityLevel(string filename)
{
// Get a bitmap.
Bitmap bmp1 = new Bitmap(filename);
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Png);
// Create an Encoder object based on the GUID
// for the Quality parameter category.
System.Drawing.Imaging.Encoder myEncoder =
System.Drawing.Imaging.Encoder.Quality;
// Create an EncoderParameters object.
// An EncoderParameters object has an array of EncoderParameter
// objects. In this case, there is only one
// EncoderParameter object in the array.
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
myEncoderParameters.Param[0] = myEncoderParameter;
bmp1.Save(#"c:\TestPhotoQualityFifty.png", jpgEncoder, myEncoderParameters);
myEncoderParameter = new EncoderParameter(myEncoder, 100L);
myEncoderParameters.Param[0] = myEncoderParameter;
bmp1.Save(#"c:\TestPhotoQualityHundred.png", jpgEncoder, myEncoderParameters);
// Save the bitmap as a JPG file with zero quality level compression.
myEncoderParameter = new EncoderParameter(myEncoder, 0L);
myEncoderParameters.Param[0] = myEncoderParameter;
bmp1.Save(#"test5.png", jpgEncoder, myEncoderParameters);
}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
Fundamentally, I wouldn't expect loading an image and then encoding it again to result in the exact same bytes, which is what your hash would depend on. Different encoders will have different implementations - I could imagine situations where even encoders which encoded semantically equivalent information could do so using a different byte output. (In some cases, even the same encoder could do that, if it included a timestamp or a GUID or something similar.)
If you require the exact same bytes as the original file, I'd just copy the file instead.
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);
}
}
My code currently looks like this:
if (fe == "CR2")
{
Image img = null;
byte[] ba = File.ReadAllBytes(open.FileName);
using (Image raw = Image.FromStream(new MemoryStream(ba)))
{
img = raw;
}
Bitmap bm = new Bitmap(img);
pictureBox1.Image = bm;
statusl.Text = fe;
}
When I open a RAW image the program stops and Visual Studio says:
Parameter is not valid: Image raw = Image.FromStream(new MemoryStream(ba))
Please help! How can I get a RAW file to show in a PictureBox ?
Create the bitmap like this:
Bitmap bmp = (Bitmap) Image.FromFile(open.FileName);
or without using bitmap:
this.pictureBox1.Image = Image.FromFile(open.FileName);
Example WPF:
BitmapDecoder bmpDec = BitmapDecoder.Create(new Uri(origFile),
BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
BitmapEncoder bmpEnc = new BmpBitmapEncoder();
bmpEnc.Frames.Add(bmpDec.Frames[0]);
Stream ms = new MemoryStream();
bmpEnc.Save(ms);
Image srcImage = Bitmap.FromStream(ms);
You're actually disposing an Image by specifying using (Image raw = Image.FromStream(new MemoryStream(ba))) later assigning the Disposed instance of image to picturebox which leads to this exception. To make to work you've to either don't dispose or clone the image.
Bitmap raw = Image.FromStream(new MemoryStream(ba) as Bitmap;
pictureBox1.Image = raw;
Or simply Clone
using (Image raw = Image.FromStream(new MemoryStream(ba)))
{
img = raw.Clone() as Bitmap;
}
Both of the above should work
you try this code :
private static void SaveImageToRawFile(string strDeviceName, Byte[] Image, int nImageSize)
{
string strFileName = strDeviceName;
strFileName += ".raw";
FileStream vFileStream = new FileStream(strFileName, FileMode.Create);
BinaryWriter vBinaryWriter = new BinaryWriter(vFileStream);
for (int vIndex = 0; vIndex < nImageSize; vIndex++)
{
vBinaryWriter.Write((byte)Image[vIndex]);
}
vBinaryWriter.Close();
vFileStream.Close();
}
private static void LoadRawFile(string strDeviceName, out Byte[] Buffer)
{
FileStream vFileStream = new FileStream(strDeviceName, FileMode.Open);
BinaryReader vBinaryReader = new BinaryReader(vFileStream);
Buffer = new Byte[vFileStream.Length];
Buffer = vBinaryReader.ReadBytes(Convert.ToInt32(vFileStream.Length));
vBinaryReader.Close();
vFileStream.Close();
}