I need to show the preview thumbnails of high resolution images in a control for user selection. I currently use ImageListView to load images.
This works fine for low to medium resolution images.But when it comes to showing thumbnails of very high resolution images there is a noticeable delay.Sample image can be downloaded from https://drive.google.com/open?id=1Qgu_aVXBiMlbHluJFU4fBvmFC45-E81C
The image size is around 5000x3000 pixels and size is around 12 MB.The issue can be replicated by using 1000 copies of this image.
The issue screen capture is uploaded here
https://giphy.com/gifs/ZEH3T3JTfN42OL3J1A
The images are loaded using a background worker
foreach (var f in filepaths)
{
imageListView1.Items.Add(f);
}
1. In order to solve this issue I tried resizing large resolution images and adding the resized image to ImageListView ... but for resizing there is a heavy time consumption and thumbnail generation is slow.
Bitmap x = UpdatedResizeImage2(new Bitmap(f), new Size(1000, 1000));
string q = Path.GetTempPath() + Path.GetFileName(f);
x.Save(Path.GetTempPath() + Path.GetFileName(f));
x.Dispose();
imageListView1.Items.Add(Path.GetTempPath() + Path.GetFileName(f));
2. I have also tried Image.CreateThumbnail Method but this is also quite slow.
Is there a better way to solve this issue?
I would suggest using image processing library such ImageMagick.
ImageMagick has optimized this feature and you have Magick.NET a nuget package for .NET.
It is simple and straight forward:
var file = new FileInfo(#"c:\temp\input.jpg");
using (MagickImage image = new MagickImage(file))
{
{
image.Thumbnail(new MagickGeometry(100, 100));
image.Write(#"C:\temp\thumbnail.jpg");
}
}
example I made:
Here is some documentation and references that might be useful:
https://imagemagick.org/Usage/thumbnails/#creation
http://www.imagemagick.org/Usage/thumbnails/
https://github.com/dlemstra/Magick.NET
https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
https://devblogs.microsoft.com/dotnet/net-core-image-processing/
https://weblogs.asp.net/bleroy/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi
Alternatives to System.Drawing for use with ASP.NET?
You could use WPF interop and use the DecodePixelWidth/Height properties. They use underlying Windows imaging layer technology ("Windows Imaging Component") to create an optimized thumbnail, saving lots of memory (and possibly CPU): How to: Use a BitmapImage (XAML)
You can also use WPF/WIC by code, with a code like this (adapted from this article The fastest way to resize images from ASP.NET. And it’s (more) supported-ish.. You just need to add a reference to PresentationCore and WindowsBase which shouldn't be an issue for a desktop app.
// needs System.Windows.Media & System.Windows.Media.Imaging (PresentationCore & WindowsBase)
public static void SaveThumbnail(string absoluteFilePath, int thumbnailSize)
{
if (absoluteFilePath == null)
throw new ArgumentNullException(absoluteFilePath);
var bitmap = BitmapDecoder.Create(new Uri(absoluteFilePath), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];
int width;
int height;
if (bitmap.Width > bitmap.Height)
{
width = thumbnailSize;
height = (int)(bitmap.Height * thumbnailSize / bitmap.Width);
}
else
{
width = (int)(bitmap.Width * thumbnailSize / bitmap.Height);
height = thumbnailSize;
}
var resized = BitmapFrame.Create(new TransformedBitmap(bitmap, new ScaleTransform(width / bitmap.Width * 96 / bitmap.DpiX, height / bitmap.Height * 96 / bitmap.DpiY, 0, 0)));
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(resized);
var thumbnailFilePath = Path.ChangeExtension(absoluteFilePath, thumbnailSize + Path.GetExtension(absoluteFilePath));
using (var stream = File.OpenWrite(thumbnailFilePath))
{
encoder.Save(stream);
}
}
Otherwise there are lots of tools out there like MagicScaler, FreeImage ImageSharp, ImageMagick, Imazen, etc. Most were written for ASP.NET/Web server scenarios (for which WPF is officially not supported but works, read the article) and are also cross-platform which you don't seem to need. I'm not sure they're generally faster or use less memory than builtin Windows technology, but you should test all this in your context.
PS: otherwise there's no magic bullet, bigger images take more time.
There's also NetVips, the C# binding for libvips.
It's quite a bit quicker than Magick.NET: between 3x and 10x faster, depending on the benchmark.
Thumbnailing is straightforward:
using NetVips;
var image = Image.Thumbnail("some-image.jpg", 128);
image.WriteToFile("x.jpg");
There's an introduction in the documentation.
Most of answers approach is to resize bitmap and then save it. Its a bit offcourse slow, specially if you say very high resolution.
Why not use existing thumbnail created by windows explorer ? This is fastest way of all (specially if you use smaller thumbnails).
//https://stackoverflow.com/a/1751610
using Microsoft.WindowsAPICodePack.Shell;
var shellFile = ShellFile.FromFilePath(pathToYourFile); Bitmap
Image image = shellFile.Thumbnail.LargeBitmap;
Nuget : https://www.nuget.org/packages/WindowsAPICodePack-Shell (around 600KB)
Note: Its same as others, if thumbnail arent cached already.
Related
It's my first time resizing images in ASP.NET Core, so after a bit of research I found this approach to be the easiest and most efficient and I implemented it as shown below.
However, I am not sure if that approach is the most efficient one since there are two issues with it that go as follows
Images lose a lot of their quality
I get this 'warning' in Visual Studio that is fine as long as I am the only one who develops it, however that will not always be the case if someone else does -
This call site is reachable on all platforms. 'Bitmap' is only supported on windows.
Therefore I wonder what are other approaches more decent that exist and I can implement to at least fix the first of both issues.
public async Task<IActionResult> Add(AddCardFormModel card, List<IFormFile> ImageFile)
{
// ...
foreach (var image in ImageFile)
{
if (image.Length > 0 || image.Length <= (2 * 1024 * 1024))
{
var imagesToBeResized = Image.FromStream(image.OpenReadStream());
var resized = new Bitmap(imagesToBeResized, new Size(250, 350));
using (var stream = new MemoryStream())
{
resized.Save(stream, ImageFormat.Jpeg);
var cardData = new Card
{
Title = card.Title,
Description = card.Description,
ImageUrl = card.ImageUrl,
CategoryId = card.CategoryId,
ConditionId = card.ConditionId,
Price = card.Price,
DealerId = dealerId,
Image = stream.ToArray()
};
this.data.Cards.Add(cardData);
this.data.SaveChanges();
}
}
}
// ...
}
The System.Drawing.Common package is only supported on Windows.
If you want your application to work cross-platform then Microsoft suggests to migrate to one of the following libraries:
ImageSharp
SkiaSharp
Microsoft.Maui.Graphics
https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only#recommended-action
You should first decide which image processing library you will use in your project and after that look for the best algorithm for resizing images using that specific library.
Check this library for image resizing.
but I suggest don't store images on the database, because the database size and log size will be increased.
you have 2 options:
Option 1: store resized images on directories and save names in the database.
Option 2: store the original size on directories and resize it in real-time.
I have a problem with ABCPdf, when I try to convert a pdf files into seperate image files as fallbacks for old browsers.
I have some working code that perfectly renders the page and resizes the rendering into the wanted size. Now my problem occurs when the pdf page is huge w7681px x h10978px. It nearly kills my development machine and the deployment machine cannot even chew the file.
I normally just render the page 1-to-1 as the pdf page and then uses other algorithms to resize this image. This is not efficient since ABCPdf takes alot of power to output this image.
I have the following code:
private byte[] GeneratePng(Doc pdfDoc, int dpi)
{
var useDpi = dpi;
pdfDoc.Rendering.DotsPerInch = useDpi;
pdfDoc.Rendering.SaveQuality = 100;
pdfDoc.Rect.String = pdfDoc.CropBox.String;
pdfDoc.Rendering.ResizeImages = true;
int attemptCount = 0;
for (;;)
{
try
{
return pdfDoc.Rendering.GetData("defineFileTypeDummyString.png");
}
catch
{
if (++attemptCount == 3) throw;
}
}
}
I have tried the following solutions:
Resizing the page
pdfDoc.SetInfo(pdfDoc.Page, "/MediaBox:Rect", "0 0 200 300");
Resizing the page and outputting it. Which doesn't seem to make any changes at all.
Resizing the images before rendering it:
foreach (IndirectObject io in pdfDoc.ObjectSoup) {
if (io is PixMap) {
PixMap pm = (PixMap)io;
pm.Realize(); // eliminate indexed color images
pm.Resize(pm.Width / 4, pm.Height / 4);
}
}
Didn't do anything either and still resulted in a long load time.
Running the reduzed size operation before rendering:
using (ReduceSizeOperation op = new ReduceSizeOperation(pdfDoc))
op.Compact(true);
Didn't do anything either. Just went directly to rendering and took a long time.
Can anyone help me here? Maybe point me to some ABCPdf resizing algorithm or something.
Ok so I talked to the customer support at ABCPdf and they gave me the following.
doc1.Read(originalPDF);
// Specify size of output page. (This example scales the page, maintaining the aspect ratio,
// but you could set the MediaBox Height and Width to any desired value.)
doc2.MediaBox.Height = doc1.MediaBox.Height / 8;
doc2.MediaBox.Width = doc1.MediaBox.Width / 8;
doc2.Rect.SetRect(doc2.MediaBox);
doc2.Page = doc2.AddPage();
// Create the output image
doc2.AddImageDoc(doc1, 1, null);
doc2.Rendering.Save(savePath);
Which is supposed to be used with single page PDFs, so if you have a pdf full of large pictures, then you should chop it up. Which you can do following my other Q/A: Chop PDFs into single pages
The rendering algorithm they use in the above code is auto detected by ABCPdf and you cannot control it yourself (and they told me that I didn't want to). So I put my faith in their code. At least I did a test and the quality looks quite similar to a InterpolationMode.HighQualityBicubic and only differed when zoomed. So I wouldn't be too concerned with it either.
At last the above code gave me a speed boost compared to rendering and then resizing of about 10x faster. So it is really worth something if you do this operation a lot.
I need to implement Instagram photo effects like amaro, hudson, sepia, rise, and so on. I know this article only use basic effects: http://code.msdn.microsoft.com/windowsdesktop/Metro-Style-lightweight-24589f50
Another way suggested by people are to implement Direct2d and then apply using that. But for that I would need to write C++ code, where I have zero experience.
Can anyone suggest some other way to implement Instagram effects in c#?
Is there any built in c++ file for these effects?
Please see this example from CodeProject : Metro Style Lightweight Image Processing
The above example contains these image effects.
Negative
Color filter
Emboss
SunLight
Black & White
Brightness
Oilpaint
Tint
Please note above example seems to be implemented on either developer preview or release preview of Windows 8. So you will get error like this
'Windows.UI.Xaml.Media.Imaging.WriteableBitmap' does not contain a
constructor that takes 1 arguments
So you have to create instance of WriteableBitmap by passing pixel height and pixel width of image. I have edited the sample and it is working for me. You have to change wb = new WriteableBitmap(bs); to wb = await GetWB();
StorageFile originalImageFile;
WriteableBitmap cropBmp;
public async Task<WriteableBitmap> GetWB()
{
if (originalImageFile != null)
{
//originalImageFile is the image either loaded from file or captured image.
using (IRandomAccessStream stream = await originalImageFile.OpenReadAsync())
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(stream);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
byte[] pixels = await GetPixelData(decoder, Convert.ToUInt32(bmp.PixelWidth), Convert.ToUInt32(bmp.PixelHeight));
cropBmp = new WriteableBitmap(bmp.PixelWidth, bmp.PixelHeight);
Stream pixStream = cropBmp.PixelBuffer.AsStream();
pixStream.Write(pixels, 0, (int)(bmp.PixelWidth * bmp.PixelHeight * 4));
}
}
return cropBmp;
}
Let me know if you are facing any problem.
i have some images..if these images are selected then it should generate another image which is the combination of all the selected images..
can anyone suggest as how to start with?
thanks
Use someting like that:
public void MergeImages(string FirstFileName, string SecondFileName)
{
Image firstImg = Image.FromFile(#"C:\temp\pic1.jpg");
Image secondImg = Image.FromFile(#"C:\temp\pic2.jpg");
Bitmap im1 = new Bitmap(firstImg);
Bitmap im2 = new Bitmap(secondImg);
Bitmap result = new Bitmap(im1.Width + im2.Width, (im1.Height > im2.Height) ? im1.Height : im2.Height);
Graphics gr = Graphics.FromImage(result);
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
gr.DrawImage(firstImg, 0, 0);
gr.DrawImage(secondImg, im1.Width + 1, 0);
gr.Save();
result.Save(#"C:\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
this code merges two images in one, systeming them into line.
i used ImageMagick to do these kind of things (http://www.imagemagick.org/Usage/layers/)
It is however a little bit different then using gdi to create images, but it is a very fast (and when you get used to the imagemagick syntax) and powerful way to edit images.
Normally you use gdi to edit images in memory; imagemagick uses a commandline utility to create images.
So for example you want to layer two images on top, you start a new process in your code in which you fire up a imagemagick proces with the right parameters and then the image is created ON DISK.
Then you can server the created image with a response.writebinary.
I was trying to generate thumbnails using Bitmap.GetThumbnailImage() function for 20+ images in a folder. I could see huge memory spike by the application when it does the following procedure (about 600,000K in Task Manager mem usage).
foreach (var image in ListOfImages)
{
var thumbnailFolder = #"\thumb";
var thumbnailFile = thumbnailFolder + "\\" + image.Name;
if (!Directory.Exists(thumbnailFolder))
{
Directory.CreateDirectory(thumbnailFolder);
}
if (!File.Exists(thumbnailFile))
{
using (FileStream fs = new FileStream(image.FullName, FileMode.Open, FileAccess.Read))
{
Image origImage = Image.FromStream(fs);
var thumbnail = origImage.GetThumbnailImage(90, 120, null, IntPtr.Zero);
thumbnail.Save(thumbnailFile);
thumbnail.Dispose();
origImage.Dispose();
}
}
}
Is there any way to reduce this much memory usage for thumbnail generation?
Give it a try using WPF.
In my experience WPF's image operations quite well optimized (actually it is the WIC library that's being used), and designed with threading in mind, and it does not depend the GDI bitmap handles like GDI+ does. I read once that GDI+ is not supported in server code because it is not entirely leakfree. For your scenario, WPF does not need a 3D video card.
WPF's BitmapDecoder even has built-in thumbnail functionality, which will take advantage of thumbnails in the image itself if available. See http://msdn.microsoft.com/en-us/library/ms750864(VS.85).aspx for basic image tasks in WPF. To access WPF, you need to reference the WindowsBase assembly (.net 3.0 or better).
Do not use Image.FromStream, use Image.FromFile instead for memory reasons. Frankly, I think you'd be better to adapt this example for quality reasons:
http://www.webcosmoforums.com/asp/321-create-high-quality-thumbnail-resize-image-dynamically-asp-net-c-code.html