1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
// 直方图操作(纯 MoonBit):均衡化 + 归一化

///|
/// 计算灰度直方图。返回 256 个 bin 的计数数组。
/// 彩色图像先转灰度(BT.601)再计算。
/// 优化:直接从 Bytes 读取,避免额外数组分配
pub fn histogram(img : @types.Image) -> Array[Int] raise @types.LoadError {
  // 输入验证
  if img.width <= 0 || img.height <= 0 {
    raise @types.LoadError::DecodeFailed("histogram: 图像尺寸必须大于0")
  }
  if img.channels < 1 || img.channels > 4 {
    raise @types.LoadError::DecodeFailed(
      "histogram: 通道数必须在1-4之间",
    )
  }
  let expected_size = img.width * img.height * img.channels
  if img.data.length() != expected_size {
    raise @types.LoadError::DecodeFailed(
      "histogram: 数据大小不匹配,期望 " +
      expected_size.to_string() +
      ",实际 " +
      img.data.length().to_string(),
    )
  }

  let hist : Array[Int] = Array::make(256, 0)
  let n = img.width * img.height
  let ch = img.channels
  let data = img.data
  for i in 0..<n {
    let base = i * ch
    let v = if ch == 1 {
      data[base].to_int()
    } else {
      let r = data[base].to_int()
      let g = data[base + 1].to_int()
      let b = data[base + 2].to_int()
      (r * 299 + g * 587 + b * 114) / 1000
    }
    hist[v] += 1
  }
  hist
}

///|
/// 直方图均衡化。基于灰度直方图计算映射 LUT,应用到所有颜色通道。
/// alpha 通道不变。
pub fn histogram_equalize(
  img : @types.Image,
) -> @types.Image raise @types.LoadError {
  // 输入验证
  if img.width <= 0 || img.height <= 0 {
    raise @types.LoadError::DecodeFailed(
      "histogram_equalize: 图像尺寸必须大于0",
    )
  }
  if img.channels < 1 || img.channels > 4 {
    raise @types.LoadError::DecodeFailed(
      "histogram_equalize: 通道数必须在1-4之间",
    )
  }
  let expected_size = img.width * img.height * img.channels
  if img.data.length() != expected_size {
    raise @types.LoadError::DecodeFailed(
      "histogram_equalize: 数据大小不匹配,期望 " +
      expected_size.to_string() +
      ",实际 " +
      img.data.length().to_string(),
    )
  }

  let hist = histogram(img)
  let n = img.width * img.height
  // 计算 CDF
  let cdf : Array[Int] = Array::make(256, 0)
  let mut cum = 0
  for i in 0..<256 {
    cum += hist[i]
    cdf[i] = cum
  }
  // 找到 CDF_min(第一个非零值)
  let mut cdf_min = 0
  for i in 0..<256 {
    if cdf[i] > 0 {
      cdf_min = cdf[i]
      break
    }
  }
  // 计算映射 LUT
  let lut : Array[Byte] = Array::make(256, b'\x00')
  let denom = n - cdf_min
  if denom <= 0 {
    // 所有像素相同,返回原图
    return img
  }
  for i in 0..<256 {
    let mapped = (cdf[i] - cdf_min) * 255 / denom
    lut[i] = mapped.clamp(min=0, max=255).to_byte()
  }
  // 应用 LUT
  let ch = img.channels
  let color_ch = if ch >= 3 { 3 } else { ch }
  let data_int : Array[Int] = Array::make(n * ch, 0)
  for i in 0..<(n * ch) {
    data_int[i] = img.data[i].to_int()
  }
  let out : Array[Byte] = Array::make(n * ch, b'\x00')
  for i in 0..<n {
    for c in 0..<color_ch {
      out[i * ch + c] = lut[data_int[i * ch + c]]
    }
    if ch == 4 {
      out[i * ch + 3] = img.data[i * ch + 3]
    }
  }
  @types.Image::{
    width: img.width,
    height: img.height,
    channels: ch,
    data: Bytes::from_array(out),
  }
}

///|
/// 直方图归一化(线性拉伸)。将像素值范围线性映射到 [0, 255]。
/// alpha 通道不变。
pub fn histogram_normalize(
  img : @types.Image,
) -> @types.Image raise @types.LoadError {
  // 输入验证
  if img.width <= 0 || img.height <= 0 {
    raise @types.LoadError::DecodeFailed(
      "histogram_normalize: 图像尺寸必须大于0",
    )
  }
  if img.channels < 1 || img.channels > 4 {
    raise @types.LoadError::DecodeFailed(
      "histogram_normalize: 通道数必须在1-4之间",
    )
  }
  let expected_size = img.width * img.height * img.channels
  if img.data.length() != expected_size {
    raise @types.LoadError::DecodeFailed(
      "histogram_normalize: 数据大小不匹配,期望 " +
      expected_size.to_string() +
      ",实际 " +
      img.data.length().to_string(),
    )
  }

  let ch = img.channels
  let color_ch = if ch >= 3 { 3 } else { ch }
  let n = img.width * img.height
  let data_int : Array[Int] = Array::make(n * ch, 0)
  for i in 0..<(n * ch) {
    data_int[i] = img.data[i].to_int()
  }
  // 找到颜色通道的最小值和最大值
  let mut min_val = 255
  let mut max_val = 0
  for i in 0..<n {
    for c in 0..<color_ch {
      let v = data_int[i * ch + c]
      if v < min_val {
        min_val = v
      }
      if v > max_val {
        max_val = v
      }
    }
  }
  let range = max_val - min_val
  if range == 0 {
    return img
  }
  // 应用线性拉伸
  let out : Array[Byte] = Array::make(n * ch, b'\x00')
  for i in 0..<n {
    for c in 0..<color_ch {
      let v = data_int[i * ch + c]
      let scaled = (v - min_val) * 255 / range
      out[i * ch + c] = scaled.clamp(min=0, max=255).to_byte()
    }
    if ch == 4 {
      out[i * ch + 3] = img.data[i * ch + 3]
    }
  }
  @types.Image::{
    width: img.width,
    height: img.height,
    channels: ch,
    data: Bytes::from_array(out),
  }
}

///|
/// 直方图比较方法
pub(all) enum HistCompareMethod {
  Correlation // 相关性
  ChiSquare // 卡方
  Bhattacharyya // 巴氏距离
}

///|
/// 比较两个直方图的相似度。返回值越大表示越相似(Correlation/ChiSquare),
/// 或距离越小越相似(Bhattacharyya)。
pub fn compare_hist(
  hist1 : Array[Int],
  hist2 : Array[Int],
  compare_method : HistCompareMethod,
) -> Float raise @types.LoadError {
  // 参数验证
  if hist1.length() == 0 {
    raise @types.LoadError::DecodeFailed(
      "compare_hist: hist1数组不能为空",
    )
  }
  if hist2.length() == 0 {
    raise @types.LoadError::DecodeFailed(
      "compare_hist: hist2数组不能为空",
    )
  }
  if hist1.length() != hist2.length() {
    raise @types.LoadError::DecodeFailed(
      "compare_hist: hist1和hist2长度必须相同",
    )
  }

  let n = hist1.length()
  match compare_method {
    Correlation => {
      let mut sum1 = 0.0F
      let mut sum2 = 0.0F
      let mut sum12 = 0.0F
      let mut sum1_sq = 0.0F
      let mut sum2_sq = 0.0F
      for i in 0..<n {
        let h1 = Float::from_int(hist1[i])
        let h2 = Float::from_int(hist2[i])
        sum1 += h1
        sum2 += h2
        sum12 += h1 * h2
        sum1_sq += h1 * h1
        sum2_sq += h2 * h2
      }
      let nf = Float::from_int(n)
      let num = nf * sum12 - sum1 * sum2
      let den = ((nf * sum1_sq - sum1 * sum1) * (nf * sum2_sq - sum2 * sum2)).sqrt()
      if den == 0.0F {
        0.0F
      } else {
        num / den
      }
    }
    ChiSquare => {
      let mut chi = 0.0F
      for i in 0..<n {
        let h1 = Float::from_int(hist1[i])
        let h2 = Float::from_int(hist2[i])
        let diff = h1 - h2
        let sum = h1 + h2
        if sum > 0.0F {
          chi += diff * diff / sum
        }
      }
      chi
    }
    Bhattacharyya => {
      let mut bc = 0.0F
      let mut s1 = 0.0F
      let mut s2 = 0.0F
      for i in 0..<n {
        let h1 = Float::from_int(hist1[i])
        let h2 = Float::from_int(hist2[i])
        bc += (h1 * h2).sqrt()
        s1 += h1
        s2 += h2
      }
      if s1 > 0.0F && s2 > 0.0F {
        bc / (s1 * s2).sqrt()
      } else {
        0.0F
      }
    }
  }
}

///|
/// 直方图匹配(规定化)。将输入图像的直方图匹配到参考图像的直方图,
/// 通过构建 LUT 实现灰度值映射。彩色图像对每个通道独立匹配。
pub fn histogram_matching(
  img : @types.Image,
  reference : @types.Image,
) -> @types.Image raise @types.LoadError {
  // 输入验证
  if img.width <= 0 || img.height <= 0 {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 图像尺寸必须大于0",
    )
  }
  if img.channels < 1 || img.channels > 4 {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 通道数必须在1-4之间",
    )
  }
  let expected_size = img.width * img.height * img.channels
  if img.data.length() != expected_size {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 数据大小不匹配,期望 " +
      expected_size.to_string() +
      ",实际 " +
      img.data.length().to_string(),
    )
  }
  if reference.width <= 0 || reference.height <= 0 {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 参考图像尺寸必须大于0",
    )
  }
  if reference.channels < 1 || reference.channels > 4 {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 参考图像通道数必须在1-4之间",
    )
  }
  let ref_expected_size = reference.width *
    reference.height *
    reference.channels
  if reference.data.length() != ref_expected_size {
    raise @types.LoadError::DecodeFailed(
      "histogram_matching: 参考图像数据大小不匹配,期望 " +
      ref_expected_size.to_string() +
      ",实际 " +
      reference.data.length().to_string(),
    )
  }

  let ch = img.channels
  let color_ch = if ch >= 3 { 3 } else { ch }
  let n = img.width * img.height
  let out : Array[Byte] = Array::make(n * ch, b'\x00')
  for c in 0..<color_ch {
    let hist_in : Array[Int] = Array::make(256, 0)
    let hist_ref : Array[Int] = Array::make(256, 0)
    for i in 0..<n {
      hist_in[img.data[i * ch + c].to_int()] += 1
    }
    let n_ref = reference.width * reference.height
    let ref_ch = reference.channels
    let ref_color_ch = if ref_ch >= 3 { 3 } else { ref_ch }
    if c < ref_color_ch {
      for i in 0..<n_ref {
        hist_ref[reference.data[i * ref_ch + c].to_int()] += 1
      }
    }
    let cdf_in : Array[Float] = Array::make(256, 0.0F)
    let cdf_ref : Array[Float] = Array::make(256, 0.0F)
    let mut cum_in = 0
    let mut cum_ref = 0
    for i in 0..<256 {
      cum_in += hist_in[i]
      cdf_in[i] = Float::from_int(cum_in) / Float::from_int(n)
      cum_ref += hist_ref[i]
      cdf_ref[i] = Float::from_int(cum_ref) / Float::from_int(n_ref)
    }
    let lut : Array[Byte] = Array::make(256, b'\x00')
    for i in 0..<256 {
      let target = cdf_in[i]
      let mut best_j = 0
      let mut best_diff = 1.0F
      for j in 0..<256 {
        let diff = (cdf_ref[j] - target).abs()
        if diff < best_diff {
          best_diff = diff
          best_j = j
        }
      }
      lut[i] = best_j.to_byte()
    }
    for i in 0..<n {
      out[i * ch + c] = lut[img.data[i * ch + c].to_int()]
    }
  }
  if ch == 4 {
    for i in 0..<n {
      out[i * ch + 3] = img.data[i * ch + 3]
    }
  }
  @types.Image::{
    width: img.width,
    height: img.height,
    channels: ch,
    data: Bytes::from_array(out),
  }
}