I am writing a Windows Phone 8.1 app that requires me to crop an image to produce another square image (250 by 250). I need xaml and code behind sample code that I can be use to do the cropping. The examples found online are for the old WP8 and they are not very helpful.Any assistance will be appreciated.
I did find a solution, there is this article I came across and it has two parts.Part 1 and Part 2 proved helpful in finding a solution with minimal modifications such as using WriteableBitmapEx methods for cropping instead. I hope this will help someone in future and save them the pain I went through.
Here is also some code I used to center crop my image
public static WriteableBitmap centerCropImage(WriteableBitmap image)
{
int originalWidth = image.PixelWidth;
int originalHeight = image.PixelHeight;
//Getting the new width
int newWidth = originalWidth > originalHeight? originalHeight : originalWidth;
//Calculating the cropping points
int cropStartX, cropStartY;
if(originalWidth > originalHeight){
cropStartX = (originalWidth - newWidth)/2;
cropStartY = 0;
}
else{
cropStartY = (originalHeight - newWidth)/2;
cropStartX = 0;
}
//Then use the following values to get the cropped image
var cropped = image.Crop(new Rect(cropStartX, cropStartY, newWidth, newWidth));
//Then resize the new square image to 250 by 250 px
var resized = WriteableBitmapExtensions.Resize(cropped, 250, 250, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
return resized;
}
Related
I'm trying to create a level editor using Windows Forms for my monogame project and need to draw small pixel based images to a picture box with no quality loss when scaled. In monogame when I need to do this I can just set the draw type to PointClamp and then each pixel is drawn as is instead of being pixelated when zoomed; I was hoping for something like this via a picturebox. Right now it looks like this But I'd prefer a more crisp and clean image like this (The second is how it'll appear in monogame). I haven't uploaded any code for this, but just assume I grabbed an image from the filestream and used the bitmap constructor to scale it up (don't think that's relevent but I'll just put it out there).
Image croppedImage, image = tileMap.tileBox.Image;
var brush = new SolidBrush(Color.Black);
try { croppedImage = CropImage(image, tileMap.highlightedRect); } catch {
return; // If crop target is outside bounds of image then return
}
float scale = Math.Min(higlightedTileBox.Width / croppedImage.Width, higlightedTileBox.Height / image.Height);
var scaleWidth = (int)(higlightedTileBox.Width * scale);
var scaleHeight = (int)(higlightedTileBox.Height * scale);
try { higlightedTileBox.Image = new Bitmap(croppedImage, new Size(scaleWidth, scaleHeight)); } catch {
return; // Image couldn't be scaled or highlighted tileBox couldn't be set to desired image
}
CropImage:
private static Image CropImage(Bitmap img, Rectangle cropArea) {
return img.Clone(cropArea, img.PixelFormat);
}
private static Image CropImage(Image img, Rectangle cropArea) {
return CropImage(new Bitmap(img), cropArea);
}
The code above is my current method in it's entirety. tileMap is a form and tilebox is the picturebox within that form.image is the full spritesheet texture before being cropped to what the user has highlighted. After being cropped I attempt to set the current picturebox (highlightedTileBox's) image to a scaled up version of the cropped image.
So I got a solution by trying around a bit.
It looks like scaling images directly by size is using some sort of interpolation.
To try different interpolation modes supported by Winforms, I created a little demo.
As you can see, every label contains the name of the InterpolationMode and is followed by its resulting image. The original bitmap I used is the small one at the top.
From your question, it looks like you would like to achieve something like NearestNeighbour.
Following code scales bmp and the result is stored in bmp2. Try if that's what you want. Consider building a proper implementation if you're using this as solution (disposing unused bitmaps etc.).
I hope it helps.
Bitmap bmp = new Bitmap("test.bmp");
Bitmap bmp2;
Graphics g = Graphics.FromImage(bmp2=new Bitmap(bmp.Width * 2, bmp.Height * 2));
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, bmp.Width * 2, bmp.Height * 2);
g.Dispose();
I've searched a bit around the discussions\forums/StackOverflow/Official documentation, but i couldn't find much information about how to achieve what i'm trying. Most of the official documentation covers the command-line version of ImageMagick.
I'll describe what i'm trying to do:
I have a image loaded that i would like to paste into a larger one.
Ex: the image i loaded has 9920 width, 7085 height. I would like to place it in the middle of a larger one (10594 width, 7387 height). I do have all border calculation ready ([larger width - original width / 2] , same goes for height).
But i don't know how to do it using MagickImage. Here's the max i got:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = new MagickImage(MagickColor.FromRgb(255, 255, 255), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
}
}
Here's the max i got. In order to achieve this, i used the following post:
How to process only one part of image by ImageMagick?
And i'm not using GDI+ because i'll be always working with larger TIFF files (big resolutions), and GDI+ tends to throw exceptions (Parameter not valid, out of memory) when it can't handle everything (i loaded three images with an resolution like that, and got out of memory).
Any help will be kindly appreciate, thanks.
Pablo.
You could either Composite the image on top of a new image with the required background or you could Clone and Extent if with the required background. In the answer from #Pablo Costa there is an example for Compositing the image so here is an example on how you could extent the image:
private void drawInkzone(MagickImage loadedImage, List<string> inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = loadedImage.Clone())
{
MagickColor background = MagickColors.Black;
int width = (int)zoneAreaWidth_Pixels;
int height = (int)zoneAreaHeight_Pixels;
image.Extent(width, height, Gravity.Center, background);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
I managed to accomplish what i needed.
Cool that i didn't had to calculate borders.
Here's the code:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation); //Larger image information
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI); //Width and height for the larger image are in mm , converted them to pixel
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);//Formula (is: mm * imageDPI) / 25.4
using (MagickImage image = new MagickImage(MagickColor.FromRgb(0, 0, 0), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
image.Composite(loadedImage, Gravity.Center);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
Hope this helps someone :)
Just no idea how to do that:
We have one image, and we know constants WIDTH and HEIGHT of one card in this image. I would like to show one image in this image. Next constant is how many cards we have, so CNT_CARDS = 52. I don't want to create each card - only show that. I'm using winforms (C#).
Pseudocode
Load the image.
For each card can apply:
int offsetTop = row * HEIGHT;
int offsetLeft = column * WIDTH;
imageInImage.Location = new Point(offsetLeft, offsetTop);
imageInImage.Size = new Size(WIDTH, HEIGHT);
For example if we want to get Queen of diamond:
int offsetTop = 2 * HEIGHT;
int offsetLeft = 11 * WIDTH;
Create a bitmap for a single card by using Graphics.DrawImage(). A boilerplate sample implementation could look like this:
static Bitmap GetCardImage(Bitmap cards, int cardnum) {
int width = cards.Width / 13;
int height = cards.Height / 4;
int left = width * (cardnum % 13);
int top = height * (cardnum % 4);
var bmp = new Bitmap(width, height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(cards,
new Rectangle(0, 0, width, height),
new Rectangle(left, top, width, height),
GraphicsUnit.Pixel);
}
return bmp;
}
You can further extend this by creating an array of bitmaps so you'll have them readily available when you need to draw them. Something you'd do at the splash screen. Let's assume you added the image with the card faces as a resource named CardFaces:
static Bitmap[] CreateDeckImages() {
var deck = new Bitmap[52];
using (var images = Properties.Resources.CardFaces) {
for (int cardnum = 0; cardnum < deck.Length; ++cardnum) {
deck[cardnum] = GetCardImage(images, cardnum);
}
}
return deck;
}
Untested, ought to be close.
this should be a good start
var bmp = new Bitmap(225, 315);
var OriBmp = new Bitmap(#"c:\gnv4Q.jpg");
var g = Graphics.FromImage(bmp);
g.DrawImage(OriBmp,0,0,new Rectangle(225,315,225,315),GraphicsUnit.Pixel);
bmp.Save(#"c:\test.png");
An alternative would be to split the image into a set of images, one image for each card.
ImageSplitter, is a website which currently has an online image splitting tool that can be used to efficiently split the OP's image into a set of images, one image for each card.
To note, since the image of the OP is 2925 by 1260 pixels, and the cards span 4 rows by 13 columns in the image, the result will be a set of 225 by 315 pixels sized card images downloaded in a zip file by using the online image splitting tool in discussion.
On the online image splitting tool website in discussion, you will currently find the option on the homepage to upload an image. Where you would find that, you may:
1. Click where it says "Click here to upload your image"
2. Click "UPLOAD IMAGE"
3. Once the image is uploaded, on the next screen, choose the "SPLIT IMAGE" tool
4. Then enter the number of Rows and Columns currently found on the tool
- for this image, enter '4' for Rows, and '13' for columns
5. Next, click "SPLIT IMAGE' currently found on the tool
By following steps 1 through 5 above, a set of images, one image for each card, contained in a zip file, will be downloaded in the download folder of the browser used for the online image splitting tool in duscussion.
Link to Visual Guide for the Online Image Splitting Tool
Does anyone know how to use the new ASP.Net MVC 3 Html Helper WebImage to crop an uploaded file into a square. I would like to have it centered if possible. I've been banging my head for the last few hours trying to figure this out...any help is appreciated!
The scenario is pretty simple, user can upload an image, the image will then be resized to a square to be used later as a thumbnail in the site.
This worked for me, hope saves some time for others...!
private static void CropImage (HttpPostedFileBase sourceImage) {
var newImage = new WebImage(sourceImage.InputStream);
var width = newImage.Width;
var height = newImage.Height;
if (width > height) {
var leftRightCrop = (width - height) / 2;
newImage.Crop(0, leftRightCrop, 0, leftRightCrop);
}
else if (height > width) {
var topBottomCrop = (height - width) / 2;
newImage.Crop(topBottomCrop, 0, topBottomCrop, 0);
}
//do something with cropped image...
//newImage.GetBytes();
}
I suggest to use Jquery image crop plugin. Because i think not good to crop square automatically because you can remove main part of image, for example if it user photo you can crop his head.
Image crop plugin is easy to use. User just select are that he want to use as preview. At the server side you recive start point coordinates and width/height. For image resize/crop at server side i use image magick. There is wrapper for image magick at .net. Also be care with wrapper because it 32 bit only. I've developed for my needs own wrapper for image magick. But i belive that it can be easy done with .net.
If you still think that autocropping is what you need, i suggest to crop max center squere of image and than recize to size that you want.
Hope this help.
P.S. I don't know but i suppose that such task can't be done using mvc WebImage.
here is a little function that crops image from the center but keeping wanted ratio. I use it for cropping images for galleries and such.
public static WebImage BestUsabilityCrop(WebImage image, decimal targetRatio)
{
decimal currentImageRatio = image.Width/(decimal) image.Height;
int difference;
//image is wider than targeted
if (currentImageRatio > targetRatio)
{
int targetWidth = Convert.ToInt32(Math.Floor(targetRatio * image.Height));
difference = image.Width - targetWidth;
int left = Convert.ToInt32(Math.Floor(difference/(decimal) 2));
int right = Convert.ToInt32(Math.Ceiling(difference/(decimal) 2));
image.Crop(0, left, 0, right);
}
//image is higher than targeted
else if (currentImageRatio < targetRatio)
{
int targetHeight = Convert.ToInt32(Math.Floor(image.Width / targetRatio));
difference = image.Height - targetHeight;
int top = Convert.ToInt32(Math.Floor(difference/(decimal) 2));
int bottom = Convert.ToInt32(Math.Ceiling(difference/(decimal) 2));
image.Crop(top, 0, bottom, 0);
}
return image;
}
I'm resizing some images to the screen resolution of the user; if the aspect ratio is wrong, the image should be cut.
My code looks like this:
protected void ConvertToBitmap(string filename)
{
var origImg = System.Drawing.Image.FromFile(filename);
var widthDivisor = (double)origImg.Width / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
var heightDivisor = (double)origImg.Height / (double)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
int newWidth, newHeight;
if (widthDivisor < heightDivisor)
{
newWidth = (int)((double)origImg.Width / widthDivisor);
newHeight = (int)((double)origImg.Height / widthDivisor);
}
else
{
newWidth = (int)((double)origImg.Width / heightDivisor);
newHeight = (int)((double)origImg.Height / heightDivisor);
}
var newImg = origImg.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
}
In most cases, this works fine. But for some images, the result has an extremely poor quality. It looks like the would have been resized to something very small (thumbnail size) and enlarged again.. But the resolution of the image is correct. What can I do?
Example orig image:
alt text http://img523.imageshack.us/img523/1430/naturaerowoods.jpg
Example resized image:
Note: I have a WPF application but I use the WinForms function for resizing because it's easier and because I already need a reference to System.Windows.Forms for a tray icon.
Change the last two lines of your method to this:
var newImg = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage(newImg);
g.DrawImage(origImg, new Rectangle(0,0,newWidth,newHeight));
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
g.Dispose();
I cannot peek into the .NET source at the moment, but most likely the problem is in the Image.GetThumbnailImage method. Even MSDN says that "it works well when the requested thumbnail image has a size of about 120 x 120 pixels, but it you request a large thumbnail image (for example, 300 x 300) from an Image that has an embedded thumbnail, there could be a noticeable loss of quality in the thumbnail image". For true resizing (i.e. not thumbnailing), you should use the Graphics.DrawImage method. You may also need to play with the Graphics.InterpolationMode to get a better quality if needed.
If you're not creating a thumbnail, using a method called GetThumbnailImage probably isn't a good idea...
For other options, have a look at this CodeProject article. In particular, it creates a new image, creates a Graphics for it and sets the interpolation mode to HighQualityBicubic and draws the original image onto the graphics. Worth a try, at least.
As indicated on MSDN, GetThumbnailImage() is not designed to do arbitrary image scaling. Anything over 120x120 should be scaled manually. Try this instead:
using(var newImg = new Bitmap(origImg, newWidth, newHeight))
{
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
}
Edit
As a point of clarification, this overload of the Bitmap constructor calls Graphics.DrawImage, though you do not have any control over the interpolation.
instead of this code:
newImg.Save(this.GetBitmapPath(filename), System.Drawing.Imaging.ImageFormat.Bmp);
use this one :
System.Drawing.Imaging.ImageCodecInfo[] info = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
System.Drawing.Imaging.EncoderParameters param = new System.Drawing.Imaging.EncoderParameters(1);
param.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
newImg.Save(dest_img, info[1], param);
For examples, the original image is JPG and the resized image is PNG. Are you converting between formats on purpose? Switching between different lossey compression schemes can cause quality loss.
Are you increasing or decreasing the size of the image when you resize it? If you are creating a larger image from a smaller one, this sort of degradation is to be expected.
Images will definitely be degraded if you enlarge them.
Some camera's put a resized thumbnail into the file itself presumably for preview purposes on the device itself.
The GetThumbnail method actually gets this Thumbnail image which is embedded within the image file instead of getting the higher res method.
The easy solution is to trick .Net into throwing away that thumbnail information before doing your resize or other operation. like so....
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
//removes thumbnails from digital camera shots
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
If you are attempting to resize constraining proportions I wrote an extension method on System.Drawing.Image that you might find handy.
/// <summary>
///
/// </summary>
/// <param name="img"></param>
/// <param name="size">Size of the constraining proportion</param>
/// <param name="constrainOnWidth"></param>
/// <returns></returns>
public static System.Drawing.Image ResizeConstrainProportions(this System.Drawing.Image img,
int size, bool constrainOnWidth, bool dontResizeIfSmaller)
{
if (dontResizeIfSmaller && (img.Width < size))
return img;
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
img.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
float ratio = 0;
ratio = (float)img.Width / (float)img.Height;
int height, width = 0;
if (constrainOnWidth)
{
height = (int)(size / ratio);
width = size;
}
else
{
width = (int)(size * ratio);
height = size;
}
return img.GetThumbnailImage(width, height, null, (new System.IntPtr(0)));
}
This is going to vary widely based on the following factors:
How closely the destination resolution matches a "natural" scale of the original resolution
The source image color depth
The image type(s) - some are more lossy than others