web-dev-qa-db-ja.com

NSDataまたはUIImageから画像タイプを見つける

サードパーティから提供されたURLから画像をロードしています。 URLにはファイル拡張子(または、そのためのファイル名)はありません(URLが隠されているため)。これからデータを(NSDataの形式で)取得し、UIImageにロードして正常に表示できます。

このデータをファイルに永続化したい。ただし、データの形式(PNG、JPG、BMP)はわかりませんか?私はそれがJPGであることを前提としています(Webからの画像だからです)が、確実に見つけるためのプログラム的な方法はありますか? StackOverflowとドキュメントを調べましたが、何も見つかりませんでした。

TIA。


編集:ファイル拡張子は本当に必要ですか?私はそれを外部ストレージ(Amazon S3)に永続化していますが、iOSまたはブラウザのコンテキストで常に使用されることを考慮しています(どちらも拡張なしでデータを解釈するのに問題がないようです)これはおそらく問題ではありません。

65
pschang

画像ファイルにNSDataがある場合は、最初のバイトを見てコンテンツタイプを推測できます。

+ (NSString *)contentTypeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];

    switch (c) {
    case 0xFF:
        return @"image/jpeg";
    case 0x89:
        return @"image/png";
    case 0x47:
        return @"image/gif";
    case 0x49:
    case 0x4D:
        return @"image/tiff";
    }
    return nil;
}
142
wl.

wl。's answer を改良すると、署名に基づいて画像のMIMEタイプを予測するためのはるかに拡張された正確な方法があります。コードは、主にphpの ext/standard/image.c に触発されました。

- (NSString *)mimeTypeByGuessingFromData:(NSData *)data {

    char bytes[12] = {0};
    [data getBytes:&bytes length:12];

    const char bmp[2] = {'B', 'M'};
    const char gif[3] = {'G', 'I', 'F'};
    const char swf[3] = {'F', 'W', 'S'};
    const char swc[3] = {'C', 'W', 'S'};
    const char jpg[3] = {0xff, 0xd8, 0xff};
    const char psd[4] = {'8', 'B', 'P', 'S'};
    const char iff[4] = {'F', 'O', 'R', 'M'};
    const char webp[4] = {'R', 'I', 'F', 'F'};
    const char ico[4] = {0x00, 0x00, 0x01, 0x00};
    const char tif_ii[4] = {'I','I', 0x2A, 0x00};
    const char tif_mm[4] = {'M','M', 0x00, 0x2A};
    const char png[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
    const char jp2[12] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};


    if (!memcmp(bytes, bmp, 2)) {
        return @"image/x-ms-bmp";
    } else if (!memcmp(bytes, gif, 3)) {
        return @"image/gif";
    } else if (!memcmp(bytes, jpg, 3)) {
        return @"image/jpeg";
    } else if (!memcmp(bytes, psd, 4)) {
        return @"image/psd";
    } else if (!memcmp(bytes, iff, 4)) {
        return @"image/iff";
    } else if (!memcmp(bytes, webp, 4)) {
        return @"image/webp";
    } else if (!memcmp(bytes, ico, 4)) {
        return @"image/vnd.Microsoft.icon";
    } else if (!memcmp(bytes, tif_ii, 4) || !memcmp(bytes, tif_mm, 4)) {
        return @"image/tiff";
    } else if (!memcmp(bytes, png, 8)) {
        return @"image/png";
    } else if (!memcmp(bytes, jp2, 12)) {
        return @"image/jp2";
    }

    return @"application/octet-stream"; // default type

}

上記のメソッドは、次の画像タイプを認識します。

  • image/x-ms-bmp(bmp)
  • image/gif(gif)
  • image/jpeg(jpg、jpeg)
  • image/psd(psd)
  • image/iff(iff)
  • image/webp(webp)
  • image/vnd.Microsoft.icon(ico)
  • image/tiff(tif、tiff)
  • image/png(png)
  • image/jp2(jp2)

残念ながら、カプセル化されたビットマップデータにアクセスできないため、UIImageインスタンスからこの種の情報を取得する簡単な方法はありません。

26
akashivskyy

@Tai LeのSwift 3のソリューションは、データ全体をバイト配列に割り当てています。画像が大きい場合、クラッシュを引き起こす可能性があります。このソリューションは、単一バイトを割り当てます。

import Foundation

public extension Data {
    var fileExtension: String {
        var values = [UInt8](repeating:0, count:1)
        self.copyBytes(to: &values, count: 1)

        let ext: String
        switch (values[0]) {
        case 0xFF:
            ext = ".jpg"
        case 0x89:
            ext = ".png"
        case 0x47:
            ext = ".gif"
        case 0x49, 0x4D :
            ext = ".tiff"
        default:
            ext = ".png"
        }
        return ext
    }
}
11
ccoroom

URLから画像を取得する場合は、おそらくHTTP応答ヘッダーを調べることができます。 Content-Typeヘッダーには有用なものが含まれていますか? (ブラウザはおそらく画像を正しく表示することができ、コンテンツタイプが適切に設定されている場合にのみそれを行うことができるので、それは想像するでしょう)

8
Dave DeLong

Swift3バージョン:

let data: Data = UIImagePNGRepresentation(yourImage)!

extension Data {
    var format: String {
        let array = [UInt8](self)
        let ext: String
        switch (array[0]) {
        case 0xFF:
            ext = "jpg"
        case 0x89:
            ext = "png"
        case 0x47:
            ext = "gif"
        case 0x49, 0x4D :
            ext = "tiff"
        default:
            ext = "unknown"
        }
        return ext
    }
}
5
Tai Le

受け入れられる回答の代替手段は、image I/O frameWorkで画像のUTIをチェックすることです。 UTIからイメージタイプを取得できます。これを試して:

CGImageSourceRef imgSrc = CGImageSourceCreateWithData((CFDataRef)data, NULL);
NSString *uti = (NSString*)CGImageSourceGetType(imgSrc);
NSLog(@"%@",uti);

たとえば、GIF画像のUTIは「com.compuserve.gif」であり、PNG画像のUTIは「public.png」ですが、image I/O frameWorkが認識しない画像からUTIを達成することはできません。

1
ooOlly

本当に重要な場合は、バイトストリームを調べる必要があると思います。 JPEGはFF D8バイトで始まります。 PNGは89 50 4E 47 0D 0A 1A 0Aで始まります。 BMPに同様のヘッダーがあるかどうかはわかりませんが、2010年にWeb上のものに遭遇する可能性はあまりないと思います。

しかし、それはあなたにとって本当に重要ですか?それを未知の画像として扱い、Cocoa Touchに仕事をさせてはいけませんか?

1
Steven Fisher

NSDataのイメージタイプをチェックするライブラリを作成しました。

https://github.com/sweetmandm/ImageFormatInspector

0
jankins

既知の各画像形式の署名チェックを実装します。 PNGデータに対してこれを行う簡単なObjective-C関数を次に示します。

// Verify that NSData contains PNG data by checking the signature

- (BOOL) isPNGData:(NSData*)data
{
  // Verify that the PNG file signature matches

  static const
  unsigned char   png_sign[8] = {137, 80, 78, 71, 13, 10, 26, 10};

  unsigned char   sig[8] = {0, 0, 0, 0, 0, 0, 0, 0};

  if ([data length] <= 8) {
    return FALSE;
  }

  [data getBytes:&sig length:8];

  BOOL same = (memcmp(sig, png_sign, 8) == 0);

  return same;
}
0
MoDJ

@ccoroomに基づく私の改善されたソリューション

//  Data+ImageContentType.Swift

import Foundation

extension Data {  
    enum ImageContentType: String {
        case jpg, png, gif, tiff, unknown

        var fileExtension: String {
            return self.rawValue
        }
    }

    var imageContentType: ImageContentType {

        var values = [UInt8](repeating: 0, count: 1)

        self.copyBytes(to: &values, count: 1)

        switch (values[0]) {
        case 0xFF:
            return .jpg
        case 0x89:
            return .png
        case 0x47:
           return .gif
        case 0x49, 0x4D :
           return .tiff
        default:
            return .unknown
        }
    }
}

使用例:

//load some image
do {
    let imageData = try Data(contentsOf: URL(string: "https://myServer/images/test.jpg")!)
} catch {
    print("Unable to load image: \(error)")
}

//content type check
guard [Data.ImageContentType.jpg,
       Data.ImageContentType.png].contains(imageData.imageContentType) else {
    print("unsupported image type")
            return
        }

//set file extension
let image = "myImage.\(imageData.imageContentType.fileExtension)" //myImage.jpg
0
Peter Kreinz