web-dev-qa-db-fra.com

Recherche du type d'image à partir de NSData ou UIImage

Je charge une image à partir d'une URL fournie par un tiers. Il n'y a pas d'extension de fichier (ou de nom de fichier d'ailleurs) sur l'URL (car il s'agit d'une URL masquée). Je peux prendre les données de cela (sous la forme de NSData) et les charger dans un UIImage et les afficher correctement.

Je souhaite conserver ces données dans un fichier. Cependant, je ne sais pas dans quel format les données sont (PNG, JPG, BMP)? Je suppose que c'est JPG (car c'est une image du Web) mais y a-t-il un moyen programmatique de le savoir avec certitude? J'ai parcouru StackOverflow et la documentation et je n'ai rien trouvé.

TIA.


Edit: ai-je vraiment besoin de l'extension de fichier? Je le persiste sur un stockage externe (Amazon S3) mais étant donné qu'il sera toujours utilisé dans le contexte d'iOS ou d'un navigateur (qui semblent tous les deux bien interpréter les données sans extension), ce n'est peut-être pas un problème .

65
pschang

Si vous avez NSData pour le fichier image, vous pouvez deviner le type de contenu en regardant le premier octet:

+ (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.

Amélioration sur réponse de wl. , voici un moyen beaucoup plus étendu et précis de prédire le type MIME de l'image en fonction de la signature. Le code a été largement inspiré par php's 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

}

La méthode ci-dessus reconnaît les types d'images suivants:

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

Malheureusement, il n'existe aucun moyen simple d'obtenir ce type d'informations à partir d'une instance UIImage, car ses données bitmap encapsulées ne sont pas accessibles.

26
akashivskyy

La solution de @Tai Le pour Swift 3 assigne des données entières dans un tableau d'octets. Si une image est grande, elle peut provoquer un crash. Cette solution affecte simplement un seul octet:

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

Si vous récupérez l'image à partir d'une URL, vous pouvez probablement inspecter les en-têtes de réponse HTTP. Est-ce que le Content-Type l'en-tête contient quelque chose d'utile? (J'imagine que ce serait le cas, car un navigateur serait probablement en mesure d'afficher correctement l'image, et il ne pourrait le faire que si le type de contenu était correctement défini)

8
Dave DeLong

Version 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

Une alternative de réponse acceptée consiste à vérifier l'UTI de l'image avec image I/O frameWork. Vous pouvez obtenir le type d'image sous forme UTI. essaye ça:

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

Par exemple, l'UTI d'une image GIF est "com.compuserve.gif" et l'UTI d'une image PNG est "public.png". MAIS vous ne pouvez pas obtenir UTI à partir d'une image qui image I/O frameWork n'est pas reconnu.

1
ooOlly

Si cela vous importe vraiment, je pense que vous devrez examiner le flux secondaire. Un JPEG commencera par les octets FF D8. Un PNG commencera par 89 50 4E 47 0D 0A 1A 0A. Je ne sais pas si BMP a un en-tête similaire, mais je ne pense pas que vous soyez trop susceptible de les rencontrer sur le Web en 2010.

Mais est-ce vraiment important pour vous? Ne pouvez-vous pas simplement le traiter comme une image inconnue et laisser Cocoa Touch faire le travail?

1
Steven Fisher

J'ai fait une bibliothèque pour vérifier le type d'image de NSData:

https://github.com/sweetmandm/ImageFormatInspector

0
jankins

Implémentez une vérification de signature pour chaque format d'image connu. Voici une fonction Objective-C rapide qui fait cela pour les données PNG:

// 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

Ma solution améliorée basée sur @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
        }
    }
}

Quelques exemples d'utilisation:

//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