Implement Touch-to-Focus (28_FOCUS_POINT.md): cache MediaPipe category mask, add focus ring view, class-based alpha selection
This commit is contained in:
@@ -69,6 +69,13 @@ class MainActivity : AppCompatActivity() {
|
||||
private var imageSegmenter: ImageSegmenter? = null
|
||||
private var isAiSupported = true
|
||||
|
||||
// Touch-to-Focus: ID lớp đối tượng được chạm chọn lấy nét (-1 = mặc định lấy nét người ID 1)
|
||||
private var userSelectedClassFocus = -1
|
||||
// Cache mặt nạ phân lớp mới nhất từ MediaPipe để phục vụ Touch-to-Focus
|
||||
private var cachedCategoryMask: ByteArray? = null
|
||||
private var cachedMaskWidth = 0
|
||||
private var cachedMaskHeight = 0
|
||||
|
||||
private fun getImageSegmenter(): ImageSegmenter? {
|
||||
synchronized(aiInferenceLock) {
|
||||
if (!isAiSupported) return null
|
||||
@@ -705,6 +712,42 @@ class MainActivity : AppCompatActivity() {
|
||||
showSavePresetDialog(preset)
|
||||
}
|
||||
}
|
||||
|
||||
// Touch-to-Focus: Chạm vào kính ngắm để chọn điểm lấy nét
|
||||
binding.viewFinder.setOnTouchListener { v, event ->
|
||||
if (event.action == android.view.MotionEvent.ACTION_UP) {
|
||||
val percentX = (event.x / v.width).coerceIn(0f, 1f)
|
||||
val percentY = (event.y / v.height).coerceIn(0f, 1f)
|
||||
|
||||
// Cập nhật Class ID lấy nét từ điểm chạm
|
||||
val mask = cachedCategoryMask
|
||||
val mW = cachedMaskWidth
|
||||
val mH = cachedMaskHeight
|
||||
if (mask != null && mW > 0 && mH > 0) {
|
||||
val maskX = (percentX * mW).toInt().coerceIn(0, mW - 1)
|
||||
val maskY = (percentY * mH).toInt().coerceIn(0, mH - 1)
|
||||
userSelectedClassFocus = mask[maskY * mW + maskX].toInt()
|
||||
}
|
||||
|
||||
// Hiện vòng tròn lấy nét màu cam tại vị trí chạm
|
||||
val focusRing = binding.viewFocusRing
|
||||
focusRing.x = event.x - focusRing.width / 2f
|
||||
focusRing.y = event.y - focusRing.height / 2f
|
||||
focusRing.visibility = android.view.View.VISIBLE
|
||||
focusRing.alpha = 1f
|
||||
focusRing.scaleX = 1.4f
|
||||
focusRing.scaleY = 1.4f
|
||||
focusRing.animate()
|
||||
.scaleX(1f).scaleY(1f)
|
||||
.alpha(0f)
|
||||
.setDuration(900)
|
||||
.withEndAction { focusRing.visibility = android.view.View.GONE }
|
||||
.start()
|
||||
|
||||
v.performClick()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateButtonToDock(button: android.view.View) {
|
||||
@@ -1689,12 +1732,26 @@ class MainActivity : AppCompatActivity() {
|
||||
val w = categoryMask.width
|
||||
val h = categoryMask.height
|
||||
val bufferCapacity = categoryMaskBuffer.capacity()
|
||||
|
||||
// Cache mask bytes for Touch-to-Focus
|
||||
val rawBytes = ByteArray(bufferCapacity)
|
||||
categoryMaskBuffer.rewind()
|
||||
categoryMaskBuffer.get(rawBytes)
|
||||
cachedCategoryMask = rawBytes
|
||||
cachedMaskWidth = w
|
||||
cachedMaskHeight = h
|
||||
categoryMaskBuffer.rewind()
|
||||
|
||||
// Create and blur alpha mask
|
||||
// Create and blur alpha mask with Touch-to-Focus class selection
|
||||
val focusClassId = if (userSelectedClassFocus >= 0) userSelectedClassFocus else -1
|
||||
val alphaMask = FloatArray(w * h)
|
||||
for (i in 0 until (w * h)) {
|
||||
val classId = if (i < bufferCapacity) categoryMaskBuffer.get(i).toInt() else 0
|
||||
alphaMask[i] = if (classId in 1..5) 1.0f else 0.0f
|
||||
val classId = if (i < bufferCapacity) rawBytes[i].toInt() else 0
|
||||
alphaMask[i] = if (focusClassId >= 0) {
|
||||
if (classId == focusClassId) 1.0f else 0.0f
|
||||
} else {
|
||||
if (classId in 1..5) 1.0f else 0.0f
|
||||
}
|
||||
}
|
||||
blurAlphaMaskBox(alphaMask, w, h, radius = 3)
|
||||
|
||||
@@ -1804,11 +1861,16 @@ class MainActivity : AppCompatActivity() {
|
||||
val h = categoryMask.height
|
||||
val bufferCapacity = categoryMaskBuffer.capacity()
|
||||
|
||||
// Create and blur alpha mask
|
||||
// Create and blur alpha mask with Touch-to-Focus class selection
|
||||
val focusClassId = if (userSelectedClassFocus >= 0) userSelectedClassFocus else -1
|
||||
val alphaMask = FloatArray(w * h)
|
||||
for (i in 0 until (w * h)) {
|
||||
val classId = if (i < bufferCapacity) categoryMaskBuffer.get(i).toInt() else 0
|
||||
alphaMask[i] = if (classId in 1..5) 1.0f else 0.0f
|
||||
alphaMask[i] = if (focusClassId >= 0) {
|
||||
if (classId == focusClassId) 1.0f else 0.0f
|
||||
} else {
|
||||
if (classId in 1..5) 1.0f else 0.0f
|
||||
}
|
||||
}
|
||||
blurAlphaMaskBox(alphaMask, w, h, radius = 5)
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#FFCC00" />
|
||||
<solid android:color="#00000000" />
|
||||
</shape>
|
||||
@@ -143,6 +143,14 @@
|
||||
android:visibility="gone"
|
||||
android:text="3" />
|
||||
|
||||
<!-- Vòng tròn định vị lấy nét (Focus Ring) khi chạm vào Viewfinder -->
|
||||
<View
|
||||
android:id="@+id/viewFocusRing"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="@drawable/bg_focus_ring"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- ================= KHỐI 1: TIMELINE FRAMES & PRESETS ================= -->
|
||||
|
||||
Reference in New Issue
Block a user