78

特定のディレクトリに NSImage を新しいファイル (png、jpg、...) として保存するにはどうすればよいですか?

4

9 に答える 9

164

このように NSImage にカテゴリを追加できます

@interface NSImage(saveAsJpegWithName)
- (void) saveAsJpegWithName:(NSString*) fileName;
@end

@implementation NSImage(saveAsJpegWithName)

- (void) saveAsJpegWithName:(NSString*) fileName
{
    // Cache the reduced image
    NSData *imageData = [self TIFFRepresentation];
    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
    NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
    imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
    [imageData writeToFile:fileName atomically:NO];        
}

@end

「TIFFRepresentation」への呼び出しは不可欠です。そうしないと、有効な画像を取得できない場合があります。

于 2010-07-09T13:29:23.803 に答える
49

次のようにします。

NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
于 2010-06-14T16:18:43.733 に答える
35

あなたの残りの部分についてはわかりませんが、私は完全なエンチラーダを食べることを好みます. 上記で説明したことは機能し、何も問題はありませんが、省略されていることがいくつかあります。ここで、私の所見を強調します。

  • 最初に、画像の解像度がそれより大きい場合でも 72 DPI のように見える最適な表現が提供されます。だからあなたは解像度を失っています
  • 次に、アニメーション GIF や PDF などの複数ページの画像についてはどうでしょう。先に進み、アニメーション GIF を試してみてください。アニメーションが失われていることがわかります
  • 最後に、EXIF、GPS などのメタデータはすべて失われます。

では、その画像を変換したい場合、本当にすべてを失いたいのでしょうか? しっかりと食事をしたい場合は、読み進めてください...

時には、古き良き学校の開発に勝るものはないこともあります。ええ、それは私たちが少し仕事をしなければならないことを意味します!

始めましょう:

NSData でカテゴリを作成します。これらをスレッド セーフにする必要があり、自分のものをスタックに置くよりも安全なことはないため、これらはクラス メソッドです。非マルチページ画像出力用とマルチページ画像出力用の 2 種類の方法があります。

単一画像のリスト: JPG、PNG、BMP、JPEG-2000

複数の画像のリスト: PDF、GIF、TIFF

最初にメモリ内に可変データ領域を作成します。

NSMutableData * imageData    = [NSMutableData data];

次に、CGImageSourceRef を取得します。ええ、すでに醜いですね。それほど悪くはありません。続けましょう...ソース画像は、データの表現または NSImage チャンクではありません。ただし、小さな問題があります。ソースに互換性がない可能性があるため、UTI を CGImageSourceCopyTypeIdentifiers() からリストされたものと照らし合わせて確認してください。

いくつかのコード:

CGImageSourceRef imageSource = nil;
if ( /* CHECK YOUR UTI HERE */ )
    return CGImageSourceCreateWithURL( (CFURLRef)aURL, nil );

NSImage * anImage = [[NSImage alloc] initWithContentsOfURL:aURL];

if ( anImage )
    return CGImageSourceCreateWithData( (CFDataRef)[anImage TIFFRepresentation], nil );

ちょっと待って、なぜ NSImage があるのですか? メタデータを持たず、CGImageSource がサポートしていない形式がいくつかありますが、これらは有効な画像です。例として、古いスタイルの PICT 画像があります。

これで CGImageSourceRef ができました。nil でないことを確認してから、CGImageDestinationRef を取得しましょう。これらすべての参照を追跡する必要があります。これまでのところ、私たちは2です!

この関数を使用します: CGImageDestinationCreateWithData()

  • 最初のパラメータは imageData です (キャスト CFMutableDataRef)
  • 2 番目のパラメータは出力 UTI です。上記のリストを思い出してください。(例: kUTTypePNG)
  • 3 番目のパラメータは、保存する画像の数です。単一の画像ファイルの場合、これは 1 です。それ以外の場合は、次のように単純に使用できます。

    CGImageSourceGetCount( imageSource );

  • 4 番目のパラメータは nil です。

この CGImageDestinationRef があるかどうかを確認し、ソースから画像を追加しましょう...これには、任意/すべてのメタデータも含まれ、解像度が保持されます。

複数の画像の場合、ループします:

for ( NSUInteger i = 0; i < count; ++i )
                CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );

単一の画像の場合、インデックス 0 の 1 行のコードです。

CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);

OK、ディスクまたはデータ コンテナーに書き込むファイナライズ:

CGImageDestinationFinalize( imageDest );

そのため、最初のミュータブル データには、すべての画像データとメタデータが含まれています。

まだ終わってないか?ほとんど、ガベージ コレクションがあってもクリーンアップする必要があります。ソース用と宛先用の 2 つの Ref を覚えているので、CFRelease() を実行します。

これで、すべてのメタデータ、解像度などを保持する変換された画像が得られます...

私の NSData カテゴリ メソッドは次のようになります。

+ (NSData *) JPGDataFromURL:(NSURL *)aURL;
+ (NSData *) PNGDataFromURL:(NSURL *)aURL;
+ (NSData *) BMPDataFromURL:(NSURL *)aURL;
+ (NSData *) JPG2DataFromURL:(NSURL *)aURL;

+ (NSData *) PDFDataFromURL:(NSURL *)aURL;
+ (NSData *) GIFDataFromURL:(NSURL *)aURL;
+ (NSData *) TIFFDataFromURL:(NSURL *)aURL;

サイズ変更やICO / ICNSはどうですか?これは別の日にしますが、要約すると、最初にサイズ変更に取り組みます...

  1. 新しいサイズでコンテキストを作成します: CGBitmapContextCreate()
  2. インデックスから画像参照を取得します: CGImageSourceCreateImageAtIndex()
  3. メタデータのコピーを取得します: CGImageSourceCopyPropertiesAtIndex()
  4. コンテキストに画像を描画します: CGContextDrawImage()
  5. コンテキストからサイズ変更された画像を取得します: CGBitmapContextCreateImage()
  6. イメージとメタデータを Dest Ref に追加します: CGImageDestinationAddImage()

ソースに埋め込まれた複数の画像をすすぎ、繰り返します。

ICO と ICNS の唯一の違いは、一方が単一の画像であり、他方が 1 つのファイル内の複数の画像であることです。どれがどれか当てられるかな?! ;-) これらの形式の場合、特定のサイズに縮小する必要があります。そうしないと、エラーが発生します。プロセスは適切な UTI を使用する場合とまったく同じですが、サイズ変更はもう少し厳密です。

これが他の人の助けになることを願っています。あなたは今の私と同じくらい満腹です!

おっと、言い忘れました。NSData オブジェクトを取得したら、writeToFile、writeToURL など、必要に応じてそれを処理するか、必要に応じて別の NSImage を作成します。

ハッピーコーディング!

于 2012-02-29T20:32:16.747 に答える
17

swift3 を使用して PNG として保存する

import AppKit

extension NSImage {
    @discardableResult
    func saveAsPNG(url: URL) -> Bool {
        guard let tiffData = self.tiffRepresentation else {
            print("failed to get tiffRepresentation. url: \(url)")
            return false
        }
        let imageRep = NSBitmapImageRep(data: tiffData)
        guard let imageData = imageRep?.representation(using: .PNG, properties: [:]) else {
            print("failed to get PNG representation. url: \(url)")
            return false
        }
        do {
            try imageData.write(to: url)
            return true
        } catch {
            print("failed to write to disk. url: \(url)")
            return false
        }
    }
}
于 2016-09-24T15:27:44.253 に答える
16

Swift 4.2 ソリューション

public extension NSImage {
    public func writePNG(toURL url: URL) {

        guard let data = tiffRepresentation,
              let rep = NSBitmapImageRep(data: data),
              let imgData = rep.representation(using: .png, properties: [.compressionFactor : NSNumber(floatLiteral: 1.0)]) else {

            Swift.print("\(self) Error Function '\(#function)' Line: \(#line) No tiff rep found for image writing to \(url)")
            return
        }

        do {
            try imgData.write(to: url)
        }catch let error {
            Swift.print("\(self) Error Function '\(#function)' Line: \(#line) \(error.localizedDescription)")
        }
    }
}
于 2017-07-11T19:01:16.403 に答える
5

迅速なスタイル:

if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
      if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
      {
           data.writeToFile("/path/to/file.png", atomically: false)
      }
}
于 2015-09-13T18:14:26.133 に答える
1

SWIFT を使用して動作することが保証されているもう 1 つのアプローチ:

ユーザーが任意の画像をドロップできる「Image Well」があります。そして、この「Image Well」には、アウトレット経由でアクセスできる (NSImage 型の) image プロパティがあります。

@IBOutlet weak var imageWell: NSImageView!

この画像を保存するコード (ボタン アクション内に配置できます) は次のとおりです。

if imageWell.image != nil {
   let bMImg = NSBitmapImageRep(data: (imageWell?.image?.TIFFRepresentation)!)
   let dataToSave = bMImg?.representationUsingType(NSBitmapImageFileType.NSJPEGFileType, properties: [NSImageCompressionFactor : 1])
   dataToSave?.writeToFile("/users/user/desktop/image.jpg", atomically: true)
}

指定されたコードの 1 行目で、Image Well に画像があるかどうかを確認します。

2 行目では、そのイメージのビットマップ表現を作成します。

3 行目では、BitmapRepresentation を JPG タイプに変換し、圧縮係数を「1」に設定します (圧縮なし)。

4 行目では、JPG データを特定のパスで保存します。「atomically: true」は、ファイルが 1 つのピースとして保存され、操作が成功することを保証することを意味します。

注意: 3行目に別の NSBitmapImageFileType を使用して、画像を別の形式で保存できます。それはたくさんあります:

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

于 2016-07-26T15:24:36.163 に答える
0

圧縮あり/なしのオブジェクト ソリューション

NSImage* image;

// No compression
NSData* data = [image TIFFRepresentation];
// With compression
NSData* data = [image
                TIFFRepresentationUsingCompression:NSTIFFCompressionLZW
                factor:0.5f];

if (data != nil) {
    NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithData:data];
    if (bitmap != nil) {
        NSData* bitmapData = [bitmap
                              representationUsingType:NSBitmapImageFileTypePNG
                              properties:@{}];
        if (bitmapData != nil) {
            [bitmapData
             writeToFile:<file_path>
             atomically:true];
        }
    }
}
于 2020-02-28T11:29:25.763 に答える