Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
araobp committed Aug 25, 2020
1 parent 3d49c67 commit e4fc80c
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,42 @@ import androidx.core.widget.addTextChangedListener
import jp.araobp.amg8833.`interface`.Amg8833Data
import jp.araobp.amg8833.`interface`.Amg8833Interface
import jp.araobp.amg8833.`interface`.IAmg8833Receiver
import jp.araobp.amg8833.analyzer.RawImage
import jp.araobp.amg8833.analyzer.Image
import kotlinx.android.synthetic.main.activity_main.*
import org.opencv.android.OpenCVLoader
import java.lang.NumberFormatException


class MainActivity : AppCompatActivity() {

private lateinit var amg8833Interface: Amg8833Interface

private lateinit var rawImage: RawImage
private lateinit var rawImage: Image

private lateinit var props: Properties

/**
* This is necessary to avoid the following error at runtime:
*
* "No implementation found for void org.opencv.core.Mat.n_delete(long) (tried Java_org_opencv
* _core_Mat_n_1delete and Java_org_opencv_core_Mat_n_1delete__J)"
*/
init {
// OpenCV initialization
OpenCVLoader.initDebug()
}

val amg8833receiver = object : IAmg8833Receiver {
override fun onAmg8833Data(amg8833Data: Amg8833Data) {
rawImage.draw(
amg8833Data.data,
checkBoxNormalize.isChecked,
checkBoxTemp.isChecked,
RawImage.Colormap.valueOf(spinnerColormap.selectedItem.toString())
)
runOnUiThread {
rawImage.draw(
amg8833Data.data,
checkBoxNormalize.isChecked,
checkBoxTemp.isChecked,
Image.Colormap.valueOf(spinnerColormap.selectedItem.toString()),
spinnerInterpolate.selectedItem.toString().toInt()
)
}
}
}

Expand All @@ -38,7 +53,7 @@ class MainActivity : AppCompatActivity() {

setContentView(R.layout.activity_main)

rawImage = RawImage(surfaceView)
rawImage = Image(surfaceView)

props = Properties(this)
props.load()
Expand Down
102 changes: 102 additions & 0 deletions android/amg8833/app/src/main/java/jp/araobp/amg8833/analyzer/Image.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package jp.araobp.amg8833.analyzer

import android.graphics.Color
import android.util.Log
import android.view.SurfaceView
import kotlin.math.roundToInt

class Image(val surfaceView: SurfaceView) {

companion object {
val TAG: String = this::class.java.simpleName
}

enum class Colormap {
Grayscale,
Okinawa,
Heatmap,
Devil,
Rainbow
}

fun draw(
data: ByteArray,
minMaxNormalization: Boolean,
showTemperature: Boolean,
colorMap: Colormap,
interpolationRepeat: Int = 0
) {

val src = data.toUByteArray().reversed().toUByteArray()

val pixels: UByteArray = when (minMaxNormalization) {
true -> minMaxNormalize(src)
false -> src
}

val canvas = surfaceView.holder.lockCanvas()
canvas.drawColor(Color.DKGRAY)

val width = canvas.width.toFloat()
val height = canvas.height.toFloat()

var tempList: List<String>? = null

if (showTemperature) {
tempList = tempList(src)
}

var numRows = 8
var numCols = 8

val finalPixels = if (interpolationRepeat > 0) {
val srcMat = pixels.toMat()
val dstMat = interpolate(srcMat, interpolationRepeat)
numRows = dstMat.rows()
numCols = dstMat.cols()
convertToUByteArray(dstMat)
} else {
pixels
}

//Log.d(TAG, "numRows: $numRows, numCols: $numCols, numPixels: ${finalPixels.size}")

val xStep = width / numCols
val yStep = xStep
val yMargin = (height - width) / 2F
val textMargin = xStep / 5F

for (row in 0 until numRows) {
for (col in 0 until numCols) {
val idx = row * numCols + col
val pixel = finalPixels[idx]
val left = xStep * col
val top = yStep * row + yMargin
val right = xStep * (col + 1)
val bottom = yStep * (row + 1) + yMargin
val brightness = when (colorMap) {
Colormap.Grayscale -> paintGrayscale(pixel)
Colormap.Okinawa -> paintOkinawa(pixel)
Colormap.Heatmap -> paintHeatmap(pixel)
Colormap.Devil -> paintDevil(pixel)
Colormap.Rainbow -> paintRainbow(pixel)
}
canvas.drawRect(left, top, right, bottom, brightness)

tempList?.let {
if (interpolationRepeat == 0) {
canvas.drawText(
it[idx],
left + textMargin,
top + TEMP_COLOR.textSize + textMargin,
TEMP_COLOR
)
}

}
}
}

surfaceView.holder.unlockCanvasAndPost(canvas)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,75 @@ package jp.araobp.amg8833.analyzer

import android.graphics.Color
import android.graphics.Paint
import kotlin.math.floor

fun paintGrayscale(brightness: Int) = Paint().apply {
fun paintGrayscale(brightness: UByte) = Paint().apply {
val v = brightness.toInt()
style = Paint.Style.FILL_AND_STROKE
color = Color.rgb(brightness, brightness, brightness)
color = Color.rgb(v, v, v)
}

fun paintOkinawa(brightness: Int) = Paint().apply {
fun paintOkinawa(brightness: UByte) = Paint().apply {
val v = brightness.toInt()
style = Paint.Style.FILL_AND_STROKE
color = Color.argb(0xff, 128 - brightness / 2, brightness, 128 + brightness / 2)
color = Color.argb(0xff, 128 - v / 2, v, 128 + v / 2)
}

fun paintHeatmap(brightness: Int) = Paint().apply {
fun paintHeatmap(brightness: UByte) = Paint().apply {
val v = brightness.toInt()
style = Paint.Style.FILL_AND_STROKE
color = Color.argb(0xff, brightness, 0, 255 - brightness)
color = Color.argb(0xff, v, 255 - v, 255 - v)
}

fun paintDevil(brightness: UByte) = Paint().apply {
val v = brightness.toInt()
style = Paint.Style.FILL_AND_STROKE
color = Color.argb(0xff, v, v / 2, v / 2)
}

// Reference: https://www.particleincell.com/2014/colormap/
fun paintRainbow(brightness: UByte) = Paint().apply {

val v = brightness

val a = (UByte.MAX_VALUE - v).toDouble() / 80
val X = floor(a).toInt()
val Y = floor(255 * (a - X)).toInt()

var r = 0
var g = 0
var b = 0

when (X) {
0 -> {
r = 255
g = Y
b = 0
}
1 -> {
r = 255 - Y
g = 255
b = 0
}
2 -> {
r = 0
g = 255
b = Y
}
3 -> {
r = 0
g = 255 - Y
b = 255
}
4 -> {
r = 0
g = 0
b = 255
}
}

style = Paint.Style.FILL_AND_STROKE
color = Color.argb(0xff, r, g, b)
}

val TEMP_COLOR = Paint().apply {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,64 @@
package jp.araobp.amg8833.analyzer

import android.graphics.Color
import org.opencv.core.CvType
import org.opencv.core.Mat
import org.opencv.core.Size
import org.opencv.imgproc.Imgproc
import org.opencv.imgproc.Imgproc.INTER_CUBIC
import org.opencv.imgproc.Imgproc.resize
import kotlin.math.roundToInt

val MAX_VALUE = UByte.MAX_VALUE.toFloat()
val TEMP_SCALE = 0.25F

fun tempList(data: UByteArray) = data.map { (it.toFloat() * TEMP_SCALE).roundToInt().toString() }.toList()

fun minMaxNormalize(data: UByteArray): FloatArray {
fun minMaxNormalize(src: UByteArray): UByteArray {

val floatArray = FloatArray(data.size)
val dst = UByteArray(src.size)
var min = UByte.MAX_VALUE.toFloat()
var max = UByte.MIN_VALUE.toFloat()

data.forEachIndexed { idx, byte ->
val d = data[idx].toFloat()
floatArray[idx] = d
src.forEachIndexed { idx, _ ->
val d = src[idx].toFloat()
if (d > max) max = d
if (d < min) min = d
}

val minmax = max - min

floatArray.forEachIndexed { idx, float ->
val f = floatArray[idx]
floatArray[idx] = MAX_VALUE * (f - min) / minmax
src.forEachIndexed { idx, _ ->
val f = src[idx].toFloat()
dst[idx] = (MAX_VALUE * (f - min) / minmax).toUInt().toUByte()
}

return floatArray
return dst
}

fun UByteArray.toMat(): Mat {
val mat = Mat(8, 8, CvType.CV_8U)
mat.put(0, 0, this.toByteArray())
return mat
}

fun convertToUByteArray(mat: Mat): UByteArray {
val dst = ByteArray(mat.width() * mat.height())
mat.get(0, 0, dst)
return dst.toUByteArray()
}

/*
* Interpolate image
*
* The reason why I use this function instead of cv::resize() is that
* the original image (8x8) is too small for bicubic interpolation.
* I found that repeating bicubic interpolation works well.
*/
fun interpolate(src: Mat, repeat: Int): Mat {
val dst = src.clone()
for (i in 1 .. repeat) {
resize(dst, dst, Size(dst.cols()*4.0, dst.rows()*4.0), 0.0, 0.0, INTER_CUBIC);
}
return dst
}
3 changes: 2 additions & 1 deletion android/amg8833/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
android:id="@+id/spinnerInterpolate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
android:layout_weight="1"
android:entries="@array/interpolation" />

<TextView
android:id="@+id/textView4"
Expand Down
Loading

0 comments on commit e4fc80c

Please sign in to comment.