I have the following code for changing the DPI of an image:
public void changeDPI(string imagePathSource,string imagePathDestination,float DPIx,float DPIy)
{
Bitmap bitmap = new Bitmap(imagePathSource);
Bitmap newBitmap = new Bitmap(bitmap);
newBitmap.SetResolution(DPIx,DPIy);
newBitmap.Save(imagePathDestination);
}
However, this ends up changing the memory size of the file. An example test image started at 267 KB, and the newBitmap version of the file ended up as 1.51 MB. How can I change the DPI without changing the file size?
Why are you making a new bitmap out of it? That converts the image to 32bpp ARGB, and loses the connection to the original loaded data.
The original loaded image's file format is available in the RawFormat property. So just set the original bitmap's resolution to your new one, and save it to the new path using bitmap.RawFormat:
public void changeDPI(String imagePathSource, String imagePathDestination, Single dpiX, Single dpiY)
{
using (Bitmap bitmap = new Bitmap(imagePathSource))
{
bitmap.SetResolution(dpiX, dpiY);
bitmap.Save(imagePathDestination, bitmap.RawFormat);
}
}
I believe that this way, it won't even recompress the actual image content, meaning you have no further quality degrading due to reapplying the jpeg compression.
Also, do make sure you always either call Dispose() on image objects, or use them in a using block. They are objects backed by unmanaged sources (GDI+ objects), so you have to be careful or they'll pollute your program's memory, and keep locks on the files you opened.
On the note of locked files, if you give the same path for both imagePathSource and imagePathDestination you'll get an error about exactly that. To get around this, read the bytes from the image in advance, and use a MemoryStream to load the image:
public void changeDPI(String imagePathSource, String imagePathDestination, Single dpiX, Single dpiY)
{
Byte[] fileData = File.ReadAllbytes(imagePathSource);
using (MemoryStream ms = new MemoryStream(fileData))
using (Bitmap loadedImage = new Bitmap(ms))
{
bitmap.SetResolution(dpiX, dpiY);
bitmap.Save(imagePathDestination, bitmap.RawFormat);
}
}
I think you must indicate the format of the output file, to save as a compressed image format like JPEG.
newBitmap.Save(imagePathDestination, System.Drawing.Imaging.ImageFormat.Jpeg);
Related
image color is becoming inverted when bitmap is converted to byte array and then to memory stream and saved. This code was part of the dynamic image creation at my site ommrudraksha.com
using (var bmp = new System.Drawing.Bitmap(width + 10, height + 10))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.Flush();
bmp.Save("ss.jpg");
}
}
Above code saves properly the image.
But when the bmp is converted to memorystream and saved, the background becomes black.
Below code generates black image.
var memStream = new MemoryStream();
bmp.Save(memStream, ImageFormat.Jpeg);
var bytes = memStream.ToArray();
var ms2 = new MemoryStream(bytes);
Bitmap.FromStream(ms).Save("ss1.jpg");
Let's examine your drawing code first:
bmp.Save("ss.jpg");
In your case, this operation is actually saving as the "PNG" file format, regardless of the file name. I found this out using a hex editor. Windows is smart enough to check the file header, though, so most likely you can still preview or otherwise open it even with a wrong extension name. You can also explicitly specify an output format with a second argument.
By default, a new PNG will also be transparent. Some image formats may default to black, even though they support alpha channel (e.g. BMP and GIF). This means that if you really want to save as a BMP, you'll have to do some additional processing.
That brings me to why your output image is black. Jpeg does not support transparency at all, so when the transparent PNG was converted, the Jpeg defaulted to black. See this post.
If you need transparency, you'll have to use an image format that supports it. You may also have to clear the entire rectangle first, depending on the format.
I'm having issues trying to use a PNG image with a build action of 'Resource' with the map marker generation of GMap.NET, which expects type Bitmap.
I create a BitmapImage using the pack Uri, and then convert the BitmapImage to a Bitmap by using the code provided by Sascha Henning in their answer here:
Converting BitmapImage to Bitmap and vice versa
The resulting code is as follows:
BitmapImage bmi = new BitmapImage(
new Uri("pack://application:,,,/Images/MapMarker_JobComplete.png"));
System.Drawing.Bitmap markerBitmap = Helpers.BitmapImageToBitmap(bmi);
GMarkerGoogle marker = new GMarkerGoogle(jobLoc, markerBitmap);
Where Helpers.BitmapImageToBitmap() is:
public static Bitmap BitmapImageToBitmap(BitmapImage bitmapSource)
{
using( MemoryStream outstream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(outstream);
return new System.Drawing.Bitmap(outstream);
}
}
The above works, but the image is rendered with a black background instead of a transparent background. I can only assume that the conversion to a BitmapImage or from a BitmapImage to Bitmap is responsible.
I found a reference to needing to save PNG images to a lower depth, but with currently installed software (Affinity), I couldn't export with a bit depth of 16 as said, only PNG-8 (bit depth of 8; red background, presumaby due to reduced quality) or the standard PNG-24 (bit depth of 32; black background)
PNG-24 PNG-8
Currently to get the desired results (transparency supported), I use the PNG with a build action of 'Content', with the following code:
System.Drawing.Image imgMarker = System.Drawing.Bitmap.FromFile(
System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
"Images/MapMarker_JobComplete.png"));
GMarkerGoogle marker = new GMarkerGoogle(jobLoc, (System.Drawing.Bitmap)imgMarker);
The above works fine, so Bitmap must support transparency, but this leaves me with the PNG files as content and I would prefer them embedded into the executable, as well as be able to use the pack Uri in XAML if/when needed.
It's clear that there's some knowledge that I'm missing or haven't been able to glean out, and there's quite a few topics about people wanting to preserve transparency, but with the end result of a BitmapImage and not a Bitmap like I require.
Copying user2819245's old comment here, as it was/is the answer:
I am not sure, but i guess (based on your explanation) that BmpBitmapEncoder does not support (creating) 32bpp BMPs. Why don't you try using PngBitmapEncoder instead?
in my c# application load form i make load like 30 picture and this make my memory full and if I add more pictures I'll get the message "that my memory is full"
how can I free my memory or increase the maximum used memory?
is there are any another way to get all my pictures to picture box without filling my memory?
1) how can I free my memory or increase the maximum used memory?
Are you receiving an OutOfMemoryException and your memory isn't filled up in your machine? This might be due to x86 compilation and you should change it to x64.
2) is there are any another way to get all my pictures to picture box without filling my memory?
Yes and no. If you are using Bitmap it will allocate the image in the memory. If you are recreating the image later with all the images together without compressing it, then you could be also losing some memory if you are not disposing the Bitmaps correctly. You could load them, compress them, and then dispose the original one, keeping the compressed/optimized one in memory.
Besides, remember to always dispose your Bitmap after using it. The GC usually takes care of them, but you should always dispose them:
using (Bitmap bitmap = new Bitmap("file.jpg")
{
// bitmap handling here
}
Of course, if you are displaying it in a picturebox you can't dispose it, but again, you are not being completely clear in your question.
Without your code I cannot provide you a better answer, and I'll gladly edit this one if you update your question.
Read:
Compress bitmap before sending over network
When do I need to use dispose() on graphics?
Update
Upon fixing your thread I've noticed you've given an image but the formatting was broken.
Read my answer and it should help you (specially the part of compression). You can also change to x64.
Update 2 - Compression code example
public void ExampleMethod()
{
pictureBox1.Image = GetCompressedFile("file.jpg", quality: 10);
}
private Image GetCompressedFile(string fileName, long quality)
{
using (Bitmap bitmap = new Bitmap(fileName))
{
return GetCompressedBitmap(bitmap, quality);
}
}
private Image GetCompressedBitmap(Bitmap bmp, long quality)
{
using (var mss = new MemoryStream())
{
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
ImageCodecInfo imageCodec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(o => o.FormatID == ImageFormat.Jpeg.Guid);
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = qualityParam;
bmp.Save(mss, imageCodec, parameters);
return Image.FromStream(mss);
}
}
In the quality parameter you set the quality. 100 means 100%, 50 means 50%.
Try setting quality as 10 and see if it works.
Remove all Bitmap bmp = new Bitmap from the code, and display with your PictureBoxInstance.Image = GetCompressedFile(...);
Check the ExampleMethod().
Code based from: https://stackoverflow.com/a/48274706/4352946
You have to remember though, that even using Dispose, the memory won’t be freed right away, you should wait the GC for that.
P.S: compressing the file on-the-go you MIGHT end up with both images (the compressed and uncompressed) in the memory
In my program, I compress a BMP into a JPEG like this:
private void ConvertBmpToStreamJPG30(Bitmap b, Stream s)
{
s.Flush();
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 30L);
b.Save(s, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
Then a function is receiving the JPEG in a MemoryStream, I transform it into a Bitmap by doing
Bitmap b = new Bitmap(stream);
When I display the image, there are a lot of lines like this :
What am I doing wrong, people?
Edit 1
Here a small visual studio solution showing the problem: http://www.fast-files.com/getfile.aspx?file=79311
It is the beginning of a screen sharing software. What it does: It takes screenshots, compare them, compress the difference and send it to another part of the program that decompress it and recompose an image with everything received. It opens a window displaying what is "sent" on the left and the recomposed image on the right.
Three things come to mind:
Try setting a better quality than 30 and see if that helps;
Check your RAM (and possibly video RAM, though I doubt that GDI+ might use VGA for compression) for hardware problems;
I've had a similar weird problem where I loaded some JPEG file, modified it a bit, and then saved it again. That produced an exception. The solution was to make a new bitmap based on the old one and save the copy. Try that.
My WPF application already can make image resizing and text watermark. My app converts a 4MB image to 600 KB image when converted image sizes are 700px x 700px and watermark text is 30 pt.
How can I reduce image size (600 KB to 250 KB or smaller)?
Should I use which library or code sample considering my application is written using WPF, C# and .NET 4?
There are two ways for reducing image size: reduce resolution or use compression parameters of the format you are using (ie. jpeg compression is based on cosine transformation which enables you to control quality (and size) of final image).
I've used BitmapSource extension method to control quality of Jpeg images I save. Maybe you will find it usefull:
public static void SaveBitmapSourceAsJpeg(this BitmapSource image, string fileName, int quality)
{
using (var fileStream = new FileStream(fileName, FileMode.Create))
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.QualityLevel = quality;
encoder.Save(fileStream);
}
}