Imagesharp resizes my image larger than the original - c#

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.

Related

image color is becoming inverted when bitmap is converted to byte array and then to memory stream and saved

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.

Trying to change image DPI without changing file size

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);

Convert first page to tif using Magick.NET

Following gm convert command converts first page of source.pdf to output.tif
convert source.pdf[0] output.tif
I wonder how to do it with Magick.NET library? Following code does not work for me.
using (MagickImage image = new MagickImage("source.pdf"))
{
image.Write("output.tif");
}
ImageMagick cannot handle PostScript and PDF files itself and by its own,
for this it uses a third party software called Ghostscript.
So, you need to install the latest version of GhostScript before you can convert a pdf using Magick.NET.
After installing GhostScript use following code to extract first page to TIF-file.
using (MagickImageCollection image = new MagickImageCollection())
{
MagickReadSettings settings = new MagickReadSettings();
settings.Density = new Density(300, 300); // Settings the density to 300 dpi will create an image with a better quality
settings.FrameIndex = 0; // First page
settings.FrameCount = 1; // Number of pages
image.Read(#"source.pdf", settings);
image.Write(#"output.tif");
}
You can adjust quality of resulting TIF by changing settings.Density param (300 dpi is for high quality offset/digital printing, 72 dpi is ok for monitor screens only).
I am not a ImageMagick Magick.NET expert, but have you tried simply add [0] to your command as
using (MagickImage image = new MagickImage("source.pdf[0]"))
{
image.Write("output.tif");
}
ImageMagick does require Ghostscript to be installed to read PDF files As was mentioned previously.

Resizing and cropping images using ImageResizer

I'm trying to resize and then square-crop incoming images. I have my image in a ReadOnlyStream and would like to output to MemoryStream.
I'm using ImageResizer library to do this.
I'd like my images to first reduce in size and then center-square-crop them. I'm using this code, but it doesn't produce what I require. It produces nothing...
var resultStream = new MemoryStream();
ImageJob job = new ImageJob(imageStream, resultStream, new Instructions {
Width = 100,
Height = 100,
Mode = FitMode.Crop
});
job.Build();
This code should downsample large images and crop them based on library defaults (center cropping).
I didn't provide any specific configuration in web.config because as I understand things it's not required.
What am I doing wrong?
ImageResizer does not reset the output stream position to 0 after writing to it, as this would break non-seekable write streams like HttpResponseStream.
You need to call resultStream.Seek(0, SeekOrigin.Begin); before reading from it.

(WPF C#) Image watermark and resize

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);
}
}

Categories