Scaling and rotating images in C# results in ghosted edges - c#

I'm using C# to scale and rotate an image with a transparent background, then using JavaScript to add it to a Google Maps API map, but once it's scaled and rotated, the edges of the image get blurred, and it becomes hard to see, especially over water.
Here is my original 300x300 image:
And here is an example of one of the output images at 100x100 (SizeInt = 100):
Before you get confused as to the background colour, the image is projected onto a map where the water is a similar colour to the image.
You can see some slight ghosting around the edges of the image.
Once it's scaled down further (to it's required scale):
It's almost unnoticeable...
I'm still relatively new to C#, but I've tried loading SVGs directly onto the map with JavaScript (icon: "some/SVG/file.svg"), but that causes serious performance problems, as there are up to 1,000 icons on the map at any given time, making the map unusable when using SVG files, so I have to use image files.
The images need to be sharp and not blurred around the edges. Here is the code used to generate the images, forgive any sloppy code, I'm still somewhat new to C#:
Here is the code used to scale the image:
The image needs to be rotated in a full circle (rounded to 5 degrees), hence the loop from 0 to 361.
private void BuildIcons(string dir)
{
int SizeInt = 30;
// Get the content path
var ContentPath = dir;
// Get the original image
Bitmap OriginalImage = new Bitmap(ContentPath + "base.png");
#region Resize image
// New size
Size size = new Size(SizeInt, SizeInt);
// New height/width temp vars
int newWidth;
int newHeight;
// Aspect ratio preservation
int originalWidth = OriginalImage.Width;
int originalHeight = OriginalImage.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
// Generate the new image
Image ResizedImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(ResizedImage))
{
graphicsHandle.SmoothingMode = SmoothingMode.HighQuality;
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphicsHandle.DrawImage(OriginalImage, 0, 0, newWidth, newHeight);
}
#endregion
int maxside = SizeInt;
#region Image rotation
// Generate images
for (int i = 0; i < 361; i += 5)
{
// New empty bitmap to hold rotated images
Bitmap ReturnBitmap = new Bitmap(maxside, maxside);
// Graphics object from the empty bitmap
// Normal
Graphics Gfx = Graphics.FromImage(ReturnBitmap);
Gfx.SmoothingMode = SmoothingMode.HighQuality;
Gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
Gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
// Move rotation point to center of image
Gfx.TranslateTransform(
(float)ResizedImage.Width / 2,
(float)ResizedImage.Height / 2
);
// Rotate
Gfx.RotateTransform(i);
// Move image back
Gfx.TranslateTransform(
-(float)ResizedImage.Width / 2,
-(float)ResizedImage.Height / 2
);
// draw original in image onto graphics object
Gfx.DrawImage(ResizedImage, new Point(0, 0));
// Save the image
ReturnBitmap.Save(ContentPath + i.ToString() + ".png");
// Clean up
ReturnBitmap.Dispose();
Gfx.Dispose();
}
#endregion
// Clean up
ResizedImage.Dispose();
OriginalImage.Dispose();
}
Any suggestions?

Related

File Size of an Image Increases close to 10 times after Rotating it to a particular angle

I am trying to rotate a image after scanning to improve the readability of scanned images if the skew angle is too high. After finding out the Deskew angle i am trying to rotate the image with the following Code.
After rotating the image and saving it subsequently , my image size is increase closed to 10 times.
What is the reason for the increase in the File Size of an image ?
public Bitmap RotateImage(Image inputImage, float angleDegrees, bool upsizeOk,
bool clipOk, Color backgroundColor)
{
/// Method to rotate an Image object. The result can be one of three cases:
/// - upsizeOk = true: output image will be larger than the input, and no clipping occurs
/// - upsizeOk = false & clipOk = true: output same size as input, clipping occurs
/// - upsizeOk = false & clipOk = false: output same size as input, image reduced, no clipping
///
// Test for zero rotation and return a clone of the input image
if (angleDegrees == 0f)
return (Bitmap)inputImage.Clone();
// Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
int oldWidth = inputImage.Width;
int oldHeight = inputImage.Height;
int newWidth = oldWidth;
int newHeight = oldHeight;
float scaleFactor = 1f;
// If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
if (upsizeOk || !clipOk)
{
double angleRadians = angleDegrees * Math.PI / 180d;
double cos = Math.Abs(Math.Cos(angleRadians));
double sin = Math.Abs(Math.Sin(angleRadians));
newWidth = (int)Math.Round(oldWidth * cos + oldHeight * sin);
newHeight = (int)Math.Round(oldWidth * sin + oldHeight * cos);
}
// If upsizing not wanted and clipping not OK need a scaling factor
if (!upsizeOk && !clipOk)
{
scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
newWidth = oldWidth;
newHeight = oldHeight;
}
// Create the new bitmap object. If background color is transparent it must be 32-bit,
// otherwise 24-bit is good enough.
Bitmap newBitmap = new Bitmap(newWidth, newHeight, backgroundColor == Color.Transparent ?
PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb);
newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);
// Create the Graphics object that does the work
using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
{
// graphicsObject.InterpolationMode = InterpolationMode.HighQualityBicubic;
// graphicsObject.PixelOffsetMode = PixelOffsetMode.HighQuality;
// graphicsObject.SmoothingMode = SmoothingMode.HighQuality;
// Fill in the specified background color if necessary
if (backgroundColor != Color.Transparent)
graphicsObject.Clear(backgroundColor);
// Set up the built-in transformation matrix to do the rotation and maybe scaling
graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);
if (scaleFactor != 1f)
graphicsObject.ScaleTransform(scaleFactor, scaleFactor);
graphicsObject.RotateTransform(angleDegrees);
graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);
// Draw the result
graphicsObject.DrawImage(inputImage, 0, 0);
}
return newBitmap;
}
And i am Saving the Bitmap with the following method
bmp.Save("C:\\Users\\pamit\\Desktop\\FileName.Tiff", jpgEncoder, myEncoderParameters);
My guess is that you have a very high quality in your encoding parameters.
Try saving it with something like:
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
myEncoderParameters.Param[0] = myEncoderParameter;
This will save it with a 50% jpeg quality. You can try to tune it to get the desired quality vs size.

Reduce bitmap image physical size?

I am loading images onto an excel page, once done this excel is around 48,284 KB This is to large and affects the download speed and is not efficient to what we are trying to achieve.
I am trying to reduce this Excel file size as much as possible and think that reducing the image sizes would do the trick as the excel file without images is around 1000 kb.
This is what i have tried so far with no affect:
public Image ReduceImageSize(Image img)
{
float iwidth = 150;
float iheight = 150;
var brush = new SolidBrush(Color.Black);
var Resizedimage = new Bitmap(img);
float scale = Math.Min(iwidth / Resizedimage.Width, iheight / Resizedimage.Height);
var graph = Graphics.FromImage(Resizedimage);
graph.InterpolationMode = InterpolationMode.Low;
graph.CompositingQuality = CompositingQuality.Default;
graph.SmoothingMode = SmoothingMode.None;
var scaleWidth = (int)(Resizedimage.Width * scale);
var scaleHeight = (int)(Resizedimage.Height * scale);
graph.FillRectangle(brush, new RectangleF(0, 0, iwidth, iheight));
graph.DrawImage(Resizedimage, new Rectangle(((int)iwidth - scaleWidth) / 2, ((int)iheight - scaleHeight) / 2, scaleWidth, scaleHeight));
return Resizedimage;
}
At this point i'm looking for any way to reduce the size if it involves losing some quality or reducing the images dimensions.
Please help.
You made Resizedimage the same size as img.
Use the following code to fix the result image size:
var Resizedimage = new Bitmap(iwidth,iheight);
An example resizing an image keeping the aspect ratio:
Image img = Image.FromFile(#"c:\temp\1.jpg");
Bitmap resizedImg = new Bitmap(150, 150);
double ratioX = (double)resizedImg.Width / (double)img.Width;
double ratioY = (double)resizedImg.Height / (double)img.Height;
double ratio = ratioX < ratioY ? ratioX : ratioY;
int newHeight = Convert.ToInt32(img.Height * ratio);
int newWidth = Convert.ToInt32(img.Width * ratio);
using (Graphics g = Graphics.FromImage(resizedImg))
{
g.DrawImage(img, 0, 0, newWidth, newHeight);
}
resizedImg.Save(#"c:\temp\2.jpg");

C# .NET: Resizing a image inside a container div element

I have following markup codes that has a container div element and an img tag nested inside the div. The container div has width, height, top and left CSS style properties.
An originally uploaded image has arbitrary width and height that may be bigger or smaller than the container div. So definitely, the originally uploaded image must be resized and well scaled, and saved as an thumbnail image to sit within the borders of the container div. That resized thumbnail image will be shown as the source (src) in the following markups:
<div id="divContainer" style="width: 600px; height: 450px; top: 50px; left: 20px;">
<img src="[my resized and well scaled thumbnail source]..." id="imgResizedThumnail" />
</div>
In a another .NET form page, there is a file tag letting users upload an original image from local hard disk. That uploaded image needs to be resized and saved as another thumbnail image with best scaling. "Best scalling" means the thumbnail image has a proportional ratio of width and height, and the thumbnail must be sit within the container div.
My C# .NET method looks like follows and I have questions about the code logic in that method.
...
using System.Drawing;
public void SaveThumbnailImageWithbestScaling(Image originalImage, int containerDivWidth, int containerDivHeight)
{
// input containerDivWidth and containerDivHeight are dynamic!
// 1. How can I calculate the scale variable?
double scale = ??? // how can I do the codes here?
// 2. Use that scale to determine the dimension of resized thumbnail image from
// the input originalImage for following variables "thumnailWidth" and "thumnailHeight"
string thumbnailFilename = "myThumnailFileName";
int thumnailWidth = ??? // how can I do the codes here?
int thumnailHeight = ??? // how can I do the codes here?
Bitmap thumnailImage = CreateThumbnail(thumbnailFilename,int thumnailWidth, int thumnailHeight);
// 3. save thumbnail
SaveThumnail(thumnailImage);
}
public void Bitmap CreateThumbnail(string lcFilename,int lnWidth, int lnHeight)
{
...
}
public void thumnailImage (Bitmap thumnail)
{
...
}
This is what I use:
public static Image Resize(
Image srcImage,
int newWidth,
int maxHeight,
int dpi = 72)
{
if(srcImage.Width<=newWidth)
{
newWidth = srcImage.Width;
}
var newHeight = srcImage.Height * newWidth / srcImage.Width;
if (newHeight > maxHeight)
{
newWidth = srcImage.Width * maxHeight / srcImage.Height;
newHeight = maxHeight;
}
var newImage = new Bitmap(newWidth, newHeight);
newImage.SetResolution(dpi, dpi);
using (var gr = Graphics.FromImage(newImage))
{
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(srcImage, new Rectangle(0, 0, newWidth, newHeight));
}
return newImage;
}
I would have done something like this:
public void SaveThumbnailImageWithbestScaling(Image originalImage, int containerDivWidth, int containerDivHeight)
{
string thumbnailFilename = "myThumnailFileName";
int thumbnailWidth = 0;
int thumbnailHeight = 0;
float imgWidth = (float)originalImage.Width;
float imgHeight = (float)originalImage.Height;
float scale_w = imgWidth / (float)containerDivWidth;
float scale_h = imgHeight / (float)containerDivHeight;
// Compute how much each scale diverge from 1 (1 means no scaling, which is desirable)
float variance_w = Math.Abs(1.0 - scale_w);
float variance_h = Math.Abs(1.0 - scale_h);
if (variance_w > variance_h)
{
// Height ratio is closer to 1
float aspect_ratio = imgWidth / imgHeight;
thumbnailHeight = containerDivHeight;
thumbnailWidth = (int)Math.Floor(aspect_ratio * imgWidth);
}
else
{
// Width ratio is closer to 1
float aspect_ratio = imgHeight / imgWidth;
thumbnailHeight = (int)Math.Floor(aspect_ratio * imgHeight);
thumbnailWidth = containerDivWidth;
}
Bitmap thumnailImage = CreateThumbnail(thumbnailFilename,int thumnailWidth, int thumnailHeight);
// 3. save thumbnail
SaveThumnail(thumnailImage);
}
The algorithm computes the ratio of each dimension, then figures out which one varies the most from the div tag's dimension. It then proceeds to snap the dimension with the least variance to the size of the div tag, and scale the other one to respect the image's aspect ratio.
Of course there are other ways of doing it, you could for example not care about aspect ratio and just snap both dimensions to that of the div tag.

Image cropping in C#

I'am using PictureBox for displaying images. My images are direct from scanner so the resolutions are up to 4000*4000... Because my display area is a lot smaller I have to display the image with pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; to preserve aspect ratio.
After that the image is in the middle of the screen.
How can I find the distance between the left side of the image control and the REAL left side of an actual image (see image bellow).
Is there any solution?
Btw. displaying the image on the left side of the screen would do the trick too.
var imageHeight = pictureBox1.Image.Height;
var imageWidth = pictureBox1.Image.Width;
var userSelection = rect.Rect;
var display = pictureBox1.DisplayRectangle;
var xFactor = (float)userSelection.Width / display.Width;
var yFactor = (float)userSelection.Height / display.Height;
var realCropSizeWidth = xFactor * imageWidth;
var realCropSizeHight = yFactor * imageHeight;
var realCropX = imageWidth / display.Width;
realCropX *= userSelection.X;
var realCropY = imageHeight / display.Height;
realCropY *= userSelection.Y;
var realCropRectangle = new Rectangle(realCropX, realCropY, (int)realCropSizeWidth,
(int)realCropSizeHight);
var image = CropImage(pictureBox1.Image, realCropRectangle);
pictureBox1.Image = image;
public Image CropImage(Image source, Rectangle rectangle)
{
var target = new Bitmap(rectangle.Width, rectangle.Height);
using (var g = Graphics.FromImage(target))
{
g.DrawImage(source, new Rectangle(0, 0, target.Width, target.Height),
rectangle,
GraphicsUnit.Pixel);
}
return target;
}
As far as I know there is no direct way of getting what you want, but some simple maths would suffice:
You know the aspect ratio of your original image which is preserved and you know the aspect ratio of your picturebox in which you are showing it. Based on that you can figure out which dimension (height or width) the image fits exactly. Once you know that you can obtain the scaling factor of the image and therefore you can calculate the other dimension of the shown image.
As the image will be centered on the dimension that is not fitted exactly to the picturebox, its straighforward to get the distance you are looking for.

resize image in asp.net

I have this code to resize an image but the image doesn't look so good:
public Bitmap ProportionallyResizeBitmap(Bitmap src, int maxWidth, int maxHeight)
{
// original dimensions
int w = src.Width;
int h = src.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
// Create new Bitmap at new dimensions
Bitmap result = new Bitmap((int)newWidth, (int)newHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(src, 0, 0, (int)newWidth, (int)newHeight);
return result;
}
Try setting the InterpolationMode of the graphics object to some value such as HighQualityBicubic. This should ensure that resize/scaled image looks much better than the "default".
So, in the code you have posted, instead of doing:
// Create new Bitmap at new dimensions
Bitmap result = new Bitmap((int)newWidth, (int)newHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(src, 0, 0, (int)newWidth, (int)newHeight);
return result;
Try doing this instead:
// Create new Bitmap at new dimensions
Bitmap result = new Bitmap((int)newWidth, (int)newHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(src, 0, 0, (int)newWidth, (int)newHeight);
}
return result;
(Note the line setting the InterpolationMode property to one of the InterpolationMode enumeration's values).
Please see this link:
How to: Use Interpolation Mode to Control Image Quality During Scaling
For further information on controlling the quality of an image when resizing/scaling.
Also see this CodeProject article:
Resizing a Photographic image with GDI+ for .NET
For information of the different visual effects the various InterpolationMode enumeration settings will have on an image. (About two-thirds of the way down the article, under the section entitled, "One last thing...").
If you need an on-the-fly resize I suggest you to try an HttpHandler I write recently, I've posted the code on my blog (sorry, but is in italian).
With some modifies you can use the code also for save the transformed image on the disk.

Categories