Implement 31_FIX_UXUI.md: add BackgroundMode, skip analyzer in non-AI_BLUR to prevent lag, add highlight and shadow to ColorPreset and processAndSaveCapturedPhoto
This commit is contained in:
1 parent
7fdce924b3
commit
dd4edc61ac
6 files changed
+278
-18
No files matched your search
@@ -8,7 +8,7 @@ import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
enum class EditAttribute {
|
||||
BRIGHTNESS, CONTRAST, SATURATION, VIBRANCE, TEMPERATURE, TINT, // Nhóm 1: Cơ bản
|
||||
BRIGHTNESS, CONTRAST, HIGHLIGHT, SHADOW, SATURATION, VIBRANCE, TEMPERATURE, TINT, // Nhóm 1: Cơ bản
|
||||
CLARITY, DEHAZE, VIGNETTE_AMOUNT, // Nhóm 2: Nâng cao
|
||||
FADED_BLACK_LEVEL, SHADOWS_TINT_R, SHADOWS_TINT_G, SHADOWS_TINT_B, // Nhóm 3: Tone Curve
|
||||
GRAIN_AMOUNT, GRAIN_SIZE, // Nhóm 4: Nhiễu hạt
|
||||
|
||||
@@ -17,7 +17,9 @@ data class BasicAdjustments(
|
||||
var saturation: Float, // -1.0f đến 1.0f
|
||||
var vibrance: Float, // -1.0f đến 1.0f -> Bảo vệ màu da người
|
||||
var temperature: Float, // -1.0f đến 1.0f -> Ấm (vàng) / Lạnh (xanh)
|
||||
var tint: Float // -1.0f đến 1.0f -> Xanh lá / Hồng dâu
|
||||
var tint: Float, // -1.0f đến 1.0f -> Xanh lá / Hồng dâu
|
||||
var highlight: Float = 0f, // -1.0f đến 1.0f -> Mới bổ sung
|
||||
var shadow: Float = 0f // -1.0f đến 1.0f -> Mới bổ sung
|
||||
)
|
||||
|
||||
data class AdvancedEffects(
|
||||
|
||||
@@ -78,6 +78,23 @@ class MainActivity : AppCompatActivity() {
|
||||
private var cachedMaskWidth = 0
|
||||
private var cachedMaskHeight = 0
|
||||
|
||||
// Định nghĩa các chế độ hoạt động của ứng dụng Photobooth
|
||||
enum class BackgroundMode {
|
||||
DEFAULT, // Giữ nguyên ảnh gốc thô của Camera
|
||||
HARDWARE_DOF, // Chỉ dùng Focus vật lý của thấu kính
|
||||
AI_BLUR // Dùng AI tách nền và làm mờ phần mềm
|
||||
}
|
||||
|
||||
private var currentBackgroundMode = BackgroundMode.DEFAULT
|
||||
|
||||
private fun updateBackgroundMode() {
|
||||
currentBackgroundMode = when {
|
||||
selectedBgAssetPath != null -> BackgroundMode.AI_BLUR
|
||||
blurIntensity > 0f || isPortraitBlurModeOpen -> BackgroundMode.HARDWARE_DOF
|
||||
else -> BackgroundMode.DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageSegmenter(): ImageSegmenter? {
|
||||
synchronized(aiInferenceLock) {
|
||||
if (!isAiSupported) return null
|
||||
@@ -207,6 +224,8 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
val brightness = extractJsonDoubleField(content, "brightness").toFloat()
|
||||
val contrast = extractJsonDoubleField(content, "contrast").toFloat()
|
||||
val highlight = extractJsonDoubleField(content, "highlight").toFloat()
|
||||
val shadow = extractJsonDoubleField(content, "shadow").toFloat()
|
||||
val saturation = extractJsonDoubleField(content, "saturation").toFloat()
|
||||
val vibrance = extractJsonDoubleField(content, "vibrance").toFloat()
|
||||
val temperature = extractJsonDoubleField(content, "temperature").toFloat()
|
||||
@@ -229,7 +248,7 @@ class MainActivity : AppCompatActivity() {
|
||||
name = name,
|
||||
themeCategory = category,
|
||||
isEditable = true,
|
||||
basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint),
|
||||
basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint, highlight, shadow),
|
||||
advanced = AdvancedEffects(clarity, dehaze, vignetteAmount),
|
||||
toneCurve = ToneCurveEmulation(fadedBlackLevel, shadowsTintR, shadowsTintG, shadowsTintB),
|
||||
grain = FilmGrain(grainAmount, grainSize)
|
||||
@@ -272,6 +291,8 @@ class MainActivity : AppCompatActivity() {
|
||||
val attributeItems = listOf(
|
||||
AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness),
|
||||
AttributeNode(EditAttribute.CONTRAST, "Contrast (Tương phản)", R.drawable.ic_attr_contrast),
|
||||
AttributeNode(EditAttribute.HIGHLIGHT, "Highlight (Vùng sáng)", R.drawable.ic_attr_highlight),
|
||||
AttributeNode(EditAttribute.SHADOW, "Shadow (Vùng tối)", R.drawable.ic_attr_shadow),
|
||||
AttributeNode(EditAttribute.SATURATION, "Saturation (Bão hòa màu)", R.drawable.ic_attr_saturation),
|
||||
AttributeNode(EditAttribute.VIBRANCE, "Vibrance (Bão hòa thông minh)", R.drawable.ic_attr_vibrance),
|
||||
AttributeNode(EditAttribute.TEMPERATURE, "Temperature (Nhiệt màu)", R.drawable.ic_attr_temperature),
|
||||
@@ -305,6 +326,7 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
backgroundOptionAdapter = BackgroundOptionAdapter(backgroundOptions) { option ->
|
||||
selectedBgAssetPath = option.assetPath
|
||||
updateBackgroundMode()
|
||||
}
|
||||
binding.rvBackgroundOptions.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.rvBackgroundOptions.adapter = backgroundOptionAdapter
|
||||
@@ -320,6 +342,7 @@ class MainActivity : AppCompatActivity() {
|
||||
binding.panelRulerView.onValueChangeListener = { valInt ->
|
||||
binding.tvPanelRulerValue.text = "$valInt"
|
||||
blurIntensity = valInt / 100f
|
||||
updateBackgroundMode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,8 +477,8 @@ class MainActivity : AppCompatActivity() {
|
||||
))
|
||||
finalMatrix.postConcat(contrastMatrix)
|
||||
|
||||
// 3. Brightness (Độ sáng Exposure thực tế)
|
||||
val brightnessOffset = preset.basic.brightness * 60f
|
||||
// 3. Brightness (Độ sáng Exposure thực tế, tích hợp nhè nhẹ Highlight/Shadow để người dùng nhận diện thay đổi trên Viewfinder)
|
||||
val brightnessOffset = (preset.basic.brightness + (preset.basic.highlight + preset.basic.shadow) * 0.15f) * 60f
|
||||
val brightnessMatrix = android.graphics.ColorMatrix(floatArrayOf(
|
||||
1f, 0f, 0f, 0f, brightnessOffset,
|
||||
0f, 1f, 0f, 0f, brightnessOffset,
|
||||
@@ -917,11 +940,13 @@ class MainActivity : AppCompatActivity() {
|
||||
// Sync ruler value
|
||||
binding.panelRulerView.value = (blurIntensity * 100f).toInt()
|
||||
binding.tvPanelRulerValue.text = "${(blurIntensity * 100f).toInt()}"
|
||||
updateBackgroundMode()
|
||||
} else {
|
||||
isPortraitBlurModeOpen = false
|
||||
restoreMainMenuButtons()
|
||||
binding.viewLeftDockBackground.visibility = android.view.View.GONE
|
||||
binding.panelRulerContainer.visibility = android.view.View.GONE
|
||||
updateBackgroundMode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -948,11 +973,13 @@ class MainActivity : AppCompatActivity() {
|
||||
binding.rvBackgroundOptions.visibility = android.view.View.VISIBLE
|
||||
binding.rvHorizontalTimeline.visibility = android.view.View.GONE
|
||||
binding.panelRulerContainer.visibility = android.view.View.GONE
|
||||
updateBackgroundMode()
|
||||
} else {
|
||||
isBgReplaceModeOpen = false
|
||||
restoreMainMenuButtons()
|
||||
binding.viewLeftDockBackground.visibility = android.view.View.GONE
|
||||
binding.rvBackgroundOptions.visibility = android.view.View.GONE
|
||||
updateBackgroundMode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1039,6 +1066,8 @@ class MainActivity : AppCompatActivity() {
|
||||
return when (currentAttribute) {
|
||||
EditAttribute.BRIGHTNESS -> preset.basic.brightness
|
||||
EditAttribute.CONTRAST -> preset.basic.contrast
|
||||
EditAttribute.HIGHLIGHT -> preset.basic.highlight
|
||||
EditAttribute.SHADOW -> preset.basic.shadow
|
||||
EditAttribute.SATURATION -> preset.basic.saturation
|
||||
EditAttribute.VIBRANCE -> preset.basic.vibrance
|
||||
EditAttribute.TEMPERATURE -> preset.basic.temperature
|
||||
@@ -1066,6 +1095,8 @@ class MainActivity : AppCompatActivity() {
|
||||
when (currentAttribute) {
|
||||
EditAttribute.BRIGHTNESS -> preset.basic.brightness = value
|
||||
EditAttribute.CONTRAST -> preset.basic.contrast = value
|
||||
EditAttribute.HIGHLIGHT -> preset.basic.highlight = value
|
||||
EditAttribute.SHADOW -> preset.basic.shadow = value
|
||||
EditAttribute.SATURATION -> preset.basic.saturation = value
|
||||
EditAttribute.VIBRANCE -> preset.basic.vibrance = value
|
||||
EditAttribute.TEMPERATURE -> preset.basic.temperature = value
|
||||
@@ -1121,7 +1152,9 @@ class MainActivity : AppCompatActivity() {
|
||||
tempPreset.basic.saturation,
|
||||
tempPreset.basic.vibrance,
|
||||
tempPreset.basic.temperature,
|
||||
tempPreset.basic.tint
|
||||
tempPreset.basic.tint,
|
||||
tempPreset.basic.highlight,
|
||||
tempPreset.basic.shadow
|
||||
),
|
||||
advanced = AdvancedEffects(
|
||||
tempPreset.advanced.clarity,
|
||||
@@ -1150,6 +1183,8 @@ class MainActivity : AppCompatActivity() {
|
||||
"basic_adjustments": {
|
||||
"brightness": ${customPreset.basic.brightness},
|
||||
"contrast": ${customPreset.basic.contrast},
|
||||
"highlight": ${customPreset.basic.highlight},
|
||||
"shadow": ${customPreset.basic.shadow},
|
||||
"saturation": ${customPreset.basic.saturation},
|
||||
"vibrance": ${customPreset.basic.vibrance},
|
||||
"temperature": ${customPreset.basic.temperature},
|
||||
@@ -1211,7 +1246,8 @@ class MainActivity : AppCompatActivity() {
|
||||
.build()
|
||||
|
||||
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
|
||||
val isBgActive = (selectedBgAssetPath != null) || (blurIntensity > 0f)
|
||||
// SỬA LỖI LAG NGẦM: Chỉ chạy MediaPipe phân đoạn AI khi thực sự cần dùng chế độ AI_BLUR
|
||||
val isBgActive = currentBackgroundMode == BackgroundMode.AI_BLUR
|
||||
if (isBgActive) {
|
||||
val rawProxyBitmap = imageProxy.toBitmap()
|
||||
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
|
||||
@@ -1523,6 +1559,47 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
// Apply non-linear Highlights and Shadows correction
|
||||
if (preset.basic.highlight != 0f || preset.basic.shadow != 0f) {
|
||||
val width = filteredBitmap.width
|
||||
val height = filteredBitmap.height
|
||||
val pixels = IntArray(width * height)
|
||||
filteredBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
||||
|
||||
val highlightVal = preset.basic.highlight
|
||||
val shadowVal = preset.basic.shadow
|
||||
|
||||
for (i in 0 until (width * height)) {
|
||||
val color = pixels[i]
|
||||
var r = ((color shr 16) and 0xFF) / 255.0f
|
||||
var g = ((color shr 8) and 0xFF) / 255.0f
|
||||
var b = (color and 0xFF) / 255.0f
|
||||
|
||||
val luminance = 0.2126f * r + 0.7152f * g + 0.0722f * b
|
||||
|
||||
if (luminance > 0.6f) {
|
||||
// Vùng sáng (Highlight): Tăng/Giảm độ sáng dựa trên thông số highlight của Preset
|
||||
val weight = (luminance - 0.6f) / 0.4f
|
||||
r += highlightVal * weight
|
||||
g += highlightVal * weight
|
||||
b += highlightVal * weight
|
||||
} else if (luminance < 0.4f) {
|
||||
// Vùng tối (Shadow): Bù sáng hoặc dìm tối dựa trên thông số shadow của Preset
|
||||
val weight = (0.4f - luminance) / 0.4f
|
||||
r += shadowVal * weight
|
||||
g += shadowVal * weight
|
||||
b += shadowVal * weight
|
||||
}
|
||||
|
||||
val finalR = (r.coerceIn(0.0f, 1.0f) * 255f).toInt()
|
||||
val finalG = (g.coerceIn(0.0f, 1.0f) * 255f).toInt()
|
||||
val finalB = (b.coerceIn(0.0f, 1.0f) * 255f).toInt()
|
||||
|
||||
pixels[i] = (0xFF shl 24) or (finalR shl 16) or (finalG shl 8) or finalB
|
||||
}
|
||||
filteredBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
|
||||
}
|
||||
|
||||
// 4. Film Grain effect
|
||||
if (preset.grain.grainAmount > 0f) {
|
||||
val grainAmount = preset.grain.grainAmount
|
||||
@@ -1718,20 +1795,11 @@ class MainActivity : AppCompatActivity() {
|
||||
capturedImage: android.graphics.Bitmap,
|
||||
isStreamMode: Boolean = false
|
||||
): android.graphics.Bitmap {
|
||||
val isBgActive = (selectedBgAssetPath != null) || (blurIntensity > 0f)
|
||||
if (!isBgActive) {
|
||||
// Chỉ chạy các thuật toán xử lý phân đoạn AI hoặc mờ phần mềm khi bật chế độ AI_BLUR
|
||||
if (currentBackgroundMode != BackgroundMode.AI_BLUR) {
|
||||
return capturedImage
|
||||
}
|
||||
|
||||
// Tắt hoàn toàn thuật toán xử lý mờ/trộn nhân tạo cho Portrait Blur (khi selectedBgAssetPath == null)
|
||||
// và trả về ảnh thô 100% để sử dụng tiêu cự thấu kính vật lý của camera.
|
||||
if (selectedBgAssetPath == null && blurIntensity > 0f) {
|
||||
if (!isStreamMode) {
|
||||
// Trong chế độ chụp ảnh: Trả về ảnh thô ngay lập tức, không chạy phân đoạn AI
|
||||
return capturedImage
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAiSupported) {
|
||||
// Khi không hỗ trợ AI và chỉ cần Portrait Blur, trả về ảnh thô để dùng thấu kính vật lý
|
||||
if (selectedBgAssetPath == null && blurIntensity > 0f) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8v16z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.41 3.59,-8 8,-8v16C7.59,20 4,16.41 4,12z" />
|
||||
</vector>
|
||||
Reference in new issue
Block a user