UIImage ( пользовательский образ ):Измените размер, затем обрезайте
-
03-07-2019 - |
Вопрос
Я бьюсь об эту проблему лицом к лицу уже буквально несколько дней, и хотя я постоянно чувствую, что нахожусь на грани откровения, я просто не могу достичь своей цели.
На этапе разработки концепции я заранее подумал, что было бы несложно взять изображение с камеры или библиотеки iPhone, уменьшить его до заданной высоты, используя функцию, эквивалентную Заливка Аспекта параметр UIImageView (полностью в коде), а затем обрезать все, что не вписывалось в переданный CGRect.
Получение исходного изображения с камеры или библиотеки было тривиальным делом.Я потрясен тем, насколько трудными оказались два других шага.
Прикрепленное изображение показывает, чего я пытаюсь достичь.Не будет ли кто-нибудь, пожалуйста, достаточно любезен, чтобы подержать меня за руку?Кажется, что каждый пример кода, который я нашел до сих пор, разбивает изображение, переворачивает его вверх ногами, выглядит дерьмово, выходит за рамки или иным образом просто работает некорректно.
Решение
Мне нужно было то же самое - в моем случае выбрать размер, который подходит после масштабирования, а затем обрезать каждый конец, чтобы остальные части соответствовали ширине.(Я работаю в альбомной ориентации, поэтому, возможно, не заметил никаких недостатков в портретном режиме.) Вот мой код - это часть категории в UIImage.Целевой размер в моем коде всегда равен полноэкранному размеру устройства.
@implementation UIImage (Extras)
#pragma mark -
#pragma mark Scale and crop image
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
UIImage *sourceImage = self;
UIImage *newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO)
{
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
{
scaleFactor = widthFactor; // scale to fit height
}
else
{
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else
{
if (widthFactor < heightFactor)
{
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
{
NSLog(@"could not scale image");
}
//pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
Другие советы
Более старое сообщение содержит код для метода изменения размера вашего UIImage.Соответствующая часть выглядит следующим образом:
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Что касается обрезки, я считаю, что если вы измените метод, чтобы использовать другой размер для масштабирования, чем для контекста, ваше результирующее изображение должно быть обрезано до границ контекста.
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {
//If scaleFactor is not touched, no scaling will occur
CGFloat scaleFactor = 1.0;
//Deciding which factor to use to scale the image (factor = targetSize / imageSize)
if (image.size.width > targetSize.width || image.size.height > targetSize.height)
if (!((scaleFactor = (targetSize.width / image.size.width)) > (targetSize.height / image.size.height))) //scale to fit width, or
scaleFactor = targetSize.height / image.size.height; // scale to fit heigth.
UIGraphicsBeginImageContext(targetSize);
//Creating the rect where the scaled image is drawn in
CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor) / 2,
(targetSize.height - image.size.height * scaleFactor) / 2,
image.size.width * scaleFactor, image.size.height * scaleFactor);
//Draw the image into the rect
[image drawInRect:rect];
//Saving the image, ending image context
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
Я предлагаю вот что.Разве она не красавица?;)
Там есть отличный фрагмент кода, связанный с изменением размера изображений + несколько других операций.Я столкнулся с этим, когда пытался понять, как изменять размер изображений...http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
Держи.Этот вариант идеален ;-)
Редактировать:смотрите комментарий ниже - "Не работает с определенными изображениями, сбой с:Качество интерпретации cgcontextset:ошибка недопустимого контекста 0x0"
// Resizes the image according to the given content mode, taking into account the image's orientation
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality {
//Get the size we want to scale it to
CGFloat horizontalRatio = bounds.width / imageToScale.size.width;
CGFloat verticalRatio = bounds.height / imageToScale.size.height;
CGFloat ratio;
switch (contentMode) {
case UIViewContentModeScaleAspectFill:
ratio = MAX(horizontalRatio, verticalRatio);
break;
case UIViewContentModeScaleAspectFit:
ratio = MIN(horizontalRatio, verticalRatio);
break;
default:
[NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode];
}
//...and here it is
CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio);
//start scaling it
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = imageToScale.CGImage;
CGContextRef bitmap = CGBitmapContextCreate(NULL,
newRect.size.width,
newRect.size.height,
CGImageGetBitsPerComponent(imageRef),
0,
CGImageGetColorSpace(imageRef),
CGImageGetBitmapInfo(imageRef));
CGContextSetInterpolationQuality(bitmap, quality);
// Draw into the context; this scales the image
CGContextDrawImage(bitmap, newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
// Clean up
CGContextRelease(bitmap);
CGImageRelease(newImageRef);
return newImage;
}
Это версия ответа Джейн Сейлз на языке Swift.Ваше здоровье!
public func resizeImage(image: UIImage, size: CGSize) -> UIImage? {
var returnImage: UIImage?
var scaleFactor: CGFloat = 1.0
var scaledWidth = size.width
var scaledHeight = size.height
var thumbnailPoint = CGPointMake(0, 0)
if !CGSizeEqualToSize(image.size, size) {
let widthFactor = size.width / image.size.width
let heightFactor = size.height / image.size.height
if widthFactor > heightFactor {
scaleFactor = widthFactor
} else {
scaleFactor = heightFactor
}
scaledWidth = image.size.width * scaleFactor
scaledHeight = image.size.height * scaleFactor
if widthFactor > heightFactor {
thumbnailPoint.y = (size.height - scaledHeight) * 0.5
} else if widthFactor < heightFactor {
thumbnailPoint.x = (size.width - scaledWidth) * 0.5
}
}
UIGraphicsBeginImageContextWithOptions(size, true, 0)
var thumbnailRect = CGRectZero
thumbnailRect.origin = thumbnailPoint
thumbnailRect.size.width = scaledWidth
thumbnailRect.size.height = scaledHeight
image.drawInRect(thumbnailRect)
returnImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return returnImage
}
Я изменил код Брэда Ларсона.Это будет аспектно заполнять изображение в заданном прямоугольнике.
-(UIImage*) scaleAndCropToSize:(CGSize)newSize;
{
float ratio = self.size.width / self.size.height;
UIGraphicsBeginImageContext(newSize);
if (ratio > 1) {
CGFloat newWidth = ratio * newSize.width;
CGFloat newHeight = newSize.height;
CGFloat leftMargin = (newWidth - newHeight) / 2;
[self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)];
}
else {
CGFloat newWidth = newSize.width;
CGFloat newHeight = newSize.height / ratio;
CGFloat topMargin = (newHeight - newWidth) / 2;
[self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)];
}
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Xamarin.Версия для iOS для принятый ответ о том, как изменить размер, а затем обрезать UIImage (заливка аспектами) находится ниже
public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize)
{
var imageSize = sourceImage.Size;
UIImage newImage = null;
var width = imageSize.Width;
var height = imageSize.Height;
var targetWidth = targetSize.Width;
var targetHeight = targetSize.Height;
var scaleFactor = 0.0f;
var scaledWidth = targetWidth;
var scaledHeight = targetHeight;
var thumbnailPoint = PointF.Empty;
if (imageSize != targetSize)
{
var widthFactor = targetWidth / width;
var heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
{
scaleFactor = widthFactor;// scale to fit height
}
else
{
scaleFactor = heightFactor;// scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f;
}
else
{
if (widthFactor < heightFactor)
{
thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f;
}
}
}
UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f);
var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight));
sourceImage.Draw(thumbnailRect);
newImage = UIGraphics.GetImageFromCurrentImageContext();
if (newImage == null)
{
Console.WriteLine("could not scale image");
}
//pop the context to get back to the default
UIGraphics.EndImageContext();
return newImage;
}
Я обратился Руководство Сэма Вирча по swift и у меня это хорошо сработало, хотя в конечном изображении есть небольшое "хлюпанье", которое я не смог устранить.
func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage {
var ratio: CGFloat = 0
var delta: CGFloat = 0
var offset = CGPointZero
if image.size.width > image.size.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.width) - (ratio * image.size.height)
offset = CGPointMake(delta / 2, 0)
} else {
ratio = newSize.width / image.size.height
delta = (ratio * image.size.height) - (ratio * image.size.width)
offset = CGPointMake(0, delta / 2)
}
let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta)
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
UIRectClip(clipRect)
image.drawInRect(clipRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Если кому-то нужна версия objective c, она есть на его веб-сайте.
Следующий простой код сработал для меня.
[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)];
[scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setDelegate:self];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
[scrollView setMaximumZoomScale:2.0];
image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)];
imageView = [[UIImageView alloc] initWithImage:image];
UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
[self.view addSubview:imageViewBk];
CGRect rect;
rect.origin.x=0;
rect.origin.y=0;
rect.size.width = image.size.width;
rect.size.height = image.size.height;
[imageView setFrame:rect];
[scrollView setContentSize:[imageView frame].size];
[scrollView setMinimumZoomScale:[scrollView frame].size.width / [imageView frame].size.width];
[scrollView setZoomScale:[scrollView minimumZoomScale]];
[scrollView addSubview:imageView];
[[self view] addSubview:scrollView];
затем вы можете делать снимки экрана к своему изображению с помощью этого
float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.origin.x = [scrollView contentOffset].x * zoomScale;
rect.origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;
CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:cr];
CGImageRelease(cr);
Я обнаружил, что Swift 3, опубликованный Евгением Канвецом, неравномерно масштабирует изображение.
Вот моя версия функции Swift 4, которая не сжимает изображение:
static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
// This function returns a newImage, based on image
// - image is scaled uniformaly to fit into a rect of size newSize
// - if the newSize rect is of a different aspect ratio from the source image
// the new image is cropped to be in the center of the source image
// (the excess source image is removed)
var ratio: CGFloat = 0
var delta: CGFloat = 0
var drawRect = CGRect()
if newSize.width > newSize.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.height) - newSize.height
drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta)
} else {
ratio = newSize.height / image.size.height
delta = (ratio * image.size.width) - newSize.width
drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height)
}
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
image.draw(in: drawRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
- (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize
{
targetSize = !targetSize.width?self.size:targetSize;
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.size.width = targetSize.width*scaleFactor;
thumbnailRect.size.height = targetSize.height*scaleFactor;
CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2;
CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2;
thumbnailRect.origin = CGPointMake(xOffset,yOffset);
[self drawInRect:thumbnailRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
{
NSLog(@"could not scale image");
}
UIGraphicsEndImageContext();
return newImage;
}
Ниже приведен пример работы:Левое изображение - (исходное изображение) ;Правое изображение в масштабе x2
Если вы хотите масштабировать изображение, но сохранить его рамку (пропорции), вызовите метод следующим образом:
[yourImage imageScale:2.0f cropForSize:CGSizeZero];
Этот вопрос, похоже, был отложен, но в моих поисках решения, которое я мог бы легче понять (и написанного на Swift), я пришел к этому (также размещенному на: Как обрезать UIImage?)
Я хотел иметь возможность обрезать из области, основанной на соотношении сторон, и масштабировать до размера, основанного на внешнем ограничивающем экстенте.Вот мой вариант:
import AVFoundation
import ImageIO
class Image {
class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {
let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))
let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)
let scale = max(
targetRect.size.width / sourceRect.size.width,
targetRect.size.height / sourceRect.size.height)
let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
image.drawInRect(drawRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
}
Есть пара вещей, которые меня сбивают с толку, это отдельные проблемы обрезки и изменения размера.Обрезка обрабатывается с помощью начальной точки прямоугольника, которую вы передаете drawInRect, а масштабирование обрабатывается с помощью части размера.В моем случае мне нужно было соотнести размер прямоугольника обрезки в исходном коде с моим выходным прямоугольником с тем же соотношением сторон.Затем выводится / вводится масштабный коэффициент, и его необходимо применить к drawRect (передать в drawInRect).
Одно из предостережений заключается в том, что этот подход фактически предполагает, что изображение, которое вы рисуете, больше, чем контекст изображения.Я не тестировал это, но я думаю, что вы можете использовать этот код для обработки обрезки / масштабирования, но явно определяя параметр scale как вышеупомянутый параметр scale.По умолчанию UIKit применяет множитель, основанный на разрешении экрана.
Наконец, следует отметить, что этот подход UIKit имеет более высокий уровень, чем подходы CoreGraphics / Quartz и Core Image, и, похоже, решает проблемы с ориентацией изображения.Также стоит упомянуть, что он довольно быстрый, уступает ImageIO, согласно этому сообщению здесь: http://nshipster.com/image-resizing/
Быстрая версия:
static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale);
image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage
}
Вот версия Swift 3 для Руководство Сэма Вирча по swift автор : Уильям Т.
extension UIImage {
static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
var ratio: CGFloat = 0
var delta: CGFloat = 0
var offset = CGPoint.zero
if image.size.width > image.size.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.width) - (ratio * image.size.height)
offset = CGPoint(x: delta / 2, y: 0)
} else {
ratio = newSize.width / image.size.height
delta = (ratio * image.size.height) - (ratio * image.size.width)
offset = CGPoint(x: 0, y: delta / 2)
}
let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta)
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
UIRectClip(clipRect)
image.draw(in: clipRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}