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.
Related
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?
I want to center a background image. However the background image is larger than my control(which is a flat style checkbox).
A picture to make my problem clear
Usually if the background image is smaller than the control, it will be shown like the black box(which is properly centered); but in my case it will show partial image in the green box(left top corner), but the end result I want is the orange box(the center of the image), or zoomed proportionally to fill the control with extra part cut off(ImageLayout.Zoom will show whole image with blank space).
Update: Code used:
Image img = Image.FromFile("xxxx.png");
mycheckbox.BackgroundImage = img;
mycheckbox.BackgroundImageLayout = ImageLayout.Center;
Image can be used instead of BackgroundImage to get the part of the image as the orange rectangle of the example.
If the image doesn't fill the entire checkbox and if it's acceptable to alter the image being set (should work without problems, but would not be dynamic in case the checkbox size changes during runtime), the image could be resized first according to the largest ratio:
var img = Image.FromFile("xxxx.png");
float ratio = Math.Max(mycheckbox.Height / (float)img.Height,mycheckbox.Width / (float)img.Width);
if (ratio > 1)
{
Func<float, int> calc = f => (int)Math.Ceiling(f * ratio);
var bmp = new Bitmap(img, calc(img.Width ), calc(img.Height ));
img.Dispose();
img = bmp;
}
mycheckbox.ImageAlign = ContentAlignment.MiddleCenter;
mycheckbox.Image = img;
The line float ratio = Math.Max(mycheckbox.Height / (float)img.Height,mycheckbox.Width / (float)img.Width); simply calculates both the height ratio and the width ratio. If either is larger than 1, it means the checkbox height or width is larger. It doesn't matter which one, only which is larger, hence the Math.Max. The check of larger than 1 is performed on the largest ratio and if needed the image is enlarged with said ratio.
Edit A more generic approach, that scales and cuts the image so that if fills the control size and the BackGroundImage property can be used:
public static void SetImage(this Control ctrl, Image img)
{
var cs = ctrl.Size;
if (img.Size != cs)
{
float ratio = Math.Max(cs.Height / (float)img.Height, cs.Width / (float)img.Width);
if (ratio > 1)
{
Func<float, int> calc = f => (int)Math.Ceiling(f * ratio);
img = new Bitmap(img, calc(img.Width), calc(img.Height));
}
var part = new Bitmap(cs.Width, cs.Height);
using (var g = Graphics.FromImage(part))
{
g.DrawImageUnscaled(img, (cs.Width - img.Width) /2, (cs.Height - img.Height) / 2);
}
img = part;
}
ctrl.BackgroundImageLayout = ImageLayout.Center;
ctrl.BackgroundImage = img;
}
I have been using GDI+ implementation in Asp.Net hadler for resizing images. I've found article Resizing images from the server using WPF/WIC instead of GDI+ describing that resizing images using Windows.Media.Imaging is more effective (much faster and consume less system resources).
This approach has one more advantage, I can read individual frames from source image, as gif. Here is part of my code:
var photoDecoder = BitmapDecoder.Create(inputStream, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.None);
IEnumerable<BitmapFrame> bitmapFrames = from bitmapFrame in photoDecoder.Frames
select bitmapFrame;
var bitmapFramesList = new List<BitmapFrame>();
bitmapFramesList.AddRange(bitmapFrames);
var firstFrame = bitmapFramesList.First();
int inputWidth = Convert.ToInt32(firstFrame.Width);
int inputHeight = Convert.ToInt32(firstFrame.Height);
var firstFrameSize= new Size(inputWidth, inputHeight);
//if targetSize is bigger than image Size render image as is
bool targetSizeIsBiggerOrEmpty = targetSize.IsEmpty || (targetSize.Width > firstFrameSize.Width && targetSize.Height > firstFrameSize.Height);
if (targetSizeIsBiggerOrEmpty)
{
inputStream.Position = ZERO;
inputStream.CopyTo(catalogImage.Stream);
}
else
{
//calculate size of target image
Size imageSize = SizeCalculator.CalculateImageSize(new Size(Convert.ToInt32(firstFrame.Width),Convert.ToInt32(firstFrame.Height)), targetSize);
if (targetSize.Width == ZERO)
targetSize.Width = imageSize.Width;
if (targetSize.Height == ZERO)
targetSize.Height = imageSize.Height;
double scaleX = Convert.ToDouble(imageSize.Width) / Convert.ToDouble(firstFrame.Width);
double scaleY = Convert.ToDouble(imageSize.Height) / Convert.ToDouble(firstFrame.Height);
double scale = Math.Min(scaleX, scaleY);
var targetFrames= new Collection<BitmapFrame>();
foreach (var sourceFrame in bitmapFramesList)
{
var transform = new ScaleTransform {ScaleX = scale, ScaleY = scale};
var transformedBitmap = new TransformedBitmap(sourceFrame, transform);
BitmapFrame bitmapFrame = BitmapFrame.Create(transformedBitmap);
targetFrames.Add(bitmapFrame);
}
//my own method for saving image to response output stream
SaveCatalogImage(catalogImage, targetFrames);
}
But here is a small problem. Each frame has different size and I'm not able to find each frame position according to first frame size. I also need timing in target image as is in source.
Where I can find those information in source image?
Using System.Drawing.Image.
If an image width or height exceed the maximum, it need to be resized proportionally .
After resized it need to make sure that neither width or height still exceed the limit.
The Width and Height will be resized until it is not exceed to maximum and minimum automatically (biggest size possible) and also maintain the ratio.
Like this?
public static void Test()
{
using (var image = Image.FromFile(#"c:\logo.png"))
using (var newImage = ScaleImage(image, 300, 400))
{
newImage.Save(#"c:\test.png", ImageFormat.Png);
}
}
public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
Much longer solution, but accounts for the following scenarios:
Is the image smaller than the bounding box?
Is the Image and the Bounding Box square?
Is the Image square and the bounding box isn't
Is the image wider and taller than the bounding box
Is the image wider than the bounding box
Is the image taller than the bounding box
private Image ResizePhoto(FileInfo sourceImage, int desiredWidth, int desiredHeight)
{
//throw error if bouning box is to small
if (desiredWidth < 4 || desiredHeight < 4)
throw new InvalidOperationException("Bounding Box of Resize Photo must be larger than 4X4 pixels.");
var original = Bitmap.FromFile(sourceImage.FullName);
//store image widths in variable for easier use
var oW = (decimal)original.Width;
var oH = (decimal)original.Height;
var dW = (decimal)desiredWidth;
var dH = (decimal)desiredHeight;
//check if image already fits
if (oW < dW && oH < dH)
return original; //image fits in bounding box, keep size (center with css) If we made it bigger it would stretch the image resulting in loss of quality.
//check for double squares
if (oW == oH && dW == dH)
{
//image and bounding box are square, no need to calculate aspects, just downsize it with the bounding box
Bitmap square = new Bitmap(original, (int)dW, (int)dH);
original.Dispose();
return square;
}
//check original image is square
if (oW == oH)
{
//image is square, bounding box isn't. Get smallest side of bounding box and resize to a square of that center the image vertically and horizontally with Css there will be space on one side.
int smallSide = (int)Math.Min(dW, dH);
Bitmap square = new Bitmap(original, smallSide, smallSide);
original.Dispose();
return square;
}
//not dealing with squares, figure out resizing within aspect ratios
if (oW > dW && oH > dH) //image is wider and taller than bounding box
{
var r = Math.Min(dW, dH) / Math.Min(oW, oH); //two dimensions so figure out which bounding box dimension is the smallest and which original image dimension is the smallest, already know original image is larger than bounding box
var nH = oH * r; //will downscale the original image by an aspect ratio to fit in the bounding box at the maximum size within aspect ratio.
var nW = oW * r;
var resized = new Bitmap(original, (int)nW, (int)nH);
original.Dispose();
return resized;
}
else
{
if (oW > dW) //image is wider than bounding box
{
var r = dW / oW; //one dimension (width) so calculate the aspect ratio between the bounding box width and original image width
var nW = oW * r; //downscale image by r to fit in the bounding box...
var nH = oH * r;
var resized = new Bitmap(original, (int)nW, (int)nH);
original.Dispose();
return resized;
}
else
{
//original image is taller than bounding box
var r = dH / oH;
var nH = oH * r;
var nW = oW * r;
var resized = new Bitmap(original, (int)nW, (int)nH);
original.Dispose();
return resized;
}
}
}
Working Solution :
For Resize image with size lower then 100Kb
WriteableBitmap bitmap = new WriteableBitmap(140,140);
bitmap.SetSource(dlg.File.OpenRead());
image1.Source = bitmap;
Image img = new Image();
img.Source = bitmap;
WriteableBitmap i;
do
{
ScaleTransform st = new ScaleTransform();
st.ScaleX = 0.3;
st.ScaleY = 0.3;
i = new WriteableBitmap(img, st);
img.Source = i;
} while (i.Pixels.Length / 1024 > 100);
More Reference at http://net4attack.blogspot.com/
This is a follow up question on Save image to file keeping aspect ration in a WPF app
I know howto scale the image, but how do I expand the canvas size, to ensure the image still has the requested width and height. In this example its 250x250 but its dynamic.
I have created this illustration to show what I'm trying to accomplice.
I can't find any way of expanding the canvas of an BitmapImage, nor a way to create an in memory image in the correct size, with a transparent background, and then merging the two images together.
CroppedBitmap doesn't seem to support adding space around an image so instead you can create a transparent image the correct size using WriteableBitmap. If the input is smaller than the target size this method will enlarge it, but that is easy to alter.
public static BitmapSource FitImage(BitmapSource input, int width, int height)
{
if (input.PixelWidth == width && input.PixelHeight == height)
return input;
if(input.Format != PixelFormats.Bgra32 || input.Format != PixelFormats.Pbgra32)
input = new FormatConvertedBitmap(input, PixelFormats.Bgra32, null, 0);
//Use the same scale for x and y to keep aspect ratio.
double scale = Math.Min((double)width / input.PixelWidth, height / (double)input.PixelHeight);
int x = (int)Math.Round((width - (input.PixelWidth * scale))/2);
int y = (int)Math.Round((height - (input.PixelHeight * scale))/2);
var scaled = new TransformedBitmap(input, new ScaleTransform(scale, scale));
var stride = scaled.PixelWidth * (scaled.Format.BitsPerPixel / 8);
var result = new WriteableBitmap(width, height, input.DpiX, input.DpiY, input.Format,null);
var data = new byte[scaled.PixelHeight * stride];
scaled.CopyPixels(data, stride, 0);
result.WritePixels(new Int32Rect(0,0,scaled.PixelWidth,scaled.PixelHeight), data, stride,x,y);
return result;
}
If you are already rendering content using RenderTargetBitmap you could wrap it in a ViewBox to do the scaling but if you're just working with normal images I'd use the above method.
You should be able to set the Stretch property to Uniform.
Just some code in case others are trying to postprocess files from a form upload.
if (file.PostedFile != null)
{
//write the new file to disk
string cachePath = String.Format("{0}temp\\", Request.PhysicalApplicationPath);
string photoPath = String.Format("{0}temp.png", cachePath);
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
file.PostedFile.SaveAs(photoPath);
//resize the new file and save it to disk
BitmapSource banana = FitImage(ReadBitmapFrame(file.PostedFile.InputStream), 640, 480);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(banana));
FileStream pngStream = new FileStream(cachePath + "test.png", FileMode.Create);
encoder.Save(pngStream);
pngStream.Close();
//set a couple images on page to the newly uploaded and newly processed files
image.Src = "temp/temp.png";
image.Visible = true;
image2.Src = "temp/test.png";
image2.Visible = true;
}