web-dev-qa-db-ja.com

iOS UIImageView画像を縮小すると、iPad 2でエイリアス画像が生成される

UIImageViewを使用して、フルサイズで表示するために選択できる画像のサムネイルを表示しています。 UIImageViewのコンテンツモードはアスペクトフィットに設定されています。

画像は通常、約500px x 500pxから100px x 100pxに縮小されます。網膜のiPadではそれらは非常によく表示されますが、iPad2ではサイズがネイティブのイメージサイズに近づくまで、エイリアスが大きくなります。

例:

Original Image

元の画像

Retina iPad 100x100

100px x 100pxでのRetina iPadレンダリング

iPad 2 100x100

iPad 2が100px x 100pxでレンダリング

IPad 2と新しいiPadの違いは、単に画面解像度であるか、GPUが画像を拡大縮小する機能を備えているためかもしれません。いずれにしても、iPad 2のレンダリングは非常に貧弱です。

最初に、新しいコンテキストを作成し、補間品質を高に設定して画像をコンテキストに描画することにより、画像サイズを縮小してみました。この場合、画像は両方のiPadで正常に見えます。

イメージコピー/サイズ変更の道を進む前に、欠けている簡単なものがないかどうかを確認したいと思いました。 UIImageが縮小されることはありませんが、縮小を処理するためにUIImageViewがあったという印象を受けましたが、現時点では縮小がうまく機能していないようです。 (もしあれば)何が欠けていますか?

pdate:注:レンダリング/サイズ変更された画像のドロップシャドウがコードに追加されます。これを無効にしても、スケーリングの品質に違いはありませんでした。

37
howard10

物事を改善しているように見える私が試した別のアプローチは、minificationFilterを設定することです:

[imageView.layer setMinificationFilter:kCAFilterTrilinear]

品質は確かに改善されており、パフォーマンスの低下には気づきませんでした。

75
howard10

小さな縮小フィルターバイアスを適用すると、画像を自分でリサンプルしたくない場合に役立ちます。

imageView.layer.minificationFilter = kCAFilterTrilinear
imageView.layer.minificationFilterBias = 0.1

enter image description here

左の画像にはフィルタリングが適用されていません。右の画像には0.1のフィルターバイアスがあります。

明示的なラスタライズは必要ないことに注意してください。

非常に小さな値を試してみると、通常、スケーリングアーティファクトを十分に平滑化する値が考えられます。ビットマップのサイズを自分で変更するよりもはるかに簡単です。確かに、バイアスが増加すると詳細が失われるため、画像を表示している画像ビューのフレームのサイズにもよりますが、0.1未満の値でも十分でしょう。

トリリニアフィルタリングは、レイヤー上で mipmapping を効果的に有効にすることを理解してください。これは、基本的に、ビットマップの追加のコピーを段階的に小さいスケールで生成することを意味します。これは、レンダリング速度を上げ、スケーリングのエイリアシングを減らすためにレンダリングで使用される非常に一般的な手法です。トレードオフは、より多くのメモリを必要とすることですが、連続してダウンサンプリングされたビットマップのメモリ使用量は指数関数的に減少します。

このテクニックのもう1つの潜在的な利点は、私自身は試していませんが、minificationFilterBiasをアニメートできることです。したがって、アニメーションの一部として画像ビューをかなり縮小する場合は、フィルターバイアスを0.0から、縮小したサイズに適切であると判断した小さな値にアニメーション化することも検討してください。

最後に、他の人が指摘したように、ソース画像が非常に大きい場合、Core Animationは常に元のビットマップを維持するため、使いすぎるとこの手法は適切ではありません。ほとんどの場合、ミップマッピングを使用するのではなく、画像のサイズを変更してからソース画像を破棄することをお勧めしますが、一時的または画像ビューの割り当てがすぐに解除される場合は、これで問題ありません。

9
CIFilter

大きな画像を小さな画像ビューに配置しただけでは、見た目が悪くなります。

解決策は、画像のサイズを適切に変更することです...私はトリックを行うサンプル関数を追加します:

- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = image.CGImage;

    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);

    CGContextConcatCTM(context, flipVertical);
    CGContextDrawImage(context, newRect, imageRef);

    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    CGImageRelease(newImageRef);
    UIGraphicsEndImageContext();

    return newImage;
}

この関数には時間がかかる場合があります。そのため、結果をキャッシュファイルに保存することができます。

7
Bastian

メモリを浪費することを恐れず、特定のケースで何をしているのかを知っている場合、これは美しく機能します。

myView.layer.shouldRasterize = YES;
myView.layer.rasterizationScale = 2;

結果の品質は、setMinificationFilterよりもはるかに優れています。

私は256x256の画像を使用しており、それらを48ピクセルのようなものにスケーリングしています。明らかに、ここでのより適切な解決策は、イメージを正確な宛先サイズにダウンスケールすることです。

7
blackjack75

次に私を助けました:

imageView.layer.minificationFilter = kCAFilterTrilinear
imageView.layer.shouldRasterize = true
imageView.layer.rasterizationScale = UIScreen.mainScreen().scale

パフォーマンスを監視するスクロールリストで使用する場合。

1