hue查找:整体偏差不会很大,但是对于亮度较高存在误差,精准度不够

lab查找:整体一般,但是精准度较好,不过算法复杂,增加耗时

hue色相查找存在误差,在有限的256色中,匹配的规则需要调整

这里使用lab算法提高精准度

RGB转Lab

fun rGBToLab(r: Int, g: Int, b: Int): DoubleArray {        // 处理负值(如-2563864)        val labR = if (r < 0) 0f else r / 255f        val labG = if (g < 0) 0f else g / 255f        val labB = if (b < 0) 0f else b / 255f        // 线性化处理(sRGB转换)        val rLin = if (labR <= 0.04045) labR / 12.92 else ((labR + 0.055) / 1.055).pow(2.4)        val gLin = if (labG <= 0.04045) labG / 12.92 else ((labG + 0.055) / 1.055).pow(2.4)        val bLin = if (labB <= 0.04045) labB / 12.92 else ((labB + 0.055) / 1.055).pow(2.4)        // XYZ转换(D65标准光源)        val x = 0.4124564 * rLin + 0.3575761 * gLin + 0.1804375 * bLin        val y = 0.2126729 * rLin + 0.7151522 * gLin + 0.0721750 * bLin        val z = 0.0193339 * rLin + 0.1191920 * gLin + 0.9503041 * bLin        // Lab转换        val xn = 0.95047f        val yn = 1.0f        val zn = 1.08883f        val fX = x / xn        val fY = y / yn        val fZ = z / zn        val l = 116f * fY.pow(1 / 3.0) - 16        val a = 500f * (fX.pow(1 / 3.0) - fY.pow(1 / 3.0))        val b = 200f * (fY.pow(1 / 3.0) - fZ.pow(1 / 3.0))        return doubleArrayOf(l, a, b)    }
View Code

对rgb进行转换,这里是简化版,如果需要精确要求极高,需要完善

处理流程跟方案一一样,使用集合缓存

接着使用 ΔE76色差公式 查找色值(另外还有ΔEab欧式距离算法,CIEDE2000色差公式,CIE76)

fun deltaE76(lab1: DoubleArray, lab2: DoubleArray): Double {        val (l1, a1, b1) = lab1        val (l2, a2, b2) = lab2        // 计算中间变量        val c1 = sqrt(a1.pow(2) + b1.pow(2))        val c2 = sqrt(a2.pow(2) + b2.pow(2))        val cAvg = (c1 + c2) / 2        val g = 0.5 * (1 - sqrt(cAvg.pow(7) / (cAvg.pow(7) + 25f.pow(7))))        val a1p = a1 * (1 + g)        val a2p = a2 * (1 + g)        val c1p = sqrt(a1p.pow(2) + b1.pow(2))        val c2p = sqrt(a2p.pow(2) + b2.pow(2))        val h1p = Math.toDegrees(kotlin.math.atan2(b1, a1p)).let { if (it < 0) it + 360 else it }        val h2p = Math.toDegrees(kotlin.math.atan2(b2, a2p)).let { if (it < 0) it + 360 else it }        // 色差计算        val deltaLp = l2 - l1        val deltaCp = c2p - c1p        val deltaHp = when {            c1p * c2p == 0.0 -> 0.0            abs(h2p - h1p) <= 180 -> h2p - h1p            h2p <= h1p -> h2p - h1p + 360            else -> h2p - h1p - 360        }        val deltaHpS = 2 * sqrt(c1p * c2p) * sin(Math.toRadians(deltaHp) / 2)        // 加权计算        val lAvg = (l1 + l2) / 2        val cpAvg = (c1p + c2p) / 2        val hpAvg = when {            c1p * c2p == 0.0 -> h1p + h2p            abs(h1p - h2p) > 180 -> (h1p + h2p + 360) / 2            else -> (h1p + h2p) / 2        }.let { if (it >= 360) it - 360 else it }        val t = 1 - 0.17 * cos(Math.toRadians(hpAvg - 30)) +                0.24 * cos(Math.toRadians(2 * hpAvg)) +                0.32 * cos(Math.toRadians(3 * hpAvg + 6)) -                0.20 * cos(Math.toRadians(4 * hpAvg - 63))        val sl = 1 + (0.015 * (lAvg - 50).pow(2)) / sqrt(20 + (lAvg - 50).pow(2))        val sc = 1 + 0.045 * cpAvg        val sh = 1 + 0.015 * cpAvg * t        val deltaTheta = 30 * exp(-((hpAvg - 275) / 25).pow(2))        val rc = 2 * sqrt(cpAvg.pow(7) / (cpAvg.pow(7) + 25f.pow(7)))        val rt = -rc * sin(2 * Math.toRadians(deltaTheta))        return sqrt(            (deltaLp / sl).pow(2) +                    (deltaCp / sc).pow(2) +                    (deltaHpS / sh).pow(2) +                    rt * (deltaCp / sc) * (deltaHpS / sh)        )    }
View Code
var minIndex = 0        var minDistance = Double.MAX_VALUE        var minLab = doubleArrayOf()        tableLabPalette.forEachIndexed { index, lab ->            val distance = ColorEabLabUtils.deltaE76(lab, list)            if (distance < minDistance) {                minDistance = distance                minLab = lab                minIndex = index            }        }
View Code

从上面图中结果可以明显看到,大部分图片中,hue原方案精准度对比lab查找,是有差距的

但是缺点也明显,在某些图片上表现不好,可以看到原方案hue更精准

经过大量调整算法,优化算法,测试下来发现,如果在不通过底层显示屏直接转换色值情况下,都存在误差

而刚好这两种查找结果有一定互补作用,那是否可以取各自的优势,结合结果,达到一个平衡

private fun findLabColor(list: DoubleArray, hue: Float): Int {        // 寻找最近颜色(简化版)        var minIndex = 0        var minDistance = Double.MAX_VALUE        var minLab = doubleArrayOf()        tableLabPalette.forEachIndexed { index, lab ->            val distance = ColorEabLabUtils.deltaE76(lab, list)            if (distance < minDistance) {                minDistance = distance                minLab = lab                minIndex = index            }        }        return if (minDistance < 26) {            tableColorList[minIndex]        } else {            findColor(hue).colorTip        }    }
View Code

在lab精度不够时,可能结果偏差较大,但是hue偏差不会很大,hue只是精度不够,所以这里兼容两种方案

在lab误差较大时,直接采用hue方案计算结果,这样直接弥补了自身的缺点,比如图一中误差小,采用lab方案,提高了精准度,而在图二精度不够时,采用hue,避免误差偏差较大

最终方案是融合方案,不过精度任然存在误差,但本身转换过程结果只有256色,所以不可能达到完美,相比原图取色结果精度是较高

import android.graphics.Bitmapimport android.graphics.Colorimport androidx.palette.graphics.Paletteimport androidx.palette.graphics.Palette.Swatchimport java.lang.Math.toDegreesimport java.lang.Math.toRadiansimport kotlin.math.absimport kotlin.math.atan2import kotlin.math.cosimport kotlin.math.expimport kotlin.math.powimport kotlin.math.sinimport kotlin.math.sqrtobject ColorEabLabUtils {    fun getPerceptuallyDominantColor(bitmap: Bitmap): Int {        val palette = Palette.from(bitmap).maximumColorCount(24).clearFilters().generate()        val swatches = palette.swatches        if (swatches.isEmpty()) return Color.WHITE        var bestSwatch: Swatch? = null        var maxScore = 0f        for (swatch in swatches) {            val hsl = swatch.getHsl()            val saturation = hsl[1] // 饱和度 (0-1)            val luminance = hsl[2] // 亮度 (0-1)            val population = swatch.population            // 评分公式:人口占比 * 饱和度 * 亮度因子            // 亮度因子确保避免过暗或过亮的颜色(0.1-0.9为理想范围)            val luminanceFactor = 1f - abs(luminance - 0.5f) * 1.8f            val score = population * saturation * luminanceFactor            if (score > maxScore) {                maxScore = score                bestSwatch = swatch            }        }        return bestSwatch?.rgb ?: palette.getDominantColor(Color.WHITE)    }    // RGB转Lab *********    fun rGBToLab(r: Int, g: Int, b: Int): DoubleArray {        // 处理负值(如-2563864)        val labR = if (r < 0) 0f else r / 255f        val labG = if (g < 0) 0f else g / 255f        val labB = if (b < 0) 0f else b / 255f        // 线性化处理(sRGB转换)        val rLin = if (labR <= 0.04045) labR / 12.92 else ((labR + 0.055) / 1.055).pow(2.4)        val gLin = if (labG <= 0.04045) labG / 12.92 else ((labG + 0.055) / 1.055).pow(2.4)        val bLin = if (labB <= 0.04045) labB / 12.92 else ((labB + 0.055) / 1.055).pow(2.4)        // XYZ转换(D65标准光源)        val x = 0.4124564 * rLin + 0.3575761 * gLin + 0.1804375 * bLin        val y = 0.2126729 * rLin + 0.7151522 * gLin + 0.0721750 * bLin        val z = 0.0193339 * rLin + 0.1191920 * gLin + 0.9503041 * bLin        // Lab转换        val xn = 0.95047f        val yn = 1.0f        val zn = 1.08883f        val fX = x / xn        val fY = y / yn        val fZ = z / zn        val l = 116f * fY.pow(1 / 3.0) - 16        val a = 500f * (fX.pow(1 / 3.0) - fY.pow(1 / 3.0))        val b = 200f * (fY.pow(1 / 3.0) - fZ.pow(1 / 3.0))        return doubleArrayOf(l, a, b)    }    // ΔEab算法    fun deltaEab(lab1: DoubleArray, lab2: DoubleArray): Double {        val dL = lab1[0] - lab2[0]        val da = lab1[1] - lab2[1]        val db = lab1[2] - lab2[2]        return sqrt(dL.pow(2) + da.pow(2) + db.pow(2))    }    // 改进的RGB转Lab(包含伽马校正和D65白点)    fun rgbToLab(r: Int, g: Int, b: Int): DoubleArray {        // 处理负值(如-2563864)        val labR = if (r < 0) 0f else r / 255f        val labG = if (g < 0) 0f else g / 255f        val labB = if (b < 0) 0f else b / 255f        // sRGB伽马校正        val rLin = if (labR <= 0.04045) labR / 12.92 else ((labR + 0.055) / 1.055).pow(2.4)        val gLin = if (labG <= 0.04045) labG / 12.92 else ((labG + 0.055) / 1.055).pow(2.4)        val bLin = if (labB <= 0.04045) labB / 12.92 else ((labB + 0.055) / 1.055).pow(2.4)        // D65标准光源XYZ转换        val x = 0.4124564 * rLin + 0.3575761 * gLin + 0.1804375 * bLin        val y = 0.2126729 * rLin + 0.7151522 * gLin + 0.0721750 * bLin        val z = 0.0193339 * rLin + 0.1191920 * gLin + 0.9503041 * bLin        // Lab转换(包含阈值处理)        val xn = 0.95047        val yn = 1.0        val zn = 1.08883        val fX = x / xn        val fY = y / yn        val fZ = z / zn        val l = if (fY > 0.008856) 116 * fY.pow(1 / 3.0) - 16 else 903.3 * fY        val a = 500 * (fX.pow(1 / 3.0) - fY.pow(1 / 3.0))        val b = 200 * (fY.pow(1 / 3.0) - fZ.pow(1 / 3.0))        return doubleArrayOf(l, a, b)    }    // 改进的ΔE76色差公式(包含亮度权重)  *********    fun deltaE76(lab1: DoubleArray, lab2: DoubleArray): Double {        val (l1, a1, b1) = lab1        val (l2, a2, b2) = lab2        // 计算中间变量        val c1 = sqrt(a1.pow(2) + b1.pow(2))        val c2 = sqrt(a2.pow(2) + b2.pow(2))        val cAvg = (c1 + c2) / 2        val g = 0.5 * (1 - sqrt(cAvg.pow(7) / (cAvg.pow(7) + 25f.pow(7))))        val a1p = a1 * (1 + g)        val a2p = a2 * (1 + g)        val c1p = sqrt(a1p.pow(2) + b1.pow(2))        val c2p = sqrt(a2p.pow(2) + b2.pow(2))        val h1p = Math.toDegrees(kotlin.math.atan2(b1, a1p)).let { if (it < 0) it + 360 else it }        val h2p = Math.toDegrees(kotlin.math.atan2(b2, a2p)).let { if (it < 0) it + 360 else it }        // 色差计算        val deltaLp = l2 - l1        val deltaCp = c2p - c1p        val deltaHp = when {            c1p * c2p == 0.0 -> 0.0            abs(h2p - h1p) <= 180 -> h2p - h1p            h2p <= h1p -> h2p - h1p + 360            else -> h2p - h1p - 360        }        val deltaHpS = 2 * sqrt(c1p * c2p) * sin(Math.toRadians(deltaHp) / 2)        // 加权计算        val lAvg = (l1 + l2) / 2        val cpAvg = (c1p + c2p) / 2        val hpAvg = when {            c1p * c2p == 0.0 -> h1p + h2p            abs(h1p - h2p) > 180 -> (h1p + h2p + 360) / 2            else -> (h1p + h2p) / 2        }.let { if (it >= 360) it - 360 else it }        val t = 1 - 0.17 * cos(Math.toRadians(hpAvg - 30)) +                0.24 * cos(Math.toRadians(2 * hpAvg)) +                0.32 * cos(Math.toRadians(3 * hpAvg + 6)) -                0.20 * cos(Math.toRadians(4 * hpAvg - 63))        val sl = 1 + (0.015 * (lAvg - 50).pow(2)) / sqrt(20 + (lAvg - 50).pow(2))        val sc = 1 + 0.045 * cpAvg        val sh = 1 + 0.015 * cpAvg * t        val deltaTheta = 30 * exp(-((hpAvg - 275) / 25).pow(2))        val rc = 2 * sqrt(cpAvg.pow(7) / (cpAvg.pow(7) + 25f.pow(7)))        val rt = -rc * sin(2 * Math.toRadians(deltaTheta))        return sqrt(            (deltaLp / sl).pow(2) +                    (deltaCp / sc).pow(2) +                    (deltaHpS / sh).pow(2) +                    rt * (deltaCp / sc) * (deltaHpS / sh)        )    }    /**     * CIEDE2000色差公式实现     * 更符合人眼感知的颜色差异计算     * @param lab1 第一个颜色的Lab值     * @param lab2 第二个颜色的Lab值     * @return Double 色差值(ΔE越小颜色越接近)     */    fun deltaE2000(lab1: DoubleArray, lab2: DoubleArray): Double {        val (l1, a1, b1) = lab1        val (l2, a2, b2) = lab2        // 步骤1:计算色度        val c1 = sqrt(a1.pow(2) + b1.pow(2))        val c2 = sqrt(a2.pow(2) + b2.pow(2))        val cAvg = (c1 + c2) / 2.0        // 步骤2:计算补偿因子        val g = 0.5 * (1.0 - sqrt(cAvg.pow(7) / (cAvg.pow(7) + 25.0.pow(7))))        val a1Prime = a1 * (1.0 + g)        val a2Prime = a2 * (1.0 + g)        val c1Prime = sqrt(a1Prime.pow(2) + b1.pow(2))        val c2Prime = sqrt(a2Prime.pow(2) + b2.pow(2))        val cBarPrime = (c1Prime + c2Prime) / 2.0        // 步骤3:计算色调角        val h1Prime = toDegrees(atan2(b1, a1Prime)).let { if (it < 0) it + 360.0 else it }        val h2Prime = toDegrees(atan2(b2, a2Prime)).let { if (it < 0) it + 360.0 else it }        // 步骤4:计算基本差异        val deltaLPrime = l2 - l1        val deltaCPrime = c2Prime - c1Prime        // 步骤5:计算色调差        var deltaHPrime = 0.0        if (c1Prime * c2Prime != 0.0) {            val deltaHPrimeRaw = when {                abs(h2Prime - h1Prime) <= 180.0 -> h2Prime - h1Prime                h2Prime <= h1Prime -> h2Prime - h1Prime + 360.0                else -> h2Prime - h1Prime - 360.0            }            deltaHPrime = 2.0 * sqrt(c1Prime * c2Prime) * sin(toRadians(deltaHPrimeRaw) / 2.0)        }        // 步骤6:计算加权平均值        val lBar = (l1 + l2) / 2.0        val cBar = (c1 + c2) / 2.0        // 步骤7:计算色调平均值        val hBarPrime = when {            abs(h1Prime - h2Prime) > 180.0 -> (h1Prime + h2Prime + 360.0) / 2.0            else -> (h1Prime + h2Prime) / 2.0        }        // 步骤8:计算补偿项        val t = 1.0 - 0.17 * cos(toRadians(hBarPrime - 30.0)) +                0.24 * cos(toRadians(2.0 * hBarPrime)) +                0.32 * cos(toRadians(3.0 * hBarPrime + 6.0)) -                0.20 * cos(toRadians(4.0 * hBarPrime - 63.0))        // 步骤9:计算旋转项        val deltaTheta = 30.0 * exp(-((hBarPrime - 275.0) / 25.0).pow(2))        val rc = 2.0 * sqrt(cBarPrime.pow(7) / (cBarPrime.pow(7) + 25.0.pow(7)))        val rt = -rc * sin(2.0 * toRadians(deltaTheta))        // 步骤10:计算权重因子        val sl = 1.0 + (0.015 * (lBar - 50.0).pow(2) / sqrt(20.0 + (lBar - 50.0).pow(2)))        val sc = 1.0 + 0.045 * cBarPrime        val sh = 1.0 + 0.015 * cBarPrime * t        // 步骤11:最终色差计算        val term1 = (deltaLPrime / sl).pow(2)        val term2 = (deltaCPrime / sc).pow(2)        val term3 = (deltaHPrime / sh).pow(2)        val term4 = rt * (deltaCPrime / sc) * (deltaHPrime / sh)        return sqrt(term1 + term2 + term3 + term4)    }    // 优化的伽马校正函数    fun preciseGammaExpand(c: Double) = if (c <= 0.04045) {        c / 12.92    } else {        ((c + 0.055) / 1.055).pow(2.4)    }    // 优化的Lab转换函数    fun preciseF(t: Double) = if (t > 0.008856) {        t.pow(1.0 / 3.0)    } else {        (7.787 * t) + (16.0 / 116.0)    }    /**     * 优化的RGB转Lab转换方法     * 使用更精确的浮点数计算和标准化处理     */    fun rgbToLabOptimized(r: Int, g: Int, b: Int): DoubleArray {        // 使用更精确的归一化        val rNormalized = r / 255.0        val gNormalized = g / 255.0        val bNormalized = b / 255.0        // 应用伽马校正        val rLinear = preciseGammaExpand(rNormalized)        val gLinear = preciseGammaExpand(gNormalized)        val bLinear = preciseGammaExpand(bNormalized)        // 使用D65标准光源的精确XYZ转换矩阵        val x = 0.4124564 * rLinear + 0.3575761 * gLinear + 0.1804375 * bLinear        val y = 0.2126729 * rLinear + 0.7151522 * gLinear + 0.0721750 * bLinear        val z = 0.0193339 * rLinear + 0.1191920 * gLinear + 0.9503041 * bLinear        // 使用CIE标准参考白点        val refX = 0.95047        val refY = 1.00000        val refZ = 1.08883        // 计算相对值        val xRelative = x / refX        val yRelative = y / refY        val zRelative = z / refZ        val fx = preciseF(xRelative)        val fy = preciseF(yRelative)        val fz = preciseF(zRelative)        // 计算Lab值        val l = (116.0 * fy) - 16.0        val a = 500.0 * (fx - fy)        val bLab = 200.0 * (fy - fz)        return doubleArrayOf(l, a, bLab)    }    /**     * 完整的CIEDE2000色差公式实现     * 包含所有补偿项和旋转项     */    fun deltaE2000Complete(lab1: DoubleArray, lab2: DoubleArray): Double {        val (l1, a1, b1) = lab1        val (l2, a2, b2) = lab2        // 步骤1:计算色度值        val c1 = sqrt(a1.pow(2) + b1.pow(2))        val c2 = sqrt(a2.pow(2) + b2.pow(2))        // 步骤2:计算平均值        val lBar = (l1 + l2) / 2.0        val cBar = (c1 + c2) / 2.0        // 步骤3:计算补偿因子        val g = 0.5 * (1.0 - sqrt(cBar.pow(7) / (cBar.pow(7) + 25.0.pow(7))))        // 步骤4:计算调整后的a'值        val a1Prime = a1 * (1.0 + g)        val a2Prime = a2 * (1.0 + g)        // 步骤5:计算调整后的色度值        val c1Prime = sqrt(a1Prime.pow(2) + b1.pow(2))        val c2Prime = sqrt(a2Prime.pow(2) + b2.pow(2))        val cBarPrime = (c1Prime + c2Prime) / 2.0        // 步骤6:计算色调角        val h1Prime = toDegrees(atan2(b1, a1Prime)).let {            if (it < 0) it + 360.0 else it        }        val h2Prime = toDegrees(atan2(b2, a2Prime)).let {            if (it < 0) it + 360.0 else it        }        // 步骤7:计算基本差异        val deltaLPrime = l2 - l1        val deltaCPrime = c2Prime - c1Prime        // 步骤8:计算色调差        var deltaHPrime = 0.0        if (c1Prime != 0.0 && c2Prime != 0.0) {            val deltaHPrimeRaw = when {                abs(h2Prime - h1Prime) <= 180.0 -> h2Prime - h1Prime                h2Prime <= h1Prime -> h2Prime - h1Prime + 360.0                else -> h2Prime - h1Prime - 360.0            }            deltaHPrime = 2.0 * sqrt(c1Prime * c2Prime) * sin(toRadians(deltaHPrimeRaw) / 2.0)        }        // 步骤9:计算色调平均值        val hBarPrime = when {            abs(h1Prime - h2Prime) > 180.0 -> (h1Prime + h2Prime + 360.0) / 2.0            else -> (h1Prime + h2Prime) / 2.0        }        // 步骤10:计算补偿项        val t = 1.0 - 0.17 * cos(toRadians(hBarPrime - 30.0)) +                0.24 * cos(toRadians(2.0 * hBarPrime)) +                0.32 * cos(toRadians(3.0 * hBarPrime + 6.0)) -                0.20 * cos(toRadians(4.0 * hBarPrime - 63.0))        // 步骤11:计算旋转项        val deltaTheta = 30.0 * exp(-((hBarPrime - 275.0) / 25.0).pow(2))        val rc = 2.0 * sqrt(cBarPrime.pow(7) / (cBarPrime.pow(7) + 25.0.pow(7)))        val rt = -rc * sin(2.0 * toRadians(deltaTheta))        // 步骤12:计算权重因子        val sl = 1.0 + (0.015 * (lBar - 50.0).pow(2) / sqrt(20.0 + (lBar - 50.0).pow(2)))        val sc = 1.0 + 0.045 * cBarPrime        val sh = 1.0 + 0.015 * cBarPrime * t        // 步骤13:最终色差计算        val term1 = (deltaLPrime / sl).pow(2)        val term2 = (deltaCPrime / sc).pow(2)        val term3 = (deltaHPrime / sh).pow(2)        val term4 = rt * (deltaCPrime / sc) * (deltaHPrime / sh)        return sqrt(term1 + term2 + term3 + term4)    }}
View Code
{  "list": [    {      "r": 0,      "g": 84,      "b": 255    },    {      "r": 18,      "g": 92,      "b": 255    },    {      "r": 65,      "g": 82,      "b": 255    },    {      "r": 90,      "g": 81,      "b": 255    },    {      "r": 110,      "g": 80,      "b": 255    },    {      "r": 127,      "g": 78,      "b": 255    },    {      "r": 142,      "g": 77,      "b": 255    },    {      "r": 157,      "g": 76,      "b": 255    },    {      "r": 170,      "g": 74,      "b": 255    },    {      "r": 197,      "g": 70,      "b": 255    },    {      "r": 209,      "g": 68,      "b": 255    },    {      "r": 237,      "g": 62,      "b": 255    },    {      "r": 255,      "g": 51,      "b": 244    },    {      "r": 255,      "g": 35,      "b": 219    },    {      "r": 255,      "g": 11,      "b": 196    },    {      "r": 255,      "g": 0,      "b": 176    },    {      "r": 255,      "g": 48,      "b": 158    },    {      "r": 255,      "g": 19,      "b": 142    },    {      "r": 255,      "g": 15,      "b": 122    },    {      "r": 255,      "g": 0,      "b": 99    },    {      "r": 255,      "g": 6,      "b": 75    },    {      "r": 255,      "g": 9,      "b": 55    },    {      "r": 255,      "g": 6,      "b": 0    },    {      "r": 255,      "g": 28,      "b": 0    },    {      "r": 255,      "g": 62,      "b": 0    },    {      "r": 255,      "g": 85,      "b": 0    },    {      "r": 255,      "g": 105,      "b": 0    },    {      "r": 255,      "g": 125,      "b": 0    },    {      "r": 255,      "g": 155,      "b": 5    },    {      "r": 255,      "g": 171,      "b": 41    },    {      "r": 255,      "g": 180,      "b": 15    },    {      "r": 255,      "g": 192,      "b": 19    },    {      "r": 255,      "g": 206,      "b": 45    },    {      "r": 255,      "g": 218,      "b": 27    },    {      "r": 255,      "g": 231,      "b": 30    },    {      "r": 255,      "g": 244,      "b": 34    },    {      "r": 251,      "g": 255,      "b": 37    },    {      "r": 235,      "g": 255,      "b": 38    },    {      "r": 219,      "g": 255,      "b": 39    },    {      "r": 203,      "g": 255,      "b": 40    },    {      "r": 185,      "g": 255,      "b": 41    },    {      "r": 165,      "g": 255,      "b": 42    },    {      "r": 142,      "g": 255,      "b": 42    },    {      "r": 109,      "g": 255,      "b": 29    },    {      "r": 69,      "g": 255,      "b": 44    },    {      "r": 24,      "g": 255,      "b": 46    },    {      "r": 22,      "g": 255,      "b": 32    },    {      "r": 0,      "g": 255,      "b": 102    },    {      "r": 0,      "g": 255,      "b": 136    },    {      "r": 6,      "g": 209,      "b": 128    },    {      "r": 0,      "g": 255,      "b": 189    },    {      "r": 0,      "g": 255,      "b": 212    },    {      "r": 0,      "g": 255,      "b": 234    },    {      "r": 0,      "g": 254,      "b": 255    },    {      "r": 0,      "g": 234,      "b": 255    },    {      "r": 0,      "g": 200,      "b": 255    },    {      "r": 0,      "g": 186,      "b": 255    },    {      "r": 0,      "g": 173,      "b": 255    },    {      "r": 72,      "g": 164,      "b": 237    },    {      "r": 2,      "g": 146,      "b": 255    },    {      "r": 0,      "g": 138,      "b": 255    },    {      "r": 0,      "g": 127,      "b": 255    },    {      "r": 0,      "g": 106,      "b": 255    },    {      "r": 255,      "g": 255,      "b": 255    },    {      "r": 78,      "g": 85,      "b": 255    },    {      "r": 100,      "g": 150,      "b": 255    },    {      "r": 98,      "g": 204,      "b": 255    },    {      "r": 103,      "g": 172,      "b": 156    },    {      "r": 84,      "g": 255,      "b": 167    },    {      "r": 58,      "g": 255,      "b": 112    },    {      "r": 52,      "g": 255,      "b": 88    },    {      "r": 149,      "g": 95,      "b": 255    },    {      "r": 162,      "g": 152,      "b": 255    },    {      "r": 171,      "g": 228,      "b": 255    },    {      "r": 159,      "g": 255,      "b": 201    },    {      "r": 130,      "g": 255,      "b": 142    },    {      "r": 108,      "g": 255,      "b": 99    },    {      "r": 97,      "g": 255,      "b": 74    },    {      "r": 79,      "g": 255,      "b": 41    },    {      "r": 236,      "g": 101,      "b": 254    },    {      "r": 254,      "g": 169,      "b": 255    },    {      "r": 225,      "g": 255,      "b": 170    },    {      "r": 184,      "g": 255,      "b": 155    },    {      "r": 155,      "g": 255,      "b": 75    },    {      "r": 133,      "g": 255,      "b": 45    },    {      "r": 255,      "g": 86,      "b": 196    },    {      "r": 255,      "g": 130,      "b": 171    },    {      "r": 255,      "g": 175,      "b": 146    },    {      "r": 255,      "g": 221,      "b": 119    },    {      "r": 235,      "g": 255,      "b": 95    },    {      "r": 203,      "g": 255,      "b": 51    },    {      "r": 255,      "g": 68,      "b": 138    },    {      "r": 255,      "g": 104,      "b": 117    },    {      "r": 255,      "g": 140,      "b": 95    },    {      "r": 255,      "g": 187,      "b": 85    },    {      "r": 255,      "g": 217,      "b": 49    },    {      "r": 255,      "g": 25,      "b": 115    },    {      "r": 255,      "g": 40,      "b": 96    },    {      "r": 255,      "g": 85,      "b": 78    },    {      "r": 255,      "g": 116,      "b": 59    },    {      "r": 255,      "g": 148,      "b": 40    },    {      "r": 255,      "g": 45,      "b": 62    },    {      "r": 255,      "g": 81,      "b": 54    },    {      "r": 255,      "g": 98,      "b": 33    },    {      "r": 255,      "g": 22,      "b": 36    },    {      "r": 255,      "g": 49,      "b": 35    },    {      "r": 255,      "g": 32,      "b": 23    },    {      "r": 35,      "g": 55,      "b": 255    },    {      "r": 35,      "g": 73,      "b": 255    },    {      "r": 35,      "g": 94,      "b": 255    },    {      "r": 35,      "g": 116,      "b": 255    },    {      "r": 65,      "g": 145,      "b": 255    },    {      "r": 35,      "g": 182,      "b": 255    },    {      "r": 35,      "g": 214,      "b": 255    },    {      "r": 2,      "g": 251,      "b": 255    },    {      "r": 0,      "g": 255,      "b": 223    },    {      "r": 0,      "g": 255,      "b": 203    },    {      "r": 22,      "g": 255,      "b": 168    },    {      "r": 54,      "g": 255,      "b": 144    },    {      "r": 58,      "g": 55,      "b": 255    },    {      "r": 59,      "g": 86,      "b": 255    },    {      "r": 61,      "g": 120,      "b": 255    },    {      "r": 64,      "g": 175,      "b": 255    },    {      "r": 66,      "g": 225,      "b": 255    },    {      "r": 65,      "g": 255,      "b": 246    },    {      "r": 83,      "g": 255,      "b": 211    },    {      "r": 51,      "g": 255,      "b": 181    },    {      "r": 46,      "g": 255,      "b": 156    },    {      "r": 41,      "g": 255,      "b": 134    },    {      "r": 37,      "g": 255,      "b": 115    },    {      "r": 83,      "g": 56,      "b": 255    },    {      "r": 84,      "g": 67,      "b": 255    },    {      "r": 85,      "g": 77,      "b": 255    },    {      "r": 86,      "g": 104,      "b": 255    },    {      "r": 89,      "g": 113,      "b": 255    },    {      "r": 90,      "g": 126,      "b": 255    },    {      "r": 94,      "g": 170,      "b": 255    },    {      "r": 102,      "g": 242,      "b": 255    },    {      "r": 84,      "g": 255,      "b": 193    },    {      "r": 68,      "g": 255,      "b": 140    },    {      "r": 56,      "g": 255,      "b": 101    },    {      "r": 110,      "g": 58,      "b": 255    },    {      "r": 112,      "g": 68,      "b": 255    },    {      "r": 116,      "g": 91,      "b": 255    },    {      "r": 120,      "g": 117,      "b": 255    },    {      "r": 124,      "g": 145,      "b": 255    },    {      "r": 130,      "g": 176,      "b": 255    },    {      "r": 149,      "g": 222,      "b": 255    },    {      "r": 142,      "g": 253,      "b": 255    },    {      "r": 127,      "g": 255,      "b": 216    },    {      "r": 114,      "g": 255,      "b": 183    },    {      "r": 103,      "g": 255,      "b": 156    },    {      "r": 94,      "g": 255,      "b": 132    },    {      "r": 86,      "g": 255,      "b": 111    },    {      "r": 78,      "g": 255,      "b": 93    },    {      "r": 72,      "g": 255,      "b": 77    },    {      "r": 87,      "g": 255,      "b": 71    },    {      "r": 62,      "g": 255,      "b": 51    },    {      "r": 143,      "g": 70,      "b": 255    },    {      "r": 140,      "g": 112,      "b": 255    },    {      "r": 170,      "g": 186,      "b": 255    },    {      "r": 178,      "g": 255,      "b": 240    },    {      "r": 143,      "g": 255,      "b": 169    },    {      "r": 118,      "g": 255,      "b": 119    },    {      "r": 100,      "g": 255,      "b": 82    },    {      "r": 85,      "g": 255,      "b": 53    },    {      "r": 74,      "g": 255,      "b": 30    },    {      "r": 178,      "g": 72,      "b": 255    },    {      "r": 192,      "g": 105,      "b": 255    },    {      "r": 195,      "g": 127,      "b": 255    },    {      "r": 205,      "g": 160,      "b": 255    },    {      "r": 216,      "g": 198,      "b": 255    },    {      "r": 191,      "g": 255,      "b": 186    },    {      "r": 173,      "g": 255,      "b": 155    },    {      "r": 162,      "g": 255,      "b": 125    },    {      "r": 143,      "g": 255,      "b": 106    },    {      "r": 130,      "g": 255,      "b": 91    },    {      "r": 121,      "g": 255,      "b": 71    },    {      "r": 112,      "g": 255,      "b": 56    },    {      "r": 104,      "g": 255,      "b": 43    },    {      "r": 93,      "g": 255,      "b": 37    },    {      "r": 217,      "g": 74,      "b": 255    },    {      "r": 242,      "g": 140,      "b": 255    },    {      "r": 255,      "g": 199,      "b": 240    },    {      "r": 203,      "g": 255,      "b": 140    },    {      "r": 169,      "g": 255,      "b": 94    },    {      "r": 143,      "g": 255,      "b": 59    },    {      "r": 124,      "g": 255,      "b": 33    },    {      "r": 255,      "g": 84,      "b": 255    },    {      "r": 255,      "g": 112,      "b": 255    },    {      "r": 255,      "g": 123,      "b": 222    },    {      "r": 255,      "g": 148,      "b": 208    },    {      "r": 255,      "g": 166,      "b": 205    },    {      "r": 255,      "g": 194,      "b": 204    },    {      "r": 255,      "g": 224,      "b": 166    },    {      "r": 255,      "g": 251,      "b": 151    },    {      "r": 233,      "g": 255,      "b": 125    },    {      "r": 210,      "g": 255,      "b": 91    },    {      "r": 194,      "g": 255,      "b": 81    },    {      "r": 179,      "g": 255,      "b": 63    },    {      "r": 154,      "g": 255,      "b": 32    },    {      "r": 158,      "g": 255,      "b": 37    },    {      "r": 255,      "g": 65,      "b": 208    },    {      "r": 255,      "g": 108,      "b": 184    },    {      "r": 255,      "g": 152,      "b": 159    },    {      "r": 255,      "g": 198,      "b": 133    },    {      "r": 255,      "g": 253,      "b": 95    },    {      "r": 220,      "g": 255,      "b": 67    },    {      "r": 188,      "g": 255,      "b": 36    },    {      "r": 255,      "g": 57,      "b": 176    },    {      "r": 255,      "g": 76,      "b": 164    },    {      "r": 255,      "g": 96,      "b": 153    },    {      "r": 255,      "g": 125,      "b": 150    },    {      "r": 255,      "g": 136,      "b": 130    },    {      "r": 255,      "g": 166,      "b": 126    },    {      "r": 255,      "g": 176,      "b": 106    },    {      "r": 255,      "g": 197,      "b": 94    },    {      "r": 255,      "g": 223,      "b": 88    },    {      "r": 255,      "g": 240,      "b": 69    },    {      "r": 248,      "g": 255,      "b": 56    },    {      "r": 228,      "g": 255,      "b": 38    },    {      "r": 255,      "g": 36,      "b": 167    },    {      "r": 255,      "g": 88,      "b": 134    },    {      "r": 255,      "g": 122,      "b": 106    },    {      "r": 255,      "g": 160,      "b": 77    },    {      "r": 255,      "g": 207,      "b": 86    },    {      "r": 255,      "g": 237,      "b": 37    },    {      "r": 255,      "g": 41,      "b": 143    },    {      "r": 255,      "g": 61,      "b": 116    },    {      "r": 255,      "g": 84,      "b": 111    },    {      "r": 255,      "g": 94,      "b": 96    },    {      "r": 255,      "g": 110,      "b": 86    },    {      "r": 255,      "g": 127,      "b": 76    },    {      "r": 255,      "g": 144,      "b": 65    },    {      "r": 255,      "g": 162,      "b": 55    },    {      "r": 255,      "g": 179,      "b": 44    },    {      "r": 255,      "g": 197,      "b": 33    },    {      "r": 255,      "g": 70,      "b": 88    },    {      "r": 254,      "g": 107,      "b": 64    },    {      "r": 255,      "g": 132,      "b": 50    },    {      "r": 255,      "g": 164,      "b": 30    },    {      "r": 255,      "g": 22,      "b": 98    },    {      "r": 255,      "g": 50,      "b": 80    },    {      "r": 254,      "g": 89,      "b": 64    },    {      "r": 255,      "g": 92,      "b": 54    },    {      "r": 255,      "g": 121,      "b": 36    },    {      "r": 255,      "g": 138,      "b": 37    },    {      "r": 255,      "g": 111,      "b": 25    },    {      "r": 255,      "g": 41,      "b": 54    },    {      "r": 255,      "g": 53,      "b": 46    },    {      "r": 255,      "g": 78,      "b": 30    },    {      "r": 255,      "g": 61,      "b": 28    },    {      "r": 255,      "g": 39,      "b": 37    },    {      "r": 255,      "g": 57,      "b": 18    },    {      "r": 255,      "g": 29,      "b": 15    },    {      "r": 255,      "g": 14,      "b": 8    }  ]}
color_rgb_256.json
import android.graphics.Bitmapimport android.graphics.Colorimport android.util.Logimport com.blankj.utilcode.util.GsonUtilsimport com.blankj.utilcode.util.ScreenUtilsimport com.blankj.utilcode.util.Utilsimport com.google.gson.Gsonimport com.google.gson.reflect.TypeTokenimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.MainScopeimport kotlinx.coroutines.launchimport java.util.concurrent.CopyOnWriteArrayListimport kotlin.math.absobject AmbientLightColorPickManager {    private const val TAG = "AmbientLightColorPickManager"    private var scope = MainScope()    private val mWidth = ScreenUtils.getScreenWidth() / 2    private val mHeight = ScreenUtils.getScreenHeight() / 2    // 256色    private val tableList = mutableListOf<ColorTableBean>()    // 原图色    private val originalList = CopyOnWriteArrayList<ColorTableBean>()    var test1Listener: ((Int) -> Unit)? = null    var test2Listener: ((Int, Int, Int) -> Unit)? = null    @JvmStatic    fun init() {        log("$TAG init")        scope.launch(Dispatchers.IO) {            initHsvColor()        }    }    private fun initHsvColor() {        tableList.clear()        runCatching {            val json = SharedPreferencesUtils.getRGB256HsvColor(Utils.getApp())            val listType = object : TypeToken<MutableList<ColorTableBean>>() {}.type            Gson().fromJson<MutableList<ColorTableBean>>(json, listType)?.let {                tableList.addAll(it)                log("initHsvColor xml list size=${tableList.size}")            }        }.getOrElse {            Log.e(TAG, "initHsvColor Exception ${it.message}")        }        if (tableList.isEmpty()) {            saveHsvColor().let {                if (it.isNotEmpty()) {                    tableList.addAll(it)                }            }            log("initHsvColor json list size=${tableList.size}")        }    }    /** 将本地rgb色值转换成hsv保存到本地 */    private fun saveHsvColor(): MutableList<ColorTableBean> {        log("saveHsvColor")        val hsvList = mutableListOf<ColorTableBean>()        runCatching {            val assetManager = Utils.getApp().assets            val file = assetManager.open("color_rgb_256.json")            val jsonStr = file.bufferedReader().readText()            file.close()            val bean = Gson().fromJson(jsonStr, AmbientLightList::class.java)            for (i in 0 until bean.list.size) {                bean.list[i].apply {                    val myColor = Color.rgb(r, g, b)                    val hsvColors = FloatArray(3)                    Color.colorToHSV(myColor, hsvColors)                    val lab = ColorEabLabUtils.rGBToLab(r, g, b)                    val bean = ColorTableBean(hsvColors, lab, myColor)                    hsvList.add(bean)                }            }            val json = Gson().toJson(hsvList)            log("saveHsvColor hsvListSize=${hsvList.size}")            SharedPreferencesUtils.setRGB256HsvColor(Utils.getApp(), json)        }.getOrElse {            Log.e(TAG, "saveHsvColor Exception ${it.message}")        }        return hsvList    }    /** 设置氛围灯 */    @JvmStatic    fun setAmbientLight(displayId: Int, index: Int) {        if (displayId != DisplayParameter.DISPLAY_CSD.displayId) return        log("setAmbientLight displayId=$displayId")        scope.launch(Dispatchers.IO) {            if (originalList.isEmpty()) {                Log.w(TAG, "setAmbientLight hueList is null")                return@launch            }            if (index < 0 || index >= originalList.size) {                Log.w(TAG, "setAmbientLight 索引异常")                return@launch            }            // 氛围灯取色            setBytesFunctionValue(index)        }    }    @JvmStatic    fun switchLight(isOn: Boolean) {        log("switchLight isOn=$isOn")    }    /** 初始化资源 */    @JvmStatic    fun loadData(displayId: Int, pictures: List<String>) {        if (displayId != DisplayParameter.DISPLAY_CSD.displayId) return        log("loadData pictures size=${pictures.size} pictures $pictures")        originalList.clear()        for ((_, picture) in pictures.withIndex()) {            runCatching {                val bitmap = GlideCacheUtils.loadImageAsBitmap(picture, mWidth, mHeight)                originalList.add(generate(bitmap))            }.getOrElse {                Log.e(TAG, "loadData exception ${it.message}")            }        }        log("loadData hueList size=${originalList.size}")    }    private fun setFunctionValue(functionId: Int, value: Int, zone: Int) {            }    private fun setBytesFunctionValue(index: Int) {        try {            originalList[index].let {                test1Listener?.invoke(it.color)                test2Listener?.invoke(                    findColor(it.hue()).colorTip, findLabColor(it), findTestColor(it.labArray)                )            }        } catch (e: Exception) {            Log.e(TAG, "setBytesFunctionValue Exception $e")        }    }    private fun findColor(bgHue: Float): ColorTipBean {        if (tableList.isEmpty()) {            Log.w(TAG, "findColor hsvList is null")            return ColorTipBean(Color.WHITE)        }        var result = tableList[0]        var minDiff = abs(result.hue() - bgHue)        for (i in 0 until tableList.size) {            val currentDiff = abs(tableList[i].hue() - bgHue)            if (currentDiff < minDiff) {                minDiff = currentDiff                result = tableList[i]            }        }        log("findColor bgHue=$bgHue,minDiff=$minDiff,result=$result")        return ColorTipBean(result.color)    }    private fun findLabColor(bean: ColorTableBean): Int {        var color = Color.WHITE        var minDiff = Double.MAX_VALUE        tableList.forEachIndexed { index, it ->            val distance = ColorEabLabUtils.deltaE76(it.labArray, bean.labArray)            if (distance < minDiff) {                minDiff = distance                color = it.color            }        }        log("findLabColor minDiff=$minDiff,color=[${bean.color},$color]")        return if (minDiff < 26) color else findColor(bean.hue()).colorTip    }    private fun findTestColor(list: DoubleArray): Int {        var color = Color.WHITE        var minDiff = Double.MAX_VALUE        tableList.forEachIndexed { index, it ->            val distance = ColorEabLabUtils.deltaE76(it.labArray, list)            if (distance < minDiff) {                minDiff = distance                color = it.color            }        }        return color    }    private fun getColors(bean: ColorTableBean): ByteArray {        val result = mutableListOf<ColorTipBean>()        val colorBean = ColorTipBean(findLabColor(bean))        result.add(colorBean)        result.add(colorBean)        result.add(colorBean)        val json = GsonUtils.toJson(ColorLightBean(result).list)        log("setBytesFunctionValue json=$json")        return json.toByteArray()    }    private fun generate(newMap: Bitmap): ColorTableBean {        val dominantColor = ColorEabLabUtils.getPerceptuallyDominantColor(newMap)        val hsvArray = FloatArray(3)        Color.colorToHSV(dominantColor, hsvArray)        val labArray = ColorEabLabUtils.rGBToLab(dominantColor)        return ColorTableBean(hsvArray, labArray, dominantColor)    }    private fun log(str: String) = Log.d(TAG, str)}
View Code

 

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]