korge icon indicating copy to clipboard operation
korge copied to clipboard

Trying to implement BitmapSRGB so that it can take in bytes from Webcam without needing to convert to TYPE_INT_ARGB_PRE

Open Kietyo opened this issue 7 months ago • 3 comments

The image looks all wrong:

image

Can you please take a look at my implementation and see if I'm missing anything?

import korlibs.image.bitmap.Bitmap
import korlibs.image.color.RGBA
import kotlin.math.pow

class BitmapSRGB(
    width: Int,
    height: Int,
    val data: ByteArray = ByteArray(width * height * 3),
): Bitmap(width, height, 8, true, data) {
    override fun getInt(x: Int, y: Int): Int {
        val idxStart = (x * 3) + (y * width * 3)
        val r = data[idxStart].toInt()
        val g = data[idxStart + 1].toInt()
        val b = data[idxStart + 2].toInt()

        val linearRgb = doubleArrayOf(r / 255.0, g / 255.0, b / 255.0)

        // Linearization
        for (i in 0..2) {
            linearRgb[i] = if (linearRgb[i] <= 0.04045) {
                linearRgb[i] / 12.92
            } else {
                ((linearRgb[i] + 0.055) / 1.055).pow(2.4)
            }
        }

        // sRGB to XYZ matrix transformation
        val srgbToXyzMatrix = arrayOf(
            doubleArrayOf(0.4124564, 0.3575761, 0.1804375),
            doubleArrayOf(0.2126729, 0.7151522, 0.0721750),
            doubleArrayOf(0.0193339, 0.1191920, 0.9503041)
        )

        val xyz = DoubleArray(3)
        for (i in 0..2) {
            for (j in 0..2) {
                xyz[i] += srgbToXyzMatrix[i][j] * linearRgb[j]
            }
        }

        // XYZ to Adobe RGB matrix transformation
        val xyzToAdobeRgbMatrix = arrayOf(
            doubleArrayOf(2.0413690, -0.5649464, -0.3446944),
            doubleArrayOf(-0.9692660, 1.8760108, 0.0415560),
            doubleArrayOf(0.0134474, -0.1183897, 1.0154096)
        )

        val linearAdobeRgb = DoubleArray(3)
        for (i in 0..2) {
            for (j in 0..2) {
                linearAdobeRgb[i] += xyzToAdobeRgbMatrix[i][j] * xyz[j]
            }
        }

        // Gamma correction
        for (i in 0..2) {
            linearAdobeRgb[i] = if (linearAdobeRgb[i] <= 0.0) {
                0.0
            } else {
                linearAdobeRgb[i].pow(1 / 2.19921875)
            }
        }

        // Convert back to integer bytes (0-255)
        val adobeRgb = IntArray(3)
        for (i in 0..2) {
            adobeRgb[i] = (linearAdobeRgb[i] * 255.0).toInt()
        }

//        return RGBA(adobeRgb[0], adobeRgb[1], adobeRgb[2]).value
        return (adobeRgb[0] shl 0) or (adobeRgb[1] shl 8) or (adobeRgb[2] shl 16) or (0xFF shl 24)
    }
    override fun setInt(x: Int, y: Int, color: Int) = TODO()

    override fun getRgbaRaw(x: Int, y: Int): RGBA = RGBA(getInt(x, y))
    
}

For context: The webcam image is in terms of sRGB:

https://github.com/Kietyo/webcam-capture-kotlin/blob/master/src/main/kotlin/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDevice.kt#L394

Resources I followed for my implementation:

  • https://en.wikipedia.org/wiki/SRGB
  • https://en.wikipedia.org/wiki/Adobe_RGB_color_space

Kietyo avatar Jul 01 '24 11:07 Kietyo