如何以全分辨率从 UIImageView 获取旋转、缩放和平移的图像?

Posted

技术标签:

【中文标题】如何以全分辨率从 UIImageView 获取旋转、缩放和平移的图像?【英文标题】:How to get a rotated, zoomed and panned image from an UIImageView at its full resolution? 【发布时间】:2012-06-19 15:14:02 【问题描述】:

我有一个 UIImageView,可以使用手势识别器进行旋转、平移和缩放。结果,它在其封闭视图中被裁剪。 一切正常,但我不知道如何将图片的可见部分保存为全分辨率。这不是屏幕抓取。

我知道我直接从 UIImageView 的可见内容中获取 UIImage,但它仅限于屏幕的分辨率。

我假设我必须对 UIImage 进行相同的转换并裁剪它。有没有简单的方法可以做到这一点?

更新: 例如,我有一个带有高分辨率图像的 UIImageView,比方说一张 8MP iPhone 4s 相机照片,它通过手势转换,因此它在其封闭视图中被缩放、旋转和移动。显然有一些裁剪正在进行,所以只显示了图像的一部分。显示的屏幕分辨率和下划线图像分辨率之间存在巨大差异,我需要图像分辨率中的图像。 UIImageView 在 UIViewContentModeScaleAspectFit 中,但使用 UIViewContentModeScaleAspectFill 的解决方案也可以。

这是我的代码:

- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer 

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 
        [gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
        [gestureRecognizer setRotation:0];
    


- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer 

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 
        [gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
        [gestureRecognizer setScale:1];
    


-(void)panGestureMoveAround:(UIPanGestureRecognizer *)gestureRecognizer;

    UIView *piece = [gestureRecognizer view];

    //We pass in the gesture to a method that will help us align our touches so that the pan and pinch will seems to originate between the fingers instead of other points or center point of the UIView    
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 

        CGPoint translation = [gestureRecognizer translationInView:[piece superview]];
        [piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y+translation.y)];
        [gestureRecognizer setTranslation:CGPointZero inView:[piece superview]];
     else if([gestureRecognizer state] == UIGestureRecognizerStateEnded) 
        //Put the code that you may want to execute when the UIView became larger than certain value or just to reset them back to their original transform scale
    



- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 
    // if the gesture recognizers are on different views, don't allow simultaneous recognition
    if (gestureRecognizer.view != otherGestureRecognizer.view)
        return NO;

    // if either of the gesture recognizers is the long press, don't allow simultaneous recognition
    if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
        return NO;

    return YES;


- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];    
    faceImageView.image = appDelegate.faceImage;

    UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotatePiece:)];
    [faceImageView addGestureRecognizer:rotationGesture];
    [rotationGesture setDelegate:self];

    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scalePiece:)];
    [pinchGesture setDelegate:self];
    [faceImageView addGestureRecognizer:pinchGesture];

    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureMoveAround:)];
    [panRecognizer setMinimumNumberOfTouches:1];
    [panRecognizer setMaximumNumberOfTouches:2];
    [panRecognizer setDelegate:self];
    [faceImageView addGestureRecognizer:panRecognizer];


    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

    [appDelegate fadeObject:moveIcons StartAlpha:0 FinishAlpha:1 Duration:2];
    currentTimer = [NSTimer timerWithTimeInterval:4.0f target:self selector:@selector(fadeoutMoveicons) userInfo:nil repeats:NO];

    [[NSRunLoop mainRunLoop] addTimer: currentTimer forMode: NSDefaultRunLoopMode];


【问题讨论】:

我们需要知道您为图像视图设置的 contentMode,以便我们可以更轻松地为图像计算正确的裁剪矩形 @phix23 内容模式为 UIViewContentModeScaleAspectFit 【参考方案1】:

以下代码使用计算的比例因子创建封闭视图的快照(faceImageView 的父视图,clipsToBounds 设置为 YES)。

假设faceImageView的内容模式为UIViewContentModeScaleAspectFit,并且faceImageView的frame设置为enclosureView的边界。

- (UIImage *)captureView 

    float imageScale = sqrtf(powf(faceImageView.transform.a, 2.f) + powf(faceImageView.transform.c, 2.f));    
    CGFloat widthScale = faceImageView.bounds.size.width / faceImageView.image.size.width;
    CGFloat heightScale = faceImageView.bounds.size.height / faceImageView.image.size.height;
    float contentScale = MIN(widthScale, heightScale);
    float effectiveScale = imageScale * contentScale;

    CGSize captureSize = CGSizeMake(enclosingView.bounds.size.width / effectiveScale, enclosingView.bounds.size.height / effectiveScale);

    NSLog(@"effectiveScale = %0.2f, captureSize = %@", effectiveScale, NSStringFromCGSize(captureSize));

    UIGraphicsBeginImageContextWithOptions(captureSize, YES, 0.0);        
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(context, 1/effectiveScale, 1/effectiveScale);
    [enclosingView.layer renderInContext:context];   
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return img;

根据当前的变换,生成的图像会有不同的大小。例如,当您放大时,尺寸会变小。您还可以将effectiveScale 设置为恒定值,以获取具有恒定大小的图像。

您的手势识别器代码不会限制比例因子,即您可以不受限制地缩小/缩小。这可能非常危险!我的捕获方法可以在您缩小很多时输出非常大的图像。

如果您已缩小拍摄图像的背景将是黑色的。如果你想让它透明,你必须将UIGraphicsBeginImageContextWithOptions的opaque-parameter设置为NO

【讨论】:

很好的解决方案——完美!我找这个已经很久了!所有其他解决方案都只是有效地截图,这是垃圾质量 试过你的代码,但它不适用于缩放图像,我使用滚动视图作为封闭视图,而 faceImageView 是我在滚动视图中的图像视图。它给出了空白图像。没有缩放它可以正常工作。你能帮忙吗?【参考方案2】:

如果您有原始图像,为什么要捕获视图?只需将转换应用于它。这样的事情可能是一个开始:

UIImage *image = [UIImage imageNamed:@"<# original #>"];

CIImage *cimage = [CIImage imageWithCGImage:image.CGImage];

// build the transform you want
CGAffineTransform t = CGAffineTransformIdentity;
CGFloat angle = [(NSNumber *)[self.faceImageView valueForKeyPath:@"layer.transform.rotation.z"] floatValue];
CGFloat scale = [(NSNumber *)[self.faceImageView valueForKeyPath:@"layer.transform.scale"] floatValue];    
t = CGAffineTransformConcat(t, CGAffineTransformMakeScale(scale, scale));
t = CGAffineTransformConcat(t, CGAffineTransformMakeRotation(-angle));

// create a new CIImage using the transform, crop, filters, etc.
CIImage *timage = [cimage imageByApplyingTransform:t];

// draw the result
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef imageRef = [context createCGImage:timage fromRect:[timage extent]];
UIImage *result = [UIImage imageWithCGImage:imageRef];

// save to disk
NSData *png = UIImagePNGRepresentation(result);
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/result.png"];
if (png && [png writeToFile:path atomically:NO]) 
    NSLog(@"\n%@", path);

CGImageRelease(imageRef);

您可以根据需要轻松裁剪输出(请参阅-[CIImage imageByCroppingToRect] 或考虑翻译、应用核心图像过滤器等,具体取决于您的确切需求。

【讨论】:

我想要 UIImageview 中图像的可见部分。这给了我与原始图像相同的图像。你能帮忙吗?结构类似于 UIscrollview 内部的 UIImageview 以提供缩放效果。我只想要分辨率质量可见的那部分。 我有和 Vibhooti 一样的问题,我正在从图像(缩放)中裁剪 Scrollview 可见矩形,它也可以工作,但没有旋转,但是如何在这里考虑旋转?,就像我一样将滚动视图可见点转换为图像(如果图像旋转,则无法使用框架:()我已经苦苦挣扎了 2 周,如果你能帮助我,那就太好了,谢谢【参考方案3】:

我认为 Bellow 代码捕获了您当前的视图 ...

- (UIImage *)captureView 

    CGRect rect = [self.view bounds];

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.yourImage.layer renderInContext:context];   
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return img;


我想你想保存显示屏幕并使用它,所以我发布了这段代码...... 希望,这对你有帮助... :)

【讨论】:

阅读问题,Q 尤其不询问屏幕抓取,因为它受限于设备分辨率。 我可以截屏,没问题,但我需要一个全分辨率的解决方案。附:您的代码甚至无法以视网膜分辨率捕获。 我认为@Tibidabo 真正需要的是一种计算原始图像中可见裁剪矩形的方法,该图像可能比图像视图框架更小或更大。这个裁剪矩形也可以有一个旋转。裁剪矩形基本上取决于 imageView 的 3 个属性:bounds、contentMode 和 transform。创建图像上下文是剪切原始图像可见部分的正确方法,但这是最简单的部分。 @phix23 谢谢。我知道应该怎么做,但是当很多人已经做过时,我只是不想花时间来实现和调试一个非常标准的解决方案。我已经完成了平移和缩放部分,这很简单。 @ParasJoshi 抱歉,我看不出它是如何工作的。创建的矩形不考虑图像视图的缩放、旋转或平移。

以上是关于如何以全分辨率从 UIImageView 获取旋转、缩放和平移的图像?的主要内容,如果未能解决你的问题,请参考以下文章

以全尺寸保存 UIImage,包括从 UIImageView 进行的转换

在 iPhone 上使用 UIImageView 进行图像缩放

IOS如何以全分辨率从相机胶卷中选择图像

从 UIImagePickerController 获取图像后,UIImageView 为 iPhone 5 旋转图像

IndoorAtlas Android SDK 1.4.2-132:以全屏+旋转方式获取平面图图像/位图

如何以全尺寸查看 instagram 个人资料图片? [关闭]