Ich habe ein Kopfgeld hinzugefügt, da jemand die erste Option als hässlichen Hack bezeichnet hat und ich anscheinend keine leicht korrekte und eindeutige Antwort auf Google finden kann. Weitere Abstimmungen / Antworten bitte.
Sie sollten die Bitmap-Bildwiederholung zuverlässiger erhalten, als wenn Sie davon ausgehen, dass es sich um die erste Darstellung handelt. Sie können auch nicht davon ausgehen, dass es eine gibt; Wenn das Bild beispielsweise aus einer PDF-Datei geladen wurde, gibt es einen NSPDFImageRep und keinen NSBitmapImageRep.
Peter Hosey
14
Das ist Buggy Hack. Siehe unten für eine regelmäßige Antwort.
Eonil
6
Wenn man eine bekommt, [* representationUsingType:properties:]: unrecognized selector sent to instanceist die Problemumgehung
Joshcodes
162
Sie können NSImage wie folgt eine Kategorie hinzufügen
Aber TIFF ist RGB und wirft alle Transparenzinformationen weg.
Hot Licks
4
Nein, TIFF macht Alpha- und mehrseitige Bilder einwandfrei. Was hat dich auf die Idee gebracht, dass es nicht so ist? Dies empfiehlt Apple als Zielformat für Retina-Bilder (TIFFs mit mehreren Darstellungen).
Was ist ImageProps? Was ist dieser Wert übergeben?
ColdSteel
34
Ich bin mir nicht sicher über den Rest von euch, aber ich esse lieber meine volle Enchilada. Was oben beschrieben wurde, wird funktionieren und nichts ist daran falsch, aber ich habe ein paar Dinge ausgelassen. Hier werde ich meine Beobachtungen hervorheben:
Zunächst wird die beste Darstellung bereitgestellt, die 72 DPI zu betragen scheint, selbst wenn die Bildauflösung größer ist. Sie verlieren also die Auflösung
Zweitens, was ist mit mehrseitigen Bildern, beispielsweise in animierten GIFs oder PDFs? Probieren Sie ein animiertes GIF aus. Sie werden feststellen, dass die Animation verloren gegangen ist
Schließlich gehen alle Metadaten wie EXIF, GPS usw. verloren.
Wenn Sie dieses Bild konvertieren möchten, möchten Sie das alles wirklich verlieren? Wenn Sie Ihre volle Mahlzeit essen möchten, dann lesen Sie weiter ...
Manchmal und ich meine manchmal gibt es nichts Besseres als eine gute alte Schulentwicklung. Ja das heißt, wir müssen ein bisschen arbeiten!
Lass uns anfangen:
Ich erstelle eine Kategorie in NSData. Dies sind Klassenmethoden, weil Sie möchten, dass diese Dinge threadsicher sind und es nichts Sichereres gibt, als Ihre Sachen auf den Stapel zu legen. Es gibt zwei Arten von Methoden, eine für die Ausgabe von nicht mehrseitigen Bildern und eine für die Ausgabe von mehrseitigen Bildern.
Liste der Einzelbilder: JPG, PNG, BMP, JPEG-2000
Liste mehrerer Bilder: PDF, GIF, TIFF
Erstellen Sie zunächst einen veränderlichen Datenraum im Speicher.
NSMutableData * imageData = [NSMutableData data];
Zweitens erhalten Sie ein CGImageSourceRef. Ja, es klingt schon hässlich, nicht wahr? Es ist nicht so schlimm, lass uns weitermachen ... Sie möchten wirklich, dass das Quellbild keine Darstellung oder ein NSImage-Datenblock ist. Wir haben jedoch ein kleines Problem. Die Quelle ist möglicherweise nicht kompatibel. Überprüfen Sie daher Ihre UTI anhand der in CGImageSourceCopyTypeIdentifiers () aufgeführten Quellen.
Etwas Code:
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 );
Warten Sie eine Sekunde, warum ist NSImage dort? Nun, es gibt einige Formate, die keine Metadaten haben und CGImageSource nicht unterstützt, aber dies sind gültige Bilder. Ein Beispiel sind PICT-Bilder im alten Stil.
Jetzt haben wir ein CGImageSourceRef, stellen Sie sicher, dass es nicht Null ist, und lassen Sie uns jetzt ein CGImageDestinationRef erhalten. Wow, all diese Schiedsrichter sind im Auge zu behalten. Bisher sind wir bei 2!
Wir werden diese Funktion verwenden: CGImageDestinationCreateWithData ()
1. Parameter ist Ihre imageData (cast CFMutableDataRef)
2nd Param ist Ihre Ausgabe-UTI. Denken Sie an die obige Liste. (zB kUTTypePNG)
3. Parameter ist die Anzahl der zu speichernden Bilder. Für Einzelbilddateien ist dies 1, andernfalls können Sie einfach Folgendes verwenden:
CGImageSourceGetCount (imageSource);
4. Param ist Null.
Überprüfen Sie, ob Sie über dieses CGImageDestinationRef verfügen, und fügen Sie nun unsere Bilder aus der Quelle hinzu. Dies schließt auch alle Metadaten ein und behält die Auflösung bei.
Für mehrere Bilder schleifen wir:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
Für ein einzelnes Bild ist es eine Codezeile bei Index 0:
Ok, finalisieren Sie es, das es auf die Festplatte oder den Datencontainer schreibt:
CGImageDestinationFinalize( imageDest );
Damit Mutable Data von Anfang an alle unsere Bilddaten und Metadaten enthält.
Sind wir schon fertig? Fast, auch mit Müllabfuhr muss man aufräumen! Denken Sie an zwei Refs, einen für die Quelle und einen für das Ziel. Führen Sie also eine CFRelease () durch.
Jetzt sind wir fertig und am Ende erhalten Sie ein konvertiertes Bild, das alle Metadaten, Auflösungen usw. beibehält.
Was ist mit Größenänderung oder ICO / ICNS? Dies ist für einen anderen Tag, aber zusammenfassend befassen Sie sich zuerst mit der Größenänderung ...
Erstellen Sie einen Kontext mit neuer Größe: CGBitmapContextCreate ()
Holen Sie sich eine Bildreferenz aus dem Index: CGImageSourceCreateImageAtIndex ()
Holen Sie sich eine Kopie der Metadaten: CGImageSourceCopyPropertiesAtIndex ()
Zeichnen Sie das Bild in den Kontext: CGContextDrawImage ()
Ruft das Bild mit geänderter Größe aus dem Kontext ab: CGBitmapContextCreateImage ()
Fügen Sie nun das Bild und die Metadaten zum Dest Ref hinzu: CGImageDestinationAddImage ()
Spülen und wiederholen Sie dies für mehrere in die Quelle eingebettete Bilder.
Der einzige Unterschied zwischen einem ICO und einem ICNS besteht darin, dass eines ein einzelnes Bild ist, während das andere mehrere Bilder in einer Datei enthält. Wetten, dass Sie erraten können, welches welches ist?! ;-) Für diese Formate müssen Sie die Größe auf eine bestimmte Größe verkleinern, da sonst ein FEHLER auftritt. Der Prozess ist zwar genau der gleiche, bei dem Sie die richtige UTI verwenden, aber die Größenänderung ist etwas strenger.
Ok, hoffe das hilft anderen da draußen und du bist so voll wie ich jetzt!
Opps, vergessen zu erwähnen. Wenn Sie das NSData-Objekt erhalten, tun Sie, was Sie möchten, z. B. writeToFile, writeToURL oder erstellen Sie ein anderes NSImage, wenn Sie möchten.
Dies ist ein guter Anfang, sicherlich besser als die anderen Antworten (obwohl diese höher bewertet werden), aber er ist nicht vollständig. Erstens verarbeitet die hier vorgestellte Logik PDF-Dateien nicht selbst (CGImageDestinationAddImageFromSource funktioniert nicht, da die Bildquelle null ist). PDF- und andere nicht kopierbare Bildquellen müssen speziell behandelt werden (um entweder Bilder über Miniaturansichten zu erhalten oder die Daten auf andere Weise zu kopieren). Zweitens werden keine Metadaten selbst kopiert. Sie müssen dies explizit tun, indem Sie das Eigenschaftenwörterbuch aus der Bildquelle abrufen und zum Bildziel hinzufügen.
Danielv
Zwei Fragen bitte. 1) Wie können Sie sicher sein, dass [anImage TIFFRepresentation] immer etwas zurückgibt? Gibt es keine NSImage's ohne TIFFRepresentation?. 2) Könnten Sie bitte ein Code-Snippet für mindestens eine (z. B. JPEPDataFromURL) dieser Funktionen vollständig bereitstellen? Ich habe versucht, Ihren Anweisungen zu folgen, und zumindest für mich - es wird nicht funktionieren (ich versuche, eingehende Videodaten als JPEG auf der Festplatte zu speichern, und meine Eingabe geht vom CMSampleBuffer von AVSession über ein NSImage - und ich versuche zu verwenden Ihr Code, um dies in einer Datei zu speichern.
Motti Shneor
15
Swift 4.2 Lösung
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)")
}
}
}
Können Sie erklären, warum Sie self.selfanstelle von verwenden self? Gibt es einen Unterschied?
MartinW
Es stellt sich heraus, dass es keinen Unterschied gibt. Vielleicht war dies in einer früheren Version von Swift, aber ich dachte, es wurde der explizite Klassenname gedruckt, aber es sieht so aus, als ob entweder nur zurückkehren<<Class-Name> <Memory-Address>>
Charlton Provatas
14
Mit swift3 als PNG speichern
import AppKit
extension NSImage {
@discardableResult
func saveAsPNG(url: URL) -> Bool {
guard let tiffData = self.tiffRepresentation else {
print("failed to get tiffRepresentation. url: \(url)")
returnfalse
}
let imageRep = NSBitmapImageRep(data: tiffData)
guard let imageData = imageRep?.representation(using: .PNG, properties: [:]) else {
print("failed to get PNG representation. url: \(url)")
returnfalse
}
do {
try imageData.write(to: url)
returntrue
} catch {
print("failed to write to disk. url: \(url)")
returnfalse
}
}
}
lass tiffData = image.tiffRepresentation! let imageRep = NSBitmapImageRep (data: tiffData) let data = imageRep? .representation (unter Verwendung von: .PNG, Eigenschaften: [:]) do {try data? .write (to: URL (fileURLWithPath: path3), Optionen: NSData.WritingOptions ())} catch {print ("(error)")}
Hoffe,
5
Schneller Stil:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}
Um mit plattformübergreifendem Code zu helfen, habe ich eine Version implementiert UIImagePNGRepresentation(), die auf einem Mac ausgeführt wird (und verwendet wird NSImage).
if let data = UIImagePNGRepresentation(myImage) {
do { try data.write(to: url, options: [.atomic]) }
catch let error { print("Error writing image (\(error))") }
}
Nur noch ein garantierter Arbeitsansatz mit SWIFT:
Ich habe einen "Image Well", in dem der Benutzer jedes Bild ablegen kann. Und dieser "Image Well" hat eine Bildeigenschaft (vom Typ NSImage), auf die über eine Steckdose zugegriffen wird:
@IBOutlet weak var imageWell: NSImageView!
Und der Code, der dieses Bild speichert (Sie können es in die Schaltflächenaktion einfügen), lautet:
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)
}
In der ersten Zeile des angegebenen Codes prüfen wir, ob unser Image Well ein Bild enthält.
In der 2. Zeile machen wir eine Bitmap-Darstellung dieses Bildes.
In der 3. Zeile konvertieren wir unsere BitmapRepresentation in einen JPG-Typ mit einem Komprimierungsfaktor von "1" (keine Komprimierung).
In der 4. Zeile speichern wir die JPG-Daten unter einem bestimmten Pfad. "atomar: wahr" bedeutet, dass die Datei als ein Stück gespeichert wird und dass wir sicher sind, dass die Operation erfolgreich sein wird.
NB: Sie können einen anderen NSBitmapImageFileType in der 3. Zeile verwenden, um Ihr Bild in einem anderen Format zu speichern. Es hat viel:
Antworten:
Mach so etwas:
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0]; NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil]; [data writeToFile: @"/path/to/file.png" atomically: NO];
quelle
[* representationUsingType:properties:]: unrecognized selector sent to instance
ist die ProblemumgehungSie können NSImage wie folgt eine Kategorie hinzufügen
@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
Der Aufruf von "TIFFRepresentation" ist wichtig, da Sie sonst möglicherweise kein gültiges Bild erhalten.
quelle
Ich bin mir nicht sicher über den Rest von euch, aber ich esse lieber meine volle Enchilada. Was oben beschrieben wurde, wird funktionieren und nichts ist daran falsch, aber ich habe ein paar Dinge ausgelassen. Hier werde ich meine Beobachtungen hervorheben:
Wenn Sie dieses Bild konvertieren möchten, möchten Sie das alles wirklich verlieren? Wenn Sie Ihre volle Mahlzeit essen möchten, dann lesen Sie weiter ...
Manchmal und ich meine manchmal gibt es nichts Besseres als eine gute alte Schulentwicklung. Ja das heißt, wir müssen ein bisschen arbeiten!
Lass uns anfangen:
Ich erstelle eine Kategorie in NSData. Dies sind Klassenmethoden, weil Sie möchten, dass diese Dinge threadsicher sind und es nichts Sichereres gibt, als Ihre Sachen auf den Stapel zu legen. Es gibt zwei Arten von Methoden, eine für die Ausgabe von nicht mehrseitigen Bildern und eine für die Ausgabe von mehrseitigen Bildern.
Liste der Einzelbilder: JPG, PNG, BMP, JPEG-2000
Liste mehrerer Bilder: PDF, GIF, TIFF
Erstellen Sie zunächst einen veränderlichen Datenraum im Speicher.
Zweitens erhalten Sie ein CGImageSourceRef. Ja, es klingt schon hässlich, nicht wahr? Es ist nicht so schlimm, lass uns weitermachen ... Sie möchten wirklich, dass das Quellbild keine Darstellung oder ein NSImage-Datenblock ist. Wir haben jedoch ein kleines Problem. Die Quelle ist möglicherweise nicht kompatibel. Überprüfen Sie daher Ihre UTI anhand der in CGImageSourceCopyTypeIdentifiers () aufgeführten Quellen.
Etwas Code:
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 );
Warten Sie eine Sekunde, warum ist NSImage dort? Nun, es gibt einige Formate, die keine Metadaten haben und CGImageSource nicht unterstützt, aber dies sind gültige Bilder. Ein Beispiel sind PICT-Bilder im alten Stil.
Jetzt haben wir ein CGImageSourceRef, stellen Sie sicher, dass es nicht Null ist, und lassen Sie uns jetzt ein CGImageDestinationRef erhalten. Wow, all diese Schiedsrichter sind im Auge zu behalten. Bisher sind wir bei 2!
Wir werden diese Funktion verwenden: CGImageDestinationCreateWithData ()
3. Parameter ist die Anzahl der zu speichernden Bilder. Für Einzelbilddateien ist dies 1, andernfalls können Sie einfach Folgendes verwenden:
CGImageSourceGetCount (imageSource);
4. Param ist Null.
Überprüfen Sie, ob Sie über dieses CGImageDestinationRef verfügen, und fügen Sie nun unsere Bilder aus der Quelle hinzu. Dies schließt auch alle Metadaten ein und behält die Auflösung bei.
Für mehrere Bilder schleifen wir:
for ( NSUInteger i = 0; i < count; ++i ) CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
Für ein einzelnes Bild ist es eine Codezeile bei Index 0:
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
Ok, finalisieren Sie es, das es auf die Festplatte oder den Datencontainer schreibt:
Damit Mutable Data von Anfang an alle unsere Bilddaten und Metadaten enthält.
Sind wir schon fertig? Fast, auch mit Müllabfuhr muss man aufräumen! Denken Sie an zwei Refs, einen für die Quelle und einen für das Ziel. Führen Sie also eine CFRelease () durch.
Jetzt sind wir fertig und am Ende erhalten Sie ein konvertiertes Bild, das alle Metadaten, Auflösungen usw. beibehält.
Meine NSData-Kategoriemethoden sehen folgendermaßen aus:
Was ist mit Größenänderung oder ICO / ICNS? Dies ist für einen anderen Tag, aber zusammenfassend befassen Sie sich zuerst mit der Größenänderung ...
Spülen und wiederholen Sie dies für mehrere in die Quelle eingebettete Bilder.
Der einzige Unterschied zwischen einem ICO und einem ICNS besteht darin, dass eines ein einzelnes Bild ist, während das andere mehrere Bilder in einer Datei enthält. Wetten, dass Sie erraten können, welches welches ist?! ;-) Für diese Formate müssen Sie die Größe auf eine bestimmte Größe verkleinern, da sonst ein FEHLER auftritt. Der Prozess ist zwar genau der gleiche, bei dem Sie die richtige UTI verwenden, aber die Größenänderung ist etwas strenger.
Ok, hoffe das hilft anderen da draußen und du bist so voll wie ich jetzt!
Opps, vergessen zu erwähnen. Wenn Sie das NSData-Objekt erhalten, tun Sie, was Sie möchten, z. B. writeToFile, writeToURL oder erstellen Sie ein anderes NSImage, wenn Sie möchten.
Viel Spaß beim Codieren!
quelle
Swift 4.2 Lösung
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)") } } }
quelle
self.self
anstelle von verwendenself
? Gibt es einen Unterschied?<<Class-Name> <Memory-Address>>
Mit swift3 als PNG speichern
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 } } }
quelle
Schneller Stil:
if let imgRep = image?.representations[0] as? NSBitmapImageRep { if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:]) { data.writeToFile("/path/to/file.png", atomically: false) } }
quelle
Um mit plattformübergreifendem Code zu helfen, habe ich eine Version implementiert
UIImagePNGRepresentation()
, die auf einem Mac ausgeführt wird (und verwendet wirdNSImage
).#if os(macOS) public func UIImagePNGRepresentation(_ image: NSImage) -> Data? { guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return nil } let imageRep = NSBitmapImageRep(cgImage: cgImage) imageRep.size = image.size // display size in points return imageRep.representation(using: .png, properties: [:]) } #endif
Verwendung:
if let data = UIImagePNGRepresentation(myImage) { do { try data.write(to: url, options: [.atomic]) } catch let error { print("Error writing image (\(error))") } }
quelle
Nur noch ein garantierter Arbeitsansatz mit SWIFT:
Ich habe einen "Image Well", in dem der Benutzer jedes Bild ablegen kann. Und dieser "Image Well" hat eine Bildeigenschaft (vom Typ NSImage), auf die über eine Steckdose zugegriffen wird:
Und der Code, der dieses Bild speichert (Sie können es in die Schaltflächenaktion einfügen), lautet:
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) }
In der ersten Zeile des angegebenen Codes prüfen wir, ob unser Image Well ein Bild enthält.
In der 2. Zeile machen wir eine Bitmap-Darstellung dieses Bildes.
In der 3. Zeile konvertieren wir unsere BitmapRepresentation in einen JPG-Typ mit einem Komprimierungsfaktor von "1" (keine Komprimierung).
In der 4. Zeile speichern wir die JPG-Daten unter einem bestimmten Pfad. "atomar: wahr" bedeutet, dass die Datei als ein Stück gespeichert wird und dass wir sicher sind, dass die Operation erfolgreich sein wird.
NB: Sie können einen anderen NSBitmapImageFileType in der 3. Zeile verwenden, um Ihr Bild in einem anderen Format zu speichern. Es hat viel:
etc.
quelle
Objektive Lösung mit und ohne Komprimierung
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]; } } }
quelle