When i save an image to an memoriestream in Exif Format i get an ArgumentNullException, Der Wert darf nicht NULL sein.Parametername: encoder
Here is my code:
Image image = Image.FromFile(#"C:\Users\tkaemmerling\Pictures\alpha-test-photo.jpg");
image.Save(#"C:\Users\tkaemmerling\Pictures\alpha-test-photo.Exif", ImageFormat.Exif);
using (MemoryStream memstream = new MemoryStream())
{
image.Save(memstream, ImageFormat.Exif);
}
How can i save the image in exif format to an memory stream?
Actually I don't think it works even for saving file on disk. If you open the saved file it has PNG in the header. If you check the internals, the save method looks like this:
public void Save(string filename, ImageFormat format)
{
if (format == null)
{
throw new ArgumentNullException("format");
}
ImageCodecInfo imageCodecInfo = format.FindEncoder();
if (imageCodecInfo == null)
{
imageCodecInfo = ImageFormat.Png.FindEncoder();
}
this.Save(filename, imageCodecInfo, null);
}
Problem is there is no encoder associated with the EXIF format - try debugging
var imageEncoders = ImageCodecInfo.GetImageEncoders();
So you may as well use
Image image = Image.FromFile(#"C:\Users\tkaemmerling\Pictures\alpha-test-photo.jpg");
image.Save(#"C:\Users\tkaemmerling\Pictures\alpha-test-photo.Exif", ImageFormat.Png);
using (MemoryStream memstream = new MemoryStream())
{
image.Save(memstream, ImageFormat.Png);
}
Related
let's assume I let the user choose an image from the computer. I load the file to a picture box. here is the conversion method:
public static Image LoadImageFromFile(string fileName)
{
Image result = null;
if (!File.Exists(fileName))
return result;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
try
{
using (Image image = Image.FromStream(fs))
{
ImageManipulation.RotateImageByExifOrientationData(image);
result =(Image) image.Clone();
image.Dispose();
}
}
finally
{
fs.Close();
}
return result;
}
then, when the user clicks on the Save button, I convert the image into a byte array and save it into the database. here is the conversion code:
public static byte[] ImageToByteArray(Image image)
{
if (image == null)
return null;
using (var ms = new MemoryStream())
{
ImageFormat imageFormat = image.RawFormat;
ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.FormatID == imageFormat.Guid);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
if (codec != null)
image.Save(ms, codec, encoderParameters);
else
image.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
}
but the problem:
I have a jpg file on the disk. I can load it into my picture box without any problem. the picture is perfectly visible in it. but when I save it, the code gives me "A generic error occurred in GDI+." Error at 'image.Save(ms, codec,encoderParameters)' line.
more odd incident: I don't get this error all the time. it is happening with specific images. for example, I downloaded an image from the internet and crop it in "Paint" and saved it as jpg. error happened. open it in Paint again and save it as png. no error!!!! that is why I am really confused . and Yes I already have tried to don't dispose the image. not helping
I know it might be a stupid question but I am desperately stuck here :)
Thank you in Advanced
I didn't find out why my code is not working but I just substitute my loading method with the code below. it is releasing the file and at the same time works as it should.
public static Image LoadImageFromFile(string fileName)
{
using (Bitmap bmb = new Bitmap(fileName))
{
MemoryStream m = new MemoryStream();
bmb.Save(m, ImageFormat.Bmp);
return Image.FromStream(m);
}
}
I'm trying to make an console application in .net core 3, this application can successfully get a bitmap from the computer and send it to another computer. But now I want to lower the quality of the image and send it as a JPEG file.
I have looked on the internet but I'm unable to get it to work. My latest attempt I tried to use the following MSDN website. But with this approach I get stuck at ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg); since my application cannot find GetEncoder. I have found GetEncoder in System.Text.Encoding.Getencoding but here getEncoder doesn't take any parameters.
So my question is which namespace do I have to use or is there another way to lower the quality of an JPEG image?
Here is my functions to compress image as jpeg
public static System.Drawing.Imaging.ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
{
System.Drawing.Imaging.ImageCodecInfo[] codecs = System.Drawing.Imaging.ImageCodecInfo.GetImageDecoders();
foreach (System.Drawing.Imaging.ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
public static byte[] GetCompressedImage(System.Drawing.Image img, long quality)
{
System.Drawing.Imaging.ImageCodecInfo ici = GetEncoder(System.Drawing.Imaging.ImageFormat.Jpeg);
System.Drawing.Imaging.EncoderParameters eps = new System.Drawing.Imaging.EncoderParameters(2);
eps.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
eps.Param[1] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)System.Drawing.Imaging.EncoderValue.CompressionLZW);
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ici, eps);
return ms.ToArray();
}
}
I am trying to save a bitmap to a MemoryStream and then convert it to string. But the problem is, I am having an error that says the line img.Save(m, img.RawFormat); cannot be null. The error is this
The bitmap is from a fingerprint scan, that I converted to image. Now I want to convert its data to a string, by using MemoryStream. This is for the fingerprint data to be saved in the database. I don't know where I went wrong. You can find my code below:
Bitmap bitmap;
bitmap = ConvertSampleToBitmap(Sample);
Bitmap img = new Bitmap(bitmap, fingerprint.Size);
this.Invoke(new Function(delegate () {
fingerprint.Image = img; // fit the image into the picture box
}));
string ping;
using (MemoryStream m = new MemoryStream())
{
img.Save(m, img.RawFormat);
ping = m.ToString();
}
I hope for an accurate answer that can pin point the major error and what parts of the code should I change.
Though any help would be much appreciated.
Interesting; what happens here is:
public void Save(Stream stream, ImageFormat format)
{
if (format == null)
{
throw new ArgumentNullException("format");
}
ImageCodecInfo encoder = format.FindEncoder();
this.Save(stream, encoder, null);
}
with the inner Save doing this check:
public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (encoder == null)
{
throw new ArgumentNullException("encoder");
}
so; we can assume that format.FindEncoder(); is returning null here. As it happens, this is indeed the default if there is no matching codec:
internal ImageCodecInfo FindEncoder()
{
foreach (ImageCodecInfo info in ImageCodecInfo.GetImageEncoders())
{
if (info.FormatID.Equals(this.guid))
{
return info;
}
}
return null;
}
So basically, it isn't clear, but the problem is: there is no encoder found for the image format that you're using. Try saving as a well-known format, not necessarily the one that it loaded from. Maybe use ImageFormat.Png and save it as a png?
img.Save(m, ImageFormat.Png);
and as already mentioned in the comments, to get base-64 of that, you'll need:
ping = Convert.ToBase64String(m.GetBuffer(), 0, (int)m.Length);
I am using the following code to compress an image and it does a nice job but I want to use the compressed image not save it. So right now I have to save the image then read it in again which is slow. Is there a way of compressing it with out saving it.
private void compress(System.Drawing.Image img, long quality, ImageCodecInfo codec)
{
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
img.Save("check1.jpg", codec, parameters);
}
private static ImageCodecInfo GetCodecInfo(string mimeType)
{
foreach (ImageCodecInfo encoder in ImageCodecInfo.GetImageEncoders())
if (encoder.MimeType == mimeType)
return encoder;
throw new ArgumentOutOfRangeException(
string.Format("'{0}' not supported", mimeType));
}
There is an overload that takes a Stream so you can save it straight to a MemoryStream and won't need to save to disk/reload.
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
var ms = new MemoryStream();
img.Save(ms, codec, parameters);
//Do whatever you need to do with the image
//e.g.
img = Image.FromStream(ms);
The reason you're getting the "Parameter not valid" exception you mention in the comments is because the image isn't being disposed of before you try to call FromStream, so you'll need to dispose it. Also, I don't know how you're calling this method, but you should probably update it to return the MemoryStream.
private MemoryStream compress(System.Drawing.Image img, long quality, ImageCodecInfo codec)
{
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
var ms = new MemoryStream();
img.Save(ms, codec, parameters);
return ms;
}
public void MyMethod()
{
MemoryStream ms;
using(var img = Image.FromFile("myfilepath.img"))
{
ms = compress(img, /*quality*/, /*codec*/);
}
using(var compressedImage = Image.FromStream(ms))
{
//Use compressedImage
}
}
Notice how I return ms from compress and capture it. Also, more importantly, how we wrap the initial img in a using statement which will dispose the file handle correctly, and after that gets disposed create the second compressedImage which is also in a using so it will also get disposed of properly when you're done.
I have an operation on the site that takes crops an image, however the resultant, cropped image is coming out significantly larger in terms of file size (original is 24k and the cropped image is like 650k). So I found that I need to apply some compression to the image before saving it. I came up with the following:
public static System.Drawing.Image CropImage(System.Drawing.Image image, Rectangle cropRectangle, ImageFormat format)
{
var croppedImage = new Bitmap(cropRectangle.Width, cropRectangle.Height);
using (var g = Graphics.FromImage(croppedImage))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(
image,
new Rectangle(new Point(0,0), new Size(cropRectangle.Width, cropRectangle.Height)),
cropRectangle,
GraphicsUnit.Pixel);
return CompressImage(croppedImage, format);
}
}
public static System.Drawing.Image CompressImage(System.Drawing.Image image, ImageFormat imageFormat)
{
var bmp = new Bitmap(image);
var codecInfo = EncoderFactory.GetEncoderInfo(imageFormat);
var encoder = System.Drawing.Imaging.Encoder.Quality;
var parameters = new EncoderParameters(1);
var parameter = new EncoderParameter(encoder, 10L);
parameters.Param[0] = parameter;
using (var ms = new MemoryStream())
{
bmp.Save(ms, codecInfo, parameters);
var resultImage = System.Drawing.Image.FromStream(ms);
return resultImage;
}
}
I set the quality low just to see if there was any change at all. There isn't. The crop is being saved correctly appearance-wise but compression is a no joy. If I bypass CompressImage() altogether, neither the file size nor the image quality appear to be any different.
So, 2 questions. Why is nothing happening? Is there a simpler way to compress the resultant image to "web-optimize" similar to how photoshop saves web images (I thought it just stripped a lot of info out of it to reduce the size).
Your problem is you must 'compress' (really encode) the image as you save it, not before you save it. An Image object in your program is always uncompressed.
By saving to the MemoryStream and reading back out from the stream will encode the image and then decode it back to the same size again (with some quality loss in the process if you are using JPEG). However, if you save it to a file with the compression parameters, you will get a compressed image file.
Using this routine with JPEG quality level 90 on a 153 KB source image gives an output image of 102 KB. If you want a smaller file size (with more encoding artifacts) change the encoder parameter to something smaller than 90.
public static void SaveJpegImage(System.Drawing.Image image, string fileName)
{
ImageCodecInfo codecInfo = ImageCodecInfo.GetImageEncoders()
.Where(r => r.CodecName.ToUpperInvariant().Contains("JPEG"))
.Select(r => r).FirstOrDefault();
var encoder = System.Drawing.Imaging.Encoder.Quality;
var parameters = new EncoderParameters(1);
var parameter = new EncoderParameter(encoder, 90L);
parameters.Param[0] = parameter;
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
image.Save(fs, codecInfo, parameters);
}
}
I believe you shouldn't dispose of the MemoryStream while you are using an image created using Image.FromStream that refers to the stream. Creating a Bitmap directly from the stream also doesn't work.
Try this:
private static Image CropAndCompressImage(Image image, Rectangle rectangle, ImageFormat imageFormat)
{
using(Bitmap bitmap = new Bitmap(image))
{
using(Bitmap cropped = bitmap.Clone(rectangle, bitmap.PixelFormat))
{
using (MemoryStream memoryStream = new MemoryStream())
{
cropped.Save(memoryStream, imageFormat);
return new Bitmap(Image.FromStream(memoryStream));
}
}
}
}