diff --git a/app/src/main/java/com/quillstudios/pokegoalshelper/EnhancedOCR.kt b/app/src/main/java/com/quillstudios/pokegoalshelper/EnhancedOCR.kt index de27650..577268a 100644 --- a/app/src/main/java/com/quillstudios/pokegoalshelper/EnhancedOCR.kt +++ b/app/src/main/java/com/quillstudios/pokegoalshelper/EnhancedOCR.kt @@ -119,9 +119,13 @@ class EnhancedOCR(private val context: Context) { } // Method 4: Class-specific OCR optimization - val classOptimizedText = performClassSpecificOCR(croppedMat, detection.className) - if (classOptimizedText.isNotEmpty()) { - candidates.add(Pair(classOptimizedText, 1.3f)) // Highest weight for class-specific + try { + val classOptimizedText = performClassSpecificOCR(croppedMat, detection.className) + if (classOptimizedText.isNotEmpty()) { + candidates.add(Pair(classOptimizedText, 1.3f)) // Highest weight for class-specific + } + } catch (e: Exception) { + Log.e(TAG, "❌ Error in class-specific OCR", e) } } catch (e: Exception) { @@ -193,21 +197,23 @@ class EnhancedOCR(private val context: Context) { // - Firebase ML Text Recognition // - Custom OCR model - try { + var bitmap: Bitmap? = null + + return try { // Convert Mat to Bitmap for OCR processing - val bitmap = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888) + bitmap = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888) Utils.matToBitmap(mat, bitmap) // TODO: Integrate with actual OCR library // For now, return a placeholder based on image properties - val result = simulateOCRResult(bitmap) - bitmap.recycle() - - return result + simulateOCRResult(bitmap) } catch (e: Exception) { Log.e(TAG, "❌ Error in basic OCR", e) - return "" + "" + } finally { + // Always clean up bitmap + bitmap?.recycle() } } diff --git a/app/src/main/java/com/quillstudios/pokegoalshelper/ScreenCaptureService.kt b/app/src/main/java/com/quillstudios/pokegoalshelper/ScreenCaptureService.kt index 50885d2..a11fe67 100644 --- a/app/src/main/java/com/quillstudios/pokegoalshelper/ScreenCaptureService.kt +++ b/app/src/main/java/com/quillstudios/pokegoalshelper/ScreenCaptureService.kt @@ -380,6 +380,9 @@ class ScreenCaptureService : Service() { } private fun processImage(image: Image) { + var bitmap: Bitmap? = null + var croppedBitmap: Bitmap? = null + try { val planes = image.planes val buffer = planes[0].buffer @@ -391,7 +394,7 @@ class ScreenCaptureService : Service() { Log.d(TAG, "🖼️ CAPTURE DEBUG: screenSize=${screenWidth}x${screenHeight}, expected bitmap=${screenWidth + rowPadding / pixelStride}x${screenHeight}") // Create bitmap from image - val bitmap = Bitmap.createBitmap( + bitmap = Bitmap.createBitmap( screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888 @@ -401,7 +404,7 @@ class ScreenCaptureService : Service() { Log.d(TAG, "🖼️ CAPTURE DEBUG: created bitmap=${bitmap.width}x${bitmap.height}") // Convert to cropped bitmap if needed - val croppedBitmap = if (rowPadding == 0) { + croppedBitmap = if (rowPadding == 0) { Log.d(TAG, "🖼️ CAPTURE DEBUG: No padding, using original bitmap") bitmap } else { @@ -437,14 +440,14 @@ class ScreenCaptureService : Service() { analyzePokemonScreen(mat) } - // Clean up - if (croppedBitmap != bitmap) { - croppedBitmap.recycle() - } - bitmap.recycle() - } catch (e: Exception) { Log.e(TAG, "Error processing image", e) + } finally { + // Always clean up bitmaps in finally block to prevent leaks + if (croppedBitmap != null && croppedBitmap != bitmap) { + croppedBitmap.recycle() + } + bitmap?.recycle() } } @@ -747,12 +750,16 @@ class ScreenCaptureService : Service() { Utils.matToBitmap(processedRoi, bitmap) // Use ML Kit OCR - val extractedText = performOCR(bitmap, detection.className) + val extractedText = try { + performOCR(bitmap, detection.className) + } finally { + // Always clean up bitmap after OCR + bitmap.recycle() + } // Clean up roi.release() processedRoi.release() - bitmap.recycle() if (extractedText != null) { Log.i(TAG, "✅ YOLO SUCCESS: ${detection.className} = '$extractedText' (conf: ${String.format("%.2f", detection.confidence)})") @@ -1149,6 +1156,10 @@ class ScreenCaptureService : Service() { } private fun convertImageToMat(image: Image): Mat? { + var bitmap: Bitmap? = null + var croppedBitmap: Bitmap? = null + var mat: Mat? = null + return try { val planes = image.planes val buffer = planes[0].buffer @@ -1157,7 +1168,7 @@ class ScreenCaptureService : Service() { val rowPadding = rowStride - pixelStride * screenWidth // Create bitmap from image - val bitmap = Bitmap.createBitmap( + bitmap = Bitmap.createBitmap( screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888 @@ -1165,29 +1176,36 @@ class ScreenCaptureService : Service() { bitmap.copyPixelsFromBuffer(buffer) // Crop bitmap to remove padding if needed - val croppedBitmap = if (rowPadding == 0) { + croppedBitmap = if (rowPadding == 0) { bitmap } else { val cropped = Bitmap.createBitmap(bitmap, 0, 0, screenWidth, screenHeight) bitmap.recycle() // Clean up original + bitmap = null // Mark as recycled cropped } // Convert bitmap to Mat - val mat = Mat() + mat = Mat() Utils.bitmapToMat(croppedBitmap, mat) // Convert from RGBA to BGR (OpenCV format for proper color channel handling) val bgrMat = Mat() Imgproc.cvtColor(mat, bgrMat, Imgproc.COLOR_RGBA2BGR) - // Clean up + // Clean up intermediate resources mat.release() croppedBitmap.recycle() bgrMat } catch (e: Exception) { Log.e(TAG, "❌ Error converting image to Mat", e) + // Clean up on error + mat?.release() + if (croppedBitmap != null && croppedBitmap != bitmap) { + croppedBitmap.recycle() + } + bitmap?.recycle() null } }