Skip to main content

oxihuman_export/
sebum_map_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct SebumMap {
6    pub width: u32,
7    pub height: u32,
8    pub data: Vec<f32>,
9}
10
11pub fn new_sebum_map(w: u32, h: u32) -> SebumMap {
12    let n = (w * h) as usize;
13    SebumMap {
14        width: w,
15        height: h,
16        data: vec![0.0; n],
17    }
18}
19
20fn idx(m: &SebumMap, x: u32, y: u32) -> usize {
21    (y * m.width + x) as usize
22}
23
24pub fn sebum_set(m: &mut SebumMap, x: u32, y: u32, v: f32) {
25    let i = idx(m, x, y);
26    m.data[i] = v;
27}
28
29pub fn sebum_get(m: &SebumMap, x: u32, y: u32) -> f32 {
30    m.data[idx(m, x, y)]
31}
32
33pub fn sebum_mean(m: &SebumMap) -> f32 {
34    if m.data.is_empty() {
35        return 0.0;
36    }
37    m.data.iter().sum::<f32>() / m.data.len() as f32
38}
39
40pub fn sebum_to_bytes(m: &SebumMap) -> Vec<u8> {
41    m.data
42        .iter()
43        .flat_map(|&v| {
44            let b = (v.clamp(0.0, 1.0) * 255.0) as u8;
45            [b, b, b, 255u8]
46        })
47        .collect()
48}
49
50/// T-zone detection: pixels above threshold.
51pub fn sebum_zones(m: &SebumMap, threshold: f32) -> Vec<bool> {
52    m.data.iter().map(|&v| v >= threshold).collect()
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_new_sebum_map() {
61        /* correct size */
62        let m = new_sebum_map(4, 4);
63        assert_eq!(m.data.len(), 16);
64    }
65
66    #[test]
67    fn test_set_get() {
68        /* round-trip */
69        let mut m = new_sebum_map(2, 2);
70        sebum_set(&mut m, 1, 1, 0.75);
71        assert!((sebum_get(&m, 1, 1) - 0.75).abs() < 1e-6);
72    }
73
74    #[test]
75    fn test_mean() {
76        /* mean */
77        let mut m = new_sebum_map(2, 1);
78        sebum_set(&mut m, 0, 0, 0.0);
79        sebum_set(&mut m, 1, 0, 1.0);
80        assert!((sebum_mean(&m) - 0.5).abs() < 1e-6);
81    }
82
83    #[test]
84    fn test_to_bytes_length() {
85        /* 4 bytes per pixel */
86        let m = new_sebum_map(3, 3);
87        let bytes = sebum_to_bytes(&m);
88        assert_eq!(bytes.len(), 3 * 3 * 4);
89    }
90
91    #[test]
92    fn test_zones() {
93        /* above threshold */
94        let mut m = new_sebum_map(2, 1);
95        sebum_set(&mut m, 0, 0, 0.3);
96        sebum_set(&mut m, 1, 0, 0.8);
97        let zones = sebum_zones(&m, 0.5);
98        assert!(!zones[0]);
99        assert!(zones[1]);
100    }
101
102    #[test]
103    fn test_mean_empty() {
104        /* empty => 0 */
105        let m = SebumMap {
106            width: 0,
107            height: 0,
108            data: vec![],
109        };
110        assert!((sebum_mean(&m)).abs() < 1e-6);
111    }
112}