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;
}
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 :)
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;
}
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
I want to ask you for help in solving a problem.
I crop the picture with Clone. But there is one detail that affects the operation of the module.
In more details: I have a picture resolution of 1600x900. I need to get a thumb 400x300. Picture 1 is of type 16:9 and the Picture 2 4:3 ratio.
In my version of the solution of the second picture is cut from first. I need the first image smaller, and then cut off. So they look proportionate.
You have to resize your photo at 533x300 and then crop it to 400x300. This will preserve your image ratio.
You need to determine what the deciding factor (height or width) is in your resizing, then resize and finally crop.
Something like this:
thumbHeight = 300;
thumbWidth = 400;
heightRatio = (thumbHeight / img1.Height);
widthRatio = (thumbWidth / img1.Width)
if (heightRatio < widthRatio)
{
resizedHeight = img1.Height * heightRatio;
resizedWidth = img1.Width * heightRatio;
}
else
{
resizedHeight = img1.Height * widthRatio;
resizedWidth = img1.Width * widthRatio;
}