Come utilizzare l'API DALL-E in Swift: genera volti
Crea un'app SwiftUI per modificare i volti nelle immagini esistenti con l'API di OpenAI. Utilizzeremo una richiesta di rilevamento dei volti Vision per mascherare i volti
Il team OpenAI ha recentemente implementato l'API DALL-E in una beta pubblica. Al momento della scrittura, sono disponibili i seguenti tre endpoint API:
/generations— Generazione di immagini dal prompt di testo/edits— Modifica delle immagini originali in base a un prompt di testo mascherando un'area/variations— Generazione di variazioni di un'immagine
Per approfondire le API e conoscere le loro implementazioni Python e Node.js, leggi la guida di riferimento di OpenAI qui . Useremo le implementazioni CURL per creare la nostra URLRequest in Swift. Mentre ci sei, genera la tua chiave API OpenAI dalla loro console . Ne avremo bisogno per eseguire la nostra applicazione in azione utilizzando Swift.
Il nostro obbiettivo
Ecco il processo per creare un'applicazione SwiftUI basata su OpenAI:
- Crea un TabView SwiftUI con contenuto su due schede per eseguire le diverse API, vale a dire
generationeedits. - Sfrutta il framework Vision
VNDetectFaceRectanglesRequestper rilevare i volti e ritagliarli per generare un'immagine maschera per l'/editsendpoint. - Usa Swift
URLSessioncon async/await per eseguire le nostre richieste API. Prepareremo unMultipartFormDataRequestfile per caricare i dati dell'immagine tramite richieste multi-part/form-data.
enum OpenAIEndpoint: String{
private var baseURL: String { return "https://api.openai.com/v1/images/" }
case generations
case edits
var url: URL {
guard let url = URL(string: baseURL) else {
preconditionFailure("The url is not valid")
}
return url.appendingPathComponent(self.rawValue)
}
}
Richiama l'API Generations in Swift
Impostiamo una ObservableObjectclasse per recuperare le storie dall'API e passarle all'interfaccia SwiftUI:
class OpenAIService: ObservableObject{
let api_key_free = "<INSERT_API_KEY_HERE>"
func generateImage(from prompt: String) async throws -> [Photo]{
var request = URLRequest(url: OpenAIEndpoint.generations.url)
request.setValue("Bearer \(api_key_free)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"prompt": prompt,
"n": 1,
"size": "256x256"
]
let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
request.httpBody = jsonData
let (data, response) = try await URLSession.shared.data(for: request)
let dalleResponse = try? JSONDecoder().decode(DALLEResponse.self, from: data)
return dalleResponse?.data ?? []
}
}
- Abbiamo contrassegnato la
generateImagefunzione conasync throwsper utilizzare la nuova sintassi async/await con URLSession (e saltare i gestori di completamento). - Our
URLRequestcomprende due campi di intestazione e un corpo JSON. - Prendiamo i dati e li decodifichiamo usando
JSONDecoder()in unaDALLEResponsestruct. Il campo datiDALLEResponsecontiene un array di stringhe URL. Alla fine restituiremo questo array[Photo]per popolare un elenco di uno o piùAsyncImagein SwiftUI.
struct DALLEResponse: Decodable {
let created: Int
let data: [Photo]
}
struct Photo: Decodable {
let url: String
}
struct ContentView: View {
@ObservedObject var fetcher = OpenAIService()
@State var photos : [Photo] = []
@State private var textPrompt: String = ""
var body: some View {
VStack {
List {
ForEach(photos, id: \.url) { photo in
AsyncImage(url: URL(string: photo.url)) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
Image(systemName: "photo.fill")
}.frame(maxWidth: .infinity, maxHeight: 500)
.listRowInsets(.init(.zero))
}
}
TextField("Enter the prompt", text: $textPrompt)
.textFieldStyle(RoundedTextFieldStyle())
.padding()
Button(action: runOpenAIGeneration, label: {Text("Generate Text From Prompt")})
}
.padding()
}
func runOpenAIGeneration(){
Task{
do{
self.photos = try await fetcher.generateImage(from: textPrompt)
}catch(let error){
print(error)
}
}
}
}
Genera volti utilizzando l'API Modifica immagine e Vision Framework
La capacità di modificare le immagini originali e creare outpaintings è l'API più intrigante da usare (credo!). Dallo scambio di volti alla generazione di abiti di moda diversi, c'è così tanto che possiamo fare mascherando le regioni in un'immagine e usando i prompt per descrivere la nuova immagine.
In questa sezione utilizzeremo il Visionframework per eseguire una richiesta di rilevamento del volto, disegnare riquadri di delimitazione sul volto rilevato, cancellare quell'area per creare un'immagine mascherata utilizzando Core Graphics ed eseguire la richiesta in più parti per generare una nuova immagine.
Iniziamo impostando la nostra richiesta Vision.
Impostazione di una richiesta di rilevamento facciale visivo
Ecco il codice per avviare ed eseguire un VNDetectFaceRectanglesRequest:
class VisionRequester: ObservableObject{
@Published var maskedImage: UIImage?
//1 - @Published
var faceRect: VNFaceObservation?
func maskFaceUsingVisionRequest(inputImage: UIImage){
let request = VNDetectFaceRectanglesRequest()
let handler = VNImageRequestHandler(cgImage: inputImage.cgImage!, options: [:])
Task{
do{
try handler.perform([request])
guard let results = request.results else {return}
faceRect = results.first //2
guard let faceRect else {return}
//3 - convert bounding boxes to UIKit coordinate system and erase face.
}catch{
print(error)
}
}
}
}
Ho commentato il @Publishedwrapper della proprietà su faceRect. Ma potresti idealmente usarlo per osservare i cambiamenti nella tua vista SwiftUI e disegnare riquadri di delimitazione (dopo aver convertito le coordinate Vision nel sistema SwiftUI) con overlaye Rectangleforme come questa:
.overlay{
Rectangle()
.stroke(Color.green,lineWidth: 3.0)
.frame(width: rect.width, height: rect.height)
.position(x: rect.midX, y: rect.midY)
}
Quindi, abbiamo l'immagine di input e VNFaceObservationil risultato recuperato dalla richiesta Vision. Prima di passare al ritaglio e alla creazione di un'immagine mascherata, convertiamo le coordinate Vision nel sistema UIKit.
Poiché le coordinate di visione sono normalizzate (ciò significa che l'angolo in basso a sinistra è l'origine) e indipendenti dalla risoluzione dello schermo, trasformeremo e ridimensioneremo la casella utilizzando il seguente codice:
func getBoundingBoxes(rect : VNFaceObservation, on imageSize: CGSize) -> CGRect {
let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -imageSize.height)
let scale = CGAffineTransform.identity.scaledBy(x: imageSize.width, y: imageSize.height)
let bounds = rect.boundingBox.applying(scale).applying(transform)
return bounds
}
Ecco il codice per ritagliare il riquadro di delimitazione dall'immagine e sostituirlo con una regione trasparente:
func erase(region: CGRect, from image: UIImage) -> UIImage? {
UIGraphicsBeginImageContext(image.size)
image.draw(at: CGPoint.zero)
let context = UIGraphicsGetCurrentContext()!
let bez = UIBezierPath(rect: region)
context.addPath(bez.cgPath)
context.clip()
context.clear(CGRect(x:0,y:0,width: image.size.width,height: image.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
La nostra funzione aggiornata maskFaceUsingVisionRequestè simile a questa:
func maskFaceUsingVisionRequest(inputImage: UIImage){
let request = VNDetectFaceRectanglesRequest()
let handler = VNImageRequestHandler(cgImage: inputImage.cgImage!, options: [:])
Task{
do{
try handler.perform([request])
guard let results = request.results else {return}
faceRect = results.first
guard let faceRect else {return}
let box = getBoundingBoxes(rect: faceRect, on: inputImage.size)
await MainActor.run{
self.maskedImage = erase(region: box, from: inputImage)
}
}catch{
print(error)
}
}
}
Tutto stabilito! Siamo riusciti a generare un'immagine mascherata. È ora di eseguire la nostra API DALL-E!
Carica immagini utilizzando URLSession nell'API DALL-E
L'API richiede che le immagini siano di forma quadrata, inferiori a <4 MB, con l'immagine mascherata dotata di un canale alfa.
Ecco il codice per eseguire la nostra richiesta di rete utilizzando async/await:
func generateEditedImage(from image: UIImage, with mask: UIImage) async throws -> [Photo] {
guard let imageData = image.pngData() else{return []}
guard let maskData = mask.pngData() else{return []}
let formFields: [String: String] = [
"prompt": "A woman wearing a red dress with an angry face",
"size": "256x256"
]
let multipart = MultipartFormDataRequest(url: OpenAIEndpoint.edits.url)
multipart.addDataField(fieldName: "image", fileName: "image.png", data: imageData, mimeType: "image/png")
multipart.addDataField(fieldName: "mask", fileName: "mask.png", data: maskData, mimeType: "image/png")
for (key, value) in formFields {
multipart.addTextField(named: key, value: value)
}
var request = multipart.asURLRequest()
request.setValue("Bearer \(api_key_free)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
let dalleResponse = try? JSONDecoder().decode(DALLEResponse.self, from: data)
return dalleResponse?.data ?? []
}
La MultiPartFormDataRequeststruct viene utilizzata per preparare una richiesta multipart con boundary, Content-Disposatione altre cose fantasiose. Dai un'occhiata al caricamento di immagini e moduli di Donny Wals su un server utilizzando la URLSessionguida per apprendere le complessità della creazione di una richiesta di dati di moduli in più parti.
Con il URLRequestper il caricamento di immagini e dati del modulo pronti, invocheremo la generateEditedImagefunzione dalla nostra vista SwiftUI nel modo seguente:
struct EditImageView: View {
@StateObject var visionProcessing = VisionRequester()
@StateObject var fetcher = OpenAIService()
@State var inputImage : UIImage = UIImage(named: "sample")!
var body: some View {
//....
if let outputImage = self.visionProcessing.maskedImage{
Image(uiImage: outputImage)
.resizable()
.aspectRatio(contentMode:.fit)
.onReceive(self.visionProcessing.$maskedImage,
perform: { updated in
if let _ = updated?.size{
Task{
do{
self.photos = try await fetcher.generateEditedImage(from: inputImage, with: outputImage)
}catch(let error){
print(error)
}
}
}
})
}
//....
}
}
Questo conclude questo tutorial. Il progetto è disponibile nel mio repository GitHub . Prova a giocare con più volti in una foto di gruppo, mascherandoli in modo iterativo o utilizzando l' variationsendpoint per generare immagini diverse.
Grazie per aver letto.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































