Solomon Opoku
10/21/2024, 3:38 AMVNRecognizeTextRequest
to recognize text from camera frames captured via AVCaptureSession
. However, I'm having trouble accessing the recognized text from the VNRecognizedText
objects returned by the Vision framework in Kotlin Native.
What I'm Trying to Achieve:
• Capture camera frames using AVCaptureVideoDataOutputSampleBufferDelegateProtocol
.
• Process frames with VNRecognizeTextRequest
to perform OCR.
• Extract the recognized text from the results to display or process further.
The Issue:
I can't seem to access the recognized text from the VNRecognizedText
objects. Here's what I've tried:
1. Using .string
Property:
val recognizedText = topCandidate?.string
2. Using .toString()
Method:
val recognizedText = topCandidate?.toString()
3. Casting to `NSString`:
val recognizedText = topCandidate as? NSString
Output: Returns object instance references like <VNRecognizedText: 0x303aabb60>
, which isn't helpful.
Here's the relevant portion of my code:
@OptIn(ExperimentalForeignApi::class)
private fun processSampleBuffer(didOutputSampleBuffer: CMSampleBufferRef) {
val pixelBuffer = CMSampleBufferGetImageBuffer(didOutputSampleBuffer) ?: return
val request = VNRecognizeTextRequest { vnRequest, error ->
if (error != null) {
println("Failed to perform OCR: ${error.localizedDescription}")
return@VNRecognizeTextRequest
}
val results = vnRequest.results as? List<VNRecognizedTextObservation> ?: emptyList()
if (results.isEmpty()) {
println("No text observations found.")
return@VNRecognizeTextRequest
}
val recognizedStrings = results.map { observation ->
val topCandidate = observation.topCandidates(1u).firstOrNull()
val recognizedText = topCandidate?.string // Unresolved reference
// Also tried topCandidate?.text, topCandidate?.description, topCandidate?.toString()
recognizedText ?: ""
}
val joinedText = recognizedStrings.joinToString("\n")
Napier.d{"Recognized Text: $joinedText"}
}
request.recognitionLevel = VNRequestTextRecognitionLevelAccurate
val handler = VNImageRequestHandler(
cVPixelBuffer = pixelBuffer,
orientation = 1u,
options = emptyMap<Any?, Any?>()
)
try {
handler.performRequests(listOf(request), error = null)
} catch (e: Exception) {
Napier.d{"Failed to perform OCR: ${e.message}"}
}
}
How can I access the recognized text from a VNRecognizedText
object in Kotlin Native?François
10/21/2024, 7:32 AMFrançois
10/21/2024, 7:34 AMFrançois
10/21/2024, 7:39 AMVNRecognizedTextObservation.observation.topCandidates(1u)
return a list of VNRecognizedText
https://developer.apple.com/documentation/vision/vnrecognizedtextobservation/topcandidates(_:)?language=objcFrançois
10/21/2024, 7:41 AMval topCandidate = observation.topCandidates(1u).firstOrNull() as? VNRecognizedText
val recognizedText = topCandidate?.string // Unresolved reference
recognizedText
shouldn’t be null,
is topCandidate?.string
throw an exception ? or just be null?Solomon Opoku
10/21/2024, 11:26 AMval request = VNRecognizeTextRequest { vnRequest, error ->
if (error != null) {
Napier.d { "Failed to perform OCR: ${error.localizedDescription}" }
scanningState.value = false
isProcessingFrame = false
return@VNRecognizeTextRequest
}
val resultsArray = vnRequest?.results as? NSArray
if (resultsArray == null || resultsArray.count.toInt() == 0) {
Napier.d { "No text observations found." }
scanningState.value = false
isProcessingFrame = false
return@VNRecognizeTextRequest
}
val recognizedStrings = mutableListOf<String>()
for (i in 0 until resultsArray.count.toInt()) {
val observation =
resultsArray.objectAtIndex(i.toULong()) as? VNRecognizedTextObservation
if (observation != null) {
val topCandidatesArray = observation.topCandidates(1u) as NSArray
if (topCandidatesArray.count > 0uL) {
val candidate = topCandidatesArray.objectAtIndex(0uL) as? NSObject
val recognizedText = candidate?.valueForKey("string") as? String ?: ""
recognizedStrings.add(recognizedText)
}
}
}
val joinedText = recognizedStrings.joinToString("\n")
dispatch_async(dispatch_get_main_queue()) {
capturedTexts.value = joinedText
isProcessingFrame = false
scanningState.value = false
}
}
François
10/21/2024, 12:30 PMFernando
10/22/2024, 9:19 AMi decided to use objc types instead of kotlinDoes that means that there are two ways to access iOS ObjectiveC types? 🤔
François
10/22/2024, 9:40 AMFrançois
10/22/2024, 9:41 AMSolomon Opoku
10/24/2024, 12:44 AMFernando
10/25/2024, 8:58 AM