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);
}
}
Related
I have a program in .NETCoreApp 3.1 and I use the SixLabors.ImageSharp version 2.1.3 to handle the processing of images in our CDN for all our sites.
The application run in docker container linux and for testing locally, I use WSL2 with ubuntu.
We are planning to review all our image and provide better format with a compressed version et a webp version.
For now the objective was to use TunyPNG (https://tinypng.com/developers/reference/dotnet) to provide a compressed and webp version from the original image and use Imagesharp to resize these versions in the different dimensions that exists.
The first tests were successfull but I found a problem. I had an original transparent PNG (587x444) with filesize of 244k. The compressed version is 70k and webp converted is 16k using TinyPNG API.
The process after is to resize these versions and then i have a problem. The webp version resized to 581x439 using Imagesharp library has a filesize of 266k.
Here is the part of the code :
...
using var image = Image.Load(absoluteFilePath, out var format);
image.Mutate(x => x.Resize(width, height));
using var fileStream = new FileStream(convertedAbsolutePath, FileMode.Create);
await image.SaveAsync(fileStream, format);
...
I tried to force webp encoder and decoder but no amelioration.
With an image without transparent background, I don't have this problem.
If I use TinyPNG to do the resize, i don't have this problem and the filesize is 14k.
Is there some configurations to change or is it a problem with the Imagesharp library ?
You can compress the image more when you use a IImageEncoder in the SaveAsyncinstead of the format. For Webp the encoder would be WebpEncoder. That has a few properties that you can change for example the NearLosslessQuality or Quality, which you can decrease to get a smaller file size. If you want to use the same method to process multiple formats you can save them as IImageEncoder and put that in SaveAsync. An example how you could do that is the following:
using var image = Image.Load(absoluteFilePath, out var format);
image.Mutate(x => x.Resize(width, height));
var encoder = GetImageEncoder(format);
using var fileStream = new FileStream(convertedAbsolutePath, FileMode.Create);
await image.SaveAsync(fileStream, encoder);
Where GetImageEncoder(IImageFormat format) is defined the following:
static IImageEncoder GetImageEncoder(IImageFormat format) {
if(format is WebpFormat) {
return new WebpEncoder() {
Quality = 20,
NearLosslessQuality = 50,
};
} else if(format is PngFormat) {
return new PngEncoder();
}
throw new NotSupportedException();
}
The Quality and NearLosslessQuality have to be in the range between 0 and 100, where a 100 is highest Quality and 0 is the worst.
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 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);
I have an image with the color format YCbCr (CMYK). When I load this with the .NET bitmap and save the result, the color format will change to RBG and the filze increase from ~1.600 KB to ~7.500 KB.
using (Image bitmap = Image.FromFile(#"C:\Test\Original.tif"))
{
bitmap.Save(#"C:\Test\result.tif");
}
The sample file is a multi frame Tiff file with the color format YCbCr (CMYK) and it is 24 bit color deep. Its a Tiff container that includes two JPEG files with no compression.
Here you can download the sample file: https://ufile.io/9x21n
I already tried a OpenCV wrapper Nuget Package (https://github.com/shimat/opencvsharp), but the result is the same.
Mat imageForProcessing = Cv2.ImRead(#"C:\Test\Original.tif");
Cv2.ImWrite(#"C:\Test\result.tif", imageForProcessing);
Is there a free third party library, a .NET or OpenCV way to read and write the image without lossing the color format and increasing the file size five times?
Update 1
I tried the TiffBitmapEncoder class with the following source code. The bit deep is now correct (decreases the result from ~7.500 KB to ~6.400 KB), but the output is againg in RGB.
FileStream stream = new FileStream(#"C:\Test\original.tif", FileMode.Open);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(stream));
encoder.Save(stream);
I would appreciate any help!
Regards
Sascha
Our users needed the ability to change images (rotate\insert scan between pages etc) so I wrote an app that allows them to load a Tiff image and edit as needed.
The problem I have is that when the image is saved it is saved as black and white and not in color. The loaded image displays in color no problem so it seems that it is the saving that is causing the issue.
This is the code I am using to save the Tiff image from an array of BitmapSource:
//create encoder
TiffBitmapEncoder tiffEncoder = new TiffBitmapEncoder();
tiffEncoder.Compression = TiffCompressOption.Ccitt4;
//loop through images and create frames
foreach (BitmapSource src in bitmapList)
{
BitmapFrame bmFrame = BitmapFrame.Create(src);
tiffEncoder.Frames.Add(bmFrame);
}
//create stream to write file
FileStream fStream = null;
try
{
fStream = new FileStream(path, FileMode.Create);
//Save Tiff image to disc
tiffEncoder.Save(fStream);
}
finally
{
if (fStream != null)
{
fStream.Close();
}
}
When I look at the properties of the original file it has a bit depth of 24 but the newly saved image has a bit depth of 1 (black and white?), is this the problem?
I am new to working with Tiffs so any guidance would be greatly appreciated.
Please let me know if you need any other info.
Tx
The images are probably converted to black/white, because of the TiffCompressOption.Ccitt4 compression in use. The CCITT compressions are intended for fax documents, and only supports black/white images.
Instead, use a more general purpose compression, like LZW or Deflate. This will let the image data pass unchanged as 24 or 32 bit color data.