Cara Menggunakan DALL-E API di Swift — Hasilkan Wajah
Bangun aplikasi SwiftUI untuk mengedit wajah pada gambar yang ada dengan API OpenAI. Kami akan menggunakan permintaan deteksi wajah Vision untuk menyamarkan wajah

Tim OpenAI baru-baru ini meluncurkan DALL-E API dalam versi beta publik. Pada saat penulisan, tiga endpoint API berikut tersedia:
/generations
— Menghasilkan gambar dari prompt teks/edits
— Mengedit gambar asli berdasarkan prompt teks dengan menyamarkan suatu wilayah/variations
— Menghasilkan variasi gambar
Untuk mendalami API lebih dalam dan mempelajari implementasi Python dan Node.js mereka, baca panduan referensi OpenAI di sini . Kami akan menggunakan implementasi CURL untuk membuat URLRequest kami di Swift. Saat Anda melakukannya, buat kunci API OpenAI Anda dari konsol mereka . Kita membutuhkan itu untuk menjalankan aplikasi kita dalam tindakan menggunakan Swift.
Tujuan kita
Inilah proses membangun aplikasi SwiftUI yang didukung OpenAI:
- Buat TabView SwiftUI dengan konten di dua tab untuk menjalankan API yang berbeda — yaitu
generation
danedits
. - Manfaatkan kerangka Vision
VNDetectFaceRectanglesRequest
untuk mendeteksi wajah dan memotongnya untuk menghasilkan gambar topeng untuk/edits
titik akhir. - Gunakan Swift
URLSession
dengan async/menunggu untuk menjalankan permintaan API kami. Kami akan menyiapkanMultipartFormDataRequest
untuk mengunggah data gambar melalui permintaan multi-bagian/formulir-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)
}
}
Aktifkan API Generasi di Swift
Mari siapkan ObservableObject
kelas untuk mengambil cerita dari API dan meneruskannya ke antarmuka 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 ?? []
}
}
- Kami telah menandai
generateImage
fungsi denganasync throws
menggunakan sintaks async/menunggu baru dengan URLSession (dan lewati penangan penyelesaian). - Kami
URLRequest
terdiri dari dua bidang tajuk dan badan JSON. - Kami mengambil data dan mendekodekannya menggunakan
JSONDecoder()
structDALLEResponse
. Bidang dataDALLEResponse
berisi larik string URL. Kami akhirnya akan mengembalikan array ini[Photo]
untuk mengisi daftar satu atau lebihAsyncImage
di 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)
}
}
}
}
Hasilkan Wajah Menggunakan Edit Image API dan Vision Framework
Kemampuan untuk mengedit gambar asli dan membuat outpaintings adalah API yang lebih menarik untuk digunakan (menurut saya!). Dari bertukar wajah hingga menghasilkan pakaian mode yang berbeda, ada banyak hal yang dapat kita lakukan dengan menyamarkan area dalam gambar dan menggunakan perintah untuk mendeskripsikan gambar baru.
Di bagian ini, kita akan menggunakan Vision
framework untuk menjalankan permintaan deteksi wajah, menggambar kotak pembatas pada wajah yang terdeteksi, menghapus area tersebut untuk membuat gambar bertopeng menggunakan Core Graphics, dan menjalankan permintaan multipart untuk membuat gambar baru.
Mari kita mulai dengan menyiapkan permintaan Visi kita.
Menyiapkan permintaan pendeteksi wajah vision
Berikut kode untuk memulai dan melakukan a 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)
}
}
}
}
Saya telah mengomentari @Published
pembungkus properti di faceRect
. Tapi idealnya Anda bisa menggunakannya untuk mengamati perubahan dalam tampilan SwiftUI dan menggambar kotak pembatas (setelah mengonversi koordinat Visi ke sistem SwiftUI) dengan overlay
dan Rectangle
bentuk seperti ini:
.overlay{
Rectangle()
.stroke(Color.green,lineWidth: 3.0)
.frame(width: rect.width, height: rect.height)
.position(x: rect.midX, y: rect.midY)
}
Jadi, kami memiliki gambar input dan mengambil VNFaceObservation
hasil dari permintaan Vision. Sebelum kita memotong dan membuat gambar bertopeng, mari kita ubah koordinat Vision ke sistem UIKit.
Karena koordinat Visi dinormalisasi (ini berarti sudut kiri bawah adalah asalnya) dan terlepas dari resolusi layar, kami akan mengubah dan menskalakan kotak menggunakan kode berikut:
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
}
Berikut kode untuk memotong kotak pembatas dari gambar dan menggantinya dengan wilayah transparan:
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
}
Fungsi kami yang diperbarui maskFaceUsingVisionRequest
terlihat seperti ini:
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)
}
}
}
Siap! Kami telah berhasil membuat gambar bertopeng. Saatnya menjalankan API DALL-E kami!
Unggah Gambar Menggunakan URLSession ke DALL-E API
API mengharuskan gambar berbentuk persegi, kurang dari <4MB, dengan gambar bertopeng memiliki saluran alfa.
Berikut kode untuk mengeksekusi permintaan jaringan kami menggunakan async/menunggu:
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 ?? []
}
Struct MultiPartFormDataRequest
digunakan untuk menyiapkan permintaan multipart dengan boundary
, Content-Disposation
, dan hal-hal mewah lainnya. Lihat gambar dan formulir unggahan Donny Wals ke server menggunakan URLSession
panduan untuk mempelajari seluk-beluk membuat permintaan data formulir multi-bagian.
Dengan URLRequest
untuk mengunggah gambar dan data formulir siap, kami akan menjalankan generateEditedImage
fungsi dari tampilan SwiftUI kami dengan cara berikut:
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)
}
}
}
})
}
//....
}
}



Itu mengakhiri tutorial ini. Proyek ini tersedia di repositori GitHub saya . Cobalah bermain-main dengan banyak wajah dalam foto grup, sembunyikan secara iteratif, atau gunakan variations
titik akhir untuk menghasilkan gambar yang berbeda.
Terima kasih sudah membaca.