C# mvc image upload resizing server side - c#

I've got an webapplication where users can upload images. The current problem i'm running into is that the images being uploaded are being saved to the database in the original format. This causes a lot of performance issues when the images are used on a webpage. I used dotTrace to profile the application and I see significant problems when images are processed from the database.
The idea I have is to resize the image when it's uploaded to the server. Take the following example which I want the application to do when the user uploads a new image;
User uploads an image
The image is being resized to an size of 7.500 x 7.500 in pixels in 72 dpi
The image is being saved into the database
Original file gets disposed
The only stored image is the one mentioned above and the webapplication contains technology to resize this on the fly.
I've already read several topics here on SO. And most of them point me into the direction of ImageMagick. This tool is already familiar at my company, and being used in PHP projects. But are there any good and stable released C# wrappers for this tool? I already found the tools below but they're either in Béta release, Alpha Release or currently not updated.
ImageMagick.NET
ImageMagick APP
I also found this topic on SO. In this topic the following code example is supplied;
private static Image CreateReducedImage(Image imgOrig, Size newSize)
{
var newBm = new Bitmap(newSize.Width, newSize.Height);
using (var newGrapics = Graphics.FromImage(newBm))
{
newGrapics.CompositingQuality = CompositingQuality.HighSpeed;
newGrapics.SmoothingMode = SmoothingMode.HighSpeed;
newGrapics.InterpolationMode = InterpolationMode.HighQualityBicubic;
newGrapics.DrawImage(imgOrig, new Rectangle(0, 0, newSize.Width, newSize.Height));
}
return newBm;
}
In short the questions i have;
Are there any advantages in relation to performance using the example above?
Is there a good and reliable C# wrapper for ImageMagick i can use to do this?
Any other good tips relating to the performance are welcome!

We use the latter approach - I can't comment on performance but it certainly makes handling dependencies simpler.
However, one thing to note is that the above code is probably too simple if your users are able to upload images in all sorts of formats. The underlying library (GDI+) has issues with a lot of color formats, but it also is dependent on the OS version. Here's the core of the code we use:
// GDI+ has problems with lots of image formats, and it also chokes on unknown ones (like CMYK).
// Therefore, we're going to take a whitelist approach.
// see http://bmpinroad.blogspot.com/2006/04/file-formats-pixel-formats.html
// also see http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/c626a478-e5ef-4a5e-9a73-599b3b7a6ecc
PixelFormat format = originalImage.PixelFormat;
if (format == PixelFormat.Format16bppArgb1555 ||
format == PixelFormat.Format64bppArgb)
{
// try to preserve transparency
format = PixelFormat.Format32bppArgb;
}
else if (format == PixelFormat.Format64bppPArgb)
{
// try to preserve pre-multiplied transparency
format = PixelFormat.Format32bppPArgb;
}
else if (format != PixelFormat.Format24bppRgb && format != PixelFormat.Format32bppRgb)
{
format = PixelFormat.Format24bppRgb;
}
// GIF saving is probably still an issue. If we ever need to tackle it, see the following:
// http://support.microsoft.com/kb/319061
// http://www.bobpowell.net/giftransparency.htm
// http://support.microsoft.com/kb/318343
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, format))
{
using (Graphics Canvas = Graphics.FromImage(newImage))
{
using (ImageAttributes attr = new ImageAttributes())
{
attr.SetWrapMode(WrapMode.TileFlipXY);
Canvas.SmoothingMode = SmoothingMode.AntiAlias;
Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
Canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
Canvas.DrawImage(originalImage, new Rectangle(new Point(0, 0), newSize), srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, GraphicsUnit.Pixel, attr);
newImage.Save(outputImageStream, originalImage.RawFormat);
}
}
}

I've never used ImageMagic, but I have used the GDI+ image resize functions, including on a site that generates and resizes 100,000+ images a day without performance issues.
I would say using the GDI+ methods are just fine. Don't worry about wrapping an external tool or framework.

I've used ImageGen in Umbraco sites. (It's certainly not tied to Umbraco, it's good for any ASP.NET app, it just so happened that some of the Umbraco packages I was using required it.) It's simple to use, and you might be able to get away with the free version...

Related

Saving Image In UWP Then Loading In WinForms

Good morning StackOverflow,
I come to you today with a scenario that is driving me slowly insane. Im hopeful you can aid me with this, as I’m certain this must be possible but I can’t solve it myself.
My issue is that I’m working on two different applications at present. The first is a UWP application to deliver packages for an internal mail system. The idea here is that upon receipt of the package the person will sign the application using a InkCanvas signature. This should then be saved to the database as a byte array, and then reloaded in either a WinForm or WebForm app (I’m currently doing the WinForm one first) as a regular old image file. However, I’m absolutely stuck on converting between the WriteableBitmap that I get from UWP and the regular Bitmap I need to load in WinForms. Any ideas?
Here’s what I’m doing presently:
Saving the UWP image:
private byte[] SaveImage()
{
var canvasStrokes = SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var width = (int) SignatureCanvas.ActualWidth;
var height = (int) SignatureCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
using (var drawingSession = renderTarget.CreateDrawingSession())
{
drawingSession.Clear(Colors.White);
drawingSession.DrawInk(SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
return renderTarget.GetPixelBytes();
}
return null;
}
Then I save the bytes to the database, and pull them from the database in the WinForms app... so am I making some boneheaded mistake here? Am I reading the signature in the wrong format? Or do I need to do something more to convert the formats from one to the other?
I’m stumped, after trying many different results from StackOverflow pages I don’t know what I’m doing wrong.
Any help would be amazing! And sorry if I’ve done something dumb.
You're actually saving raw bitmap data to your database.
I don't remember well how the Winform importer was working but I doubt it can import raw bitmap data.
You should encode your raw data to a PNG or JPEG image first and save the result. You will end with a regular old image file that should be readable from Winform.
using (IRandomAccessStream stream = /* the stream where you want to save the data */)
{
byte[] bytes = renderTarget.GetPixelBytes();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)canvas.Width, (uint)canvas.Height,
96, 96, bytes);
await encoder.FlushAsync();
}

A Generic error occured in GDI+ in Image.Save() method

I'm trying to use this class but I'm getting a Generic error occured in GDI+ in Image.Save() method. From what I read it's some stream I need to close but I don't know which one.
I'm calling using this:
Image image = Image.FromFile(#"C:\a.jpg");
using (var resized = ImageUtilities.ResizeImage(image, 50, 100))
{
//save the resized image as a jpeg with a quality of 90
ImageUtilities.SaveJpeg(#"C:\myimage.jpeg", resized, 90);
}
Why is that error and how do I solve this?
Unless your program is running as administrator you can not save directly to the root of C: make a folder and save it inside there instead.
Have you tested saving the images in different locations?
If it is still failing then without knowing exactly what is going on in your code I would hazard a guess to say that the original image is getting disposed somewhere before it should be. That's usually the most common cause of the error.
I've written a library that handles many different imaging operations whilst ensuring that memory is correctly handled. It's well tested and very simple to use.
You can get it here. http://imageprocessor.org/
Example code using the library:
using (ImageFactory imageFactory = new ImageFactory())
{
// Load, resize, set the quality and save an image.
imageFactory.Load(#"C:\a.jpg")
.Resize(new Size(50, 100))
.Quality(90)
.Save(#"C:\myimage.jpeg);
}

InkPicture control - How to save both ink and image to an image file

I've seen articles demonstrating that the ink can be saved with the background image so that the ink is overlayed onto the image and then saved as a single image file. But, unfortunately the articles I've seen don't go into any detail about how it is done.
I can save the background image easy enough with the following statement:
axInkPicture1.Picture.Save(#"\path\to\file.gif");
...but there is no ink overlayed onto the background image
I don't know of any direct method to save the ink, but this is how I am currently doing it:
string tempPath = Path.GetTempPath();
string tempFile = tempPath + #"\file.gif";
// save to a temp file
if (axInkPicture1.Ink.Strokes.Count > 0)
{
byte[] bytes = (byte[])axInkPicture1.Ink.Save(InkPersistenceFormat.IPF_GIF, InkPersistenceCompressionMode.IPCM_Default);
using (MemoryStream ms = new MemoryStream(bytes))
{
using (Bitmap gif = new Bitmap(ms))
{
gif.Save(tempFile);
}
}
}
This saves the ink, but there is no background image.
How do I save both the ink and image into a single file?
Any help or direction pointing is appreciated...
### EDIT
Here is what else I have tried so far
InkRectangle inkBox = axInkPicture1.Ink.GetBoundingBox();
byte[] gifbits = (byte[])axInkPicture1.Ink.Save(InkPersistenceFormat.IPF_GIF, InkPersistenceCompressionMode.IPCM_Default);
using (System.IO.MemoryStream buffer = new System.IO.MemoryStream(gifbits))
using (var inkImage = new Bitmap(buffer))
using (var picture = new Bitmap(axInkPicture1.Picture))
using (var g = Graphics.FromImage(picture))
{
g.DrawImage(inkImage, new Rectangle(inkBox.Left, inkBox.Right, axInkPicture1.Picture.Width, axInkPicture1.Height));
picture.Save(tempFile, System.Drawing.Imaging.ImageFormat.Gif);
}
...but I'm still not having any luck. This saves only the background picture and not the ink. Any ideas what I'm doing wrong?
Thanks in advance
Soooo.... after much head banging and gnashing of teeth, I finally came up with a solution to my problem, although maybe not the solution I had expected and maybe not the best solution, but it works. I'm new to the world of c# and .net so please feel free to correct me me if I state anything stupid here.
What I found was that the code in my edit above was actually working, I just couldn't see the ink because it was outside the bounds of the rectangle. Apparently rectangles acquired using the GetBoundingBox() method gives you inkSpace coordinates which are vastly different than pixel coordinates.
With that in mind, my next goal became to convert the inkSpace coordinates to pixel coordinates using the inkSpaceToPixels method. I struggled with this for awhile and could never figure out how to do this using the activex version of inkPicure or axInkPicture. It required some handle to hdcDisplay which I couldn't quite wrap my head around.
In the end, I used the .net version (Microsoft.Ink) from Microsoft.Ink.dll. The only reason I was using the activeX version to begin with was because I couldn't get the Windows Tablet PC SDK to install on my development machine for some reason. It kept complaining that I needed the WinXP Tablet PC OS to install it. I figured out that I was able to install it on another (Win7) PC and then just copy the .dlls over from that PC.
So, here is the final version that is now working... whew!
//set the temporary file paths
string tempPath = Path.GetTempPath();
string tempFile = tempPath + #"tmpFile.png";
//get the bounding box and reference point for the ink strokes (in inkspace coordinates!)
Rectangle inkBox = inkPicture1.Ink.GetBoundingBox();
Point point = new Point(inkBox.Left, inkBox.Top);
MessageBox.Show(point.X.ToString() + ", " + point.Y.ToString());//"before" coordinates
// Convert inkspace coordinates to pixel coordinates using Renderer
Graphics tempGraphics = CreateGraphics();
Microsoft.Ink.Renderer inkRenderer = new Renderer();
inkRenderer.InkSpaceToPixel(tempGraphics, ref point);;
MessageBox.Show(point.X.ToString() + ", " + point.Y.ToString());//"after" coordinates
// Clean up
tempGraphics.Dispose();
// save the ink and background image to a temp file
if (inkPicture1.Ink.Strokes.Count > 0)
{
byte[] bytes = inkPicture1.Ink.Save(PersistenceFormat.Gif, CompressionMode.NoCompression);
using (MemoryStream ms = new MemoryStream(bytes))
{
using (Bitmap gif = new Bitmap(ms))
using (var bmp = new Bitmap(inkPicture1.BackgroundImage))
using (var g = Graphics.FromImage(bmp))
{
g.DrawImage(gif, new Rectangle(point.X, point.Y, gif.Width, gif.Height));
bmp.Save(tempFile, System.Drawing.Imaging.ImageFormat.Png);
}
}
}
Sorry for the long winded explanation, but I felt that simply posting the solution without adequate explanation would leave too many open questions...
I guess I don't have enough 50 reputation to comment, so I will answer with this comment for others that might land here after a google search like I did:
This was very helpful. I translated to VB and it worked.
After some flailing around I found the DLL at:
C:\Program Files (x86)\Common Files\Microsoft Shared\Ink\Microsoft.Ink.dll
Apparently I am not the only one to have a problem locating the DLL as there are many google entries on how to get it -- many are wrong and most are not this simple.
If you are just starting out with signature capture, remember to add a background image to your control or it will fail on this line:
Using bmp = New Bitmap(InkPicture1.BackgroundImage)

High performing Bitmap Drawing solution in c#

I have a use case where I need to render a collage of bitmaps as a preview. The application is implemented as a MVC based REST service, and I have a fairly vanilla implementation:
using (var bitmap = new Bitmap((int)maxWidth, (int)maxHeight))
{
using (var graphic = Graphics.FromImage(bitmap))
{
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
// now again for each mod
foreach (var mod in mods)
{
var frame = frames.First(f => f.Index == mod.FrameIndex);
var fileInfo = GetFileInfo(mod);
using (var modImage = Image.FromFile(fileInfo.FullName))
{
graphic.DrawImage(modImage, (int)frame.Left, (int)frame.Top);
}
}
bitmap.Save(previewFileName);
}
}
While this code works fine, it performs very poorly (especially with larger images). I am open to using third party libraries as well, I just need a faster performing solution.
Any help would be mostly appreciated.
Update
To clarify the use case, caching doesn't help. These images are uploaded by the customer, then they request a preview of the selected collage. It's the writing of the images to the collage that is slow.
I just now realized that you're drawing the modImages in their original size (because you're using the override that only takes a left and top coordinate). If that's what you want, you could as well just use the DrawImageUnscaled()-Method of the graphics class, that should be much faster:
var frame = frames.First(f => f.Index == mod.FrameIndex);
var fileInfo = GetFileInfo(mod);
using (var modImage = Image.FromFile(fileInfo.FullName))
{
graphic.DrawImageUnscaled(modImage, (int)frame.Left, (int)frame.Top);
}
How important is the image quality? If you need faster results, you should try InterpolationMode.NearestNeighbour, this should be much faster, but results will be rather low-quality.
HeighQualityBicubic (what you're currently using) produces best results, but at lowest performance.
If you have a static number of Thumbnails, similar to Netflix, I would create a Cache in memory that keeps the thumbnail bitmaps. This could even be a static object. Once you have generated the thumbnail, you never need to generate it again. It would be a cleaner approach than trying to find a faster hammer to hit it with every time.
Example :
if(MyThumbnailImageDictionary["whateverimageId"] != null)
return (Bitmap)MyThumbnailImageDictionary["whateverimageId"];
If the thumbnails are dynamic such as from a user uploading random pictures, then this method will not help as the size of the image cache will grow tremendous.
You can use the following code to generate a Thubnail, but I am not sure how much faster it would be :
Image image = Image.FromFile(fileName);
Image thumb = image.GetThumbnailImage(120, 120, ()=>false, IntPtr.Zero);

Resize image on the fly in .net and c#

I'm looking for a way to resize images without saving them on the server. The ways that i have found includes a controller file and such.
Is there a way to get the image from the stream, resize it and add it to the response?
Check out ImageResizer - it's a suite of NuGet packages designed for this exact purpose.
It runs eBay in Denmark, MSN Olympics, and a few other big sites.
Dynamic image processing can be done safely and efficiently, but not in a sane amount of code. It's trickier than it appears.
I wouldn't recommend this but you can do next thing:
using (Image img = Image.FromStream(originalImage))
{
using (Bitmap bitmap = new Bitmap(img, width, height))
{
bitmap.Save(outputStream, ImageFormat.Jpeg);
}
}
Be aware that this could cause OutOfMemoryException.

Categories