12

UIImage は、スケーリングすると常に不明瞭になります。鮮明さを保つにはどうすればよいですか?

- (UIImage *)rescaleImageToSize:(CGSize)size {
    CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height);
    UIGraphicsBeginImageContext(rect.size);
    [self drawInRect:rect];  // scales image to rect
    UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resImage;
}
4

4 に答える 4

46

丸め

まず、スケーリングする前にサイズを丸めていることを確認してください。 drawInRect:この場合、他の方法で使用可能な画像をぼかすことができます。最も近い整数値に丸めるには:

size.width = truncf(size.width);
size.height = truncf(size.height);

特定のタスクでは、切り捨て (floorf) または切り上げ (ceilf) が必要になる場合があります。

CILanczosScaleTransform は利用できません

次に、CILanczosScaleTransform に関する以前の推奨事項は無視してください。Core Image の一部は iOS 5.0 で利用できますが、Lanczos のスケーリングは利用できません。利用可能になった場合は、それを使用してください。Mac OS で作業しているユーザーは、利用可能です。使用してください。

vImage スケーリング

ただし、vImageで利用できる高品質のスケーリング アルゴリズムがあります。次の図は、それを使用する方法 (vImageScaledImage) をさまざまなコンテキスト補間オプションと比較する方法を示しています。また、これらのオプションが異なるズーム レベルでどのように異なる動作をするかにも注目してください。

このでは、ほとんどの線の詳細が保持されています。 図のスケーリング比較

この写真では、左下の葉を比較してください。 樹木写真の縮尺比較

この写真で、右下のテクスチャを比較します。 岩の写真の縮尺比較

ピクセル アートには使用しないでください。奇妙なスケーリング アーティファクトが作成されます。 スケーリング アーティファクトを示すピクセル アートのスケーリング比較

一部の画像では興味深い丸め効果があります。 スペースインベーダーでのスケーリング比較

パフォーマンス

当然のことながら、kCGImageInterpolationHigh は最も遅い標準画像補間オプションです。ここで実装されている vImageScaledImage は、さらに低速です。フラクタル イメージを元のサイズの半分に縮小するには、UIImageInterpolationHigh の 110% の時間がかかりました。4 分の 1 に縮小するには、340% の時間がかかりました。

シミュレーターで実行すると、別の考えになるかもしれません。そこでは、kCGImageInterpolationHigh よりもはるかに高速になる可能性があります。おそらく、vImage のマルチコア最適化により、デスクトップでの相対的な優位性が得られます。

コード

// Method: vImageScaledImage:(UIImage*) sourceImage withSize:(CGSize) destSize
// Returns even better scaling than drawing to a context with kCGInterpolationHigh.
// This employs the vImage routines in Accelerate.framework.
// For more information about vImage, see https://developer.apple.com/library/mac/#documentation/performance/Conceptual/vImage/Introduction/Introduction.html#//apple_ref/doc/uid/TP30001001-CH201-TPXREF101
// Large quantities of memory are manually allocated and (hopefully) freed here.  Test your application for leaks before and after using this method.
- (UIImage*) vImageScaledImage:(UIImage*) sourceImage withSize:(CGSize) destSize;
{
    UIImage *destImage = nil;

    if (sourceImage)
    {
        // First, convert the UIImage to an array of bytes, in the format expected by vImage.
        // Thanks: http://stackoverflow.com/a/1262893/1318452
        CGImageRef sourceRef = [sourceImage CGImage];
        NSUInteger sourceWidth = CGImageGetWidth(sourceRef);
        NSUInteger sourceHeight = CGImageGetHeight(sourceRef);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        unsigned char *sourceData = (unsigned char*) calloc(sourceHeight * sourceWidth * 4, sizeof(unsigned char));
        NSUInteger bytesPerPixel = 4;
        NSUInteger sourceBytesPerRow = bytesPerPixel * sourceWidth;
        NSUInteger bitsPerComponent = 8;
        CGContextRef context = CGBitmapContextCreate(sourceData, sourceWidth, sourceHeight,
                                                     bitsPerComponent, sourceBytesPerRow, colorSpace,
                                                     kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big);
        CGContextDrawImage(context, CGRectMake(0, 0, sourceWidth, sourceHeight), sourceRef);
        CGContextRelease(context);

        // We now have the source data.  Construct a pixel array
        NSUInteger destWidth = (NSUInteger) destSize.width;
        NSUInteger destHeight = (NSUInteger) destSize.height;
        NSUInteger destBytesPerRow = bytesPerPixel * destWidth;
        unsigned char *destData = (unsigned char*) calloc(destHeight * destWidth * 4, sizeof(unsigned char));

        // Now create vImage structures for the two pixel arrays.
        // Thanks: https://github.com/dhoerl/PhotoScrollerNetwork
        vImage_Buffer src = {
            .data = sourceData,
            .height = sourceHeight,
            .width = sourceWidth,
            .rowBytes = sourceBytesPerRow
        };

        vImage_Buffer dest = {
            .data = destData,
            .height = destHeight,
            .width = destWidth,
            .rowBytes = destBytesPerRow
        };

        // Carry out the scaling.
        vImage_Error err = vImageScale_ARGB8888 (
                                                 &src,
                                                 &dest,
                                                 NULL,
                                                 kvImageHighQualityResampling 
                                                 );

        // The source bytes are no longer needed.
        free(sourceData);

        // Convert the destination bytes to a UIImage.
        CGContextRef destContext = CGBitmapContextCreate(destData, destWidth, destHeight,
                                                         bitsPerComponent, destBytesPerRow, colorSpace,
                                                         kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big);
        CGImageRef destRef = CGBitmapContextCreateImage(destContext);

        // Store the result.
        destImage = [UIImage imageWithCGImage:destRef];

        // Free up the remaining memory.
        CGImageRelease(destRef);

        CGColorSpaceRelease(colorSpace);
        CGContextRelease(destContext);

        // The destination bytes are no longer needed.
        free(destData);

        if (err != kvImageNoError)
        {
            NSString *errorReason = [NSString stringWithFormat:@"vImageScale returned error code %d", err];
            NSDictionary *errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                       sourceImage, @"sourceImage", 
                                       [NSValue valueWithCGSize:destSize], @"destSize",
                                       nil];

            NSException *exception = [NSException exceptionWithName:@"HighQualityImageScalingFailureException" reason:errorReason userInfo:errorInfo];

            @throw exception;
        }
    }
    return destImage;
}
于 2012-04-11T02:19:46.850 に答える
2

入れてみてください:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);

高品質の補間を得るために描画する前に。

于 2012-04-09T01:53:09.993 に答える
0

画像を拡大し、シャープなコーナーを維持たいという特殊なケースでは、画像の補間をオフにする必要があります。画像がピクセル アートの場合は、これが必要です。他のほとんどの場合、そうではありません。

UIGraphicsBeginImageContext(rect.size);

// Turn off interpolation to keep the scaled image from being blurred.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);

[self drawInRect:rect];  // scales image to rect
UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

縮小する場合、単純に kCGInterpolationHigh で描画することも最適なオプションではありません。 それはいいのですが、CILanczosScaleTransformの方が少しシャープです。

編集: iOS 5.1 以降、CILanczosScaleTransform は利用できません。使用できる高品質のスケーリング ルーチンについては、他の回答を参照してください。

于 2012-04-09T03:09:36.417 に答える