Swift에서 DALL-E API를 사용하는 방법 — 얼굴 생성
OpenAI의 API로 기존 이미지에서 얼굴을 편집하는 SwiftUI 앱을 빌드합니다. Vision 얼굴 감지 요청을 사용하여 얼굴을 마스킹합니다.

OpenAI 팀은 최근 공개 베타에서 DALL-E API를 출시했습니다. 작성 시점에 다음 세 가지 API 엔드포인트를 사용할 수 있습니다.
/generations
— 텍스트 프롬프트에서 이미지 생성/edits
— 영역을 마스킹하여 텍스트 프롬프트를 기반으로 원본 이미지 편집/variations
— 이미지의 변형 생성
API에 대해 자세히 알아보고 Python 및 Node.js 구현에 대해 알아보려면 여기에서 OpenAI의 참조 가이드를 읽어보세요 . CURL 구현을 사용하여 Swift에서 URLRequest를 빌드합니다. 그 동안 콘솔 에서 OpenAI API 키를 생성하십시오 . Swift를 사용하여 애플리케이션을 실행하려면 이것이 필요합니다.
우리의 목표
다음은 OpenAI 기반 SwiftUI 애플리케이션을 구축하는 프로세스입니다.
generation
서로 다른 API(즉 및 )를 실행하기 위해 두 개의 탭에 걸쳐 콘텐츠가 있는 SwiftUI TabView를 생성합니다edits
.- Vision 프레임워크를 활용하여
VNDetectFaceRectanglesRequest
얼굴을 감지하고 잘라서/edits
끝점에 대한 마스크 이미지를 생성합니다. - async/await와 함께 Swift를 사용
URLSession
하여 API 요청을 실행합니다.MultipartFormDataRequest
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)
}
}
Swift에서 Generations API 호출
ObservableObject
API에서 스토리를 가져와서 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 ?? []
}
}
- URLSession(및 건너뛰기 완료 처리기)과 함께 새로운 async/await 구문을 사용하기 위해
generateImage
함수를 로 표시했습니다 .async throws
- 우리
URLRequest
는 두 개의 헤더 필드와 JSON 본문으로 구성됩니다. - 우리는 데이터를 잡고 구조체
JSONDecoder()
로 디코딩합니다.DALLEResponse
의 데이터 필드에는DALLEResponse
URL 문자열 배열이 있습니다. 우리는 결국 SwiftUI에서[Photo]
하나 이상의 목록을 채우기 위해 이 배열을 반환할 것입니다.AsyncImage
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)
}
}
}
}
Edit Image API 및 Vision Framework를 사용하여 얼굴 생성
원본 이미지를 편집하고 외부 그림을 만드는 기능은 사용하기에 더 흥미로운 API입니다(제 생각에는!). 얼굴을 바꾸는 것부터 다양한 패션 복장을 생성하는 것까지 이미지의 영역을 마스킹하고 프롬프트를 사용하여 새 이미지를 설명함으로써 할 수 있는 일이 너무 많습니다.
이 섹션에서는 Vision
프레임워크를 사용하여 얼굴 감지 요청을 실행하고, 감지된 얼굴에 경계 상자를 그리고, Core Graphics를 사용하여 마스크된 이미지를 생성하기 위해 해당 영역을 지우고, 새 이미지를 생성하기 위해 멀티파트 요청을 실행합니다.
Vision 요청을 설정하여 시작하겠습니다.
비전 얼굴 감지기 요청 설정
다음은 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)
}
}
}
}
@Published
에 대한 속성 래퍼를 주석 처리했습니다 faceRect
. 그러나 SwiftUI 보기의 변경 사항을 관찰하고 경계 상자를 그리는 데 이상적으로 사용할 수 있습니다(Vision 좌표를 SwiftUI 시스템으로 변환한 후 overlay
) Rectangle
.
.overlay{
Rectangle()
.stroke(Color.green,lineWidth: 3.0)
.frame(width: rect.width, height: rect.height)
.position(x: rect.midX, y: rect.midY)
}
VNFaceObservation
따라서 Vision 요청에서 입력 이미지와 검색된 결과가 있습니다. 자르기 및 마스크된 이미지 만들기로 이동하기 전에 Vision 좌표를 UIKit 시스템으로 변환해 보겠습니다.
Vision 좌표는 정규화되고(왼쪽 하단 모서리가 원점임을 의미) 화면 해상도와 무관하므로 다음 코드를 사용하여 상자를 변환하고 크기를 조정합니다.
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
}
다음은 이미지에서 경계 상자를 잘라내고 투명 영역으로 바꾸는 코드입니다.
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
}
업데이트된 maskFaceUsingVisionRequest
기능은 다음과 같습니다.
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)
}
}
}
모든 설정! 마스킹된 이미지를 생성하는 데 성공했습니다. DALL-E API를 실행할 시간입니다!
URLSession을 사용하여 DALL-E API에 이미지 업로드
API는 이미지가 알파 채널이 있는 마스킹된 이미지와 함께 <4MB 미만의 사각형 모양이어야 합니다.
다음은 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 ?? []
}
구조체는 , 및 기타 멋진 항목 MultiPartFormDataRequest
으로 다중 요청을 준비하는 데 사용됩니다 . Donny Wals의 가이드 를 사용하여 서버에 이미지 및 양식 업로드를 확인 하여 여러 부분으로 구성된 양식 데이터 요청 작성의 복잡성을 알아보세요.boundary
Content-Disposation
URLSession
이미지 업로드 와 URLRequest
양식 데이터가 준비되면 generateEditedImage
다음과 같은 방법으로 SwiftUI 보기에서 함수를 호출합니다.
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)
}
}
}
})
}
//....
}
}



이것으로 이 튜토리얼을 마칩니다. 프로젝트는 내 GitHub 저장소 에서 사용할 수 있습니다 . 그룹 사진에서 여러 얼굴을 가지고 놀거나 반복적으로 마스킹하거나 variations
끝점을 사용하여 다른 이미지를 생성해 보십시오.
읽어 주셔서 감사합니다.