I'm doing image resizing as follows:
private byte[] ResizeImage(System.Drawing.Image image, double scaleFactor)
{
//a holder for the result
int newWidth = (int)(image.Width * scaleFactor);
int newHeight = (int)(image.Height * scaleFactor);
Bitmap result = new Bitmap(newWidth, newHeight);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(result, typeof(byte[]));
}
Whilst it all appears to work perfectly and for the most part is fine, users are saying they are getting error messages when they try to open the resized images in Adobe software.
Illustrator error:
The file "MyPhoto.jpg" is in an unknown format and cannot be opened.
Photoshop error:
Could not complete your request because an unknown or invalid JPEG
marker type is found.
As I say I can open the image fine in Windows viewer, Picasa, GIMP etc.
Just seems to be Adobe software with the issue.
Any ideas? Thanks
This can be resolved simply by including the ImageFormat when saving.
image.Save("filename.jpg", ImageFormat.Jpeg)
Related
I am trying to upload an image to a server through a ASP.net core. My frontend sends a base64 string of an image to my server, and I want my server to resize it to 480*680 px and save it. I convert the image to a byte array:
byte[] bytes = Convert.FromBase64String(newItem.Image);
And use this function to Resize and Save:
private string ResizeAndSave(byte[] bytes)
{
Guid guid = Guid.NewGuid();
string newFileName = $"{guid}.jpg";
using (var ms = new MemoryStream(bytes))
{
var image = new Bitmap(ms);
var destRect = new Rectangle(0, 0, 640, 480);
var destImage = new Bitmap(640, 480);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
};
var saveDir = _hostingEnvironment.WebRootPath + "\\images";
if (!Directory.Exists(saveDir))
{
Directory.CreateDirectory(saveDir);
}
destImage.Save($"{saveDir}/{newFileName}", ImageFormat.Jpeg);
};
return newFileName;
}
If I save the ByteArray directly, everything works, and the image comes out as I want orientation wise. But since I plan on uploading directly from a phone I want to scale the images down some. So I convert it to a Bitmap and work some magic.
However when the bitmap is loaded the image is flipped 90 degrees on the side.
I thought whatever and switched the width/height of the BitMap and Rectangle functions. Which works. Except the image was on its side, so I added a:
destImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
Before the image is saved again. It works. For IPhone - But if I upload from an Android the image is completely upside down.
I take that I need to read some EXIF tags, but there doesn't seem to be any orientation exif tags on the image I am testing with (and the bitmap PropertyIdList is empty), and even without EXIF tags it shouldn't rotate the image just by making a new Bitmap - Right?
Can anyone suggest a good way to get the correct rotation as well as downscaling of the image to a manageable size?
I am creating EMF images in C# with scaling mode of 125% (Please refer the link updated in the bottom of the post to change the scaling mode in windows machine). The image size and resolution change, according to the machine's scaling settings regardless of the DPI settings in use in the code.
//Set the height and width of the EMF image
int imageWidth = 1280;
int imageHeight = 720;
//Adjust the witdh for the screen resoultion
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
imageWidth = (int)(imageWidth / 96.0 * graphics.DpiX);
imageHeight = (int)(imageHeight / 96.0 * graphics.DpiY);
}
Image image = null;
//Stream to create a EMF image
MemoryStream stream = new MemoryStream();
//Create graphics with bitmap and render the graphics in stream for EMF image
using (Bitmap bitmap = new Bitmap(imageWidth, imageHeight))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
IntPtr hdc = g.GetHdc();
Rectangle rect = new Rectangle(0, 0, imageWidth, imageHeight);
image = new Metafile(stream, hdc, rect, MetafileFrameUnit.Pixel, EmfType.EmfPlusDual);
g.ReleaseHdc();
}
}
using (Graphics graphics = Graphics.FromImage(image))
{
SetGraphicsProperties(graphics);
graphics.DrawRectangle(Pens.Black, new Rectangle(100, 100, 100, 100));
}
//This gives the expected resolution and file size regardless of scaling settigs of the windows machine
image.Save("RasterImage.emf");
image.Dispose();
stream.Position = 0;
//This gives the unexpected resolution and file size with different scaling settings in windows machine. Only works as expected when the scaling settings are set with 100%. Usually, I will dump this stream into a file-stream to get the vector graphics image.
Image streamImg = Image.FromStream(stream);// Just for example, I used this Image.FromStream() method to repoduce the issue.
streamImg.Save("StreamImage.emf");
streamImg.Dispose();
// Just to set the graphics properties
internal static void SetGraphicsProperties(Graphics graphics)
{
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.GammaCorrected;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PageUnit = GraphicsUnit.Pixel;
}
In the above code, I am saving two images.
RasterImage.emf
StreamImage.emf
Both images are created with different resolutions and file size. This makes some scaling issues in my drawings.
To change the display settings in windows please do the below,
RClick desktop-> Display Settings -> Scale and Layout (Choose 125%)
Link for changing the scaling - https://winaero.com/blog/set-display-custom-scaling-windows-10/
I am using windows 10. How to avoid these variations in image resolution and size?
Thanks,
Meikandan
I've been using this method to resize uploaded .JPG images to a max width, but it's resulting in images being larger in kb than the source. What am I doing wrong? Is there something else I need to do when saving the new image?
I've tried all sorts of combinations of PixelFormat, e.g. PixelFormat.Format16bppRgb555
E.g: source image is a .JPG 1900w, trying to resize to 1200w...
- Source file is 563KB,
- resized file is 926KB or larger, even 1.9MB
public static void ResizeToMaxWidth(string fileName, int maxWidth)
{
Image image = Image.FromFile(fileName);
if (image.Width > maxWidth)
{
double ratio = ((double)image.Width / (double)image.Height);
int newHeight = (int)Math.Round(Double.Parse((maxWidth / ratio).ToString()));
Bitmap resizedImage = new Bitmap(maxWidth, newHeight);
Graphics graphics = Graphics.FromImage(resizedImage);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
Rectangle rectDestination = new Rectangle(0, 0, maxWidth, newHeight);
graphics.DrawImage(image, rectDestination, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
graphics.Dispose();
image.Dispose();
resizedImage.Save(fileName);
resizedImage.Dispose();
}
image.Dispose();
}
You need to specify that you want to save it as the jpeg format:
resizedImage.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
Otherwise it will default to saving as BMP / PNG (I can't remember which).
For uploading image I am using plupload on client side. Then in my controlled I have next logic:
public ActionResult UploadFile()
{
try
{
var file = Request.Files.Count > 0 ? Request.Files[0] : null;
using (var fileStream = new MemoryStream())
{
using (var oldImage = new Bitmap(file.InputStream))
{
var format = oldImage.RawFormat;
using (var newImage = ImageUtility.ResizeImage(oldImage, 800, 2000))
{
newImage.Save(fileStream, format);
}
byte[] bits = fileStream.ToArray();
}
}
{
catch (Exception ex)
{
}
}
ImageUtility.ResizeImage Method:
public static class ImageUtility
{
public static Bitmap ResizeImage(Bitmap image, int width, int height)
{
if (image.Width <= width && image.Height <= height)
{
return image;
}
int newWidth;
int newHeight;
if (image.Width > image.Height)
{
newWidth = width;
newHeight = (int)(image.Height * ((float)width / image.Width));
}
else
{
newHeight = height;
newWidth = (int)(image.Width * ((float)height / image.Height));
}
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
{
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphics.FillRectangle(Brushes.Transparent, 0, 0, newWidth, newHeight);
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
}
}
The issue which i have here that Image size is increased.
I uploaded image of 1.62MB and after this controller is called and it creates instance if Bitmap and then save Bitmap to filestream and read bits with "fileStream.ToArray();" I am getting 2.35MB in "bits".
Can anyone tell me what's the reason of increasing the image size after I save it as bitmap. I need Bitmap because I need to check with and height of uploaded image and resize it if I need.
The answer is simple, the bitmap takes up more memory the whatever format the image was in previously because it's uncompressed it stays in that uncompressed format after saving it.
jpeg, png, gif, etc. are compressed and therefore use less bytes tha a bitmap which is uncompressed.
If you just want to save the original image, just save file.InputStream.
If you need to resize, you can use a library to apply jpg/png/etc compression and then save the result.
What is the goal here? Are you merely trying to upload an image? Does it need to be validated as an image? Or are you just trying to upload the file?
If upload is the goal, without any regard to validation, just move the bits and save them with the name of the file. As soon as you do this ...
using (var oldImage = new Bitmap(file.InputStream))
... you are converting to a bitmap. Here is where you are telling the bitmap what format to use (raw).
var format = oldImage.RawFormat;
If you merely want to move the file (upload), you can run the memory stream to a filestream object and you save the bits.
If you want a few checks on whether the image is empty, etc, you can try this page (http://www.codeproject.com/Articles/1956/NET-Image-Uploading), but realize it is still putting it in an image, which is not your desire if you simply want to save "as is".
i am using this code to take a jpg image and save it as a thumbnail but it seems very slow ..
Image thumbNail = image.GetThumbnailImage(width, height, null, new IntPtr());
is there any faster way to generate a large amount of thumbnails from a directory of images?
Try Draw Image - Re Edited
public Image ResizeImage(Image openImage, int NewWidth, int NewHeight) {
var openBitmap = new Bitmap(openImage);
var newBitmap = new Bitmap(NewWidth, NewHeight);
using (Graphics g = Graphics.FromImage(openBitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(newBitmap, new Rectangle(0, 0, NewWidth, NewHeight));
}
openBitmap.Dispose(); //Clear The Old Large Bitmap From Memory
return (Image)newBitmap;
}
Typical 3-4mb Image Takes Between 4-8ms
1) By far the fastest and most reliable way to create .Jpg thumbnails is to use partial decompression.
Jpg's have a unique aspect in that it's possible to extract a 1/8 size or 1/16 size (or any power of 2 size) copy of the original without decompressing or sampling the entire original image.
Ever notice how programs like Picassa or Windows Explorer seem to create thumbnails super fast? This is how they do it (when they are not already cached).
This functionality is easily available in any library based on the Independent JPEG Group library code, and most of them are. For example ImageMagick which has a .NET layer available.
2) You can further increase speed by using a hardware accelerated library like libjpeg turbo, although it may require interop.
3) Some explanation of this special .jpg feature is here.
Try it:
public bool GenerateThumbNail(string fileName, string thumbNailFileName,
ImageFormat format, int height, int width)
{
try
{
using (Image img = Image.FromFile(fileName))
{
Image thumbNail = new Bitmap(width, height, img.PixelFormat);
Graphics g = Graphics.FromImage(thumbNail);
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rect = new Rectangle(0, 0, width, height);
g.DrawImage(img, rect);
thumbNail.Save(thumbNailFileName, format);
}
return true;
}
catch (Exception)
{
return false;
}
}
It uses DrawImage too.