SilkFX: add gainmap metadata testing

Add the ability to test adjustments to the gainmap metadata and compare
to the original metadata.

Bug: 279647216
Test: Played with new gainmap metadata menu; working as intended
Change-Id: Icec81a693e03c1cf2d4a854b06ebdfb05565b518
This commit is contained in:
Nick Deakin
2023-04-25 17:47:39 -04:00
parent a235daba42
commit 74fbdfa044
4 changed files with 593 additions and 8 deletions

View File

@@ -34,7 +34,7 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="SDR original" />
android:text="SDR" />
<RadioButton android:id="@+id/output_gainmap"
android:layout_width="wrap_content"
@@ -46,13 +46,34 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="HDR (sdr+gainmap)" />
android:text="HDR" />
<RadioButton android:id="@+id/output_hdr_test"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="HDR (test)" />
</RadioGroup>
<Spinner
android:id="@+id/image_selection"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:orientation="horizontal">
<Spinner
android:id="@+id/image_selection"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content" />
<Button
android:id="@+id/gainmap_metadata"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Gainmap Metadata..." />
</LinearLayout>
<TextView
android:id="@+id/error_msg"
@@ -67,4 +88,4 @@
</LinearLayout>
</com.android.test.silkfx.hdr.GainmapImage>
</com.android.test.silkfx.hdr.GainmapImage>

View File

@@ -0,0 +1,264 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="350dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true"
android:padding="8dp"
android:orientation="vertical"
android:background="#444444">
<TextView
android:id="@+id/gainmap_metadata_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="Metadata for &quot;HDR (test)&quot; (values in linear space):" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_gainmapmin_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Gain Map Min:" />
<TextView
android:id="@+id/gainmap_metadata_gainmapmin_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_gainmapmin"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_gainmapmax_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Gain Map Max:" />
<TextView
android:id="@+id/gainmap_metadata_gainmapmax_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_gainmapmax"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_capacitymin_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Capacity Min:" />
<TextView
android:id="@+id/gainmap_metadata_capacitymin_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_capacitymin"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_capacitymax_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Capacity Max:" />
<TextView
android:id="@+id/gainmap_metadata_capacitymax_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_capacitymax"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_gamma_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Gamma:" />
<TextView
android:id="@+id/gainmap_metadata_gamma_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_gamma"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_offsetsdr_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Offset SDR:" />
<TextView
android:id="@+id/gainmap_metadata_offsetsdr_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_offsetsdr"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gainmap_metadata_offsethdr_text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Offset HDR:" />
<TextView
android:id="@+id/gainmap_metadata_offsethdr_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="4"
android:text="TODO" />
<SeekBar
android:id="@+id/gainmap_metadata_offsethdr"
android:min="0"
android:max="100"
android:layout_width="150dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/gainmap_metadata_reset"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Reset" />
<Button
android:id="@+id/gainmap_metadata_done"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Done" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -27,6 +27,7 @@ import android.util.AttributeSet
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.FrameLayout
import android.widget.RadioGroup
import android.widget.Spinner
@@ -44,6 +45,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
private var gainmap: Gainmap? = null
private var gainmapVisualizer: Bitmap? = null
private lateinit var imageView: SubsamplingScaleImageView
private lateinit var gainmapMetadataEditor: GainmapMetadataEditor
init {
gainmapImages = context.assets.list("gainmaps")!!
@@ -58,6 +60,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
super.onFinishInflate()
imageView = findViewById(R.id.image)!!
gainmapMetadataEditor = GainmapMetadataEditor(this, imageView)
findViewById<RadioGroup>(R.id.output_mode)!!.also {
it.check(outputMode)
@@ -92,6 +95,10 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
}
}
findViewById<Button>(R.id.gainmap_metadata)!!.setOnClickListener {
gainmapMetadataEditor.openEditor()
}
setImage(0)
imageView.apply {
@@ -132,6 +139,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
findViewById<RadioGroup>(R.id.output_mode)!!.visibility = View.VISIBLE
gainmap = bitmap!!.gainmap
gainmapMetadataEditor.setGainmap(gainmap)
val map = gainmap!!.gainmapContents
if (map.config != Bitmap.Config.ALPHA_8) {
gainmapVisualizer = map
@@ -175,7 +183,15 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
imageView.setImage(ImageSource.cachedBitmap(when (outputMode) {
R.id.output_hdr -> {
bitmap!!.gainmap = gainmap; bitmap!!
gainmapMetadataEditor.useOriginalMetadata()
bitmap!!.gainmap = gainmap
bitmap!!
}
R.id.output_hdr_test -> {
gainmapMetadataEditor.useEditMetadata()
bitmap!!.gainmap = gainmap
bitmap!!
}
R.id.output_sdr -> {
@@ -186,4 +202,4 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
else -> throw IllegalStateException()
}))
}
}
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.silkfx.hdr
import android.graphics.Gainmap
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.PopupWindow
import android.widget.SeekBar
import android.widget.TextView
import com.android.test.silkfx.R
data class GainmapMetadata(
var ratioMin: Float,
var ratioMax: Float,
var capacityMin: Float,
var capacityMax: Float,
var gamma: Float,
var offsetSdr: Float,
var offsetHdr: Float
)
class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private var gainmap: Gainmap? = null
private var showingEdits = false
private var metadataPopup: PopupWindow? = null
private var originalMetadata: GainmapMetadata = GainmapMetadata(
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
private var currentMetadata: GainmapMetadata = originalMetadata.copy()
private val maxProgress = 100.0f
private val minRatioMin = .001f
private val maxRatioMin = 1.0f
private val minRatioMax = 1.0f
private val maxRatioMax = 16.0f
private val minCapacityMin = 1.0f
private val maxCapacityMin = maxRatioMax
private val minCapacityMax = 1.001f
private val maxCapacityMax = maxRatioMax
private val minGamma = 0.1f
private val maxGamma = 3.0f
// Min and max offsets are 0.0 and 1.0 respectively
fun setGainmap(newGainmap: Gainmap?) {
gainmap = newGainmap
originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0],
gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(),
gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0],
gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0])
currentMetadata = originalMetadata.copy()
}
fun useOriginalMetadata() {
showingEdits = false
applyMetadata(originalMetadata)
}
fun useEditMetadata() {
showingEdits = true
applyMetadata(currentMetadata)
}
fun closeEditor() {
metadataPopup?.let {
it.dismiss()
metadataPopup = null
}
}
fun openEditor() {
if (metadataPopup != null) return
val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null)
metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0)
(view.getParent() as ViewGroup).removeView(view)
parent.addView(view)
view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener {
closeEditor()
}
view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener {
resetGainmapMetadata()
}
updateMetadataUi()
val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) return
val normalized = progress.toFloat() / maxProgress
when (seekBar) {
gainmapMinSeek -> updateGainmapMin(normalized)
gainmapMaxSeek -> updateGainmapMax(normalized)
capacityMinSeek -> updateCapacityMin(normalized)
capacityMaxSeek -> updateCapacityMax(normalized)
gammaSeek -> updateGamma(normalized)
offsetSdrSeek -> updateOffsetSdr(normalized)
offsetHdrSeek -> updateOffsetHdr(normalized)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
}
private fun updateMetadataUi() {
val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
gainmapMaxSeek.setProgress(
((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
capacityMinSeek.setProgress(
((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
capacityMaxSeek.setProgress(
((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
gammaSeek.setProgress(
((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
// Log base 3 via: log_b(x) = log_y(x) / log_y(b)
offsetSdrSeek.setProgress(
((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
.toFloat() * maxProgress).toInt())
offsetHdrSeek.setProgress(
((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
.toFloat() * maxProgress).toInt())
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
"%.3f".format(currentMetadata.ratioMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
"%.3f".format(currentMetadata.ratioMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
"%.3f".format(currentMetadata.capacityMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
"%.3f".format(currentMetadata.capacityMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
"%.3f".format(currentMetadata.gamma))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
"%.5f".format(currentMetadata.offsetSdr))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
"%.5f".format(currentMetadata.offsetHdr))
}
private fun resetGainmapMetadata() {
currentMetadata = originalMetadata.copy()
applyMetadata(currentMetadata)
updateMetadataUi()
}
private fun applyMetadata(newMetadata: GainmapMetadata) {
gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax)
gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
renderView.invalidate()
}
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
"%.3f".format(newValue))
currentMetadata.ratioMin = newValue
if (showingEdits) {
gainmap!!.setRatioMin(newValue, newValue, newValue)
renderView.invalidate()
}
}
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
"%.3f".format(newValue))
currentMetadata.ratioMax = newValue
if (showingEdits) {
gainmap!!.setRatioMax(newValue, newValue, newValue)
renderView.invalidate()
}
}
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
"%.3f".format(newValue))
currentMetadata.capacityMin = newValue
if (showingEdits) {
gainmap!!.setMinDisplayRatioForHdrTransition(newValue)
renderView.invalidate()
}
}
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
"%.3f".format(newValue))
currentMetadata.capacityMax = newValue
if (showingEdits) {
gainmap!!.setDisplayRatioForFullHdr(newValue)
renderView.invalidate()
}
}
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
"%.3f".format(newValue))
currentMetadata.gamma = newValue
if (showingEdits) {
gainmap!!.setGamma(newValue, newValue, newValue)
renderView.invalidate()
}
}
private fun updateOffsetSdr(normalized: Float) {
var newValue = 0.0f
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
"%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
if (showingEdits) {
gainmap!!.setEpsilonSdr(newValue, newValue, newValue)
renderView.invalidate()
}
}
private fun updateOffsetHdr(normalized: Float) {
var newValue = 0.0f
if (normalized > 0.0f ) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
"%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
if (showingEdits) {
gainmap!!.setEpsilonHdr(newValue, newValue, newValue)
renderView.invalidate()
}
}
}