From d8302cecdf7b56baa323b3467e7b011894cefd9f Mon Sep 17 00:00:00 2001 From: Sj-Si Date: Tue, 11 Nov 2025 17:14:17 -0500 Subject: [PATCH 1/3] Add image assets for energy bar. --- .../main/assets/images/energy_bar_left_part.png | Bin 0 -> 481 bytes .../assets/images/energy_bar_right_part_0.png | Bin 0 -> 344 bytes .../assets/images/energy_bar_right_part_1.png | Bin 0 -> 289 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 android/app/src/main/assets/images/energy_bar_left_part.png create mode 100644 android/app/src/main/assets/images/energy_bar_right_part_0.png create mode 100644 android/app/src/main/assets/images/energy_bar_right_part_1.png diff --git a/android/app/src/main/assets/images/energy_bar_left_part.png b/android/app/src/main/assets/images/energy_bar_left_part.png new file mode 100644 index 0000000000000000000000000000000000000000..710a9dd92cc6008bf6f48674689a36213562ed49 GIT binary patch literal 481 zcmV<70UrK|P)pGlSxEDR45filF6+2Vs&prH>ip*)PL=t5J`KaT-EQ4(x7BLRX0t>h(QGyar!3pH+wJy#zxVlk zrBbO}E(^mk_>RZpd_KQkuMqWmJ)6yH#b`7-91ci}#bT{i3kHM2G|l(>J()~SrxOHu z(&;oCVG7f-t;J$-zuyTg6bj*RSQ7l>@z}B~$8j>5Oe&S~cs!ceY&Mhy-FVwAtD-$`u%o_nlgT8042Q$% zbc#v(YAcnBGMtX#sH6$P$rcC%Q1^Ph!C;_M;Y6d+;I=8VTrNAEjwVP-Hr|K2DBjo~ XvXkgX>gOg?00000NkvXXu0mjfkhk8K literal 0 HcmV?d00001 diff --git a/android/app/src/main/assets/images/energy_bar_right_part_0.png b/android/app/src/main/assets/images/energy_bar_right_part_0.png new file mode 100644 index 0000000000000000000000000000000000000000..60e4a2003bc0bdecefefcda19bdd00b783a7c09b GIT binary patch literal 344 zcmV-e0jK_nP)pG3Q0skR2UgWkV~#WKoCV~zes!rMo2^?HeoxKV274rLXaR#h(!Fl z-}SolUX7~jRNY(E5~FWfmU*6MSymK9RaMu0o!3bunWSkN$FVHSbzRT%OkaweNRs4v zUKB-mEX&gO{dt~5CLho=&G&sm$osxs*KONIDg6K<2m)Q#kxbLH@B8yS0zges6ache z*Cjmgwk!(|B*QQq#{pR}0UHkP`-TBJ1W;9#OQFO=fWJ}37$y(`K!G=6V;=tiU0{Y` z@M!_C2?wTW!Vet+tm_JZ#cbQ=LjsKB_!$y{F95@aAHb|T=N44E0%_rP6zV<0000;>NsPhsVktj$Ti!R&Iy_QlC1;vX$v$-a8wdMetniL0q=ezx+P zg^iYH`k&fw_PV`o-RpVh)So|^k{UTxw9QJcuSMYe=TA1`dk=6dyjhdBR(D42{csUh zXX9f(ny&lGvNWprUOuz&W@)UD>Lj;UCk%LQeEzpb_3Mh`B_0kEo5x_HrfL6NrP1|4TFA@d`J_i0b^GQObnV^B-t^}8W4`#=e#@>MlhW|L leEaLMl5O|jmd3LGVysb*F`bbTydUU222WQ%mvv4FO#sYld3gW; literal 0 HcmV?d00001 From 226d3d4414a4379a4dc028262f7f2f1234e0eb77 Mon Sep 17 00:00:00 2001 From: Sj-Si Date: Tue, 11 Nov 2025 17:14:41 -0500 Subject: [PATCH 2/3] Add function to analyze energy bar. --- .../utils/CustomImageUtils.kt | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt b/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt index 024abb62..040c3afd 100644 --- a/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt +++ b/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt @@ -1576,4 +1576,121 @@ class CustomImageUtils(context: Context, private val game: Game) : ImageUtils(co debugName ) } + + fun analyzeEnergyBar(): Int? { + val (sourceBitmap, templateBitmap) = getBitmaps("energy") + if (templateBitmap == null) { + game.printToLog("analyzeEnergyBar:: Failed to find template bitmap for \"energy\".", isError = true, tag = tag) + return null + } + val energyTextLocation = findImage("energy", tries = 1, region = regionTopHalf).first + if (energyTextLocation == null) { + game.printToLog("analyzeEnergyBar:: Failed to find bitmap for \"energy\".", isError = true, tag = tag) + return null + } + + // Get top right of energyText. + var x: Int = (energyTextLocation.x + (templateBitmap.width / 2)).toInt() + var y: Int = (energyTextLocation.y - (templateBitmap.height / 2)).toInt() + var w: Int = 700 + var h: Int = 75 + + // Crop just the energy bar in the image. + // This crop extends to the right beyond the energy bar a bit + // since the bar is able to grow. + var croppedBitmap = createSafeBitmap(sourceBitmap, x, y, w, h, "analyzeEnergyBar:: Crop energy bar.") + if (croppedBitmap == null) { + game.printToLog("analyzeEnergyBar:: Failed to crop bitmap.", isError = true, tag = tag) + return null + } + + // Now find the left and right brackets of the energy bar + // to refine our cropped region. + + val energyBarLeftPartTemplateBitmap: Bitmap? = getBitmaps("energy_bar_left_part").second + if (energyBarLeftPartTemplateBitmap == null) { + game.printToLog("analyzeEnergyBar:: Failed to find left part of energy bar.", isError = true, tag = tag) + return null + } + + val leftPartLocation: Point? = match(croppedBitmap, energyBarLeftPartTemplateBitmap, "energy_bar_left_part").second + if (leftPartLocation == null) { + game.printToLog("analyzeEnergyBar:: Failed to find left part of energy bar.", isError = true, tag = tag) + return null + } + + // The right side of the energy bar looks very different depending on whether + // the max energy has been increased. Thus we need to look for one of two bitmaps. + var energyBarRightPartTemplateBitmap: Bitmap? = getBitmaps("energy_bar_right_part_0").second + var rightPartLocation: Point? = null + if (energyBarRightPartTemplateBitmap == null) { + energyBarRightPartTemplateBitmap = getBitmaps("energy_bar_right_part_1").second + if (energyBarRightPartTemplateBitmap == null) { + game.printToLog("analyzeEnergyBar:: Failed to find right part of energy bar.", isError = true, tag = tag) + return null + } + rightPartLocation = match(croppedBitmap, energyBarRightPartTemplateBitmap, "energy_bar_right_part_1").second + } else { + rightPartLocation = match(croppedBitmap, energyBarRightPartTemplateBitmap, "energy_bar_right_part_0").second + } + + if (rightPartLocation == null) { + game.printToLog("analyzeEnergyBar:: Failed to find right part of energy bar.", isError = true, tag = tag) + return null + } + + // Crop the energy bar further to refine the cropped region so that + // we can measure the length of the bar. + // This crop is just a single pixel high line at the center of the + // bounding region. + val left: Int = (leftPartLocation.x + (energyBarLeftPartTemplateBitmap.width / 2)).toInt() + val right: Int = (rightPartLocation.x - (energyBarRightPartTemplateBitmap.width / 2)).toInt() + x = left + y = (croppedBitmap.height / 2).toInt() + w = (right - left).toInt() + h = 1 + + croppedBitmap = createSafeBitmap(croppedBitmap, x, y, w, h, "analyzeEnergyBar:: Refine cropped energy bar.") + if (croppedBitmap == null) { + game.printToLog("analyzeEnergyBar:: Failed to refine cropped bitmap region.", isError = true, tag = tag) + return null + } + + // HSV color range for gray portion of energy bar. + val grayLower = Scalar(0.0, 0.0, 116.0) + val grayUpper = Scalar(180.0, 255.0, 118.0) + val colorLower = Scalar(5.0, 0.0, 120.0) + val colorUpper = Scalar(180.0, 255.0, 255.0) + + // Convert the cropped region to HSV + val barMat = Mat() + Utils.bitmapToMat(croppedBitmap, barMat) + val hsvMat = Mat() + Imgproc.cvtColor(barMat, hsvMat, Imgproc.COLOR_BGR2HSV) + + // Create masks for the gray and color portions of the image. + val grayMask = Mat() + val colorMask = Mat() + Core.inRange(hsvMat, grayLower, grayUpper, grayMask) + Core.inRange(hsvMat, colorLower, colorUpper, colorMask) + + // Calculate ratio of color and gray pixels. + val grayPixels = Core.countNonZero(grayMask) + val colorPixels = Core.countNonZero(colorMask) + val totalPixels = grayPixels + colorPixels + + var fillPercent: Double = 0.0 + if (totalPixels > 0) { + fillPercent = (colorPixels.toDouble() / totalPixels.toDouble()) * 100.0 + } + val result: Int = fillPercent.toInt().coerceIn(0, 100) + + barMat.release() + hsvMat.release() + grayMask.release() + colorMask.release() + + game.printToLog("analyzeEnergyBar:: Pixel Colors: Gray=$grayPixels, Color=$colorPixels, Energy=$result", tag = tag) + return result + } } \ No newline at end of file From 7128474ac3e6d8c436ccd5fb158118aa69cb7ae7 Mon Sep 17 00:00:00 2001 From: Sj-Si Date: Tue, 11 Nov 2025 17:47:55 -0500 Subject: [PATCH 3/3] Add docstring --- .../uma_android_automation/utils/CustomImageUtils.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt b/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt index 040c3afd..aaaf6198 100644 --- a/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt +++ b/android/app/src/main/java/com/steve1316/uma_android_automation/utils/CustomImageUtils.kt @@ -1577,6 +1577,11 @@ class CustomImageUtils(context: Context, private val game: Game) : ImageUtils(co ) } + /** + * Gets the filled percentage of the energy bar. + * + * @return If energy bar is detected, returns the filled percentage, else returns null. + */ fun analyzeEnergyBar(): Int? { val (sourceBitmap, templateBitmap) = getBitmaps("energy") if (templateBitmap == null) {