Working on an iOS app with the Roboflow upload API:
func classifyScreenshotByApp(_ screenshots: [Screenshot]) {
let group = DispatchGroup()
for screenshot in screenshots {
group.enter()
guard let image = screenshot.image,
let imageData = image.jpegData(compressionQuality: 0.1) else {
print("Failed to process image data")
return
}
let fileContent = imageData.base64EncodedString()
let postData = fileContent.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://classify.roboflow.com/app-classification/17?api_key=\(API_KEY)&name=YOUR_IMAGE.jpg")!, timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
// Execute Post Request
URLSession.shared.dataTask(with: request, completionHandler: { [self] data, response, error in
defer { group.leave() }
if let error = error {
print("API error: \(error.localizedDescription)")
return
}
When I look at the uploaded image in Roboflow Annotate, I can see the image, but the label shows as “Undefined”
![CleanShot 2023-05-24 at 07.48.56](https://canada1.discourse-cdn.com/flex029/uploads/roboflow1/original/2X/b/b7ce6917a49d20a616f8e69dfab174d0463e6004.png)
Ah whoops I pasted wrong code snippet
func uploadImage(image: UIImage, project: String, newLabel: String, completion: @escaping (UploadResult)->()) {
let encodedImage = convertImageToBase64String(img: image)
let uuid = UUID().uuidString
if let url = URL(string: "https://api.roboflow.com/dataset/\(project)/upload?api_key=\(API_KEY)&name=\(uuid)&split=train&label=\(newLabel)") {
var request = URLRequest(url: url, timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = encodedImage.toData()
Thanks for the code example!
As I understand, you’re aiming to upload an image to a classification project with the upload API and a new specific label in mind. Your request is succeeding in that your image is being uploaded successfully to your classification project, but the label you’re passing isn’t present.
To upload an image for a classification project and apply a label to it, you currently do this in two separate requests:
- The first request uploads the image to the project (as you’re doing). You’ll receive an imageID for that image in your project.
- The second request then applies a label to the imageID
As an example of #2 as a bash script:
cat YOUR_ANNOTATION.xml | curl -d @- \
"https://api.roboflow.com/dataset/your-dataset/annotate/abc123?\
api_key=YOUR_KEY&\
name=YOUR_ANNOTATION.xml"
I don’t know much Swift, and I asked chatGPT to give an example of the add classification label bash snippet in Swift instead. It provided this:
import Foundation
let url = URL(string: "https://api.roboflow.com/dataset/your-dataset/annotate/abc123?api_key=YOUR_KEY&name=YOUR_ANNOTATION.xml")!
let inputData = Data(/* data to be sent in the request */)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = inputData
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Error: \(error)")
} else if let data = data {
let responseString = String(data: data, encoding: .utf8)
print("Response: \(responseString ?? "")")
}
}
task.resume()
Awesome; got it working like so:
func annotateImage(imageID: String, label: String, completion: @escaping (Bool)->()) {
let url = URL(string: "https://api.roboflow.com/dataset/MY_PROJECT/annotate/\(imageID)?api_key=\(API_KEY)")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
// Specify the type of the body
request.setValue("application/xml", forHTTPHeaderField: "Content-Type")
// The new label should be passed as XML in the body
let labelXML = "<annotation>\n\t<folder>images</folder>\n\t<filename>\(imageID)</filename>\n\t<path>/images/\(imageID).jpg</path>\n\t<source>\n\t\t<database>Unknown</database>\n\t</source>\n\t<size>\n\t\t<width>0</width>\n\t\t<height>0</height>\n\t\t<depth>3</depth>\n\t</size>\n\t<segmented>0</segmented>\n\t<object>\n\t\t<name>\(label)</name>\n\t\t<pose>Unspecified</pose>\n\t\t<truncated>0</truncated>\n\t\t<difficult>0</difficult>\n\t\t<bndbox>\n\t\t\t<xmin>0</xmin>\n\t\t\t<ymin>0</ymin>\n\t\t\t<xmax>0</xmax>\n\t\t\t<ymax>0</ymax>\n\t\t</bndbox>\n\t</object>\n</annotation>"
let labelData = labelXML.data(using: .utf8)
request.httpBody = labelData
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
completion(false)
return
}
guard let data = data else {
completion(false)
return
}
do {
guard let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
print(dict)
} catch {
completion(false)
}
completion(true)
}
task.resume()
}