扫描质量评估测试通过
  亮度: 209.1
  对比度: 97.9678008327226
  质量评分: 100
文档预处理测试通过
  输出通道: 1
版面分析测试通过
  检测到文本块数: 4
    块 0: y=50, height=20
    块 1: y=100, height=20
    块 2: y=150, height=20
    块 3: y=200, height=20
OCR 识别流水线测试通过
  文本块数: 4
  平均置信度: 0.85
  识别文本长度: 64
OCR 结果导出测试通过
  文本长度: 64
  hOCR 长度: 1032
  JSON 长度: 947
批量 OCR 测试通过
  结果数: 2
  第一页页码: 1
  第二页页码: 2
Total: 6611 uncovered line(s) in 261 file(s)

1 uncovered line(s) in cmd\bench\main.mbt:

2	fn main {
3	  @src.bench_run()	<-- UNCOVERED
4	}
 	…

55 uncovered line(s) in src\bench.mbt:

 2	fn bench_make_rgb(width : Int, height : Int) -> @types.Image {
 3	  let n = width * height	<-- UNCOVERED
 4	  let data : Array[Byte] = Array::make(n * 3, b'\x80')
 5	  for i in 0..<n {
 6	    data[i * 3] = (i * 37 % 256).to_byte()	<-- UNCOVERED
 7	    data[i * 3 + 1] = (i * 71 % 256).to_byte()
 8	    data[i * 3 + 2] = (i * 113 % 256).to_byte()
 9	  }
10	  @types.Image::{ width, height, channels: 3, data: Bytes::from_array(data) }	<-- UNCOVERED
11	}
  	…

14	fn bench_make_rgba(width : Int, height : Int) -> @types.Image {
15	  let n = width * height	<-- UNCOVERED
16	  let data : Array[Byte] = Array::make(n * 4, b'\xFF')
17	  for i in 0..<n {
18	    data[i * 4] = (i * 37 % 256).to_byte()	<-- UNCOVERED
19	    data[i * 4 + 1] = (i * 71 % 256).to_byte()
20	    data[i * 4 + 2] = (i * 113 % 256).to_byte()
21	    data[i * 4 + 3] = b'\xFF'
22	  }
23	  @types.Image::{ width, height, channels: 4, data: Bytes::from_array(data) }	<-- UNCOVERED
24	}
  	…

 27	pub fn bench_run() -> Unit {
 28	  let b = @bench.Bench()	<-- UNCOVERED
 29	  let img128 = bench_make_rgb(128, 128)
 30	  let img256 = bench_make_rgb(256, 256)
 31	  let img_rgba128 = bench_make_rgba(128, 128)
 32	  let empty = @types.Image::{ width: 0, height: 0, channels: 0, data: b"" }
 33	
 34	  // === 编解码 ===
 35	  b.bench(name="png encode 128x128", () => {	<-- UNCOVERED
 36	    ignore(@codec.encode_png_pure(img128))
 37	  })
 38	  b.bench(name="png decode 128x128", () => {	<-- UNCOVERED
 39	    let png = @codec.encode_png_pure(img128)
 40	    ignore(@codec.decode_png_pure(png) catch { _ => empty })
 41	  })
 42	  b.bench(name="bmp encode 128x128", () => {	<-- UNCOVERED
 43	    ignore(@codec.encode_bmp_pure(img128))
 44	  })
 45	  b.bench(name="jpeg encode 128x128", () => {	<-- UNCOVERED
 46	    ignore(@codec.encode_jpeg_pure(img128) catch { _ => b"" })
 47	  })
 48	  b.bench(name="gif encode 128x128", () => {	<-- UNCOVERED
 49	    ignore(@codec.encode_gif_pure(img128) catch { _ => b"" })
 50	  })
 51	  b.bench(name="pnm encode 128x128", () => {	<-- UNCOVERED
 52	    ignore(@codec.encode_pnm_pure(img128))
 53	  })
 54	  b.bench(name="qoi encode 128x128", () => {	<-- UNCOVERED
 55	    ignore(@codec.encode_qoi_pure(img_rgba128) catch { _ => b"" })
 56	  })
 57	  b.bench(name="qoi decode 128x128", () => {	<-- UNCOVERED
 58	    let qoi = @codec.encode_qoi_pure(img_rgba128) catch { _ => b"" }
 59	    ignore(@codec.decode_qoi_pure(qoi) catch { _ => empty })
 60	  })
 61	  b.bench(name="webp lossy encode 128x128", () => {	<-- UNCOVERED
 62	    ignore(@codec.encode_webp_lossy_pure(img_rgba128) catch { _ => b"" })
 63	  })
 64	
 65	  // === 缩放 ===
 66	  b.bench(name="resize 256->128", () => {	<-- UNCOVERED
 67	    ignore(@pure_util.resize_pure(img256, 128, 128) catch { _ => img128 })
 68	  })
 69	
 70	  // === 滤波 ===
 71	  b.bench(name="gaussian_blur 128x128", () => {	<-- UNCOVERED
 72	    ignore(@filter.gaussian_blur(img128, 5, 1.0F) catch { _ => empty })
 73	  })
 74	  b.bench(name="box_blur 128x128", () => {	<-- UNCOVERED
 75	    ignore(@filter.box_blur(img128, 5) catch { _ => empty })
 76	  })
 77	  b.bench(name="sharpen 128x128", () => {	<-- UNCOVERED
 78	    ignore(@filter.sharpen(img128, 1.0F) catch { _ => empty })
 79	  })
 80	  b.bench(name="bilateral_filter 128x128", () => {	<-- UNCOVERED
 81	    ignore(
 82	      @filter.bilateral_filter(img128, 3, 2.0F, 30.0F) catch {
 83	        _ => empty
 84	      },
 85	    )
 86	  })
 87	
 88	  // === 边缘检测 ===
 89	  b.bench(name="edge_detect_sobel 128x128", () => {	<-- UNCOVERED
 90	    ignore(@edge.edge_detect_sobel(img128) catch { _ => empty })
 91	  })
 92	  b.bench(name="canny_edge 128x128", () => {	<-- UNCOVERED
 93	    ignore(@edge.canny_edge(img128) catch { _ => empty })
 94	  })
 95	
 96	  // === 特征检测 ===
 97	  b.bench(name="harris_corners 128x128", () => {	<-- UNCOVERED
 98	    ignore(@feature.harris_corners(img128) catch { _ => [] })
 99	  })
100	
101	  // === 频域 ===
102	  b.bench(name="fft_2d 128x128", () => {	<-- UNCOVERED
103	    ignore(@frequency.fft_2d(img128) catch { _ => [] })
104	  })
105	
106	  // === 分割 ===
107	  b.bench(name="slic 128x128", () => {	<-- UNCOVERED
108	    ignore(
109	      @segment.slic(img128, 16, 10, 5) catch {
110	        _ =>
111	          @segment.SuperpixelResult::{ labels: [], centers: [], num_labels: 0 }	<-- UNCOVERED
112	      },
113	    )
114	  })
115	  b.bench(name="connected_components 128x128", () => {	<-- UNCOVERED
116	    ignore(try! @segment.connected_components(img128, 8))
117	  })
118	
119	  // === 色彩 ===
120	  b.bench(name="to_grayscale 128x128", () => {	<-- UNCOVERED
121	    ignore(@color.to_grayscale(img128) catch { _ => empty })
122	  })
123	  b.bench(name="adjust_brightness 128x128", () => {	<-- UNCOVERED
124	    ignore(@color.adjust_brightness(img128, 50) catch { _ => empty })
125	  })
126	  b.bench(name="adjust_gamma 128x128", () => {	<-- UNCOVERED
127	    ignore(@color.adjust_gamma(img128, 2.2F) catch { _ => empty })
128	  })
129	  b.bench(name="clahe 128x128", () => {	<-- UNCOVERED
130	    ignore(@color.clahe(img128, 8, 2.0F) catch { _ => empty })
131	  })
132	  b.bench(name="histogram 128x128", () => {	<-- UNCOVERED
133	    ignore(@feature.histogram(img128) catch { _ => [] })
134	  })
135	  b.bench(name="premultiply_alpha 128x128", () => {	<-- UNCOVERED
136	    ignore(@color.premultiply_alpha(img_rgba128) catch { _ => empty })
137	  })
138	
139	  // === 几何 ===
140	  b.bench(name="crop 128x128", () => {	<-- UNCOVERED
141	    ignore(@transform.crop(img128, 10, 10, 64, 64) catch { _ => img128 })
142	  })
143	  b.bench(name="rotate_90 128x128", () => ignore(@transform.rotate_90(img128)))	<-- UNCOVERED
144	  b.bench(name="rotate 45deg 128x128", () => {
145	    ignore(@transform.rotate(img128, 45.0F))
146	  })
147	  b.bench(name="flip_horizontal 128x128", () => {	<-- UNCOVERED
148	    ignore(@transform.flip_horizontal(img128))
149	  })
150	  b.bench(name="warp_affine 128x128", () => {	<-- UNCOVERED
151	    ignore(
152	      @transform.warp_affine(
153	        img128,
154	        (1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F),
155	        128,
156	        128,
157	      ),
158	    )
159	  })
160	  // === 高级算法 ===
161	  b.bench(name="sift_detect 128x128", () => {	<-- UNCOVERED
162	    ignore(@feature.sift_detect(img128) catch { _ => [] })
163	  })
164	  b.bench(name="orb_detect 128x128", () => {	<-- UNCOVERED
165	    ignore(@feature.orb_detect(img128) catch { _ => [] })
166	  })
167	  b.bench(name="template_match 128x128", () => {	<-- UNCOVERED
168	    ignore(@feature.template_match(img128, img128) catch { _ => [] })
169	  })
170	  b.bench(name="grab_cut 128x128", () => {	<-- UNCOVERED
171	    ignore(@segment.grab_cut(img128, 64, 64, 64, 64) catch { _ => img128 })
172	  })
173	  b.bench(name="watershed 128x128", () => {	<-- UNCOVERED
174	    ignore(
175	      @segment.watershed(img128, Array::make(128 * 128, 0)) catch {
176	        _ => []
177	      },
178	    )
179	  })
180	  b.bench(name="watershed_auto 128x128", () => {	<-- UNCOVERED
181	    ignore(@segment.watershed_auto(img128) catch { _ => ([], 0) })
182	  })
183	  b.bench(name="nlm_denoise 128x128", () => {	<-- UNCOVERED
184	    ignore(@filter.nlm_denoise(img128) catch { _ => img128 })
185	  })
186	  b.bench(name="inpaint 128x128", () => {	<-- UNCOVERED
187	    ignore(@filter.inpaint(img128, img128) catch { _ => img128 })
188	  })
189	  b.bench(name="seam_carve_resize 128->96", () => {	<-- UNCOVERED
190	    ignore(@transform.seam_carve_resize(img128, 96, 96) catch { _ => img128 })
191	  })
192	  b.bench(name="find_contours 128x128", () => {	<-- UNCOVERED
193	    ignore(@edge.find_contours(img128) catch { _ => [] })
194	  })
195	  b.bench(name="edge_detect_laplacian 128x128", () => {	<-- UNCOVERED
196	    ignore(@edge.edge_detect_laplacian(img128) catch { _ => empty })
197	  })
198	  b.bench(name="median_blur 128x128", () => {	<-- UNCOVERED
199	    ignore(@filter.median_blur(img128, 5) catch { _ => empty })
200	  })
201	  b.bench(name="flood_fill 128x128", () => {	<-- UNCOVERED
202	    ignore(
203	      @segment.flood_fill(img128, 64, 64, [b'\xFF', b'\xFF', b'\xFF']) catch {
204	        _ => img128
205	      },
206	    )
207	  })
208	  b.bench(name="region_growing 128x128", () => {	<-- UNCOVERED
209	    ignore(
210	      @segment.region_growing_segment(img128, [(64, 64)]) catch {
211	        _ =>
212	          (	<-- UNCOVERED
213	            @segment.SegmentLabelImage::{
214	              width: 0,
215	              height: 0,
216	              labels: [],
217	              num_regions: 0,
218	            },
219	            [],
220	          )
221	      },
222	    )
223	  })
224	  b.bench(name="dehaze 128x128", () => {	<-- UNCOVERED
225	    ignore(@color.dehaze(img128) catch { _ => img128 })
226	  })
227	
228	  println(b.dump_summaries().to_string())	<-- UNCOVERED
229	}
   	…

9 uncovered line(s) in src\lib\lib.mbt:

 18	pub fn detect_format(data : Bytes) -> @types.ImageFormat {
 19	  let len = data.length()
 20	  if len < 2 {
 21	    return @types.ImageFormat::Unknown
 22	  }
 23	  // PNG: 8字节签名
 24	  if len >= 8 &&
 25	    data[0].to_int() == 0x89 &&
 26	    data[1].to_int() == 0x50 &&
 27	    data[2].to_int() == 0x4E &&
 28	    data[3].to_int() == 0x47 &&
 29	    data[4].to_int() == 0x0D &&
 30	    data[5].to_int() == 0x0A &&
 31	    data[6].to_int() == 0x1A &&
 32	    data[7].to_int() == 0x0A {
 33	    return @types.ImageFormat::Png
 34	  }
 35	  // JPEG: SOI marker
 36	  if data[0].to_int() == 0xFF && data[1].to_int() == 0xD8 {
 37	    return @types.ImageFormat::Jpeg
 38	  }
 39	  // BMP: "BM"
 40	  if data[0].to_int() == 0x42 && data[1].to_int() == 0x4D {
 41	    return @types.ImageFormat::Bmp
 42	  }
 43	  // QOI: "qoif"
 44	  if len >= 4 &&
 45	    data[0].to_int() == 0x71 &&
 46	    data[1].to_int() == 0x6F &&
 47	    data[2].to_int() == 0x69 &&
 48	    data[3].to_int() == 0x66 {
 49	    return @types.ImageFormat::Qoi
 50	  }
 51	  // PNM: "P5"/"P6"
 52	  if data[0].to_int() == 0x50 &&
 53	    data[1].to_int() >= 0x35 &&
 54	    data[1].to_int() <= 0x36 {
 55	    return @types.ImageFormat::Pnm
 56	  }
 57	  // PSD: "8BPS"
 58	  if len >= 4 &&
 59	    data[0].to_int() == 0x38 &&
 60	    data[1].to_int() == 0x42 &&
 61	    data[2].to_int() == 0x50 &&
 62	    data[3].to_int() == 0x53 {
 63	    return @types.ImageFormat::Psd
 64	  }
 65	  // GIF: "GIF87a" or "GIF89a"
 66	  if len >= 6 &&
 67	    data[0].to_int() == 0x47 &&
 68	    data[1].to_int() == 0x49 &&
 69	    data[2].to_int() == 0x46 {
 70	    return @types.ImageFormat::Gif
 71	  }
 72	  // HDR: #?RADIANCE
 73	  if len >= 10 &&
 74	    data[0].to_int() == 0x23 &&
 75	    data[1].to_int() == 0x3F &&
 76	    data[2].to_int() == 0x52 {
 77	    return @types.ImageFormat::Hdr
 78	  }
 79	  // WebP: RIFF....WEBP
 80	  if len >= 12 &&
 81	    data[0].to_int() == 0x52 &&
 82	    data[1].to_int() == 0x49 &&
 83	    data[2].to_int() == 0x46 &&
 84	    data[3].to_int() == 0x46 &&
 85	    data[8].to_int() == 0x57 &&
 86	    data[9].to_int() == 0x45 &&
 87	    data[10].to_int() == 0x42 &&
 88	    data[11].to_int() == 0x50 {
 89	    return @types.ImageFormat::Webp
 90	  }
 91	  // ICNS: 前4字节 "icns"
 92	  if len >= 4 &&
 93	    data[0].to_int() == 0x69 &&
 94	    data[1].to_int() == 0x63 &&
 95	    data[2].to_int() == 0x6E &&
 96	    data[3].to_int() == 0x73 {
 97	    return @types.ImageFormat::Icns
 98	  }
 99	  // TIFF: 字节序标记 (II=0x4949 LE, MM=0x4D4D BE) + magic 42 (offset 2-3)
100	  if (data[0].to_int() == 0x49 && data[1].to_int() == 0x49) ||
101	    (data[0].to_int() == 0x4D && data[1].to_int() == 0x4D) {
102	    if len >= 4 &&
103	      (
104	        (data[2].to_int() == 0x2A && data[3].to_int() == 0x00) ||
105	        (data[2].to_int() == 0x00 && data[3].to_int() == 0x2A)
106	      ) {
107	      return @types.ImageFormat::Tiff
108	    }
109	  }
110	  // ICO/CUR: 00 00 XX 00 + 图像数量 (≥1) + 目录条目
111	  // XX = 01 (ICO) 或 02 (CUR)，总长度至少 22 字节（6 头 + 16 目录条目）
112	  if len >= 22 &&
113	    data[0].to_int() == 0x00 &&
114	    data[1].to_int() == 0x00 &&
115	    (data[2].to_int() == 0x01 || data[2].to_int() == 0x02) &&
116	    data[3].to_int() == 0x00 &&
117	    data[4].to_int() != 0 { // 图像数量必须非零
118	    // 读取第一个目录条目的 width/height
119	    let width = data[6].to_int()
120	    let height = data[7].to_int()
121	    // CUR: width=0 且 height=0 表示热点坐标占位
122	    if data[2].to_int() == 0x02 && width == 0 && height == 0 {
123	      return @types.ImageFormat::Cur
124	    }
125	    return @types.ImageFormat::Ico
126	  }
127	  // JPEG-XL: 裸码流 0xFF 0x0A 或 ISOBMFF 容器
128	  if len >= 2 && data[0].to_int() == 0xFF && data[1].to_int() == 0x0A {
129	    return @types.ImageFormat::JpegXl	<-- UNCOVERED
130	  }
131	  if len >= 12 &&
132	    data[0].to_int() == 0x00 &&
133	    data[1].to_int() == 0x00 &&
134	    data[2].to_int() == 0x00 &&
135	    data[3].to_int() == 0x0C &&
136	    data[4].to_int() == 0x4A &&	<-- UNCOVERED
137	    data[5].to_int() == 0x58 &&
138	    data[6].to_int() == 0x4C &&
139	    data[7].to_int() == 0x20 {
140	    return @types.ImageFormat::JpegXl
141	  }
142	  // AVIF/HEIF: ftyp box (bytes 4-7 = "ftyp")
143	  if len >= 12 &&
144	    data[4].to_int() == 0x66 &&
145	    data[5].to_int() == 0x74 &&	<-- UNCOVERED
146	    data[6].to_int() == 0x79 &&
147	    data[7].to_int() == 0x70 {
148	    let b8 = data[8].to_int()
149	    let b9 = data[9].to_int()
150	    let b10 = data[10].to_int()
151	    let b11 = data[11].to_int()
152	    // AVIF: "avif" (0x61 0x76 0x69 0x66) or "avis" (0x61 0x76 0x69 0x73)
153	    if b8 == 0x61 && b9 == 0x76 && b10 == 0x69 && (b11 == 0x66 || b11 == 0x73) {
154	      return @types.ImageFormat::Avif	<-- UNCOVERED
155	    }
156	    // HEIF: "heic", "heix", "hevc", "mif1"
157	    if (b8 == 0x68 && b9 == 0x65 && b10 == 0x69 && (b11 == 0x63 || b11 == 0x78)) ||
158	      (b8 == 0x68 && b9 == 0x65 && b10 == 0x76 && b11 == 0x63) ||
159	      (b8 == 0x6D && b9 == 0x69 && b10 == 0x66 && b11 == 0x31) {
160	      return @types.ImageFormat::Heif	<-- UNCOVERED
161	    }
162	  }
163	  @types.ImageFormat::Unknown
164	}
   	…

173	pub fn load_from_bytes_auto(
174	  data : Bytes,
175	  req_channels? : Int? = None,
176	) -> @types.Image raise @types.LoadError {
177	  let img = match detect_format(data) {
178	    @types.ImageFormat::Png => @codec.decode_png_pure(data, req_channels~)
179	    @types.ImageFormat::Jpeg => @codec.decode_jpeg_pure(data)
180	    @types.ImageFormat::Bmp => @codec.decode_bmp_pure(data)
181	    @types.ImageFormat::Qoi => @codec.decode_qoi_pure(data)
182	    @types.ImageFormat::Pnm => @codec.decode_pnm_pure(data)
183	    @types.ImageFormat::Psd => @codec.decode_psd_pure(data)
184	    @types.ImageFormat::Gif => @codec.decode_gif_pure(data)
185	    @types.ImageFormat::Webp => @codec.decode_webp_pure(data)
186	    @types.ImageFormat::Hdr =>
187	      raise @types.LoadError::DecodeFailed(
188	        "HDR 格式需要显式调用 decode_hdr_pure",
189	      )
190	    @types.ImageFormat::Tga => @codec.decode_tga_pure(data)	<-- UNCOVERED
191	    @types.ImageFormat::Tiff => @codec.decode_tiff_pure(data)
192	    @types.ImageFormat::Ico => @codec.decode_ico_pure(data)
193	    @types.ImageFormat::Cur => @codec.decode_cur_pure(data)	<-- UNCOVERED
194	    @types.ImageFormat::Icns => @codec.decode_icns_pure(data)
195	    @types.ImageFormat::Apng =>
196	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
197	        "APNG 格式需要显式调用 decode_apng_pure",
198	      )
199	    @types.ImageFormat::Avif => @codec.avif_decode(data)	<-- UNCOVERED
200	    @types.ImageFormat::Heif => @codec.heif_decode(data)
201	    @types.ImageFormat::JpegXl => @codec.jpegxl_decode(data)
202	    @types.ImageFormat::Unknown =>
203	      raise @types.LoadError::DecodeFailed(
204	        "不支持或无法识别的格式（TGA 无 magic bytes，需显式调用 decode_tga_pure）",
205	      )
206	  }
207	  match req_channels {
208	    Some(n) => @color.convert_channels_pure(img, n)
209	    None => img
210	  }
211	}
   	…

3 uncovered line(s) in src\meta\exif_read.mbt:

 74	pub fn read_exif_from_bytes(data : Bytes) -> ExifInfo? {
 75	  let len = data.length()
 76	  if len < 4 || data[0].to_int() != 0xFF || data[1].to_int() != 0xD8 {
 77	    return None
 78	  }
 79	  // 查找 APP1 marker (0xFF 0xE1)
 80	  let mut pos = 2
 81	  let mut exif_start = -1
 82	  let mut exif_len = 0
 83	  while pos < len - 1 {
 84	    if data[pos].to_int() == 0xFF && data[pos + 1].to_int() == 0xE1 {
 85	      exif_len = read_u16_be(data, pos + 2) - 2
 86	      exif_start = pos + 4
 87	      break
 88	    }
 89	    if data[pos].to_int() == 0xFF {
 90	      let marker = data[pos + 1].to_int()
 91	      if marker == 0xDA || marker == 0xD9 {
 92	        break
 93	      }
 94	      let seg_len = read_u16_be(data, pos + 2)
 95	      pos += 2 + seg_len
 96	    } else {
 97	      pos += 1	<-- UNCOVERED
 98	    }
 99	  }
100	  if exif_start < 0 || exif_start + exif_len > len {
101	    return None
102	  }
103	  // 检查 "Exif\0\0"
104	  if exif_len < 6 ||
105	    data[exif_start].to_int() != 0x45 ||
106	    data[exif_start + 1].to_int() != 0x78 ||
107	    data[exif_start + 2].to_int() != 0x69 ||
108	    data[exif_start + 3].to_int() != 0x66 {
109	    return None
110	  }
111	  let tiff_start = exif_start + 6
112	  if tiff_start + 8 > len {
113	    return None	<-- UNCOVERED
114	  }
115	  // 读取字节序
116	  let little = if data[tiff_start].to_int() == 0x49 &&
117	    data[tiff_start + 1].to_int() == 0x49 {
118	    true
119	  } else if data[tiff_start].to_int() == 0x4D &&
120	    data[tiff_start + 1].to_int() == 0x4D {
121	    false
122	  } else {
123	    return None
124	  }
125	  // IFD0 偏移
126	  let ifd0_offset = if little {
127	    read_u32_le(data, tiff_start + 4)
128	  } else {
129	    read_u32_be(data, tiff_start + 4)
130	  }
131	  let ifd0_pos = tiff_start + ifd0_offset
132	  if ifd0_pos + 2 > len {
133	    return None	<-- UNCOVERED
134	  }
135	  let entry_count = if little {
136	    read_u16_le(data, ifd0_pos)
137	  } else {
138	    read_u16_be(data, ifd0_pos)
139	  }
140	  // 默认值
141	  let mut make = ""
142	  let mut model = ""
143	  let mut orientation = 1
144	  let mut date_time = ""
145	  // 解析 IFD 条目
146	  for i in 0..<entry_count {
147	    let entry_pos = ifd0_pos + 2 + i * 12
148	    if entry_pos + 12 > len {
149	      break
150	    }
151	    let tag = if little {
152	      read_u16_le(data, entry_pos)
153	    } else {
154	      read_u16_be(data, entry_pos)
155	    }
156	    let count = if little {
157	      read_u32_le(data, entry_pos + 4)
158	    } else {
159	      read_u32_be(data, entry_pos + 4)
160	    }
161	    let entry_offset = entry_pos - tiff_start
162	    match tag {
163	      0x010F =>
164	        make = read_exif_string(data, tiff_start, entry_offset, count, little)
165	      0x0110 =>
166	        model = read_exif_string(data, tiff_start, entry_offset, count, little)
167	      0x0112 =>
168	        orientation = if little {
169	          read_u16_le(data, tiff_start + entry_offset + 8)
170	        } else {
171	          read_u16_be(data, tiff_start + entry_offset + 8)
172	        }
173	      0x0132 =>
174	        date_time = read_exif_string(
175	          data, tiff_start, entry_offset, count, little,
176	        )
177	      _ => ()
178	    }
179	  }
180	  Some(ExifInfo::{ make, model, orientation, date_time })
181	}
   	…

2 uncovered line(s) in src\meta\exif_write.mbt:

152	pub fn write_exif_to_bytes(
153	  info : ExifInfo,
154	  jpeg_data : Bytes,
155	) -> Bytes raise @types.LoadError {
156	  let len = jpeg_data.length()
157	  if len < 2 || jpeg_data[0].to_int() != 0xFF || jpeg_data[1].to_int() != 0xD8 {
158	    raise @types.LoadError::DecodeFailed("Not a valid JPEG: missing SOI marker")
159	  }
160	  let exif_data = create_exif_segment(info)
161	  let exif_len = exif_data.length()
162	  let app1_size = exif_len + 2
163	  let mut pos = 2
164	  let mut existing_app1_end = -1
165	  while pos < len - 1 {
166	    if jpeg_data[pos].to_int() == 0xFF {
167	      let marker = jpeg_data[pos + 1].to_int()
168	      if marker == 0xE1 {
169	        let seg_len = jpeg_data[pos + 2].to_int() * 256 +
170	          jpeg_data[pos + 3].to_int()
171	        existing_app1_end = pos + 2 + seg_len
172	        break
173	      }
174	      if marker == 0xDA || marker == 0xD9 {
175	        break
176	      }
177	      if marker == 0x00 || (marker >= 0xD0 && marker <= 0xD7) {
178	        pos += 2	<-- UNCOVERED
179	      } else {
180	        let seg_len = jpeg_data[pos + 2].to_int() * 256 +
181	          jpeg_data[pos + 3].to_int()
182	        pos += 2 + seg_len
183	      }
184	    } else {
185	      pos += 1	<-- UNCOVERED
186	    }
187	  }
188	  let result : Array[Byte] = []
189	  result.push(b'\xFF')
190	  result.push(b'\xD8')
191	  result.push(b'\xFF')
192	  result.push(b'\xE1')
193	  write_u16_be(result, app1_size)
194	  for i in 0..<exif_len {
195	    result.push(exif_data[i])
196	  }
197	  let rest_start = if existing_app1_end >= 0 { existing_app1_end } else { 2 }
198	  for i in rest_start..<len {
199	    result.push(jpeg_data[i])
200	  }
201	  Bytes::from_array(result)
202	}
   	…

18 uncovered line(s) in src\process\advanced_imaging\advanced_imaging.mbt:

149	pub fn quantum_ghost_imaging(
150	  num_patterns : Int,
151	  width : Int,
152	  height : Int,
153	  algorithm : QuantumImagingAlgorithm,
154	) -> @types.Image {
155	  ignore(algorithm)
156	  let total = width * height
157	  let out : Array[Byte] = []
158	  let accumulator : Array[Double] = []
159	  for _ in 0..<total {
160	    accumulator.push(0.0)
161	  }
162	
163	  // 简化的鬼成像：随机模式与桶信号的关联
164	  let mut state = 42
165	  for _ in 0..<num_patterns {
166	    // 生成随机照明模式
167	    let pattern : Array[Bool] = []
168	    let mut bucket_signal = 0.0
169	    for _ in 0..<total {
170	      state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
171	      let is_on = state % 2 == 0
172	      pattern.push(is_on)
173	      if is_on {
174	        bucket_signal += 1.0
175	      }
176	    }
177	
178	    // 关联计算
179	    for i in 0..<total {
180	      if pattern[i] {
181	        accumulator[i] += bucket_signal
182	      }
183	    }
184	  }
185	
186	  // 归一化
187	  let mut max_val = 0.0
188	  for i in 0..<total {
189	    if accumulator[i] > max_val {
190	      max_val = accumulator[i]
191	    }
192	  }
193	
194	  for i in 0..<total {
195	    let normalized = if max_val > 0.0 {
196	      accumulator[i] / max_val * 255.0
197	    } else {
198	      0.0	<-- UNCOVERED
199	    }
200	    out.push(normalized.to_int().to_byte())
201	  }
202	
203	  @types.Image::{ width, height, channels: 1, data: Bytes::from_array(out) }
204	}
   	…

208	pub fn infrared_pseudocolor(
209	  infrared_image : @types.Image,
210	  ir_type : InfraredImagingType,
211	) -> @types.Image {
212	  ignore(ir_type)
213	  let w = infrared_image.width
214	  let h = infrared_image.height
215	  let total = w * h
216	  let out : Array[Byte] = []
217	
218	  for i in 0..<total {
219	    let gray = infrared_image.data[i].to_double() / 255.0
220	
221	    // 铁彩虹伪彩色映射
222	    let mut r = 0.0
223	    let mut g = 0.0
224	    let mut b = 0.0
225	
226	    if gray < 0.25 {
227	      r = 0.0
228	      g = 0.0
229	      b = gray * 4.0 * 255.0
230	    } else if gray < 0.5 {	<-- UNCOVERED
231	      let t = (gray - 0.25) * 4.0
232	      r = 0.0
233	      g = t * 255.0
234	      b = 255.0 - t * 255.0
235	    } else if gray < 0.75 {	<-- UNCOVERED
236	      let t = (gray - 0.5) * 4.0
237	      r = t * 255.0
238	      g = 255.0
239	      b = 0.0
240	    } else {
241	      let t = (gray - 0.75) * 4.0	<-- UNCOVERED
242	      r = 255.0
243	      g = 255.0 - t * 255.0
244	      b = t * 128.0
245	    }
246	
247	    out.push(r.to_int().to_byte())
248	    out.push(g.to_int().to_byte())
249	    out.push(b.to_int().to_byte())
250	  }
251	
252	  @types.Image::{
253	    width: w,
254	    height: h,
255	    channels: 3,
256	    data: Bytes::from_array(out),
257	  }
258	}
   	…

319	pub fn analyze_polarization(
320	  images_0deg : @types.Image,
321	  images_45deg : @types.Image,
322	  images_90deg : @types.Image,
323	  images_135deg : @types.Image,
324	) -> PolarizationData {
325	  let w = images_0deg.width
326	  let h = images_0deg.height
327	  let total = w * h
328	  let intensity_out : Array[Byte] = []
329	  let dop_out : Array[Byte] = []
330	  let aop_out : Array[Byte] = []
331	
332	  for i in 0..<total {
333	    let i0 = images_0deg.data[i].to_double()
334	    let i45 = images_45deg.data[i].to_double()
335	    let i90 = images_90deg.data[i].to_double()
336	    let i135 = images_135deg.data[i].to_double()
337	
338	    // Stokes参数
339	    let s0 = (i0 + i90) / 2.0
340	    let s1 = i0 - i90
341	    let s2 = i45 - i135
342	
343	    // 偏振度
344	    let dop = if s0 > 0.0 {
345	      (s1 * s1 + s2 * s2).sqrt() / s0 * 255.0
346	    } else {
347	      0.0
348	    }
349	
350	    // 偏振角
351	    let aop = if s1 != 0.0 {
352	      (0.5 * atan_approx(s2 / s1) + 3.14159 / 2.0) / 3.14159 * 255.0	<-- UNCOVERED
353	    } else {
354	      0.0
355	    }
356	
357	    let intensity_clamped = if s0 > 255.0 { 255.0 } else { s0 }
358	    let dop_clamped = if dop > 255.0 { 255.0 } else { dop }
359	    let aop_clamped = if aop > 255.0 { 255.0 } else { aop }
360	
361	    intensity_out.push(intensity_clamped.to_int().to_byte())
362	    dop_out.push(dop_clamped.to_int().to_byte())
363	    aop_out.push(aop_clamped.to_int().to_byte())
364	  }
365	
366	  let stokes : Array[@types.Image] = []
367	  stokes.push(images_0deg)
368	  stokes.push(images_45deg)
369	  stokes.push(images_90deg)
370	  stokes.push(images_135deg)
371	
372	  PolarizationData::{
373	    intensity: @types.Image::{
374	      width: w,
375	      height: h,
376	      channels: 1,
377	      data: Bytes::from_array(intensity_out),
378	    },
379	    degree_of_polarization: @types.Image::{
380	      width: w,
381	      height: h,
382	      channels: 1,
383	      data: Bytes::from_array(dop_out),
384	    },
385	    angle_of_polarization: @types.Image::{
386	      width: w,
387	      height: h,
388	      channels: 1,
389	      data: Bytes::from_array(aop_out),
390	    },
391	    stokes_parameters: stokes,
392	    num_stokes: 4,
393	  }
394	}
   	…

398	pub fn thermal_to_temperature(
399	  thermal_image : @types.Image,
400	  emissivity : Double,
401	  min_temp : Double,
402	  max_temp : Double,
403	) -> ThermalData {
404	  let w = thermal_image.width
405	  let h = thermal_image.height
406	  let total = w * h
407	  let temp_out : Array[Byte] = []
408	
409	  let mut min_t = 1000.0
410	  let mut max_t = -1000.0
411	  let mut sum_t = 0.0
412	
413	  for i in 0..<total {
414	    let gray = thermal_image.data[i].to_double() / 255.0
415	    let temp = min_temp + gray * (max_temp - min_temp)
416	    let corrected = temp / emissivity
417	
418	    if corrected < min_t {
419	      min_t = corrected
420	    }
421	    if corrected > max_t {
422	      max_t = corrected
423	    }
424	    sum_t += corrected
425	
426	    let normalized = (corrected - min_temp) / (max_temp - min_temp) * 255.0
427	    let clamped = if normalized < 0.0 {
428	      0.0	<-- UNCOVERED
429	    } else if normalized > 255.0 {
430	      255.0	<-- UNCOVERED
431	    } else {
432	      normalized
433	    }
434	    temp_out.push(clamped.to_int().to_byte())
435	  }
436	
437	  ThermalData::{
438	    temperature_map: @types.Image::{
439	      width: w,
440	      height: h,
441	      channels: 1,
442	      data: Bytes::from_array(temp_out),
443	    },
444	    min_temperature: min_t,
445	    max_temperature: max_t,
446	    average_temperature: sum_t / total.to_double(),
447	    emissivity,
448	    units: "Celsius",
449	  }
450	}
   	…

454	pub fn xray_enhance(xray_image : @types.Image, energy_kev : Double) -> XRayData {
455	  let w = xray_image.width
456	  let h = xray_image.height
457	  let total = w * h
458	  let out : Array[Byte] = []
459	
460	  // 简化的X射线增强：对比度增强+噪声抑制
461	  let mut sum = 0.0
462	  let mut sum_sq = 0.0
463	  for i in 0..<total {
464	    let v = xray_image.data[i].to_double()
465	    sum += v
466	    sum_sq += v * v
467	  }
468	  let mean = sum / total.to_double()
469	  let variance = sum_sq / total.to_double() - mean * mean
470	  let std_dev = if variance > 0.0 { variance.sqrt() } else { 1.0 }
471	
472	  for i in 0..<total {
473	    let v = xray_image.data[i].to_double()
474	    // 标准化+对比度拉伸
475	    let normalized = (v - mean) / std_dev
476	    let enhanced = normalized * 40.0 + 128.0
477	    let clamped = if enhanced < 0.0 {
478	      0.0	<-- UNCOVERED
479	    } else if enhanced > 255.0 {
480	      255.0	<-- UNCOVERED
481	    } else {
482	      enhanced
483	    }
484	    out.push(clamped.to_int().to_byte())
485	  }
486	
487	  XRayData::{
488	    projection: @types.Image::{
489	      width: w,
490	      height: h,
491	      channels: 1,
492	      data: Bytes::from_array(out),
493	    },
494	    energy_kev,
495	    dose: 0.1,
496	    contrast: std_dev / mean,
497	    noise_level: std_dev * 0.1,
498	  }
499	}
   	…

503	pub fn ultrasound_synthesize(
504	  width : Int,
505	  height : Int,
506	  frequency_mhz : Double,
507	  depth_cm : Double,
508	) -> UltrasoundData {
509	  let _ = width * height
510	  let b_out : Array[Byte] = []
511	  let doppler_out : Array[Byte] = []
512	
513	  let mut state = 42
514	  for y in 0..<height {
515	    for x in 0..<width {
516	      state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
517	      let noise = (state % 1000).to_double() / 1000.0
518	
519	      // 简化的B模式：扇形扫描+斑点噪声
520	      let center_x = width.to_double() / 2.0
521	      let dx = (x.to_double() - center_x) / (width.to_double() / 2.0)
522	      let dy = y.to_double() / height.to_double()
523	      let distance = (dx * dx + dy * dy).sqrt()
524	
525	      let attenuation = 1.0 - dy * 0.5
526	      let speckle = noise * 0.3
527	      let structure = if distance < 0.3 { 150.0 } else { 50.0 }
528	      let b_value = (structure + speckle * 100.0) * attenuation
529	      let b_clamped = if b_value < 0.0 {
530	        0.0	<-- UNCOVERED
531	      } else if b_value > 255.0 {
532	        255.0	<-- UNCOVERED
533	      } else {
534	        b_value
535	      }
536	      b_out.push(b_clamped.to_int().to_byte())
537	
538	      // 简化的多普勒：血流信号
539	      let doppler_value = if distance < 0.2 && dy > 0.3 {
540	        noise * 255.0	<-- UNCOVERED
541	      } else {
542	        0.0
543	      }
544	      doppler_out.push(doppler_value.to_int().to_byte())
545	    }
546	  }
547	
548	  UltrasoundData::{
549	    b_mode: @types.Image::{
550	      width,
551	      height,
552	      channels: 1,
553	      data: Bytes::from_array(b_out),
554	    },
555	    doppler: @types.Image::{
556	      width,
557	      height,
558	      channels: 1,
559	      data: Bytes::from_array(doppler_out),
560	    },
561	    frequency_mhz,
562	    depth_cm,
563	    gain: 50.0,
564	    frame_rate: 30.0,
565	  }
566	}
   	…

570	pub fn mri_synthesize(
571	  width : Int,
572	  height : Int,
573	  field_strength_t : Double,
574	) -> MRIData {
575	  let _ = width * height
576	  let t1_out : Array[Byte] = []
577	  let t2_out : Array[Byte] = []
578	  let flair_out : Array[Byte] = []
579	
580	  let center_x = width.to_double() / 2.0
581	  let center_y = height.to_double() / 2.0
582	  let radius = width.to_double() * 0.35
583	
584	  for y in 0..<height {
585	    for x in 0..<width {
586	      let dx = x.to_double() - center_x
587	      let dy = y.to_double() - center_y
588	      let dist = (dx * dx + dy * dy).sqrt()
589	
590	      // 简化的脑部MRI
591	      let mut t1_value = 0.0
592	      let mut t2_value = 0.0
593	      let mut flair_value = 0.0
594	
595	      if dist < radius * 0.3 {
596	        // 脑室
597	        t1_value = 30.0
598	        t2_value = 200.0
599	        flair_value = 30.0
600	      } else if dist < radius * 0.6 {
601	        // 灰质
602	        t1_value = 100.0	<-- UNCOVERED
603	        t2_value = 150.0
604	        flair_value = 120.0
605	      } else if dist < radius {
606	        // 白质
607	        t1_value = 180.0
608	        t2_value = 100.0
609	        flair_value = 160.0
610	      } else {
611	        // 背景
612	        t1_value = 10.0
613	        t2_value = 10.0
614	        flair_value = 10.0
615	      }
616	
617	      t1_out.push(t1_value.to_int().to_byte())
618	      t2_out.push(t2_value.to_int().to_byte())
619	      flair_out.push(flair_value.to_int().to_byte())
620	    }
621	  }
622	
623	  MRIData::{
624	    t1_weighted: @types.Image::{
625	      width,
626	      height,
627	      channels: 1,
628	      data: Bytes::from_array(t1_out),
629	    },
630	    t2_weighted: @types.Image::{
631	      width,
632	      height,
633	      channels: 1,
634	      data: Bytes::from_array(t2_out),
635	    },
636	    flair: @types.Image::{
637	      width,
638	      height,
639	      channels: 1,
640	      data: Bytes::from_array(flair_out),
641	    },
642	    tr_ms: 500.0,
643	    te_ms: 14.0,
644	    slice_thickness_mm: 5.0,
645	    field_strength_t,
646	  }
647	}
   	…

651	pub fn pet_synthesize(
652	  width : Int,
653	  height : Int,
654	  radiotracer : String,
655	) -> PETData {
656	  let _ = width * height
657	  let out : Array[Byte] = []
658	
659	  let center_x = width.to_double() / 2.0
660	  let center_y = height.to_double() / 2.0
661	
662	  let mut state = 42
663	  for y in 0..<height {
664	    for x in 0..<width {
665	      state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
666	      let noise = (state % 1000).to_double() / 1000.0
667	
668	      let dx = x.to_double() - center_x
669	      let dy = y.to_double() - center_y
670	      let dist = (dx * dx + dy * dy).sqrt()
671	
672	      // 简化的PET：高摄取区域+泊松噪声
673	      let uptake = if dist < 50.0 {
674	        200.0
675	      } else if dist < 100.0 {	<-- UNCOVERED
676	        100.0
677	      } else {
678	        20.0	<-- UNCOVERED
679	      }
680	
681	      let value = uptake * (0.8 + noise * 0.4)
682	      let clamped = if value > 255.0 { 255.0 } else { value }
683	      out.push(clamped.to_int().to_byte())
684	    }
685	  }
686	
687	  PETData::{
688	    activity_map: @types.Image::{
689	      width,
690	      height,
691	      channels: 1,
692	      data: Bytes::from_array(out),
693	    },
694	    radiotracer,
695	    injected_dose_mbq: 370.0,
696	    uptake_time_min: 60.0,
697	    reconstruction_algorithm: "OSEM",
698	    suv_scale: 1.0,
699	  }
700	}
   	…

704	pub fn advanced_imaging_report_to_string(
705	  modality : ImagingModality,
706	  image : @types.Image,
707	  config : AdvancedImagingConfig,
708	) -> String {
709	  let modality_name = match modality {
710	    ImagingModality::Quantum => "量子成像"	<-- UNCOVERED
711	    ImagingModality::Terahertz => "太赫兹成像"
712	    ImagingModality::Infrared => "红外成像"
713	    ImagingModality::Hyperspectral => "高光谱成像"
714	    ImagingModality::Polarization => "偏振成像"
715	    ImagingModality::Thermal => "热成像"
716	    ImagingModality::XRay => "X射线成像"
717	    ImagingModality::Ultrasound => "超声成像"
718	    ImagingModality::MRI => "MRI成像"
719	    ImagingModality::PET => "PET成像"	<-- UNCOVERED
720	    ImagingModality::CT => "CT成像"
721	    _ => "未知"
722	  }
723	
724	  let mut report = "## 高级成像报告\n\n"
725	  report = report + "### 成像信息\n\n"
726	  report = report + "| 指标 | 值 |\n|------|------|\n"
727	  report = report + "| 成像模态 | " + modality_name + " |\n"
728	  report = report +
729	    "| 图像尺寸 | " +
730	    image.width.to_string() +
731	    "x" +
732	    image.height.to_string() +
733	    " |\n"
734	  report = report + "| 通道数 | " + image.channels.to_string() + " |\n"
735	  report = report + "| 切片数 | " + config.num_slices.to_string() + " |\n"
736	  report = report +
737	    "| 校准因子 | " +
738	    config.calibration_factor.to_string() +
739	    " |\n"
740	  report = report +
741	    "| 噪声抑制 | " +
742	    (if config.use_noise_reduction { "开启" } else { "关闭" }) +
743	    " |\n"
744	  report = report +
745	    "| 图像增强 | " +
746	    (if config.use_enhancement { "开启" } else { "关闭" }) +
747	    " |\n"
748	
749	  report
750	}
   	…

755	fn atan_approx(x : Double) -> Double {
756	  // 简化的反正切近似
757	  x - x * x * x / 3.0 + x * x * x * x * x / 5.0	<-- UNCOVERED
758	}
   	…

8 uncovered line(s) in src\process\agriculture_vision_advanced\agriculture_vision_advanced.mbt:

397	pub fn predict_yield(
398	  crop_type : CropType,
399	  area_hectares : Double,
400	  growth_stage : String,
401	  health_index : Double,
402	  config : AgricultureVisionConfig,
403	) -> YieldPredictionResult {
404	  ignore(config)
405	  // 简化的产量预测
406	  let base_yields = [5.0, 6.0, 8.0, 3.0, 2.5, 40.0, 20.0, 30.0, 15.0, 5.0]
407	  let crop_idx = match crop_type {
408	    CropType::Wheat => 0
409	    CropType::Rice => 1	<-- UNCOVERED
410	    CropType::Corn => 2
411	    CropType::Soybean => 3	<-- UNCOVERED
412	    CropType::Cotton => 4
413	    CropType::Tomato => 5
414	    CropType::Potato => 6
415	    CropType::Apple => 7
416	    CropType::Grape => 8
417	    _ => 9
418	  }
419	
420	  let base_yield = base_yields[crop_idx]
421	  let predicted_yield = base_yield * area_hectares * health_index
422	
423	  let factors : Array[String] = []
424	  factors.push("weather")
425	  factors.push("soil_quality")
426	  factors.push("pest_pressure")
427	  factors.push("water_availability")
428	
429	  YieldPredictionResult::{
430	    crop_type,
431	    predicted_yield,
432	    yield_unit: "tons",
433	    confidence: 0.75,
434	    current_growth_stage: growth_stage,
435	    expected_harvest_date: "2026-09-15",
436	    factors_analysis: factors,
437	    num_factors: factors.length(),
438	    historical_comparison: 1.05,
439	  }
440	}
   	…

444	pub fn analyze_soil(
445	  soil_image : Array[Double],
446	  config : AgricultureVisionConfig,
447	) -> SoilAnalysisResult {
448	  ignore(soil_image)
449	  // 简化的土壤分析
450	  let mut state = 42
451	  state = (state * 11035 + 12345) % 1000000
452	  let ph = 5.5 + (state % 1000).to_double() / 1000.0 * 2.0
453	  state = (state * 11035 + 12345) % 1000000
454	  let nitrogen = (state % 1000).to_double() / 10.0
455	  state = (state * 11035 + 12345) % 1000000
456	  let phosphorus = (state % 1000).to_double() / 10.0
457	  state = (state * 11035 + 12345) % 1000000
458	  let potassium = (state % 1000).to_double() / 10.0
459	  state = (state * 11035 + 12345) % 1000000
460	  let organic_matter = (state % 1000).to_double() / 100.0
461	  state = (state * 11035 + 12345) % 1000000
462	  let moisture = (state % 1000).to_double() / 10.0
463	  state = (state * 11035 + 12345) % 1000000
464	  let temperature = 15.0 + (state % 1000).to_double() / 100.0
465	
466	  let fertility = (nitrogen + phosphorus + potassium + organic_matter * 10.0) /
467	    4.0
468	  let is_ph_optimal = ph >= config.soil_ph_optimal_min &&
469	    ph <= config.soil_ph_optimal_max
470	
471	  let recommendation = if is_ph_optimal && fertility > 50.0 {
472	    "Soil is optimal for planting"	<-- UNCOVERED
473	  } else if !is_ph_optimal {
474	    "Adjust soil pH with lime or sulfur"	<-- UNCOVERED
475	  } else {
476	    "Add organic fertilizer to improve fertility"
477	  }
478	
479	  SoilAnalysisResult::{
480	    soil_type: SoilType::Loam,
481	    ph,
482	    nitrogen,
483	    phosphorus,
484	    potassium,
485	    organic_matter,
486	    moisture,
487	    temperature,
488	    fertility_score: fertility,
489	    recommendation,
490	  }
491	}
   	…

495	pub fn agriculture_vision_report_to_string(
496	  task_type : AgricultureVisionTaskType,
497	  config : AgricultureVisionConfig,
498	  result_description : String,
499	) -> String {
500	  let mut report = "## 农业视觉高级报告\n\n"
501	
502	  report = report + "### 任务信息\n\n"
503	  report = report + "| 指标 | 值 |\n|------|------|\n"
504	  report = report +
505	    "| 任务类型 | " +
506	    (match task_type {
507	      AgricultureVisionTaskType::CropIdentification => "作物识别"	<-- UNCOVERED
508	      AgricultureVisionTaskType::PestDetection => "虫害检测"
509	      AgricultureVisionTaskType::DiseaseDetection => "病害检测"	<-- UNCOVERED
510	      AgricultureVisionTaskType::YieldPrediction => "产量预测"
511	      AgricultureVisionTaskType::SoilAnalysis => "土壤分析"	<-- UNCOVERED
512	      AgricultureVisionTaskType::WeedDetection => "杂草检测"
513	      AgricultureVisionTaskType::GrowthMonitoring => "生长监测"
514	      _ => "未知"
515	    }) +
516	    " |\n"
517	  report = report +
518	    "| 作物类型 | " +
519	    (match config.crop_type {
520	      CropType::Wheat => "小麦"
521	      CropType::Rice => "水稻"	<-- UNCOVERED
522	      CropType::Corn => "玉米"
523	      CropType::Soybean => "大豆"
524	      CropType::Cotton => "棉花"
525	      CropType::Tomato => "番茄"
526	      CropType::Potato => "土豆"
527	      CropType::Apple => "苹果"
528	      CropType::Grape => "葡萄"
529	      _ => "未知"
530	    }) +
531	    " |\n"
532	  report = report +
533	    "| 检测阈值 | " +
534	    config.detection_threshold.to_string() +
535	    " |\n"
536	  report = report +
537	    "| 病害严重度阈值 | " +
538	    config.disease_severity_threshold.to_string() +
539	    " |\n"
540	  report = report +
541	    "| 虫害侵扰阈值 | " +
542	    config.pest_infestation_threshold.to_string() +
543	    " |\n"
544	  report = report +
545	    "| 多光谱 | " +
546	    (if config.use_multispectral { "开启" } else { "关闭" }) +
547	    " |\n"
548	  report = report +
549	    "| NDVI | " +
550	    (if config.use_ndvi { "开启" } else { "关闭" }) +
551	    " |\n"
552	  report = report +
553	    "| 无人机高度 | " +
554	    config.drone_altitude.to_string() +
555	    " m |\n"
556	  report = report +
557	    "| 自动推荐 | " +
558	    (if config.auto_recommendation { "开启" } else { "关闭" }) +
559	    " |\n"
560	
561	  report = report + "\n### 结果\n\n"
562	  report = report + result_description + "\n"
563	
564	  report
565	}
   	…

6 uncovered line(s) in src\process\astronomy_imaging_advanced\astronomy_imaging_advanced.mbt:

 6	fn log_approx(x : Double) -> Double {
 7	  if x <= 0.0 {
 8	    -100.0	<-- UNCOVERED
 9	  } else {
10	    let mut result = 0.0
11	    let mut y = x
12	    while y > 1.0 {
13	      y = y / 2.71828	<-- UNCOVERED
14	      result = result + 1.0
15	    }
16	    result + (y - 1.0) / y
17	  }
18	}
  	…

398	pub fn stack_images(
399	  images : Array[Array[Double]],
400	  width : Int,
401	  height : Int,
402	  config : AstronomyImagingConfig,
403	) -> ImageStackingResult {
404	  let num_images = images.length()
405	  let stacked : Array[Double] = []
406	
407	  // 简化的中值叠加
408	  for pixel_idx in 0..<(width * height) {
409	    let values : Array[Double] = []
410	    for img in images {
411	      if pixel_idx < img.length() {
412	        values.push(img[pixel_idx])
413	      }
414	    }
415	    // 简化的中值
416	    let median_val = if values.length() > 0 {
417	      values[values.length() / 2]
418	    } else {
419	      0.0	<-- UNCOVERED
420	    }
421	    stacked.push(median_val)
422	  }
423	
424	  let noise_reduction = num_images.to_double()
425	  let total_exposure = num_images.to_double() * config.exposure_time
426	
427	  ImageStackingResult::{
428	    num_images,
429	    stacked_image: stacked,
430	    width,
431	    height,
432	    exposure_time_total: total_exposure,
433	    noise_reduction_factor: noise_reduction,
434	    dynamic_range_improvement: num_images.to_double(),
435	    stacking_method: config.stacking_method,
436	    rejected_images: 0,
437	    quality_score: 0.85,
438	  }
439	}
   	…

443	pub fn astronomy_imaging_report_to_string(
444	  task_type : AstronomyImagingTaskType,
445	  config : AstronomyImagingConfig,
446	  result_description : String,
447	) -> String {
448	  let mut report = "## 天文图像处理高级报告\n\n"
449	
450	  report = report + "### 任务信息\n\n"
451	  report = report + "| 指标 | 值 |\n|------|------|\n"
452	  report = report +
453	    "| 任务类型 | " +
454	    (match task_type {
455	      AstronomyImagingTaskType::ObjectDetection => "天体检测"	<-- UNCOVERED
456	      AstronomyImagingTaskType::GalaxyClassification => "星系分类"
457	      AstronomyImagingTaskType::Photometry => "测光"
458	      AstronomyImagingTaskType::Spectroscopy => "光谱分析"	<-- UNCOVERED
459	      AstronomyImagingTaskType::ImageStacking => "图像叠加"
460	      AstronomyImagingTaskType::CosmologicalParameterEstimation =>
461	        "宇宙学参数估计"	<-- UNCOVERED
462	      AstronomyImagingTaskType::TransitDetection => "凌星检测"
463	      _ => "未知"
464	    }) +
465	    " |\n"
466	  report = report +
467	    "| 检测阈值 | " +
468	    config.detection_threshold.to_string() +
469	    " sigma |\n"
470	  report = report +
471	    "| 测光孔径 | " +
472	    config.photometry_aperture.to_string() +
473	    " px |\n"
474	  report = report +
475	    "| 像素尺度 | " +
476	    config.pixel_scale.to_string() +
477	    " arcsec/px |\n"
478	  report = report + "| 增益 | " + config.gain.to_string() + " e-/ADU |\n"
479	  report = report +
480	    "| 读出噪声 | " +
481	    config.read_noise.to_string() +
482	    " e- |\n"
483	  report = report +
484	    "| 曝光时间 | " +
485	    config.exposure_time.to_string() +
486	    " s |\n"
487	  report = report + "| 滤镜 | " + config.filter_name + " |\n"
488	  report = report +
489	    "| 望远镜口径 | " +
490	    config.telescope_diameter.to_string() +
491	    " m |\n"
492	  report = report + "| 焦距 | " + config.focal_length.to_string() + " m |\n"
493	  report = report +
494	    "| WCS | " +
495	    (if config.use_wcs { "开启" } else { "关闭" }) +
496	    " |\n"
497	  report = report +
498	    "| 暗场校正 | " +
499	    (if config.use_dark_subtraction { "开启" } else { "关闭" }) +
500	    " |\n"
501	  report = report +
502	    "| 平场校正 | " +
503	    (if config.use_flat_correction { "开启" } else { "关闭" }) +
504	    " |\n"
505	  report = report + "| 叠加方法 | " + config.stacking_method + " |\n"
506	  report = report +
507	    "| 叠加图像数 | " +
508	    config.num_stack_images.to_string() +
509	    " |\n"
510	
511	  report = report + "\n### 结果\n\n"
512	  report = report + result_description + "\n"
513	
514	  report
515	}
   	…

10 uncovered line(s) in src\process\audio_processing\audio_processing.mbt:

334	pub fn recognize_speech(
335	  audio : AudioData,
336	  config : AudioConfig,
337	) -> SpeechRecognitionResult {
338	  // 简化的语音识别：基于音频特征生成虚拟文本
339	  let words = [
340	    "hello", "world", "this", "is", "a", "test", "of", "speech", "recognition",
341	  ]
342	  let num_words = (audio.duration_s * 2.0).to_int()
343	  let selected_words : Array[String] = []
344	  let timestamps : Array[Double] = []
345	
346	  let mut state = 42
347	  for i in 0..<num_words {
348	    state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()	<-- UNCOVERED
349	    let word_idx = state % words.length()
350	    selected_words.push(words[word_idx])
351	    timestamps.push(i.to_double() * 0.5)
352	  }
353	
354	  let mut transcript = ""
355	  for i in 0..<selected_words.length() {
356	    if i > 0 {	<-- UNCOVERED
357	      transcript = transcript + " "
358	    }
359	    transcript = transcript + selected_words[i]
360	  }
361	
362	  SpeechRecognitionResult::{
363	    audio,
364	    transcript,
365	    confidence: 0.85,
366	    word_timestamps: timestamps,
367	    words: selected_words,
368	    num_words: selected_words.length(),
369	    model_type: config.asr_model,
370	    language: config.language,
371	    wer: 0.1,
372	  }
373	}
   	…

510	pub fn detect_speech_activity(
511	  audio : AudioData,
512	  threshold : Double,
513	) -> VADResult {
514	  let segments : Array[Array[Double]] = []
515	  let mut speech_duration = 0.0
516	  let mut in_speech = false
517	  let mut segment_start = 0.0
518	
519	  let frame_duration = 512.0 / audio.sample_rate.to_double()
520	  let num_frames = audio.num_samples / 512
521	
522	  for frame_idx in 0..<num_frames {
523	    // 计算帧能量
524	    let mut energy = 0.0
525	    let start_sample = frame_idx * 512
526	    let end_sample = if start_sample + 512 < audio.num_samples {
527	      start_sample + 512
528	    } else {
529	      audio.num_samples	<-- UNCOVERED
530	    }
531	
532	    for i in start_sample..<end_sample {
533	      energy += audio.samples[i] * audio.samples[i]
534	    }
535	    energy /= (end_sample - start_sample).to_double()
536	
537	    let is_speech = energy > threshold
538	
539	    if is_speech && !in_speech {
540	      segment_start = frame_idx.to_double() * frame_duration
541	      in_speech = true
542	    } else if !is_speech && in_speech {
543	      let segment_end = frame_idx.to_double() * frame_duration	<-- UNCOVERED
544	      let segment : Array[Double] = []
545	      segment.push(segment_start)	<-- UNCOVERED
546	      segment.push(segment_end)
547	      segments.push(segment)
548	      speech_duration += segment_end - segment_start
549	      in_speech = false
550	    }
551	  }
552	
553	  if in_speech {
554	    let segment_end = num_frames.to_double() * frame_duration
555	    let segment : Array[Double] = []
556	    segment.push(segment_start)
557	    segment.push(segment_end)
558	    segments.push(segment)
559	    speech_duration += segment_end - segment_start
560	  }
561	
562	  let silence_duration = audio.duration_s - speech_duration
563	  let speech_ratio = if audio.duration_s > 0.0 {
564	    speech_duration / audio.duration_s
565	  } else {
566	    0.0	<-- UNCOVERED
567	  }
568	
569	  VADResult::{
570	    audio,
571	    speech_segments: segments,
572	    num_segments: segments.length(),
573	    speech_duration,
574	    silence_duration,
575	    speech_ratio,
576	    threshold,
577	  }
578	}
   	…

582	pub fn audio_processing_report_to_string(
583	  task_type : AudioTaskType,
584	  config : AudioConfig,
585	  audio : AudioData,
586	  result_description : String,
587	) -> String {
588	  let mut report = "## 语音处理与音频分析报告\n\n"
589	
590	  report = report + "### 音频信息\n\n"
591	  report = report + "| 指标 | 值 |\n|------|------|\n"
592	  report = report + "| 采样率 | " + audio.sample_rate.to_string() + " Hz |\n"
593	  report = report + "| 声道数 | " + audio.num_channels.to_string() + " |\n"
594	  report = report + "| 采样数 | " + audio.num_samples.to_string() + " |\n"
595	  report = report + "| 时长 | " + audio.duration_s.to_string() + " s |\n"
596	  report = report + "| 位深 | " + audio.bit_depth.to_string() + " bit |\n"
597	  report = report +
598	    "| 单声道 | " +
599	    (if audio.is_mono { "是" } else { "否" }) +
600	    " |\n"
601	
602	  report = report + "\n### 任务配置\n\n"
603	  report = report + "| 指标 | 值 |\n|------|------|\n"
604	  report = report +
605	    "| 任务类型 | " +
606	    (match task_type {
607	      AudioTaskType::SpeechRecognition => "语音识别"
608	      AudioTaskType::SpeakerIdentification => "说话人识别"	<-- UNCOVERED
609	      AudioTaskType::SpeakerVerification => "说话人验证"
610	      AudioTaskType::EmotionRecognition => "情感语音识别"
611	      AudioTaskType::AudioClassification => "音频分类"
612	      AudioTaskType::MusicInformationRetrieval => "音乐信息检索"
613	      AudioTaskType::VoiceActivityDetection => "语音活动检测"
614	      AudioTaskType::NoiseReduction => "降噪"
615	      _ => "未知"
616	    }) +
617	    " |\n"
618	  report = report +
619	    "| ASR模型 | " +
620	    (match config.asr_model {
621	      ASRModelType::GMMHMM => "GMM-HMM"	<-- UNCOVERED
622	      ASRModelType::DNNHMM => "DNN-HMM"
623	      ASRModelType::CTC => "CTC"
624	      ASRModelType::Attention => "Attention"
625	      ASRModelType::Transformer => "Transformer"
626	      ASRModelType::Conformer => "Conformer"
627	      ASRModelType::Whisper => "Whisper"
628	      ASRModelType::Wav2Vec => "Wav2Vec"	<-- UNCOVERED
629	      _ => "未知"
630	    }) +
631	    " |\n"
632	  report = report +
633	    "| 特征类型 | " +
634	    (match config.feature_type {
635	      AudioFeatureType::MFCC => "MFCC"
636	      AudioFeatureType::MelSpectrogram => "梅尔频谱图"	<-- UNCOVERED
637	      AudioFeatureType::Spectrogram => "频谱图"
638	      AudioFeatureType::Chroma => "色度特征"
639	      _ => "未知"
640	    }) +
641	    " |\n"
642	  report = report + "| 帧长度 | " + config.frame_length.to_string() + " |\n"
643	  report = report + "| 跳数 | " + config.hop_length.to_string() + " |\n"
644	  report = report + "| MFCC系数 | " + config.n_mfcc.to_string() + " |\n"
645	  report = report + "| 梅尔频带 | " + config.n_mels.to_string() + " |\n"
646	  report = report + "| 语言 | " + config.language + " |\n"
647	
648	  report = report + "\n### 结果\n\n"
649	  report = report + result_description + "\n"
650	
651	  report
652	}
   	…

6 uncovered line(s) in src\process\automl_advanced\automl_advanced.mbt:

395	pub fn automl_report_to_string(
396	  task_type : AutoMLTaskType,
397	  config : AutoMLConfig,
398	  result_description : String,
399	) -> String {
400	  let mut report = "## 自动机器学习高级报告\n\n"
401	
402	  report = report + "### 任务信息\n\n"
403	  report = report + "| 指标 | 值 |\n|------|------|\n"
404	  report = report +
405	    "| 任务类型 | " +
406	    (match task_type {
407	      AutoMLTaskType::HyperparameterOptimization => "超参数优化"
408	      AutoMLTaskType::NeuralArchitectureSearch => "神经架构搜索"	<-- UNCOVERED
409	      AutoMLTaskType::FeatureEngineering => "特征工程"
410	      AutoMLTaskType::ModelSelection => "模型选择"
411	      AutoMLTaskType::PipelineOptimization => "流水线优化"
412	      AutoMLTaskType::DataAugmentationSearch => "数据增强搜索"
413	      AutoMLTaskType::NeuralArchitectureAndHPO =>
414	        "架构与超参数联合搜索"	<-- UNCOVERED
415	      _ => "未知"
416	    }) +
417	    " |\n"
418	  report = report +
419	    "| HPO算法 | " +
420	    (match config.hpo_algorithm {
421	      HPOAlgorithmType::GridSearch => "网格搜索"	<-- UNCOVERED
422	      HPOAlgorithmType::RandomSearch => "随机搜索"
423	      HPOAlgorithmType::BayesianOptimization => "贝叶斯优化"
424	      HPOAlgorithmType::TPE => "TPE"	<-- UNCOVERED
425	      HPOAlgorithmType::SMAC => "SMAC"
426	      HPOAlgorithmType::Hyperband => "Hyperband"
427	      HPOAlgorithmType::BOHB => "BOHB"
428	      HPOAlgorithmType::ASHA => "ASHA"
429	      HPOAlgorithmType::PBT => "PBT"
430	      HPOAlgorithmType::CMAES => "CMA-ES"
431	      HPOAlgorithmType::GeneticAlgorithm => "遗传算法"
432	      _ => "未知"
433	    }) +
434	    " |\n"
435	  report = report +
436	    "| NAS算法 | " +
437	    (match config.nas_algorithm {
438	      NASAlgorithmType::ReinforcementLearning => "强化学习"	<-- UNCOVERED
439	      NASAlgorithmType::Evolutionary => "进化算法"
440	      NASAlgorithmType::Differentiable => "可微搜索"
441	      NASAlgorithmType::OneShot => "一次性搜索"
442	      NASAlgorithmType::ProxylessNAS => "ProxylessNAS"
443	      NASAlgorithmType::DARTS => "DARTS"
444	      NASAlgorithmType::ENAS => "ENAS"	<-- UNCOVERED
445	      NASAlgorithmType::NASNet => "NASNet"
446	      NASAlgorithmType::AmoebaNet => "AmoebaNet"
447	      NASAlgorithmType::PNAS => "PNAS"
448	      _ => "未知"
449	    }) +
450	    " |\n"
451	  report = report +
452	    "| 最大试验次数 | " +
453	    config.max_trials.to_string() +
454	    " |\n"
455	  report = report +
456	    "| 最大时间(ms) | " +
457	    config.max_time_ms.to_string() +
458	    " |\n"
459	  report = report + "| CV折数 | " + config.num_cv_folds.to_string() + " |\n"
460	  report = report +
461	    "| 搜索空间大小 | " +
462	    config.search_space_size.to_string() +
463	    " |\n"
464	  report = report +
465	    "| 早停 | " +
466	    (if config.use_early_stopping { "开启" } else { "关闭" }) +
467	    " |\n"
468	
469	  report = report + "\n### 结果\n\n"
470	  report = report + result_description + "\n"
471	
472	  report
473	}
   	…

6 uncovered line(s) in src\process\automl_nas\automl_nas.mbt:

272	pub fn evaluate_model(
273	  architecture : NetworkArchitecture,
274	  hyperparameters : HyperparameterConfig,
275	) -> ModelEvaluationMetrics {
276	  // 简化的模型评估：基于架构和超参数的启发式评分
277	  let base_accuracy = 0.7
278	  let architecture_bonus = if architecture.num_layers > 10 {
279	    0.05
280	  } else if architecture.num_layers > 5 {
281	    0.03
282	  } else {
283	    0.01
284	  }
285	
286	  let lr_bonus = if hyperparameters.learning_rate > 0.0001 &&
287	    hyperparameters.learning_rate < 0.01 {
288	    0.05
289	  } else {
290	    0.0	<-- UNCOVERED
291	  }
292	
293	  let dropout_bonus = if hyperparameters.dropout_rate > 0.1 &&
294	    hyperparameters.dropout_rate < 0.4 {
295	    0.03
296	  } else {
297	    0.0	<-- UNCOVERED
298	  }
299	
300	  let accuracy = base_accuracy + architecture_bonus + lr_bonus + dropout_bonus
301	  let loss = 1.0 - accuracy
302	
303	  ModelEvaluationMetrics::{
304	    accuracy,
305	    loss,
306	    precision: accuracy * 0.95,
307	    recall: accuracy * 0.9,
308	    f1_score: accuracy * 0.92,
309	    auc_roc: accuracy * 0.98,
310	    inference_time_ms: architecture.inference_time_ms,
311	    memory_usage_mb: architecture.memory_mb,
312	    model_size_mb: architecture.memory_mb,
313	    num_parameters: architecture.num_parameters,
314	    flops: architecture.flops,
315	  }
316	}
   	…

398	pub fn automl_report_to_string(
399	  result : SearchResult,
400	  config : NASConfig,
401	  metrics : ModelEvaluationMetrics,
402	) -> String {
403	  ignore(metrics)
404	  let mut report = "## 神经架构搜索与AutoML报告\n\n"
405	
406	  report = report + "### 搜索配置\n\n"
407	  report = report + "| 指标 | 值 |\n|------|------|\n"
408	  report = report +
409	    "| NAS算法 | " +
410	    (match config.algorithm {
411	      NASAlgorithm::ReinforcementLearning => "强化学习"	<-- UNCOVERED
412	      NASAlgorithm::Evolutionary => "进化算法"
413	      NASAlgorithm::GradientBased => "基于梯度"	<-- UNCOVERED
414	      NASAlgorithm::BayesianOptimization => "贝叶斯优化"
415	      NASAlgorithm::RandomSearch => "随机搜索"
416	      _ => "未知"
417	    }) +
418	    " |\n"
419	  report = report +
420	    "| 搜索空间 | " +
421	    (match config.search_space_type {
422	      SearchSpaceType::Macro => "宏观"	<-- UNCOVERED
423	      SearchSpaceType::Micro => "微观"
424	      SearchSpaceType::CellBased => "基于单元"
425	      _ => "未知"	<-- UNCOVERED
426	    }) +
427	    " |\n"
428	  report = report + "| 试验次数 | " + config.num_trials.to_string() + " |\n"
429	  report = report + "| 最大层数 | " + config.max_layers.to_string() + " |\n"
430	  report = report +
431	    "| 种群大小 | " +
432	    config.population_size.to_string() +
433	    " |\n"
434	  report = report +
435	    "| 权重共享 | " +
436	    (if config.use_weight_sharing { "开启" } else { "关闭" }) +
437	    " |\n"
438	
439	  report = report + "\n### 搜索结果\n\n"
440	  report = report + "| 指标 | 值 |\n|------|------|\n"
441	  report = report +
442	    "| 最佳精度 | " +
443	    (result.best_accuracy * 100.0).to_string() +
444	    "% |\n"
445	  report = report + "| 最佳损失 | " + result.best_loss.to_string() + " |\n"
446	  report = report +
447	    "| 完成试验 | " +
448	    result.num_trials_completed.to_string() +
449	    " |\n"
450	  report = report +
451	    "| 搜索时间 | " +
452	    result.search_time_s.to_string() +
453	    " s |\n"
454	  report = report +
455	    "| 最佳层数 | " +
456	    result.best_architecture.num_layers.to_string() +
457	    " |\n"
458	  report = report +
459	    "| 参数量 | " +
460	    result.best_architecture.num_parameters.to_string() +
461	    " |\n"
462	  report = report +
463	    "| FLOPs | " +
464	    result.best_architecture.flops.to_string() +
465	    " |\n"
466	  report = report +
467	    "| 推理时间 | " +
468	    result.best_architecture.inference_time_ms.to_string() +
469	    " ms |\n"
470	  report = report +
471	    "| 内存占用 | " +
472	    result.best_architecture.memory_mb.to_string() +
473	    " MB |\n"
474	
475	  report = report + "\n### 最佳超参数\n\n"
476	  report = report + "| 超参数 | 值 |\n|------|------|\n"
477	  report = report +
478	    "| 学习率 | " +
479	    result.best_hyperparameters.learning_rate.to_string() +
480	    " |\n"
481	  report = report +
482	    "| 批次大小 | " +
483	    result.best_hyperparameters.batch_size.to_string() +
484	    " |\n"
485	  report = report +
486	    "| 训练轮数 | " +
487	    result.best_hyperparameters.num_epochs.to_string() +
488	    " |\n"
489	  report = report +
490	    "| 优化器 | " +
491	    result.best_hyperparameters.optimizer +
492	    " |\n"
493	  report = report +
494	    "| 权重衰减 | " +
495	    result.best_hyperparameters.weight_decay.to_string() +
496	    " |\n"
497	  report = report +
498	    "| Dropout率 | " +
499	    result.best_hyperparameters.dropout_rate.to_string() +
500	    " |\n"
501	  report = report +
502	    "| 隐藏维度 | " +
503	    result.best_hyperparameters.hidden_dim.to_string() +
504	    " |\n"
505	  report = report +
506	    "| 层数 | " +
507	    result.best_hyperparameters.num_layers.to_string() +
508	    " |\n"
509	  report = report +
510	    "| 注意力头数 | " +
511	    result.best_hyperparameters.num_heads.to_string() +
512	    " |\n"
513	  report = report +
514	    "| 激活函数 | " +
515	    result.best_hyperparameters.activation +
516	    " |\n"
517	
518	  report
519	}
   	…

4 uncovered line(s) in src\process\autonomous_driving_advanced\autonomous_driving_advanced.mbt:

293	pub fn plan_path(
294	  current_position : Array[Double],
295	  target_position : Array[Double],
296	  detected_objects : Array[ADDetectedObject],
297	  config : ADConfig,
298	) -> PlannedPath {
299	  let points : Array[PathPoint] = []
300	  let num_points = 50
301	
302	  for i in 0..<num_points {
303	    let t = i.to_double() / (num_points - 1).to_double()
304	    let x = current_position[0] + (target_position[0] - current_position[0]) * t
305	    let y = current_position[1] + (target_position[1] - current_position[1]) * t
306	
307	    let point = PathPoint::{
308	      x,
309	      y,
310	      z: 0.0,
311	      heading: 0.0,
312	      curvature: 0.0,
313	      velocity: 10.0,
314	      acceleration: 0.0,
315	      timestamp: t * config.planning_horizon,
316	    }
317	    points.push(point)
318	  }
319	
320	  // 简化的安全检查
321	  let mut is_safe = true
322	  let mut risk_level = 0.1
323	
324	  for obj in detected_objects {
325	    let dist = (obj.position_3d[0] - target_position[0]) *
326	      (obj.position_3d[0] - target_position[0]) +
327	      (obj.position_3d[1] - target_position[1]) *
328	      (obj.position_3d[1] - target_position[1])
329	    if dist < config.safety_margin {
330	      is_safe = false	<-- UNCOVERED
331	      risk_level = 0.8
332	    }
333	  }
334	
335	  PlannedPath::{
336	    points,
337	    num_points,
338	    path_length: 100.0,
339	    estimated_time: 10.0,
340	    is_safe,
341	    risk_level,
342	    path_type: "lane_follow",
343	  }
344	}
   	…

350	pub fn decision_control(
351	  planned_path : PlannedPath,
352	  current_velocity : Double,
353	  detected_objects : Array[ADDetectedObject],
354	  config : ADConfig,
355	) -> ControlCommand {
356	  ignore(config)
357	  ignore(planned_path)
358	  let target_velocity = 10.0
359	  let mut throttle = 0.3
360	  let mut brake = 0.0
361	  let mut is_emergency = false
362	
363	  // 简化的速度控制
364	  if current_velocity < target_velocity {
365	    throttle = 0.5
366	    brake = 0.0
367	  } else if current_velocity > target_velocity {
368	    throttle = 0.0	<-- UNCOVERED
369	    brake = 0.3
370	  }
371	
372	  // 简化的障碍物检测
373	  for obj in detected_objects {
374	    if obj.object_type == ADObjectType::Pedestrian ||
375	      obj.object_type == ADObjectType::Car {
376	      let dist = obj.position_3d[0] * obj.position_3d[0] +
377	        obj.position_3d[1] * obj.position_3d[1]
378	      if dist < 5.0 {
379	        brake = 1.0
380	        throttle = 0.0
381	        is_emergency = true
382	      }
383	    }
384	  }
385	
386	  ControlCommand::{
387	    steering_angle: 0.0,
388	    throttle,
389	    brake,
390	    gear: "drive",
391	    target_velocity,
392	    target_acceleration: 0.0,
393	    is_emergency_brake: is_emergency,
394	    is_horn: is_emergency,
395	    is_light: true,
396	    timestamp: 0.0,
397	  }
398	}
   	…

402	pub fn autonomous_driving_report_to_string(
403	  task_type : AutonomousDrivingTaskType,
404	  config : ADConfig,
405	  result_description : String,
406	) -> String {
407	  let mut report = "## 自动驾驶高级报告\n\n"
408	
409	  report = report + "### 任务信息\n\n"
410	  report = report + "| 指标 | 值 |\n|------|------|\n"
411	  report = report +
412	    "| 任务类型 | " +
413	    (match task_type {
414	      AutonomousDrivingTaskType::EnvironmentPerception => "环境感知"	<-- UNCOVERED
415	      AutonomousDrivingTaskType::PathPlanning => "路径规划"
416	      AutonomousDrivingTaskType::DecisionControl => "决策控制"	<-- UNCOVERED
417	      AutonomousDrivingTaskType::SensorFusion => "传感器融合"
418	      AutonomousDrivingTaskType::BEVPerception => "BEV感知"
419	      AutonomousDrivingTaskType::EndToEnd => "端到端"
420	      AutonomousDrivingTaskType::Localization => "定位"
421	      _ => "未知"
422	    }) +
423	    " |\n"
424	  report = report +
425	    "| 传感器数量 | " +
426	    config.num_sensors.to_string() +
427	    " |\n"
428	  report = report +
429	    "| 检测阈值 | " +
430	    config.detection_threshold.to_string() +
431	    " |\n"
432	  report = report +
433	    "| 规划视野 | " +
434	    config.planning_horizon.to_string() +
435	    " s |\n"
436	  report = report +
437	    "| 控制频率 | " +
438	    config.control_frequency.to_string() +
439	    " Hz |\n"
440	  report = report +
441	    "| 最大速度 | " +
442	    config.max_speed.to_string() +
443	    " m/s |\n"
444	  report = report +
445	    "| 安全距离 | " +
446	    config.safety_margin.to_string() +
447	    " m |\n"
448	  report = report +
449	    "| BEV感知 | " +
450	    (if config.use_bev { "开启" } else { "关闭" }) +
451	    " |\n"
452	  report = report +
453	    "| 传感器融合 | " +
454	    (if config.use_sensor_fusion { "开启" } else { "关闭" }) +
455	    " |\n"
456	
457	  report = report + "\n### 结果\n\n"
458	  report = report + result_description + "\n"
459	
460	  report
461	}
   	…

57 uncovered line(s) in src\process\barcode\barcode_decode.mbt:

119	pub fn decode_ean13(
120	  img : @types.Image,
121	  config? : BarcodeConfig,
122	) -> BarcodeResult? {
123	  let cfg = match config {
124	    Some(c) => c
125	    None => barcode_default_config()
126	  }
127	  if !cfg.enable_ean13 {
128	    return None	<-- UNCOVERED
129	  }
130	  let w = img.width
131	  let h = img.height
132	  let data = img.data
133	  // 在多条水平扫描线上尝试解码
134	  let mut best_result : BarcodeResult? = None
135	  let mut best_confidence = 0.0
136	  let line_step = h / (cfg.scan_lines + 1)
137	  for line_idx in 1..<(cfg.scan_lines + 1) {
138	    let y = line_idx * line_step
139	    if y >= h {
140	      break	<-- UNCOVERED
141	    }
142	    // 提取扫描线的像素值
143	    let scan_line : Array[Int] = []
144	    for x in 0..<w {
145	      scan_line.push(data[y * w + x].to_int())
146	    }
147	    // 二值化（Otsu或简单阈值）
148	    let binary = binarize_scanline(scan_line)
149	    // 尝试解码
150	    let result = decode_ean13_from_binary(binary, y, w, h)
151	    match result {
152	      Some(r) =>
153	        if r.confidence > best_confidence {	<-- UNCOVERED
154	          best_confidence = r.confidence
155	          best_result = Some(r)
156	        }
157	      None => ignore(())
158	    }
159	  }
160	  best_result
161	}
   	…

181	fn decode_ean13_from_binary(
182	  binary : Array[Int],
183	  y : Int,
184	  _img_width : Int,
185	  _img_height : Int,
186	) -> BarcodeResult? {
187	  let w = binary.length()
188	  if w < 95 { // EAN-13 至少需要 95 个模块
189	    return None
190	  }
191	  // 寻找起始符（101）
192	  let mut start_idx = -1
193	  for i in 0..<(w - 94) {
194	    if binary[i] == 1 && binary[i + 1] == 0 && binary[i + 2] == 1 {	<-- UNCOVERED
195	      // 检查左侧静区
196	      let mut quiet = true	<-- UNCOVERED
197	      for q in 0..<5 {
198	        if i - q - 1 >= 0 && binary[i - q - 1] == 1 {	<-- UNCOVERED
199	          quiet = false
200	        }
201	      }
202	      if quiet {
203	        start_idx = i	<-- UNCOVERED
204	        break
205	      }
206	    }
207	  }
208	  if start_idx < 0 {
209	    return None	<-- UNCOVERED
210	  }
211	  // 估计模块宽度
212	  let module_width = estimate_module_width(binary, start_idx)	<-- UNCOVERED
213	  if module_width < 1 {
214	    return None	<-- UNCOVERED
215	  }
216	  // 解析左侧6位数字
217	  let left_digits : Array[Int] = []
218	  let left_parity : Array[Int] = []
219	  let mut pos = start_idx + 3 * module_width // 跳过起始符
220	  for _ in 0..<6 {
221	    let (digit, parity) = decode_ean13_digit(binary, pos, module_width, true)	<-- UNCOVERED
222	    if digit < 0 {
223	      return None	<-- UNCOVERED
224	    }
225	    left_digits.push(digit)	<-- UNCOVERED
226	    left_parity.push(parity)
227	    pos += 7 * module_width
228	  }
229	  // 检查中间分隔符（01010）
230	  pos += 5 * module_width // 跳过中间分隔符
231	  // 解析右侧6位数字
232	  let right_digits : Array[Int] = []
233	  for _ in 0..<6 {
234	    let (digit, _) = decode_ean13_digit(binary, pos, module_width, false)	<-- UNCOVERED
235	    if digit < 0 {
236	      return None	<-- UNCOVERED
237	    }
238	    right_digits.push(digit)	<-- UNCOVERED
239	    pos += 7 * module_width
240	  }
241	  // 根据奇偶编码模式确定第一位数字
242	  let first_digit = match_parity_pattern(left_parity)	<-- UNCOVERED
243	  if first_digit < 0 {
244	    return None	<-- UNCOVERED
245	  }
246	  // 组合完整条码
247	  let mut barcode = first_digit.to_string()	<-- UNCOVERED
248	  for d in left_digits {
249	    barcode += d.to_string()	<-- UNCOVERED
250	  }
251	  for d in right_digits {
252	    barcode += d.to_string()	<-- UNCOVERED
253	  }
254	  // 验证校验位
255	  let valid = validate_ean13_checksum(barcode)	<-- UNCOVERED
256	  let confidence = if valid { 0.95 } else { 0.5 }
257	  Some(BarcodeResult::{
258	    barcode_type: BarcodeType::Ean13,
259	    data: barcode,
260	    confidence,
261	    x: start_idx,
262	    y,
263	    width: 95 * module_width,
264	    height: 1,
265	  })
266	}
   	…

270	fn estimate_module_width(binary : Array[Int], start_idx : Int) -> Int {
271	  // 起始符是 101，测量第一个条的宽度
272	  let mut width = 0	<-- UNCOVERED
273	  let mut i = start_idx
274	  while i < binary.length() && binary[i] == 1 {	<-- UNCOVERED
275	    width += 1
276	    i += 1
277	  }
278	  if width == 0 {
279	    width = 1	<-- UNCOVERED
280	  }
281	  width
282	}
   	…

286	fn decode_ean13_digit(
287	  binary : Array[Int],
288	  start : Int,
289	  module_width : Int,
290	  is_left : Bool,
291	) -> (Int, Int) {
292	  // 采样7个模块
293	  let modules : Array[Int] = []	<-- UNCOVERED
294	  for i in 0..<7 {
295	    let pos = start + i * module_width + module_width / 2	<-- UNCOVERED
296	    if pos < binary.length() {
297	      modules.push(binary[pos])
298	    } else {
299	      modules.push(0)	<-- UNCOVERED
300	    }
301	  }
302	  // 匹配编码表
303	  if is_left {
304	    // 尝试 L 码（奇数）
305	    for d in 0..<10 {	<-- UNCOVERED
306	      if modules == ean13_l_codes[d] {
307	        return (d, 0) // 0 = L码（奇数）
308	      }
309	    }
310	    // 尝试 G 码（偶数）
311	    for d in 0..<10 {
312	      if modules == ean13_g_codes[d] {	<-- UNCOVERED
313	        return (d, 1) // 1 = G码（偶数）
314	      }
315	    }
316	  } else {
317	    // 右侧 R 码
318	    for d in 0..<10 {	<-- UNCOVERED
319	      if modules == ean13_r_codes[d] {
320	        return (d, 0)
321	      }
322	    }
323	  }
324	  (-1, 0)
325	}
   	…

329	fn match_parity_pattern(parity : Array[Int]) -> Int {
330	  for d in 0..<10 {	<-- UNCOVERED
331	    let mut is_match = true
332	    for i in 0..<6 {
333	      if parity[i] != ean13_parity_patterns[d][i] {	<-- UNCOVERED
334	        is_match = false
335	      }
336	    }
337	    if is_match {
338	      return d	<-- UNCOVERED
339	    }
340	  }
341	  -1
342	}
   	…

346	fn validate_ean13_checksum(barcode : String) -> Bool {
347	  if barcode.length() != 13 {	<-- UNCOVERED
348	    return false
349	  }
350	  let mut sum = 0
351	  for i in 0..<12 {
352	    let digit = barcode[i].to_int() - '0'.to_int()	<-- UNCOVERED
353	    if i % 2 == 0 {
354	      sum += digit // 奇数位（从0开始）权重1	<-- UNCOVERED
355	    } else {
356	      sum += digit * 3 // 偶数位权重3	<-- UNCOVERED
357	    }
358	  }
359	  let checksum = (10 - sum % 10) % 10
360	  let actual = barcode[12].to_int() - '0'.to_int()	<-- UNCOVERED
361	  checksum == actual
362	}
   	…

377	pub fn detect_qr_finder_patterns(img : @types.Image) -> Array[QrFinderPattern] {
378	  let w = img.width
379	  let h = img.height
380	  let data = img.data
381	  let patterns : Array[QrFinderPattern] = []
382	  // 水平扫描寻找 1:1:3:1:1 比例的图案
383	  for y in 0..<h {
384	    let mut x = 0
385	    while x < w {
386	      // 寻找起始黑条
387	      if data[y * w + x].to_int() < 128 {
388	        let (found, pattern_x, pattern_size) = scan_qr_pattern_horizontal(
389	          data, w, x, y,
390	        )
391	        if found && pattern_size > 10 {
392	          // 验证垂直方向
393	          let vertical_valid = verify_qr_pattern_vertical(	<-- UNCOVERED
394	            data, w, h, pattern_x, y, pattern_size,
395	          )
396	          if vertical_valid {
397	            patterns.push(QrFinderPattern::{	<-- UNCOVERED
398	              x: pattern_x.to_double(),
399	              y: y.to_double(),
400	              size: pattern_size.to_double(),
401	              quality: 0.8,
402	            })
403	          }
404	          x = pattern_x + pattern_size + 1
405	        } else {
406	          x += 1
407	        }
408	      } else {
409	        x += 1
410	      }
411	    }
412	  }
413	  patterns
414	}
   	…

418	fn scan_qr_pattern_horizontal(
419	  data : Bytes,
420	  w : Int,
421	  start_x : Int,
422	  y : Int,
423	) -> (Bool, Int, Int) {
424	  let mut x = start_x
425	  // 模块1：黑条
426	  let mut m1 = 0
427	  while x < w && data[y * w + x].to_int() < 128 {
428	    m1 += 1
429	    x += 1
430	  }
431	  // 模块2：白条
432	  let mut m2 = 0
433	  while x < w && data[y * w + x].to_int() >= 128 {
434	    m2 += 1
435	    x += 1
436	  }
437	  // 模块3：黑条（中心，应该是3倍）
438	  let mut m3 = 0
439	  while x < w && data[y * w + x].to_int() < 128 {
440	    m3 += 1
441	    x += 1
442	  }
443	  // 模块4：白条
444	  let mut m4 = 0
445	  while x < w && data[y * w + x].to_int() >= 128 {
446	    m4 += 1
447	    x += 1
448	  }
449	  // 模块5：黑条
450	  let mut m5 = 0
451	  while x < w && data[y * w + x].to_int() < 128 {
452	    m5 += 1
453	    x += 1
454	  }
455	  // 检查比例 1:1:3:1:1
456	  let total = m1 + m2 + m3 + m4 + m5
457	  if total < 20 {
458	    return (false, 0, 0)
459	  }
460	  let module_val = total / 7
461	  let m1d = m1.to_double()
462	  let m2d = m2.to_double()
463	  let m3d = m3.to_double()
464	  let m4d = m4.to_double()
465	  let m5d = m5.to_double()
466	  let mod_d = module_val.to_double()
467	  let ratio_ok = m1d >= mod_d * 0.5 &&
468	    m1d <= mod_d * 1.5 &&
469	    m2d >= mod_d * 0.5 &&
470	    m2d <= mod_d * 1.5 &&
471	    m3d >= mod_d * 2.5 &&
472	    m3d <= mod_d * 3.5 &&
473	    m4d >= mod_d * 0.5 &&
474	    m4d <= mod_d * 1.5 &&
475	    m5d >= mod_d * 0.5 &&
476	    m5d <= mod_d * 1.5
477	  if ratio_ok {
478	    return (true, start_x, total)	<-- UNCOVERED
479	  }
480	  (false, 0, 0)
481	}
   	…

485	fn verify_qr_pattern_vertical(
486	  data : Bytes,
487	  w : Int,
488	  h : Int,
489	  start_x : Int,
490	  start_y : Int,
491	  size : Int,
492	) -> Bool {
493	  let center_x = start_x + size / 2	<-- UNCOVERED
494	  if center_x >= w {
495	    return false	<-- UNCOVERED
496	  }
497	  let mut y = start_y
498	  // 模块1：黑条
499	  let mut m1 = 0
500	  while y < h && data[y * w + center_x].to_int() < 128 {	<-- UNCOVERED
501	    m1 += 1
502	    y += 1
503	  }
504	  // 模块2：白条
505	  let mut m2 = 0
506	  while y < h && data[y * w + center_x].to_int() >= 128 {	<-- UNCOVERED
507	    m2 += 1
508	    y += 1
509	  }
510	  // 模块3：黑条
511	  let mut m3 = 0
512	  while y < h && data[y * w + center_x].to_int() < 128 {	<-- UNCOVERED
513	    m3 += 1
514	    y += 1
515	  }
516	  // 模块4：白条
517	  let mut m4 = 0
518	  while y < h && data[y * w + center_x].to_int() >= 128 {	<-- UNCOVERED
519	    m4 += 1
520	    y += 1
521	  }
522	  // 模块5：黑条
523	  let mut m5 = 0
524	  while y < h && data[y * w + center_x].to_int() < 128 {	<-- UNCOVERED
525	    m5 += 1
526	    y += 1
527	  }
528	  let total = m1 + m2 + m3 + m4 + m5
529	  if total < 20 {
530	    return false	<-- UNCOVERED
531	  }
532	  let module_val = total / 7
533	  let m1d = m1.to_double()	<-- UNCOVERED
534	  let m2d = m2.to_double()
535	  let m3d = m3.to_double()
536	  let m4d = m4.to_double()
537	  let m5d = m5.to_double()
538	  let mod_d = module_val.to_double()
539	  m1d >= mod_d * 0.5 &&
540	  m1d <= mod_d * 1.5 &&
541	  m2d >= mod_d * 0.5 &&
542	  m2d <= mod_d * 1.5 &&
543	  m3d >= mod_d * 2.5 &&
544	  m3d <= mod_d * 3.5 &&
545	  m4d >= mod_d * 0.5 &&
546	  m4d <= mod_d * 1.5 &&
547	  m5d >= mod_d * 0.5 &&
548	  m5d <= mod_d * 1.5
549	}
   	…

553	pub fn decode_qrcode(
554	  img : @types.Image,
555	  config? : BarcodeConfig,
556	) -> BarcodeResult? {
557	  let cfg = match config {
558	    Some(c) => c
559	    None => barcode_default_config()
560	  }
561	  if !cfg.enable_qrcode {
562	    return None	<-- UNCOVERED
563	  }
564	  // 检测定位图案
565	  let patterns = detect_qr_finder_patterns(img)
566	  if patterns.length() < 3 {
567	    return None
568	  }
569	  // 取前3个定位图案（左上、右上、左下）
570	  let p1 = patterns[0]
571	  let p2 = patterns[1]
572	  let p3 = patterns[2]
573	  // 估算 QR Code 尺寸
574	  let avg_size = (p1.size + p2.size + p3.size) / 3.0
575	  let module_size = avg_size / 7.0
576	  let _ = (21.0 * module_size).to_int() // Version 1 是 21x21 模块	<-- UNCOVERED
577	  // 计算边界框
578	  let min_x = p1.x.min(p2.x).min(p3.x).to_int()	<-- UNCOVERED
579	  let min_y = p1.y.min(p2.y).min(p3.y).to_int()
580	  let max_x = (p1.x + p1.size).max(p2.x + p2.size).max(p3.x + p3.size).to_int()
581	  let max_y = (p1.y + p1.size).max(p2.y + p2.size).max(p3.y + p3.size).to_int()
582	  let width = max_x - min_x
583	  let height = max_y - min_y
584	  // 基础版：只检测定位，不做完整解码
585	  // 返回检测结果，data 字段包含基本信息
586	  let info = "QR Code detected: 3 finder patterns, ~" +
587	    module_size.to_int().to_string() +	<-- UNCOVERED
588	    "px/module_val, ~v1 size"
589	  Some(BarcodeResult::{
590	    barcode_type: BarcodeType::QrCode,
591	    data: info,
592	    confidence: 0.7,
593	    x: min_x,
594	    y: min_y,
595	    width,
596	    height,
597	  })
598	}
   	…

602	pub fn decode_barcode(
603	  img : @types.Image,
604	  config? : BarcodeConfig,
605	) -> Array[BarcodeResult] {
606	  let results : Array[BarcodeResult] = []
607	  // 尝试 EAN-13
608	  let ean13_result = match config {
609	    Some(c) => decode_ean13(img, config=c)
610	    None => decode_ean13(img)
611	  }
612	  match ean13_result {
613	    Some(r) => results.push(r)	<-- UNCOVERED
614	    None => ignore(())
615	  }
616	  // 尝试 QR Code
617	  let qr_result = match config {
618	    Some(c) => decode_qrcode(img, config=c)
619	    None => decode_qrcode(img)
620	  }
621	  match qr_result {
622	    Some(r) => results.push(r)	<-- UNCOVERED
623	    None => ignore(())
624	  }
625	  results
626	}
   	…

42 uncovered line(s) in src\process\barcode\linear_barcodes.mbt:

128	fn code128_char_a(code : Int) -> String {
129	  if code >= 0 && code <= 95 {	<-- UNCOVERED
130	    if code < 64 {
131	      (code + 32).to_char().map(c => c.to_string()).unwrap_or("")
132	    } else {
133	      // 控制字符
134	      (code - 64).to_char().map(c => c.to_string()).unwrap_or("")	<-- UNCOVERED
135	    }
136	  } else {
137	    "?"	<-- UNCOVERED
138	  }
139	}
   	…

143	fn code128_char_b(code : Int) -> String {
144	  if code >= 0 && code <= 95 {	<-- UNCOVERED
145	    (code + 32).to_char().map(c => c.to_string()).unwrap_or("")
146	  } else {
147	    "?"	<-- UNCOVERED
148	  }
149	}
   	…

153	pub fn decode_code128(img : @types.Image) -> BarcodeResult? {
154	  let w = img.width
155	  let h = img.height
156	  let data = img.data
157	  // 在中间扫描线尝试解码
158	  let y = h / 2
159	  let scan_line : Array[Int] = []
160	  for x in 0..<w {
161	    scan_line.push(if data[y * w + x].to_int() < 128 { 1 } else { 0 })
162	  }
163	  // 寻找起始符（Code A/B/C）
164	  let start_patterns = [
165	    (103, [2, 1, 1, 4, 1, 2]), // Start A
166	    (104, [2, 1, 1, 2, 1, 4]), // Start B
167	    (105, [2, 1, 1, 2, 3, 2]), // Start C (实际是 2,1,3,2,1,1)
168	  ]
169	  let mut start_idx = -1
170	  let mut start_code = 0
171	  for i in 0..<(w - 20) {
172	    for _tup0 in start_patterns {
173	      let (code, pattern) = _tup0
174	      if match_bar_pattern(scan_line, i, pattern) {
175	        start_idx = i	<-- UNCOVERED
176	        start_code = code
177	        break
178	      }
179	    }
180	    if start_idx >= 0 {
181	      break	<-- UNCOVERED
182	    }
183	  }
184	  if start_idx < 0 {
185	    return None
186	  }
187	  // 确定字符集
188	  let charset = match start_code {	<-- UNCOVERED
189	    103 => Code128Charset::CodeA
190	    104 => Code128Charset::CodeB
191	    105 => Code128Charset::CodeC
192	    _ => Code128Charset::CodeB
193	  }
194	  // 解码数据码字
195	  let mut pos = start_idx + 11 // 起始符占 11 个模块
196	  let codes : Array[Int] = []
197	  let mut checksum = start_code
198	  let mut code_count = 0
199	  while pos < w - 13 {
200	    // 匹配一个码字（6个条空，11个模块）
201	    let (matched_code, matched_width) = match_code128_symbol(scan_line, pos)	<-- UNCOVERED
202	    if matched_code < 0 {
203	      break	<-- UNCOVERED
204	    }
205	    if matched_code == 106 { // Stop 符
206	      break	<-- UNCOVERED
207	    }
208	    codes.push(matched_code)	<-- UNCOVERED
209	    code_count += 1
210	    checksum += matched_code * code_count
211	    pos += matched_width
212	  }
213	  if codes.length() < 2 {	<-- UNCOVERED
214	    return None
215	  }
216	  // 验证校验位
217	  let expected_checksum = checksum % 103
218	  let actual_checksum = codes[codes.length() - 1]	<-- UNCOVERED
219	  let valid = expected_checksum == actual_checksum
220	  // 解码数据
221	  let mut result = ""
222	  let mut current_charset = charset
223	  for i in 0..<(codes.length() - 1) {	<-- UNCOVERED
224	    let code = codes[i]
225	    // 处理字符集切换
226	    if code == 101 { // Code A
227	      current_charset = Code128Charset::CodeA	<-- UNCOVERED
228	      continue
229	    } else if code == 100 { // Code B	<-- UNCOVERED
230	      current_charset = Code128Charset::CodeB
231	      continue
232	    } else if code == 99 { // Code C	<-- UNCOVERED
233	      current_charset = Code128Charset::CodeC
234	      continue
235	    }
236	    match current_charset {	<-- UNCOVERED
237	      Code128Charset::CodeA => result += code128_char_a(code)
238	      Code128Charset::CodeB => result += code128_char_b(code)
239	      Code128Charset::CodeC => {
240	        // Code C: 两个数字一个码字
241	        let d1 = code / 10
242	        let d2 = code % 10
243	        result += d1.to_string()	<-- UNCOVERED
244	        result += d2.to_string()
245	      }
246	    }
247	  }
248	  let confidence = if valid { 0.9 } else { 0.5 }	<-- UNCOVERED
249	  Some(BarcodeResult::{
250	    barcode_type: BarcodeType::Code128,
251	    data: result,
252	    confidence,
253	    x: start_idx,
254	    y,
255	    width: pos - start_idx,
256	    height: 1,
257	  })
258	}
   	…

262	fn match_bar_pattern(
263	  scan_line : Array[Int],
264	  start : Int,
265	  pattern : Array[Int],
266	) -> Bool {
267	  let mut pos = start
268	  let mut is_bar = true
269	  for width in pattern {
270	    for _ in 0..<width {
271	      if pos >= scan_line.length() {
272	        return false	<-- UNCOVERED
273	      }
274	      let expected = if is_bar { 1 } else { 0 }
275	      if scan_line[pos] != expected {
276	        return false
277	      }
278	      pos += 1
279	    }
280	    is_bar = !is_bar
281	  }
282	  true
283	}
   	…

287	fn match_code128_symbol(scan_line : Array[Int], start : Int) -> (Int, Int) {
288	  // 尝试匹配所有 108 个码字
289	  for code in 0..<108 {	<-- UNCOVERED
290	    let pattern = code128_patterns[code]
291	    let mut total_width = 0
292	    for w in pattern {
293	      total_width += w	<-- UNCOVERED
294	    }
295	    if match_bar_pattern(scan_line, start, pattern) {	<-- UNCOVERED
296	      return (code, total_width)
297	    }
298	  }
299	  (-1, 0)
300	}
   	…

304	pub fn decode_upca(img : @types.Image) -> BarcodeResult? {
305	  // UPC-A 与 EAN-13 兼容（UPC-A 是 EAN-13 第一位为 0 的特例）
306	  let result = decode_ean13(img)
307	  match result {
308	    Some(r) =>
309	      if r.data.length() == 13 && r.data[0] == '0' {	<-- UNCOVERED
310	        // 去掉前导 0，转换为 UPC-A
311	        let mut upca = ""	<-- UNCOVERED
312	        for i in 1..<13 {
313	          upca += r.data[i].to_string()	<-- UNCOVERED
314	        }
315	        Some(BarcodeResult::{
316	          barcode_type: BarcodeType::UpcA,
317	          data: upca,
318	          confidence: r.confidence,
319	          x: r.x,
320	          y: r.y,
321	          width: r.width,
322	          height: r.height,
323	        })
324	      } else {
325	        None	<-- UNCOVERED
326	      }
327	    None => None
328	  }
329	}
   	…

393	pub fn decode_code39(img : @types.Image) -> BarcodeResult? {
394	  let w = img.width
395	  let h = img.height
396	  let data = img.data
397	  let y = h / 2
398	  let scan_line : Array[Int] = []
399	  for x in 0..<w {
400	    scan_line.push(if data[y * w + x].to_int() < 128 { 1 } else { 0 })
401	  }
402	  // 寻找起始符 *
403	  let mut start_idx = -1
404	  for i in 0..<(w - 20) {
405	    if match_bar_pattern(scan_line, i, code39_start_pattern) {
406	      start_idx = i	<-- UNCOVERED
407	      break
408	    }
409	  }
410	  if start_idx < 0 {
411	    return None
412	  }
413	  // 解码数据字符
414	  let mut pos = start_idx + 10 // 起始符 + 间隔
415	  let result_chars : Array[String] = []
416	  while pos < w - 15 {
417	    // 跳过空白间隔
418	    while pos < w && scan_line[pos] == 0 {	<-- UNCOVERED
419	      pos += 1
420	    }
421	    if pos >= w - 9 {
422	      break	<-- UNCOVERED
423	    }
424	    // 检查是否是终止符
425	    if match_bar_pattern(scan_line, pos, code39_start_pattern) {	<-- UNCOVERED
426	      break
427	    }
428	    // 匹配数据字符
429	    let mut matched = false
430	    for code in 0..<code39_patterns.length() {	<-- UNCOVERED
431	      if match_bar_pattern(scan_line, pos, code39_patterns[code]) {
432	        result_chars.push(code39_chars[code])
433	        let mut total_width = 0
434	        for width in code39_patterns[code] {
435	          total_width += width	<-- UNCOVERED
436	        }
437	        pos += total_width + 1 // 字符 + 间隔
438	        matched = true
439	        break
440	      }
441	    }
442	    if !matched {
443	      pos += 1	<-- UNCOVERED
444	    }
445	  }
446	  if result_chars.length() == 0 {	<-- UNCOVERED
447	    return None
448	  }
449	  let mut result = ""
450	  for c in result_chars {
451	    result += c	<-- UNCOVERED
452	  }
453	  Some(BarcodeResult::{
454	    barcode_type: BarcodeType::Code128, // 复用类型
455	    data: result,
456	    confidence: 0.8,
457	    x: start_idx,
458	    y,
459	    width: pos - start_idx,
460	    height: 1,
461	  })
462	}
   	…

466	pub fn decode_linear_barcode(img : @types.Image) -> Array[BarcodeResult] {
467	  let results : Array[BarcodeResult] = []
468	  // 尝试 EAN-13
469	  let ean13 = decode_ean13(img)
470	  match ean13 {
471	    Some(r) => results.push(r)	<-- UNCOVERED
472	    None => ignore(())
473	  }
474	  // 尝试 UPC-A
475	  let upca = decode_upca(img)
476	  match upca {
477	    Some(r) => results.push(r)	<-- UNCOVERED
478	    None => ignore(())
479	  }
480	  // 尝试 Code128
481	  let code128 = decode_code128(img)
482	  match code128 {
483	    Some(r) => results.push(r)	<-- UNCOVERED
484	    None => ignore(())
485	  }
486	  // 尝试 Code39
487	  let code39 = decode_code39(img)
488	  match code39 {
489	    Some(r) => results.push(r)	<-- UNCOVERED
490	    None => ignore(())
491	  }
492	  results
493	}
   	…

71 uncovered line(s) in src\process\barcode\qr_decode.mbt:

 66	pub fn decode_qrcode_full(img : @types.Image) -> QrDecodeResult? {
 67	  let w = img.width
 68	  let h = img.height
 69	  let data = img.data
 70	  // 1. 检测定位图案
 71	  let patterns = detect_qr_finder_patterns_full(img)
 72	  if patterns.length() < 3 {
 73	    return None
 74	  }
 75	  // 2. 取前3个定位图案，估算 QR Code 边界
 76	  let p1 = patterns[0]
 77	  let p2 = patterns[1]
 78	  let p3 = patterns[2]
 79	  // 计算模块大小
 80	  let module_size = (p1.size / 7.0).min(p2.size / 7.0).min(p3.size / 7.0)	<-- UNCOVERED
 81	  if module_size < 1.0 {
 82	    return None	<-- UNCOVERED
 83	  }
 84	  // 3. 估算版本（基于定位图案之间的距离）
 85	  let dist12 = ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)).sqrt()	<-- UNCOVERED
 86	  let dist13 = ((p1.x - p3.x) * (p1.x - p3.x) + (p1.y - p3.y) * (p1.y - p3.y)).sqrt()
 87	  let modules_between = dist12.max(dist13) / module_size
 88	  let version = ((modules_between - 10.0) / 4.0 + 1.0).to_int().max(1).min(10)
 89	  let qr_size = 17 + version * 4 // 版本 N 的模块数 = 17 + 4N
 90	  // 4. 计算 QR Code 区域边界
 91	  let min_x = p1.x.min(p2.x).min(p3.x).to_int()	<-- UNCOVERED
 92	  let min_y = p1.y.min(p2.y).min(p3.y).to_int()
 93	  let max_x = (p1.x + p1.size).max(p2.x + p2.size).max(p3.x + p3.size).to_int()
 94	  let max_y = (p1.y + p1.size).max(p2.y + p2.size).max(p3.y + p3.size).to_int()
 95	  let qr_w = max_x - min_x
 96	  let qr_h = max_y - min_y
 97	  if qr_w < 21 || qr_h < 21 {
 98	    return None	<-- UNCOVERED
 99	  }
100	  // 5. 采样模块（将图像转换为模块矩阵）
101	  let modules = sample_qr_modules(data, w, h, min_x, min_y, qr_w, qr_h, qr_size)	<-- UNCOVERED
102	  // 6. 读取格式信息
103	  let format_info = read_qr_format_info(modules, qr_size)	<-- UNCOVERED
104	  if format_info < 0 {
105	    return None	<-- UNCOVERED
106	  }
107	  let (ec_level, mask_pattern) = decode_format_info(format_info)	<-- UNCOVERED
108	  // 7. 应用掩码去除
109	  let unmasked = apply_qr_mask(modules, qr_size, mask_pattern)	<-- UNCOVERED
110	  // 8. 提取数据位（zigzag 顺序）
111	  let data_bits = extract_qr_data_bits(unmasked, qr_size, version)	<-- UNCOVERED
112	  if data_bits.length() < 20 {
113	    return None
114	  }
115	  // 9. 解码数据
116	  let (decoded, _bytes_used) = decode_qr_data(data_bits, version)	<-- UNCOVERED
117	  // 10. 返回结果
118	  Some(QrDecodeResult::{
119	    data: decoded,
120	    version,
121	    error_correction_level: qr_ec_level_names[ec_level],
122	    mask_pattern,
123	    confidence: 0.7,
124	    corrected_errors: 0,
125	  })
126	}
   	…

137	fn sample_qr_modules(
138	  data : Bytes,
139	  img_w : Int,
140	  img_h : Int,
141	  roi_x : Int,
142	  roi_y : Int,
143	  roi_w : Int,
144	  roi_h : Int,
145	  qr_size : Int,
146	) -> Array[Int] {
147	  let modules : Array[Int] = []	<-- UNCOVERED
148	  let cell_w = roi_w.to_double() / qr_size.to_double()
149	  let cell_h = roi_h.to_double() / qr_size.to_double()
150	  for row in 0..<qr_size {
151	    for col in 0..<qr_size {	<-- UNCOVERED
152	      // 采样每个模块的中心区域
153	      let mut sum = 0	<-- UNCOVERED
154	      let mut count = 0
155	      let sample_size = 3
156	      for dy in 0..<sample_size {
157	        for dx in 0..<sample_size {	<-- UNCOVERED
158	          let px = roi_x +
159	            (col.to_double() * cell_w +
160	            (dx.to_double() + 0.5) * cell_w / sample_size.to_double()).to_int()
161	          let py = roi_y +
162	            (row.to_double() * cell_h +	<-- UNCOVERED
163	            (dy.to_double() + 0.5) * cell_h / sample_size.to_double()).to_int()
164	          if px >= 0 && px < img_w && py >= 0 && py < img_h {
165	            sum += data[py * img_w + px].to_int()	<-- UNCOVERED
166	            count += 1
167	          }
168	        }
169	      }
170	      let avg = if count > 0 { sum / count } else { 255 }	<-- UNCOVERED
171	      modules.push(if avg < 128 { 1 } else { 0 })
172	    }
173	  }
174	  modules
175	}
   	…

179	fn read_qr_format_info(modules : Array[Int], qr_size : Int) -> Int {
180	  // 格式信息存储在两个位置：
181	  // 1. 左上角定位图案周围（15位）
182	  // 2. 右上角和左下角（15位）
183	  // 读取第一个位置（左上角）
184	  let mut format_bits = 0	<-- UNCOVERED
185	  // 格式信息位的位置（相对于 QR 矩阵）
186	  // 行 8，列 0-5 和 7-8（共 8 位）
187	  // 列 8，行 0-5 和 7（共 7 位）
188	  let positions = [
189	    (8, 0),
190	    (8, 1),
191	    (8, 2),
192	    (8, 3),
193	    (8, 4),
194	    (8, 5),
195	    (8, 7),
196	    (8, 8),
197	    (7, 8),
198	    (5, 8),
199	    (4, 8),
200	    (3, 8),
201	    (2, 8),
202	    (1, 8),
203	    (0, 8),
204	  ]
205	  for i in 0..<15 {
206	    let (row, col) = positions[i]	<-- UNCOVERED
207	    if row < qr_size && col < qr_size {
208	      if modules[row * qr_size + col] == 1 {	<-- UNCOVERED
209	        format_bits = format_bits | (1 << (14 - i))
210	      }
211	    }
212	  }
213	  // 在表中查找最接近的格式信息
214	  let mut best_match = -1
215	  let mut best_dist = 16
216	  for _tup0 in qr_format_info_table {
217	    let (bits, _, _) = _tup0	<-- UNCOVERED
218	    let dist = hamming_distance(format_bits, bits)
219	    if dist < best_dist {
220	      best_dist = dist	<-- UNCOVERED
221	      best_match = bits
222	    }
223	  }
224	  best_match
225	}
   	…

229	fn hamming_distance(a : Int, b : Int) -> Int {
230	  let xor = a ^ b	<-- UNCOVERED
231	  let mut count = 0
232	  let mut x = xor
233	  while x > 0 {
234	    count += x & 1	<-- UNCOVERED
235	    x = x >> 1
236	  }
237	  count
238	}
   	…

242	fn decode_format_info(format_bits : Int) -> (Int, Int) {
243	  for _tup1 in qr_format_info_table {	<-- UNCOVERED
244	    let (bits, ec, mask) = _tup1
245	    if bits == format_bits {
246	      return (ec, mask)	<-- UNCOVERED
247	    }
248	  }
249	  (0, 0) // 默认
250	}
   	…

254	fn apply_qr_mask(
255	  modules : Array[Int],
256	  qr_size : Int,
257	  mask_pattern : Int,
258	) -> Array[Int] {
259	  let result = modules.copy()	<-- UNCOVERED
260	  for row in 0..<qr_size {
261	    for col in 0..<qr_size {	<-- UNCOVERED
262	      // 跳过功能模块区域（定位图案、格式信息、版本信息）
263	      if is_function_module(row, col, qr_size) {	<-- UNCOVERED
264	        continue
265	      }
266	      let should_invert = match mask_pattern {	<-- UNCOVERED
267	        0 => (row + col) % 2 == 0
268	        1 => row % 2 == 0
269	        2 => col % 3 == 0
270	        3 => (row + col) % 3 == 0
271	        4 => (row / 2 + col / 3) % 2 == 0
272	        5 => row * col % 2 + row * col % 3 == 0
273	        6 => (row * col % 2 + row * col % 3) % 2 == 0
274	        7 => ((row + col) % 2 + row * col % 3) % 2 == 0
275	        _ => false
276	      }
277	      if should_invert {
278	        result[row * qr_size + col] = 1 - result[row * qr_size + col]	<-- UNCOVERED
279	      }
280	    }
281	  }
282	  result
283	}
   	…

287	fn is_function_module(row : Int, col : Int, qr_size : Int) -> Bool {
288	  // 左上角定位图案（8x8）
289	  if row < 9 && col < 9 {	<-- UNCOVERED
290	    return true
291	  }
292	  // 右上角定位图案
293	  if row < 9 && col >= qr_size - 8 {
294	    return true	<-- UNCOVERED
295	  }
296	  // 左下角定位图案
297	  if row >= qr_size - 8 && col < 9 {
298	    return true	<-- UNCOVERED
299	  }
300	  // 定时图案（行/列 6）
301	  if row == 6 || col == 6 {
302	    return true	<-- UNCOVERED
303	  }
304	  // 格式信息区域
305	  if row == 8 || col == 8 {
306	    return true	<-- UNCOVERED
307	  }
308	  false
309	}
   	…

313	fn extract_qr_data_bits(
314	  modules : Array[Int],
315	  qr_size : Int,
316	  _version : Int,
317	) -> Array[Int] {
318	  let bits : Array[Int] = []	<-- UNCOVERED
319	  let mut col = qr_size - 1
320	  let mut going_up = true
321	  while col > 0 {
322	    // 跳过列 6（定时图案）
323	    if col == 6 {	<-- UNCOVERED
324	      col -= 1
325	    }
326	    if going_up {
327	      // 从下往上
328	      let mut row = qr_size - 1	<-- UNCOVERED
329	      while row >= 0 {
330	        for c in 0..<2 {	<-- UNCOVERED
331	          let cc = col - c
332	          if !is_function_module(row, cc, qr_size) {
333	            bits.push(modules[row * qr_size + cc])
334	          }
335	        }
336	        row -= 1
337	      }
338	    } else {
339	      // 从上往下
340	      let mut row = 0	<-- UNCOVERED
341	      while row < qr_size {
342	        for c in 0..<2 {	<-- UNCOVERED
343	          let cc = col - c
344	          if !is_function_module(row, cc, qr_size) {
345	            bits.push(modules[row * qr_size + cc])
346	          }
347	        }
348	        row += 1
349	      }
350	    }
351	    going_up = !going_up
352	    col -= 2
353	  }
354	  bits
355	}
   	…

359	fn decode_qr_data(bits : Array[Int], _version : Int) -> (String, Int) {
360	  if bits.length() < 4 {	<-- UNCOVERED
361	    return ("", 0)
362	  }
363	  // 读取模式指示符（4位）
364	  let mut mode = 0
365	  for i in 0..<4 {
366	    mode = (mode << 1) | bits[i]	<-- UNCOVERED
367	  }
368	  let mut pos = 4
369	  // 根据模式读取字符计数
370	  let char_count_bits = match mode {	<-- UNCOVERED
371	    1 => 10 // 数字
372	    2 => 9 // 字母数字
373	    4 => 8 // 字节
374	    8 => 8 // 中文（Kanji）
375	    _ => 8
376	  }
377	  let mut char_count = 0
378	  for i in 0..<char_count_bits {
379	    if pos + i < bits.length() {	<-- UNCOVERED
380	      char_count = (char_count << 1) | bits[pos + i]
381	    }
382	  }
383	  pos += char_count_bits
384	  // 解码数据
385	  let mut result = ""
386	  match mode {	<-- UNCOVERED
387	    1 => {
388	      // 数字模式：每 3 位数字用 10 位表示
389	      let mut i = 0
390	      while i < char_count && pos + 10 <= bits.length() {	<-- UNCOVERED
391	        let remaining = char_count - i
392	        let bits_needed = if remaining >= 3 {
393	          10	<-- UNCOVERED
394	        } else if remaining == 2 {
395	          7
396	        } else {
397	          4	<-- UNCOVERED
398	        }
399	        let mut value = 0
400	        for j in 0..<bits_needed {
401	          if pos + j < bits.length() {	<-- UNCOVERED
402	            value = (value << 1) | bits[pos + j]
403	          }
404	        }
405	        pos += bits_needed
406	        if remaining >= 3 {
407	          result += (value / 100).to_string()	<-- UNCOVERED
408	          result += (value / 10 % 10).to_string()
409	          result += (value % 10).to_string()
410	          i += 3
411	        } else if remaining == 2 {	<-- UNCOVERED
412	          result += (value / 10).to_string()
413	          result += (value % 10).to_string()
414	          i += 2
415	        } else {
416	          result += value.to_string()	<-- UNCOVERED
417	          i += 1
418	        }
419	      }
420	    }
421	    2 => {	<-- UNCOVERED
422	      // 字母数字模式：每 2 个字符用 11 位表示
423	      let alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
424	      let mut i = 0
425	      while i < char_count && pos + 11 <= bits.length() {	<-- UNCOVERED
426	        let remaining = char_count - i
427	        if remaining >= 2 {
428	          let mut value = 0	<-- UNCOVERED
429	          for j in 0..<11 {
430	            if pos + j < bits.length() {	<-- UNCOVERED
431	              value = (value << 1) | bits[pos + j]
432	            }
433	          }
434	          pos += 11
435	          let c1 = value / 45
436	          let c2 = value % 45
437	          if c1 < alphanum.length() {	<-- UNCOVERED
438	            result += alphanum[c1].to_string()
439	          }
440	          if c2 < alphanum.length() {	<-- UNCOVERED
441	            result += alphanum[c2].to_string()
442	          }
443	          i += 2
444	        } else {
445	          let mut value = 0	<-- UNCOVERED
446	          for j in 0..<6 {
447	            if pos + j < bits.length() {	<-- UNCOVERED
448	              value = (value << 1) | bits[pos + j]
449	            }
450	          }
451	          pos += 6
452	          if value < alphanum.length() {	<-- UNCOVERED
453	            result += alphanum[value].to_string()
454	          }
455	          i += 1
456	        }
457	      }
458	    }
459	    4 => {	<-- UNCOVERED
460	      // 字节模式：每个字节 8 位
461	      let mut i = 0
462	      while i < char_count && pos + 8 <= bits.length() {	<-- UNCOVERED
463	        let mut byte = 0
464	        for j in 0..<8 {
465	          byte = (byte << 1) | bits[pos + j]	<-- UNCOVERED
466	        }
467	        pos += 8
468	        // 简单 ASCII 解码
469	        if byte >= 32 && byte < 127 {
470	          result += byte.to_char().map(c => c.to_string()).unwrap_or("")	<-- UNCOVERED
471	        } else {
472	          result += "?"	<-- UNCOVERED
473	        }
474	        i += 1
475	      }
476	    }
477	    _ =>
478	      // 其他模式暂不支持，返回空
479	      result = ""	<-- UNCOVERED
480	  }
481	  (result, pos)
482	}
   	…

12 uncovered line(s) in src\process\color\adaptive_threshold.mbt:

  6	pub fn adaptive_threshold_mean(
  7	  img : @types.Image,
  8	  block_size : Int,
  9	  c : Int,
 10	) -> @types.Image raise @types.LoadError {
 11	  // 输入验证
 12	  if img.width <= 0 || img.height <= 0 {
 13	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 14	      "adaptive_threshold_mean: 图像尺寸必须大于0",
 15	    )
 16	  }
 17	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 18	    raise @types.LoadError::DecodeFailed(
 19	      "adaptive_threshold_mean 仅支持 channels=1/3/4",
 20	    )
 21	  }
 22	  let expected_size = img.width * img.height * img.channels
 23	  if img.data.length() != expected_size {
 24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 25	      "adaptive_threshold_mean: 数据大小不匹配，期望 " +
 26	      expected_size.to_string() +	<-- UNCOVERED
 27	      "，实际 " +
 28	      img.data.length().to_string(),	<-- UNCOVERED
 29	    )
 30	  }
 31	  if block_size < 3 || block_size % 2 == 0 {
 32	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")
 33	  }
 34	  let ch = img.channels
 35	  let color_ch = if ch >= 3 { 3 } else { 1 }
 36	  let w = img.width
 37	  let h = img.height
 38	  let n = w * h
 39	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 40	  let half = block_size / 2
 41	  // 预计算像素 Int 数组
 42	  let src : Array[Int] = Array::make(n * ch, 0)
 43	  for i in 0..<(n * ch) {
 44	    src[i] = img.data[i].to_int()
 45	  }
 46	  // 内部区域 count 恒为 block_size²
 47	  let inner_count = block_size * block_size
 48	  for c_idx in 0..<color_ch {
 49	    // 内部区域：x in [half, w-half), y in [half, h-half)
 50	    for y in half..<(h - half) {
 51	      for x in half..<(w - half) {
 52	        let mut sum = 0
 53	        for dy in -half..<=half {
 54	          let row = (y + dy) * w
 55	          for dx in -half..<=half {
 56	            sum += src[(row + x + dx) * ch + c_idx]
 57	          }
 58	        }
 59	        let mean = sum / inner_count
 60	        let threshold = mean - c
 61	        let v = src[(y * w + x) * ch + c_idx]
 62	        out[(y * w + x) * ch + c_idx] = if v > threshold {
 63	          b'\xFF'
 64	        } else {
 65	          b'\x00'
 66	        }
 67	      }
 68	    }
 69	    // 边界区域
 70	    for y in 0..<h {
 71	      let y_inner = y >= half && y < h - half
 72	      for x in 0..<w {
 73	        if y_inner && x >= half && x < w - half {
 74	          continue
 75	        }
 76	        let mut sum = 0
 77	        let mut count = 0
 78	        for dy in -half..<=half {
 79	          for dx in -half..<=half {
 80	            let ny = y + dy
 81	            let nx = x + dx
 82	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
 83	              sum += src[(ny * w + nx) * ch + c_idx]
 84	              count += 1
 85	            }
 86	          }
 87	        }
 88	        let mean = if count > 0 { sum / count } else { 0 }
 89	        let threshold = mean - c
 90	        let v = src[(y * w + x) * ch + c_idx]
 91	        out[(y * w + x) * ch + c_idx] = if v > threshold {
 92	          b'\xFF'
 93	        } else {
 94	          b'\x00'
 95	        }
 96	      }
 97	    }
 98	  }
 99	  if ch == 4 {
100	    for i in 0..<n {
101	      out[i * 4 + 3] = img.data[i * 4 + 3]
102	    }
103	  }
104	  @types.Image::{
105	    width: w,
106	    height: h,
107	    channels: ch,
108	    data: Bytes::from_array(out),
109	  }
110	}
   	…

114	pub fn adaptive_threshold_gaussian(
115	  img : @types.Image,
116	  block_size : Int,
117	  c : Int,
118	) -> @types.Image raise @types.LoadError {
119	  // 输入验证
120	  if img.width <= 0 || img.height <= 0 {
121	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
122	      "adaptive_threshold_gaussian: 图像尺寸必须大于0",
123	    )
124	  }
125	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
126	    raise @types.LoadError::DecodeFailed(
127	      "adaptive_threshold_gaussian 仅支持 channels=1/3/4",
128	    )
129	  }
130	  let expected_size = img.width * img.height * img.channels
131	  if img.data.length() != expected_size {
132	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
133	      "adaptive_threshold_gaussian: 数据大小不匹配，期望 " +
134	      expected_size.to_string() +	<-- UNCOVERED
135	      "，实际 " +
136	      img.data.length().to_string(),	<-- UNCOVERED
137	    )
138	  }
139	  if block_size < 3 || block_size % 2 == 0 {
140	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")
141	  }
142	  let ch = img.channels
143	  let color_ch = if ch >= 3 { 3 } else { 1 }
144	  let w = img.width
145	  let h = img.height
146	  let n = w * h
147	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
148	  let half = block_size / 2
149	  let sigma = Float::from_int(half) / 2.0F
150	  let two_sigma_sq = 2.0F * sigma * sigma
151	  // 预计算高斯权重（1D 扁平化）
152	  let weights : Array[Float] = Array::make(block_size * block_size, 0.0F)
153	  let mut total_weight = 0.0F
154	  for dy in 0..<block_size {
155	    for dx in 0..<block_size {
156	      let fy = Float::from_int(dy - half)
157	      let fx = Float::from_int(dx - half)
158	      let wt = @math.expf(-(fx * fx + fy * fy) / two_sigma_sq)
159	      weights[dy * block_size + dx] = wt
160	      total_weight += wt
161	    }
162	  }
163	  // 预计算像素 Float 数组
164	  let src : Array[Float] = Array::make(n * ch, 0.0F)
165	  for i in 0..<(n * ch) {
166	    src[i] = Float::from_int(img.data[i].to_int())
167	  }
168	  for c_idx in 0..<color_ch {
169	    // 内部区域：weight_sum 恒为 total_weight
170	    for y in half..<(h - half) {
171	      for x in half..<(w - half) {
172	        let mut sum = 0.0F
173	        for dy in 0..<block_size {
174	          let row = (y + dy - half) * w
175	          let wbase = dy * block_size
176	          for dx in 0..<block_size {
177	            sum += src[(row + x + dx - half) * ch + c_idx] * weights[wbase + dx]
178	          }
179	        }
180	        let mean = sum / total_weight
181	        let threshold = mean - Float::from_int(c)
182	        let v = src[(y * w + x) * ch + c_idx]
183	        out[(y * w + x) * ch + c_idx] = if v > threshold {
184	          b'\xFF'
185	        } else {
186	          b'\x00'
187	        }
188	      }
189	    }
190	    // 边界区域
191	    for y in 0..<h {
192	      let y_inner = y >= half && y < h - half
193	      for x in 0..<w {
194	        if y_inner && x >= half && x < w - half {
195	          continue
196	        }
197	        let mut sum = 0.0F
198	        let mut weight_sum = 0.0F
199	        for dy in -half..<=half {
200	          for dx in -half..<=half {
201	            let ny = y + dy
202	            let nx = x + dx
203	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
204	              let weight = weights[(dy + half) * block_size + (dx + half)]
205	              sum += src[(ny * w + nx) * ch + c_idx] * weight
206	              weight_sum += weight
207	            }
208	          }
209	        }
210	        let mean = if weight_sum > 0.0F { sum / weight_sum } else { 0.0F }
211	        let threshold = mean - Float::from_int(c)
212	        let v = src[(y * w + x) * ch + c_idx]
213	        out[(y * w + x) * ch + c_idx] = if v > threshold {
214	          b'\xFF'
215	        } else {
216	          b'\x00'
217	        }
218	      }
219	    }
220	  }
221	  if ch == 4 {
222	    for i in 0..<n {
223	      out[i * 4 + 3] = img.data[i * 4 + 3]
224	    }
225	  }
226	  @types.Image::{
227	    width: w,
228	    height: h,
229	    channels: ch,
230	    data: Bytes::from_array(out),
231	  }
232	}
   	…

236	pub fn threshold_otsu(
237	  img : @types.Image,
238	) -> @types.Image raise @types.LoadError {
239	  // 输入验证
240	  if img.width <= 0 || img.height <= 0 {
241	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
242	      "threshold_otsu: 图像尺寸必须大于0",
243	    )
244	  }
245	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
246	    raise @types.LoadError::DecodeFailed(
247	      "threshold_otsu 仅支持 channels=1/3/4",
248	    )
249	  }
250	  let expected_size = img.width * img.height * img.channels
251	  if img.data.length() != expected_size {
252	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
253	      "threshold_otsu: 数据大小不匹配，期望 " +
254	      expected_size.to_string() +	<-- UNCOVERED
255	      "，实际 " +
256	      img.data.length().to_string(),	<-- UNCOVERED
257	    )
258	  }
259	  let ch = img.channels
260	  let color_ch = if ch >= 3 { 3 } else { 1 }
261	  let w = img.width
262	  let h = img.height
263	  let n = w * h
264	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
265	  for c_idx in 0..<color_ch {
266	    let hist : Array[Int] = Array::make(256, 0)
267	    for i in 0..<n {
268	      hist[img.data[i * ch + c_idx].to_int()] += 1
269	    }
270	    let mut total = 0
271	    let mut sum = 0
272	    for t in 0..<256 {
273	      total += hist[t]
274	      sum += t * hist[t]
275	    }
276	    let mut best_t = 0
277	    let mut best_var = -1.0F
278	    let mut w0 = 0
279	    let mut sum0 = 0
280	    for t in 0..<256 {
281	      w0 += hist[t]
282	      sum0 += t * hist[t]
283	      if w0 == 0 || w0 == total {
284	        continue
285	      }
286	      let w1 = total - w0
287	      let sum1 = sum - sum0
288	      let mean0 = Float::from_int(sum0) / Float::from_int(w0)
289	      let mean1 = Float::from_int(sum1) / Float::from_int(w1)
290	      let var_between = Float::from_int(w0) *
291	        Float::from_int(w1) *
292	        (mean0 - mean1) *
293	        (mean0 - mean1)
294	      if var_between > best_var {
295	        best_var = var_between
296	        best_t = t
297	      }
298	    }
299	    for i in 0..<n {
300	      let v = img.data[i * ch + c_idx].to_int()
301	      out[i * ch + c_idx] = if v > best_t { b'\xFF' } else { b'\x00' }
302	    }
303	  }
304	  if ch == 4 {
305	    for i in 0..<n {
306	      out[i * 4 + 3] = img.data[i * 4 + 3]
307	    }
308	  }
309	  @types.Image::{
310	    width: w,
311	    height: h,
312	    channels: ch,
313	    data: Bytes::from_array(out),
314	  }
315	}
   	…

28 uncovered line(s) in src\process\color\adaptive_threshold_16f.mbt:

 11	pub fn adaptive_threshold_mean_16(
 12	  img : @types.Image16,
 13	  block_size : Int,
 14	  c : Int,
 15	) -> @types.Image16 raise @types.LoadError {
 16	  // 输入验证
 17	  if img.width <= 0 || img.height <= 0 {
 18	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 19	      "adaptive_threshold_mean_16: 图像尺寸必须大于0",
 20	    )
 21	  }
 22	  if block_size < 3 || block_size % 2 == 0 {
 23	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")
 24	  }
 25	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 26	    raise @types.LoadError::DecodeFailed(
 27	      "adaptive_threshold_mean_16 仅支持 channels=1/3/4",
 28	    )
 29	  }
 30	  let expected_size = img.width * img.height * img.channels * 2
 31	  if img.data.length() != expected_size {
 32	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 33	      "adaptive_threshold_mean_16: 数据大小不匹配，期望 " +
 34	      expected_size.to_string() +	<-- UNCOVERED
 35	      "，实际 " +
 36	      img.data.length().to_string(),	<-- UNCOVERED
 37	    )
 38	  }
 39	  let ch = img.channels
 40	  let color_ch = if ch >= 3 { 3 } else { 1 }
 41	  let w = img.width
 42	  let h = img.height
 43	  let n = w * h
 44	  let bpp = ch * 2
 45	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
 46	  let half = block_size / 2
 47	  let src : Array[Int] = Array::make(n * ch, 0)
 48	  for i in 0..<(n * ch) {
 49	    src[i] = read_u16_le_c(img.data, i * 2)
 50	  }
 51	  let inner_count = block_size * block_size
 52	  for c_idx in 0..<color_ch {
 53	    for y in half..<(h - half) {
 54	      for x in half..<(w - half) {
 55	        let mut sum = 0
 56	        for dy in -half..<=half {
 57	          let row = (y + dy) * w
 58	          for dx in -half..<=half {
 59	            sum += src[(row + x + dx) * ch + c_idx]
 60	          }
 61	        }
 62	        let mean = sum / inner_count
 63	        let threshold = mean - c
 64	        let v = src[(y * w + x) * ch + c_idx]
 65	        if v > threshold {
 66	          write_u16_le_c(out, (y * w + x) * bpp + c_idx * 2, 65535)
 67	        }
 68	      }
 69	    }
 70	    for y in 0..<h {
 71	      let y_inner = y >= half && y < h - half
 72	      for x in 0..<w {
 73	        if y_inner && x >= half && x < w - half {
 74	          continue
 75	        }
 76	        let mut sum = 0
 77	        let mut count = 0
 78	        for dy in -half..<=half {
 79	          for dx in -half..<=half {
 80	            let ny = y + dy
 81	            let nx = x + dx
 82	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
 83	              sum += src[(ny * w + nx) * ch + c_idx]
 84	              count += 1
 85	            }
 86	          }
 87	        }
 88	        let mean = if count > 0 { sum / count } else { 0 }
 89	        let threshold = mean - c
 90	        let v = src[(y * w + x) * ch + c_idx]
 91	        if v > threshold {
 92	          write_u16_le_c(out, (y * w + x) * bpp + c_idx * 2, 65535)
 93	        }
 94	      }
 95	    }
 96	  }
 97	  if ch == 4 {
 98	    for i in 0..<n {	<-- UNCOVERED
 99	      let alpha_off = i * bpp + 6
100	      out[alpha_off] = img.data[alpha_off]
101	      out[alpha_off + 1] = img.data[alpha_off + 1]
102	    }
103	  }
104	  @types.Image16::{
105	    width: w,
106	    height: h,
107	    channels: ch,
108	    data: Bytes::from_array(out),
109	  }
110	}
   	…

115	pub fn adaptive_threshold_mean_f(
116	  img : @types.ImageF,
117	  block_size : Int,
118	  c : Float,
119	) -> @types.ImageF raise @types.LoadError {
120	  if block_size < 3 || block_size % 2 == 0 {
121	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")	<-- UNCOVERED
122	  }
123	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
124	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
125	      "adaptive_threshold_mean_f 仅支持 channels=1/3/4",
126	    )
127	  }
128	  let ch = img.channels
129	  let color_ch = if ch >= 3 { 3 } else { 1 }
130	  let w = img.width
131	  let h = img.height
132	  let n = w * h
133	  let bpp = ch * 4
134	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
135	  let half = block_size / 2
136	  let src : Array[Float] = Array::make(n * ch, 0.0F)
137	  for i in 0..<(n * ch) {
138	    src[i] = read_float_le(img.data, i * 4)
139	  }
140	  let inner_count = Float::from_int(block_size * block_size)
141	  for c_idx in 0..<color_ch {
142	    for y in half..<(h - half) {
143	      for x in half..<(w - half) {
144	        let mut sum = 0.0F
145	        for dy in -half..<=half {
146	          let row = (y + dy) * w
147	          for dx in -half..<=half {
148	            sum += src[(row + x + dx) * ch + c_idx]
149	          }
150	        }
151	        let mean = sum / inner_count
152	        let threshold = mean - c
153	        let v = src[(y * w + x) * ch + c_idx]
154	        if v > threshold {
155	          write_f32_le(out, (y * w + x) * bpp + c_idx * 4, 1.0F)
156	        }
157	      }
158	    }
159	    for y in 0..<h {
160	      let y_inner = y >= half && y < h - half
161	      for x in 0..<w {
162	        if y_inner && x >= half && x < w - half {
163	          continue
164	        }
165	        let mut sum = 0.0F
166	        let mut count = 0.0F
167	        for dy in -half..<=half {
168	          for dx in -half..<=half {
169	            let ny = y + dy
170	            let nx = x + dx
171	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
172	              sum += src[(ny * w + nx) * ch + c_idx]
173	              count += 1.0F
174	            }
175	          }
176	        }
177	        let mean = if count > 0.0F { sum / count } else { 0.0F }
178	        let threshold = mean - c
179	        let v = src[(y * w + x) * ch + c_idx]
180	        if v > threshold {
181	          write_f32_le(out, (y * w + x) * bpp + c_idx * 4, 1.0F)
182	        }
183	      }
184	    }
185	  }
186	  if ch == 4 {
187	    for i in 0..<n {	<-- UNCOVERED
188	      let alpha_off = i * bpp + 12
189	      for b in 0..<4 {
190	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
191	      }
192	    }
193	  }
194	  @types.ImageF::{
195	    width: w,
196	    height: h,
197	    channels: ch,
198	    data: Bytes::from_array(out),
199	  }
200	}
   	…

204	pub fn adaptive_threshold_gaussian_16(
205	  img : @types.Image16,
206	  block_size : Int,
207	  c : Int,
208	) -> @types.Image16 raise @types.LoadError {
209	  // 输入验证
210	  if img.width <= 0 || img.height <= 0 {
211	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
212	      "adaptive_threshold_gaussian_16: 图像尺寸必须大于0",
213	    )
214	  }
215	  if block_size < 3 || block_size % 2 == 0 {
216	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")	<-- UNCOVERED
217	  }
218	  let expected_size = img.width * img.height * img.channels * 2
219	  if img.data.length() != expected_size {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "adaptive_threshold_gaussian_16: 数据大小不匹配，期望 " +
222	      expected_size.to_string() +	<-- UNCOVERED
223	      "，实际 " +
224	      img.data.length().to_string(),	<-- UNCOVERED
225	    )
226	  }
227	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
228	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
229	      "adaptive_threshold_gaussian_16 仅支持 channels=1/3/4",
230	    )
231	  }
232	  let ch = img.channels
233	  let color_ch = if ch >= 3 { 3 } else { 1 }
234	  let w = img.width
235	  let h = img.height
236	  let n = w * h
237	  let bpp = ch * 2
238	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
239	  let half = block_size / 2
240	  let sigma = Float::from_int(half) / 2.0F
241	  let two_sigma_sq = 2.0F * sigma * sigma
242	  let weights : Array[Float] = Array::make(block_size * block_size, 0.0F)
243	  let mut total_weight = 0.0F
244	  for dy in 0..<block_size {
245	    for dx in 0..<block_size {
246	      let fy = Float::from_int(dy - half)
247	      let fx = Float::from_int(dx - half)
248	      let wt = @math.expf(-(fx * fx + fy * fy) / two_sigma_sq)
249	      weights[dy * block_size + dx] = wt
250	      total_weight += wt
251	    }
252	  }
253	  let src : Array[Float] = Array::make(n * ch, 0.0F)
254	  for i in 0..<(n * ch) {
255	    src[i] = Float::from_int(read_u16_le_c(img.data, i * 2))
256	  }
257	  for c_idx in 0..<color_ch {
258	    for y in half..<(h - half) {
259	      for x in half..<(w - half) {
260	        let mut sum = 0.0F
261	        for dy in 0..<block_size {
262	          let row = (y + dy - half) * w
263	          let wbase = dy * block_size
264	          for dx in 0..<block_size {
265	            sum += src[(row + x + dx - half) * ch + c_idx] * weights[wbase + dx]
266	          }
267	        }
268	        let mean = sum / total_weight
269	        let threshold = mean - Float::from_int(c)
270	        let v = src[(y * w + x) * ch + c_idx]
271	        if v > threshold {
272	          write_u16_le_c(out, (y * w + x) * bpp + c_idx * 2, 65535)
273	        }
274	      }
275	    }
276	    for y in 0..<h {
277	      let y_inner = y >= half && y < h - half
278	      for x in 0..<w {
279	        if y_inner && x >= half && x < w - half {
280	          continue
281	        }
282	        let mut sum = 0.0F
283	        let mut weight_sum = 0.0F
284	        for dy in -half..<=half {
285	          for dx in -half..<=half {
286	            let ny = y + dy
287	            let nx = x + dx
288	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
289	              let weight = weights[(dy + half) * block_size + (dx + half)]
290	              sum += src[(ny * w + nx) * ch + c_idx] * weight
291	              weight_sum += weight
292	            }
293	          }
294	        }
295	        let mean = if weight_sum > 0.0F { sum / weight_sum } else { 0.0F }
296	        let threshold = mean - Float::from_int(c)
297	        let v = src[(y * w + x) * ch + c_idx]
298	        if v > threshold {
299	          write_u16_le_c(out, (y * w + x) * bpp + c_idx * 2, 65535)
300	        }
301	      }
302	    }
303	  }
304	  if ch == 4 {
305	    for i in 0..<n {	<-- UNCOVERED
306	      let alpha_off = i * bpp + 6
307	      out[alpha_off] = img.data[alpha_off]
308	      out[alpha_off + 1] = img.data[alpha_off + 1]
309	    }
310	  }
311	  @types.Image16::{
312	    width: w,
313	    height: h,
314	    channels: ch,
315	    data: Bytes::from_array(out),
316	  }
317	}
   	…

321	pub fn adaptive_threshold_gaussian_f(
322	  img : @types.ImageF,
323	  block_size : Int,
324	  c : Float,
325	) -> @types.ImageF raise @types.LoadError {
326	  if block_size < 3 || block_size % 2 == 0 {
327	    raise @types.LoadError::DecodeFailed("block_size 必须为 >=3 的奇数")	<-- UNCOVERED
328	  }
329	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
330	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
331	      "adaptive_threshold_gaussian_f 仅支持 channels=1/3/4",
332	    )
333	  }
334	  let ch = img.channels
335	  let color_ch = if ch >= 3 { 3 } else { 1 }
336	  let w = img.width
337	  let h = img.height
338	  let n = w * h
339	  let bpp = ch * 4
340	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
341	  let half = block_size / 2
342	  let sigma = Float::from_int(half) / 2.0F
343	  let two_sigma_sq = 2.0F * sigma * sigma
344	  let weights : Array[Float] = Array::make(block_size * block_size, 0.0F)
345	  let mut total_weight = 0.0F
346	  for dy in 0..<block_size {
347	    for dx in 0..<block_size {
348	      let fy = Float::from_int(dy - half)
349	      let fx = Float::from_int(dx - half)
350	      let wt = @math.expf(-(fx * fx + fy * fy) / two_sigma_sq)
351	      weights[dy * block_size + dx] = wt
352	      total_weight += wt
353	    }
354	  }
355	  let src : Array[Float] = Array::make(n * ch, 0.0F)
356	  for i in 0..<(n * ch) {
357	    src[i] = read_float_le(img.data, i * 4)
358	  }
359	  for c_idx in 0..<color_ch {
360	    for y in half..<(h - half) {
361	      for x in half..<(w - half) {
362	        let mut sum = 0.0F
363	        for dy in 0..<block_size {
364	          let row = (y + dy - half) * w
365	          let wbase = dy * block_size
366	          for dx in 0..<block_size {
367	            sum += src[(row + x + dx - half) * ch + c_idx] * weights[wbase + dx]
368	          }
369	        }
370	        let mean = sum / total_weight
371	        let threshold = mean - c
372	        let v = src[(y * w + x) * ch + c_idx]
373	        if v > threshold {
374	          write_f32_le(out, (y * w + x) * bpp + c_idx * 4, 1.0F)
375	        }
376	      }
377	    }
378	    for y in 0..<h {
379	      let y_inner = y >= half && y < h - half
380	      for x in 0..<w {
381	        if y_inner && x >= half && x < w - half {
382	          continue
383	        }
384	        let mut sum = 0.0F
385	        let mut weight_sum = 0.0F
386	        for dy in -half..<=half {
387	          for dx in -half..<=half {
388	            let ny = y + dy
389	            let nx = x + dx
390	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
391	              let weight = weights[(dy + half) * block_size + (dx + half)]
392	              sum += src[(ny * w + nx) * ch + c_idx] * weight
393	              weight_sum += weight
394	            }
395	          }
396	        }
397	        let mean = if weight_sum > 0.0F { sum / weight_sum } else { 0.0F }
398	        let threshold = mean - c
399	        let v = src[(y * w + x) * ch + c_idx]
400	        if v > threshold {
401	          write_f32_le(out, (y * w + x) * bpp + c_idx * 4, 1.0F)
402	        }
403	      }
404	    }
405	  }
406	  if ch == 4 {
407	    for i in 0..<n {	<-- UNCOVERED
408	      let alpha_off = i * bpp + 12
409	      for b in 0..<4 {
410	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
411	      }
412	    }
413	  }
414	  @types.ImageF::{
415	    width: w,
416	    height: h,
417	    channels: ch,
418	    data: Bytes::from_array(out),
419	  }
420	}
   	…

425	pub fn threshold_otsu_16(
426	  img : @types.Image16,
427	) -> @types.Image16 raise @types.LoadError {
428	  // 输入验证
429	  if img.width <= 0 || img.height <= 0 {
430	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
431	      "threshold_otsu_16: 图像尺寸必须大于0",
432	    )
433	  }
434	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
435	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
436	      "threshold_otsu_16 仅支持 channels=1/3/4",
437	    )
438	  }
439	  let expected_size = img.width * img.height * img.channels * 2
440	  if img.data.length() != expected_size {
441	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
442	      "threshold_otsu_16: 数据大小不匹配，期望 " +
443	      expected_size.to_string() +	<-- UNCOVERED
444	      "，实际 " +
445	      img.data.length().to_string(),	<-- UNCOVERED
446	    )
447	  }
448	  let ch = img.channels
449	  let color_ch = if ch >= 3 { 3 } else { 1 }
450	  let w = img.width
451	  let h = img.height
452	  let n = w * h
453	  let bpp = ch * 2
454	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
455	  for c_idx in 0..<color_ch {
456	    let hist : Array[Int] = Array::make(65536, 0)
457	    for i in 0..<n {
458	      hist[read_u16_le_c(img.data, i * bpp + c_idx * 2)] += 1
459	    }
460	    let mut total = 0
461	    let mut sum = 0
462	    for t in 0..<65536 {
463	      total += hist[t]
464	      sum += t * hist[t]
465	    }
466	    let mut best_t = 0
467	    let mut best_var = -1.0F
468	    let mut w0 = 0
469	    let mut sum0 = 0
470	    for t in 0..<65536 {
471	      w0 += hist[t]
472	      sum0 += t * hist[t]
473	      if w0 == 0 || w0 == total {
474	        continue
475	      }
476	      let w1 = total - w0
477	      let sum1 = sum - sum0
478	      let mean0 = Float::from_int(sum0) / Float::from_int(w0)
479	      let mean1 = Float::from_int(sum1) / Float::from_int(w1)
480	      let var_between = Float::from_int(w0) *
481	        Float::from_int(w1) *
482	        (mean0 - mean1) *
483	        (mean0 - mean1)
484	      if var_between > best_var {
485	        best_var = var_between
486	        best_t = t
487	      }
488	    }
489	    for i in 0..<n {
490	      let v = read_u16_le_c(img.data, i * bpp + c_idx * 2)
491	      if v > best_t {
492	        write_u16_le_c(out, i * bpp + c_idx * 2, 65535)
493	      }
494	    }
495	  }
496	  if ch == 4 {
497	    for i in 0..<n {
498	      let alpha_off = i * bpp + 6
499	      out[alpha_off] = img.data[alpha_off]
500	      out[alpha_off + 1] = img.data[alpha_off + 1]
501	    }
502	  }
503	  @types.Image16::{
504	    width: w,
505	    height: h,
506	    channels: ch,
507	    data: Bytes::from_array(out),
508	  }
509	}
   	…

514	pub fn threshold_otsu_f(
515	  img : @types.ImageF,
516	) -> @types.ImageF raise @types.LoadError {
517	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
518	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
519	      "threshold_otsu_f 仅支持 channels=1/3/4",
520	    )
521	  }
522	  let ch = img.channels
523	  let color_ch = if ch >= 3 { 3 } else { 1 }
524	  let w = img.width
525	  let h = img.height
526	  let n = w * h
527	  let bpp = ch * 4
528	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
529	  for c_idx in 0..<color_ch {
530	    let hist : Array[Int] = Array::make(256, 0)
531	    let src : Array[Float] = Array::make(n, 0.0F)
532	    for i in 0..<n {
533	      let v = read_float_le(img.data, i * bpp + c_idx * 4)
534	      src[i] = v
535	      let bin = if v < 0.0F {
536	        0	<-- UNCOVERED
537	      } else if v >= 1.0F {
538	        255	<-- UNCOVERED
539	      } else {
540	        (v * 255.0F + 0.5F).to_int()
541	      }
542	      hist[bin] += 1
543	    }
544	    let mut total = 0
545	    let mut sum = 0
546	    for t in 0..<256 {
547	      total += hist[t]
548	      sum += t * hist[t]
549	    }
550	    let mut best_t = 0
551	    let mut best_var = -1.0F
552	    let mut w0 = 0
553	    let mut sum0 = 0
554	    for t in 0..<256 {
555	      w0 += hist[t]
556	      sum0 += t * hist[t]
557	      if w0 == 0 || w0 == total {
558	        continue
559	      }
560	      let w1 = total - w0
561	      let sum1 = sum - sum0
562	      let mean0 = Float::from_int(sum0) / Float::from_int(w0)
563	      let mean1 = Float::from_int(sum1) / Float::from_int(w1)
564	      let var_between = Float::from_int(w0) *
565	        Float::from_int(w1) *
566	        (mean0 - mean1) *
567	        (mean0 - mean1)
568	      if var_between > best_var {
569	        best_var = var_between
570	        best_t = t
571	      }
572	    }
573	    let threshold_f = Float::from_int(best_t) / 255.0F
574	    for i in 0..<n {
575	      if src[i] > threshold_f {
576	        write_f32_le(out, i * bpp + c_idx * 4, 1.0F)
577	      }
578	    }
579	  }
580	  if ch == 4 {
581	    for i in 0..<n {
582	      let alpha_off = i * bpp + 12
583	      for b in 0..<4 {
584	        out[alpha_off + b] = img.data[alpha_off + b]
585	      }
586	    }
587	  }
588	  @types.ImageF::{
589	    width: w,
590	    height: h,
591	    channels: ch,
592	    data: Bytes::from_array(out),
593	  }
594	}
   	…

6 uncovered line(s) in src\process\color\clahe.mbt:

 8	pub fn clahe(
 9	  img : @types.Image,
10	  tile_size : Int,
11	  clip_limit : Float,
12	) -> @types.Image raise @types.LoadError {
13	  // 输入验证
14	  if img.width <= 0 || img.height <= 0 {
15	    raise @types.LoadError::DecodeFailed("clahe: 图像尺寸必须大于0")	<-- UNCOVERED
16	  }
17	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
18	    raise @types.LoadError::DecodeFailed("clahe 仅支持 channels=1/3/4")
19	  }
20	  let expected_size = img.width * img.height * img.channels
21	  if img.data.length() != expected_size {
22	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
23	      "clahe: 数据大小不匹配，期望 " +
24	      expected_size.to_string() +	<-- UNCOVERED
25	      "，实际 " +
26	      img.data.length().to_string(),	<-- UNCOVERED
27	    )
28	  }
29	  if tile_size < 2 {
30	    raise @types.LoadError::DecodeFailed("tile_size 必须 >= 2")
31	  }
32	  if clip_limit <= 0.0 {
33	    raise @types.LoadError::DecodeFailed("clip_limit 必须大于0")	<-- UNCOVERED
34	  }
35	  let ch = img.channels
36	  let color_ch = if ch >= 3 { 3 } else { 1 }
37	  let n = img.width * img.height
38	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
39	  for c in 0..<color_ch {
40	    clahe_channel(img, out, c, ch, tile_size, clip_limit)
41	  }
42	  if ch == 4 {
43	    for i in 0..<n {
44	      out[i * 4 + 3] = img.data[i * 4 + 3]
45	    }
46	  }
47	  @types.Image::{
48	    width: img.width,
49	    height: img.height,
50	    channels: ch,
51	    data: Bytes::from_array(out),
52	  }
53	}
  	…

 56	fn clahe_channel(
 57	  img : @types.Image,
 58	  out : Array[Byte],
 59	  channel : Int,
 60	  ch : Int,
 61	  tile_size : Int,
 62	  clip_limit : Float,
 63	) -> Unit {
 64	  let w = img.width
 65	  let h = img.height
 66	  let n = w * h
 67	  // 预计算 to_int 数组
 68	  let data_int : Array[Int] = Array::make(n * ch, 0)
 69	  for i in 0..<(n * ch) {
 70	    data_int[i] = img.data[i].to_int()
 71	  }
 72	  // 块数
 73	  let ntx = (w + tile_size - 1) / tile_size
 74	  let nty = (h + tile_size - 1) / tile_size
 75	  let n_tiles = ntx * nty
 76	  let luts : Array[Array[Int]] = Array::make(n_tiles, Array::make(256, 0))
 77	  // 计算每块的直方图和 LUT
 78	  for ty in 0..<nty {
 79	    for tx in 0..<ntx {
 80	      let hist : Array[Int] = Array::make(256, 0)
 81	      let x0 = tx * tile_size
 82	      let y0 = ty * tile_size
 83	      let x1 = if x0 + tile_size < w { x0 + tile_size } else { w }
 84	      let y1 = if y0 + tile_size < h { y0 + tile_size } else { h }
 85	      let tile_pixels = (x1 - x0) * (y1 - y0)
 86	      for y in y0..<y1 {
 87	        let row_base = y * w
 88	        for x in x0..<x1 {
 89	          let v = data_int[(row_base + x) * ch + channel]
 90	          hist[v] += 1
 91	        }
 92	      }
 93	      // 裁剪直方图
 94	      let clip_count = (Float::from_int(tile_pixels) * clip_limit / 256.0).to_int()
 95	      let mut excess = 0
 96	      for i in 0..<256 {
 97	        if hist[i] > clip_count {
 98	          excess += hist[i] - clip_count
 99	          hist[i] = clip_count
100	        }
101	      }
102	      // 均匀分配多余的像素
103	      let redistribute = excess / 256
104	      let mut remainder = excess - redistribute * 256
105	      for i in 0..<256 {
106	        hist[i] += redistribute
107	        if remainder > 0 {
108	          hist[i] += 1
109	          remainder -= 1
110	        }
111	      }
112	      // 计算 CDF 并归一化到 0-255
113	      let lut = luts[ty * ntx + tx]
114	      let mut cdf = 0
115	      for i in 0..<256 {
116	        cdf += hist[i]
117	        lut[i] = if tile_pixels > 0 {
118	          clamp_i(cdf * 255 / tile_pixels, 0, 255)
119	        } else {
120	          i	<-- UNCOVERED
121	        }
122	      }
123	    }
124	  }
125	  // 预计算 tile 坐标和权重
126	  let inv_tile_size = 1.0F / Float::from_int(tile_size)
127	  let tx_arr : Array[Int] = Array::make(w, 0)
128	  let wx_arr : Array[Float] = Array::make(w, 0.0F)
129	  for x in 0..<w {
130	    let fx = Float::from_int(x) * inv_tile_size - 0.5F
131	    let t = fx.to_int()
132	    tx_arr[x] = t
133	    wx_arr[x] = fx - Float::from_int(t)
134	  }
135	  let ty_arr : Array[Int] = Array::make(h, 0)
136	  let wy_arr : Array[Float] = Array::make(h, 0.0F)
137	  for y in 0..<h {
138	    let fy = Float::from_int(y) * inv_tile_size - 0.5F
139	    let t = fy.to_int()
140	    ty_arr[y] = t
141	    wy_arr[y] = fy - Float::from_int(t)
142	  }
143	  // 双线性插值应用 LUT
144	  for y in 0..<h {
145	    let ty = ty_arr[y]
146	    let wy = wy_arr[y]
147	    let row_base = y * w
148	    let wy_inv = 1.0F - wy
149	    for x in 0..<w {
150	      let tx = tx_arr[x]
151	      let wx = wx_arr[x]
152	      let v = data_int[(row_base + x) * ch + channel]
153	      // 4 个相邻块
154	      let t00 = tile_clamp(tx, ty, ntx, nty)
155	      let t01 = tile_clamp(tx + 1, ty, ntx, nty)
156	      let t10 = tile_clamp(tx, ty + 1, ntx, nty)
157	      let t11 = tile_clamp(tx + 1, ty + 1, ntx, nty)
158	      let v00 = Float::from_int(luts[t00][v])
159	      let v01 = Float::from_int(luts[t01][v])
160	      let v10 = Float::from_int(luts[t10][v])
161	      let v11 = Float::from_int(luts[t11][v])
162	      let wx_inv = 1.0F - wx
163	      let top = v00 * wx_inv + v01 * wx
164	      let bot = v10 * wx_inv + v11 * wx
165	      let result = clamp_i((top * wy_inv + bot * wy).to_int(), 0, 255)
166	      out[(row_base + x) * ch + channel] = result.to_byte()
167	    }
168	  }
169	}
   	…

67 uncovered line(s) in src\process\color\color_16f.mbt:

26	pub fn adjust_brightness_16(
27	  img : @types.Image16,
28	  delta : Int,
29	) -> @types.Image16 raise @types.LoadError {
30	  // 输入验证
31	  if img.width <= 0 || img.height <= 0 {
32	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
33	      "adjust_brightness_16: 图像尺寸必须大于0",
34	    )
35	  }
36	  if img.channels < 1 || img.channels > 4 {
37	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
38	      "adjust_brightness_16: 通道数必须在1-4之间",
39	    )
40	  }
41	  let expected_size = img.width * img.height * img.channels * 2
42	  if img.data.length() != expected_size {
43	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
44	      "adjust_brightness_16: 数据大小不匹配，期望 " +
45	      expected_size.to_string() +	<-- UNCOVERED
46	      "，实际 " +
47	      img.data.length().to_string(),	<-- UNCOVERED
48	    )
49	  }
50	  let n = img.width * img.height
51	  let ch = img.channels
52	  let color_ch = if ch >= 3 { 3 } else { ch }
53	  let bpp = ch * 2
54	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
55	  for i in 0..<n {
56	    for c in 0..<color_ch {
57	      let offset = i * bpp + c * 2
58	      let val = read_u16_le_c(img.data, offset)
59	      let adjusted = if val + delta < 0 {
60	        0
61	      } else if val + delta > 65535 {
62	        65535
63	      } else {
64	        val + delta
65	      }
66	      write_u16_le_c(out, offset, adjusted)
67	    }
68	    if ch == 4 {
69	      let alpha_off = i * bpp + 6
70	      out[alpha_off] = img.data[alpha_off]
71	      out[alpha_off + 1] = img.data[alpha_off + 1]
72	    }
73	  }
74	  @types.Image16::{
75	    width: img.width,
76	    height: img.height,
77	    channels: ch,
78	    data: Bytes::from_array(out),
79	  }
80	}
  	…

 84	pub fn adjust_brightness_f(
 85	  img : @types.ImageF,
 86	  delta : Float,
 87	) -> @types.ImageF raise @types.LoadError {
 88	  // 输入验证
 89	  if img.width <= 0 || img.height <= 0 {
 90	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 91	      "adjust_brightness_f: 图像尺寸必须大于0",
 92	    )
 93	  }
 94	  if img.channels < 1 || img.channels > 4 {
 95	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 96	      "adjust_brightness_f: 通道数必须在1-4之间",
 97	    )
 98	  }
 99	  let expected_size = img.width * img.height * img.channels * 4
100	  if img.data.length() != expected_size {
101	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
102	      "adjust_brightness_f: 数据大小不匹配，期望 " +
103	      expected_size.to_string() +	<-- UNCOVERED
104	      "，实际 " +
105	      img.data.length().to_string(),	<-- UNCOVERED
106	    )
107	  }
108	  let n = img.width * img.height
109	  let ch = img.channels
110	  let color_ch = if ch >= 3 { 3 } else { ch }
111	  let bpp = ch * 4
112	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
113	  for i in 0..<n {
114	    for c in 0..<color_ch {
115	      let offset = i * bpp + c * 4
116	      let val = read_float_le(img.data, offset)
117	      write_f32_le(out, offset, val + delta)
118	    }
119	    if ch == 4 {
120	      let alpha_off = i * bpp + 12
121	      for b in 0..<4 {
122	        out[alpha_off + b] = img.data[alpha_off + b]
123	      }
124	    }
125	  }
126	  @types.ImageF::{
127	    width: img.width,
128	    height: img.height,
129	    channels: ch,
130	    data: Bytes::from_array(out),
131	  }
132	}
   	…

136	pub fn adjust_contrast_16(
137	  img : @types.Image16,
138	  factor : Float,
139	) -> @types.Image16 raise @types.LoadError {
140	  // 输入验证
141	  if img.width <= 0 || img.height <= 0 {
142	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
143	      "adjust_contrast_16: 图像尺寸必须大于0",
144	    )
145	  }
146	  if img.channels < 1 || img.channels > 4 {
147	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
148	      "adjust_contrast_16: 通道数必须在1-4之间",
149	    )
150	  }
151	  let expected_size = img.width * img.height * img.channels * 2
152	  if img.data.length() != expected_size {
153	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
154	      "adjust_contrast_16: 数据大小不匹配，期望 " +
155	      expected_size.to_string() +	<-- UNCOVERED
156	      "，实际 " +
157	      img.data.length().to_string(),	<-- UNCOVERED
158	    )
159	  }
160	  if factor < 0.0F {
161	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
162	      "adjust_contrast_16: factor必须大于等于0",
163	    )
164	  }
165	  let n = img.width * img.height
166	  let ch = img.channels
167	  let color_ch = if ch >= 3 { 3 } else { ch }
168	  let bpp = ch * 2
169	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
170	  for i in 0..<n {
171	    for c in 0..<color_ch {
172	      let offset = i * bpp + c * 2
173	      let val = read_u16_le_c(img.data, offset)
174	      let adjusted = (Float::from_int(val - 32768) * factor + 32768.0F).to_int()
175	      let clamped = if adjusted < 0 {
176	        0
177	      } else if adjusted > 65535 {
178	        65535
179	      } else {
180	        adjusted
181	      }
182	      write_u16_le_c(out, offset, clamped)
183	    }
184	    if ch == 4 {
185	      let alpha_off = i * bpp + 6
186	      out[alpha_off] = img.data[alpha_off]
187	      out[alpha_off + 1] = img.data[alpha_off + 1]
188	    }
189	  }
190	  @types.Image16::{
191	    width: img.width,
192	    height: img.height,
193	    channels: ch,
194	    data: Bytes::from_array(out),
195	  }
196	}
   	…

200	pub fn adjust_contrast_f(
201	  img : @types.ImageF,
202	  factor : Float,
203	) -> @types.ImageF raise @types.LoadError {
204	  // 输入验证
205	  if img.width <= 0 || img.height <= 0 {
206	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
207	      "adjust_contrast_f: 图像尺寸必须大于0",
208	    )
209	  }
210	  if img.channels < 1 || img.channels > 4 {
211	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
212	      "adjust_contrast_f: 通道数必须在1-4之间",
213	    )
214	  }
215	  let expected_size = img.width * img.height * img.channels * 4
216	  if img.data.length() != expected_size {
217	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
218	      "adjust_contrast_f: 数据大小不匹配，期望 " +
219	      expected_size.to_string() +	<-- UNCOVERED
220	      "，实际 " +
221	      img.data.length().to_string(),	<-- UNCOVERED
222	    )
223	  }
224	  if factor < 0.0F {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "adjust_contrast_f: factor必须大于等于0",
227	    )
228	  }
229	  let n = img.width * img.height
230	  let ch = img.channels
231	  let color_ch = if ch >= 3 { 3 } else { ch }
232	  let bpp = ch * 4
233	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
234	  for i in 0..<n {
235	    for c in 0..<color_ch {
236	      let offset = i * bpp + c * 4
237	      let val = read_float_le(img.data, offset)
238	      write_f32_le(out, offset, (val - 0.5F) * factor + 0.5F)
239	    }
240	    if ch == 4 {
241	      let alpha_off = i * bpp + 12
242	      for b in 0..<4 {
243	        out[alpha_off + b] = img.data[alpha_off + b]
244	      }
245	    }
246	  }
247	  @types.ImageF::{
248	    width: img.width,
249	    height: img.height,
250	    channels: ch,
251	    data: Bytes::from_array(out),
252	  }
253	}
   	…

258	pub fn adjust_gamma_16(
259	  img : @types.Image16,
260	  gamma : Float,
261	) -> @types.Image16 raise @types.LoadError {
262	  // 输入验证
263	  if img.width <= 0 || img.height <= 0 {
264	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
265	      "adjust_gamma_16: 图像尺寸必须大于0",
266	    )
267	  }
268	  if img.channels < 1 || img.channels > 4 {
269	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
270	      "adjust_gamma_16: 通道数必须在1-4之间",
271	    )
272	  }
273	  let expected_size = img.width * img.height * img.channels * 2
274	  if img.data.length() != expected_size {
275	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
276	      "adjust_gamma_16: 数据大小不匹配，期望 " +
277	      expected_size.to_string() +	<-- UNCOVERED
278	      "，实际 " +
279	      img.data.length().to_string(),	<-- UNCOVERED
280	    )
281	  }
282	  if gamma <= 0.0F {
283	    raise @types.LoadError::DecodeFailed("adjust_gamma_16: gamma必须>0")	<-- UNCOVERED
284	  }
285	  let n = img.width * img.height
286	  let ch = img.channels
287	  let color_ch = if ch >= 3 { 3 } else { ch }
288	  let bpp = ch * 2
289	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
290	  for i in 0..<n {
291	    for c in 0..<color_ch {
292	      let offset = i * bpp + c * 2
293	      let val = read_u16_le_c(img.data, offset)
294	      let normalized = Float::from_int(val) / 65535.0F
295	      let corrected = @math.powf(normalized, gamma)
296	      let new_val = (corrected * 65535.0F + 0.5F).to_int()
297	      let clamped = if new_val < 0 {
298	        0	<-- UNCOVERED
299	      } else if new_val > 65535 {
300	        65535	<-- UNCOVERED
301	      } else {
302	        new_val
303	      }
304	      write_u16_le_c(out, offset, clamped)
305	    }
306	    if ch == 4 {
307	      let alpha_off = i * bpp + 6	<-- UNCOVERED
308	      out[alpha_off] = img.data[alpha_off]
309	      out[alpha_off + 1] = img.data[alpha_off + 1]
310	    }
311	  }
312	  @types.Image16::{
313	    width: img.width,
314	    height: img.height,
315	    channels: ch,
316	    data: Bytes::from_array(out),
317	  }
318	}
   	…

323	pub fn adjust_gamma_f(
324	  img : @types.ImageF,
325	  gamma : Float,
326	) -> @types.ImageF raise @types.LoadError {
327	  // 输入验证
328	  if img.width <= 0 || img.height <= 0 {
329	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
330	      "adjust_gamma_f: 图像尺寸必须大于0",
331	    )
332	  }
333	  if img.channels < 1 || img.channels > 4 {
334	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
335	      "adjust_gamma_f: 通道数必须在1-4之间",
336	    )
337	  }
338	  let expected_size = img.width * img.height * img.channels * 4
339	  if img.data.length() != expected_size {
340	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
341	      "adjust_gamma_f: 数据大小不匹配，期望 " +
342	      expected_size.to_string() +	<-- UNCOVERED
343	      "，实际 " +
344	      img.data.length().to_string(),	<-- UNCOVERED
345	    )
346	  }
347	  if gamma <= 0.0F {
348	    raise @types.LoadError::DecodeFailed("adjust_gamma_f: gamma必须>0")	<-- UNCOVERED
349	  }
350	  let n = img.width * img.height
351	  let ch = img.channels
352	  let color_ch = if ch >= 3 { 3 } else { ch }
353	  let bpp = ch * 4
354	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
355	  for i in 0..<n {
356	    for c in 0..<color_ch {
357	      let offset = i * bpp + c * 4
358	      let val = read_float_le(img.data, offset)
359	      let corrected = if val < 0.0F {
360	        0.0F	<-- UNCOVERED
361	      } else if val > 1.0F {
362	        1.0F	<-- UNCOVERED
363	      } else {
364	        @math.powf(val, gamma)
365	      }
366	      write_f32_le(out, offset, corrected)
367	    }
368	    if ch == 4 {
369	      let alpha_off = i * bpp + 12	<-- UNCOVERED
370	      for b in 0..<4 {
371	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
372	      }
373	    }
374	  }
375	  @types.ImageF::{
376	    width: img.width,
377	    height: img.height,
378	    channels: ch,
379	    data: Bytes::from_array(out),
380	  }
381	}
   	…

385	pub fn invert_16(img : @types.Image16) -> @types.Image16 raise @types.LoadError {
386	  // 输入验证
387	  if img.width <= 0 || img.height <= 0 {
388	    raise @types.LoadError::DecodeFailed("invert_16: 图像尺寸必须大于0")	<-- UNCOVERED
389	  }
390	  if img.channels < 1 || img.channels > 4 {
391	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
392	      "invert_16: 通道数必须在1-4之间",
393	    )
394	  }
395	  let expected_size = img.width * img.height * img.channels * 2
396	  if img.data.length() != expected_size {
397	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
398	      "invert_16: 数据大小不匹配，期望 " +
399	      expected_size.to_string() +	<-- UNCOVERED
400	      "，实际 " +
401	      img.data.length().to_string(),	<-- UNCOVERED
402	    )
403	  }
404	  let n = img.width * img.height
405	  let ch = img.channels
406	  let color_ch = if ch >= 3 { 3 } else { ch }
407	  let bpp = ch * 2
408	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
409	  for i in 0..<n {
410	    for c in 0..<color_ch {
411	      let offset = i * bpp + c * 2
412	      let val = read_u16_le_c(img.data, offset)
413	      write_u16_le_c(out, offset, 65535 - val)
414	    }
415	    if ch == 4 {
416	      let alpha_off = i * bpp + 6	<-- UNCOVERED
417	      out[alpha_off] = img.data[alpha_off]
418	      out[alpha_off + 1] = img.data[alpha_off + 1]
419	    }
420	  }
421	  @types.Image16::{
422	    width: img.width,
423	    height: img.height,
424	    channels: ch,
425	    data: Bytes::from_array(out),
426	  }
427	}
   	…

431	pub fn invert_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
432	  // 输入验证
433	  if img.width <= 0 || img.height <= 0 {
434	    raise @types.LoadError::DecodeFailed("invert_f: 图像尺寸必须大于0")	<-- UNCOVERED
435	  }
436	  if img.channels < 1 || img.channels > 4 {
437	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
438	      "invert_f: 通道数必须在1-4之间",
439	    )
440	  }
441	  let expected_size = img.width * img.height * img.channels * 4
442	  if img.data.length() != expected_size {
443	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
444	      "invert_f: 数据大小不匹配，期望 " +
445	      expected_size.to_string() +	<-- UNCOVERED
446	      "，实际 " +
447	      img.data.length().to_string(),	<-- UNCOVERED
448	    )
449	  }
450	  let n = img.width * img.height
451	  let ch = img.channels
452	  let color_ch = if ch >= 3 { 3 } else { ch }
453	  let bpp = ch * 4
454	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
455	  for i in 0..<n {
456	    for c in 0..<color_ch {
457	      let offset = i * bpp + c * 4
458	      let val = read_float_le(img.data, offset)
459	      write_f32_le(out, offset, 1.0F - val)
460	    }
461	    if ch == 4 {
462	      let alpha_off = i * bpp + 12	<-- UNCOVERED
463	      for b in 0..<4 {
464	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
465	      }
466	    }
467	  }
468	  @types.ImageF::{
469	    width: img.width,
470	    height: img.height,
471	    channels: ch,
472	    data: Bytes::from_array(out),
473	  }
474	}
   	…

479	pub fn to_grayscale_16(
480	  img : @types.Image16,
481	) -> @types.Image16 raise @types.LoadError {
482	  // 输入验证
483	  if img.width <= 0 || img.height <= 0 {
484	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
485	      "to_grayscale_16: 图像尺寸必须大于0",
486	    )
487	  }
488	  if img.channels < 1 || img.channels > 4 {
489	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
490	      "to_grayscale_16: 通道数必须在1-4之间",
491	    )
492	  }
493	  let expected_size = img.width * img.height * img.channels * 2
494	  if img.data.length() != expected_size {
495	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
496	      "to_grayscale_16: 数据大小不匹配，期望 " +
497	      expected_size.to_string() +	<-- UNCOVERED
498	      "，实际 " +
499	      img.data.length().to_string(),	<-- UNCOVERED
500	    )
501	  }
502	  let n = img.width * img.height
503	  let ch = img.channels
504	  let bpp = ch * 2
505	  let out : Array[Byte] = Array::make(n * 2, b'\x00')
506	  for i in 0..<n {
507	    if ch >= 3 {
508	      let r = read_u16_le_c(img.data, i * bpp)
509	      let g = read_u16_le_c(img.data, i * bpp + 2)
510	      let b = read_u16_le_c(img.data, i * bpp + 4)
511	      let y = (Float::from_int(r) * 0.299F +
512	      Float::from_int(g) * 0.587F +
513	      Float::from_int(b) * 0.114F +
514	      0.5F).to_int()
515	      let clamped = if y < 0 { 0 } else if y > 65535 { 65535 } else { y }	<-- UNCOVERED
516	      write_u16_le_c(out, i * 2, clamped)
517	    } else {
518	      let val = read_u16_le_c(img.data, i * bpp)	<-- UNCOVERED
519	      write_u16_le_c(out, i * 2, val)
520	    }
521	  }
522	  @types.Image16::{
523	    width: img.width,
524	    height: img.height,
525	    channels: 1,
526	    data: Bytes::from_array(out),
527	  }
528	}
   	…

533	pub fn to_grayscale_f(
534	  img : @types.ImageF,
535	) -> @types.ImageF raise @types.LoadError {
536	  // 输入验证
537	  if img.width <= 0 || img.height <= 0 {
538	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
539	      "to_grayscale_f: 图像尺寸必须大于0",
540	    )
541	  }
542	  if img.channels < 1 || img.channels > 4 {
543	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
544	      "to_grayscale_f: 通道数必须在1-4之间",
545	    )
546	  }
547	  let expected_size = img.width * img.height * img.channels * 4
548	  if img.data.length() != expected_size {
549	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
550	      "to_grayscale_f: 数据大小不匹配，期望 " +
551	      expected_size.to_string() +	<-- UNCOVERED
552	      "，实际 " +
553	      img.data.length().to_string(),	<-- UNCOVERED
554	    )
555	  }
556	  let n = img.width * img.height
557	  let ch = img.channels
558	  let bpp = ch * 4
559	  let out : Array[Byte] = Array::make(n * 4, b'\x00')
560	  for i in 0..<n {
561	    if ch >= 3 {
562	      let r = read_float_le(img.data, i * bpp)
563	      let g = read_float_le(img.data, i * bpp + 4)
564	      let b = read_float_le(img.data, i * bpp + 8)
565	      let y = r * 0.299F + g * 0.587F + b * 0.114F
566	      write_f32_le(out, i * 4, y)
567	    } else {
568	      let val = read_float_le(img.data, i * bpp)	<-- UNCOVERED
569	      write_f32_le(out, i * 4, val)
570	    }
571	  }
572	  @types.ImageF::{
573	    width: img.width,
574	    height: img.height,
575	    channels: 1,
576	    data: Bytes::from_array(out),
577	  }
578	}
   	…

53 uncovered line(s) in src\process\color\color_adjust.mbt:

18	pub fn adjust_brightness(
19	  img : @types.Image,
20	  delta : Int,
21	) -> @types.Image raise @types.LoadError {
22	  // 输入验证
23	  if img.width <= 0 || img.height <= 0 {
24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
25	      "adjust_brightness: 图像尺寸必须大于0",
26	    )
27	  }
28	  if img.channels < 1 || img.channels > 4 {
29	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
30	      "adjust_brightness: 通道数必须在1-4之间",
31	    )
32	  }
33	  let expected_size = img.width * img.height * img.channels
34	  if img.data.length() != expected_size {
35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
36	      "adjust_brightness: 数据大小不匹配，期望 " +
37	      expected_size.to_string() +	<-- UNCOVERED
38	      "，实际 " +
39	      img.data.length().to_string(),	<-- UNCOVERED
40	    )
41	  }
42	  let n = img.width * img.height
43	  let ch = img.channels
44	  let color_ch = if ch >= 3 { 3 } else { ch }
45	  let lut : Array[Byte] = Array::make(256, b'\x00')
46	  for i in 0..<256 {
47	    lut[i] = clamp_byte(i + delta)
48	  }
49	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
50	  for i in 0..<n {
51	    for c in 0..<color_ch {
52	      out[i * ch + c] = lut[img.data[i * ch + c].to_int()]
53	    }
54	    if ch == 4 {
55	      out[i * ch + 3] = img.data[i * ch + 3]
56	    }
57	  }
58	  @types.Image::{
59	    width: img.width,
60	    height: img.height,
61	    channels: ch,
62	    data: Bytes::from_array(out),
63	  }
64	}
  	…

 70	pub fn adjust_contrast(
 71	  img : @types.Image,
 72	  factor : Float,
 73	) -> @types.Image raise @types.LoadError {
 74	  // 输入验证
 75	  if img.width <= 0 || img.height <= 0 {
 76	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 77	      "adjust_contrast: 图像尺寸必须大于0",
 78	    )
 79	  }
 80	  if img.channels < 1 || img.channels > 4 {
 81	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 82	      "adjust_contrast: 通道数必须在1-4之间",
 83	    )
 84	  }
 85	  let expected_size = img.width * img.height * img.channels
 86	  if img.data.length() != expected_size {
 87	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 88	      "adjust_contrast: 数据大小不匹配，期望 " +
 89	      expected_size.to_string() +	<-- UNCOVERED
 90	      "，实际 " +
 91	      img.data.length().to_string(),	<-- UNCOVERED
 92	    )
 93	  }
 94	  if factor < 0.0 {
 95	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 96	      "adjust_contrast: factor必须大于等于0",
 97	    )
 98	  }
 99	  let n = img.width * img.height
100	  let ch = img.channels
101	  let _ = if ch >= 3 { 3 } else { ch }
102	  let lut : Array[Byte] = Array::make(256, b'\x00')
103	  for i in 0..<256 {
104	    let adjusted = (Float::from_int(i - 128) * factor + 128.0F).to_int()
105	    lut[i] = clamp_byte(adjusted)
106	  }
107	  let total = n * ch
108	  let out : Array[Byte] = Array::make(total, b'\x00')
109	  let data = img.data
110	  if ch == 4 {
111	    // RGBA：处理 RGB，保留 A
112	    for i in 0..<n {
113	      let base = i * 4
114	      out[base] = lut[data[base].to_int()]
115	      out[base + 1] = lut[data[base + 1].to_int()]
116	      out[base + 2] = lut[data[base + 2].to_int()]
117	      out[base + 3] = data[base + 3]
118	    }
119	  } else if ch == 3 {
120	    // RGB
121	    for i in 0..<n {
122	      let base = i * 3
123	      out[base] = lut[data[base].to_int()]
124	      out[base + 1] = lut[data[base + 1].to_int()]
125	      out[base + 2] = lut[data[base + 2].to_int()]
126	    }
127	  } else {
128	    // 单通道
129	    for i in 0..<n {	<-- UNCOVERED
130	      out[i] = lut[data[i].to_int()]
131	    }
132	  }
133	  @types.Image::{
134	    width: img.width,
135	    height: img.height,
136	    channels: ch,
137	    data: Bytes::from_array(out),
138	  }
139	}
   	…

144	pub fn adjust_gamma(
145	  img : @types.Image,
146	  gamma : Float,
147	) -> @types.Image raise @types.LoadError {
148	  // 输入验证
149	  if img.width <= 0 || img.height <= 0 {
150	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
151	      "adjust_gamma: 图像尺寸必须大于0",
152	    )
153	  }
154	  if img.channels < 1 || img.channels > 4 {
155	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
156	      "adjust_gamma: 通道数必须在1-4之间",
157	    )
158	  }
159	  let expected_size = img.width * img.height * img.channels
160	  if img.data.length() != expected_size {
161	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
162	      "adjust_gamma: 数据大小不匹配，期望 " +
163	      expected_size.to_string() +	<-- UNCOVERED
164	      "，实际 " +
165	      img.data.length().to_string(),	<-- UNCOVERED
166	    )
167	  }
168	  if gamma <= 0.0 {
169	    raise @types.LoadError::DecodeFailed("adjust_gamma: gamma必须大于0")	<-- UNCOVERED
170	  }
171	  // 预计算 256 项查找表
172	  let lut : Array[Byte] = Array::make(256, b'\x00')
173	  for i in 0..<256 {
174	    let normalized = Float::from_int(i) / 255.0F
175	    let corrected = @math.powf(normalized, gamma)
176	    let val = (corrected * 255.0F + 0.5F).to_int()
177	    lut[i] = clamp_byte(val)
178	  }
179	  let n = img.width * img.height
180	  let ch = img.channels
181	  let color_ch = if ch >= 3 { 3 } else { ch }
182	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
183	  for i in 0..<n {
184	    for c in 0..<color_ch {
185	      out[i * ch + c] = lut[img.data[i * ch + c].to_int()]
186	    }
187	    if ch == 4 {
188	      out[i * ch + 3] = img.data[i * ch + 3]
189	    }
190	  }
191	  @types.Image::{
192	    width: img.width,
193	    height: img.height,
194	    channels: ch,
195	    data: Bytes::from_array(out),
196	  }
197	}
   	…

201	pub fn invert(img : @types.Image) -> @types.Image raise @types.LoadError {
202	  // 输入验证
203	  if img.width <= 0 || img.height <= 0 {
204	    raise @types.LoadError::DecodeFailed("invert: 图像尺寸必须大于0")	<-- UNCOVERED
205	  }
206	  if img.channels < 1 || img.channels > 4 {
207	    raise @types.LoadError::DecodeFailed("invert: 通道数必须在1-4之间")	<-- UNCOVERED
208	  }
209	  let expected_size = img.width * img.height * img.channels
210	  if img.data.length() != expected_size {
211	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
212	      "invert: 数据大小不匹配，期望 " +
213	      expected_size.to_string() +	<-- UNCOVERED
214	      "，实际 " +
215	      img.data.length().to_string(),	<-- UNCOVERED
216	    )
217	  }
218	  let n = img.width * img.height
219	  let ch = img.channels
220	  let color_ch = if ch >= 3 { 3 } else { ch }
221	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
222	  for i in 0..<n {
223	    for c in 0..<color_ch {
224	      out[i * ch + c] = (255 - img.data[i * ch + c].to_int()).to_byte()
225	    }
226	    if ch == 4 {
227	      out[i * ch + 3] = img.data[i * ch + 3]
228	    }
229	  }
230	  @types.Image::{
231	    width: img.width,
232	    height: img.height,
233	    channels: ch,
234	    data: Bytes::from_array(out),
235	  }
236	}
   	…

241	pub fn rgb_to_hsv(
242	  r : Int,
243	  g : Int,
244	  b : Int,
245	) -> (Float, Float, Float) raise @types.LoadError {
246	  // 输入验证
247	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
248	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
249	      "rgb_to_hsv: r,g,b必须在[0,255]范围内",
250	    )
251	  }
252	  let rf = Float::from_int(r) / 255.0F
253	  let gf = Float::from_int(g) / 255.0F
254	  let bf = Float::from_int(b) / 255.0F
255	  let mx = rf.max(gf).max(bf)
256	  let mn = rf.min(gf).min(bf)
257	  let delta = mx - mn
258	  let v = mx
259	  let s = if mx == 0.0F { 0.0F } else { delta / mx }
260	  let h = if delta == 0.0F {
261	    0.0F
262	  } else if mx == rf {
263	    60.0F * ((gf - bf) / delta)
264	  } else if mx == gf {
265	    60.0F * ((bf - rf) / delta + 2.0F)
266	  } else {
267	    60.0F * ((rf - gf) / delta + 4.0F)
268	  }
269	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
270	  (h_norm, s, v)
271	}
   	…

276	pub fn hsv_to_rgb(
277	  h : Float,
278	  s : Float,
279	  v : Float,
280	) -> (Int, Int, Int) raise @types.LoadError {
281	  // 输入验证
282	  if s < 0.0F || s > 1.0F || v < 0.0F || v > 1.0F {
283	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
284	      "hsv_to_rgb: s,v必须在[0,1]范围内",
285	    )
286	  }
287	  let c = v * s
288	  // 归一化 h 到 [0,6)
289	  let mut hp = h / 60.0F
290	  while hp >= 6.0F {
291	    hp = hp - 6.0F
292	  }
293	  while hp < 0.0F {
294	    hp = hp + 6.0F
295	  }
296	  // hp % 2.0
297	  let hp_mod2 = if hp < 2.0F {
298	    hp
299	  } else if hp < 4.0F {
300	    hp - 2.0F
301	  } else {
302	    hp - 4.0F
303	  }
304	  let x = c * (1.0F - (hp_mod2 - 1.0F).abs())
305	  let m = v - c
306	  let (r1, g1, b1) = if hp < 1.0F {
307	    (c, x, 0.0F)
308	  } else if hp < 2.0F {
309	    (x, c, 0.0F)
310	  } else if hp < 3.0F {
311	    (0.0F, c, x)
312	  } else if hp < 4.0F {
313	    (0.0F, x, c)
314	  } else if hp < 5.0F {
315	    (x, 0.0F, c)
316	  } else {
317	    (c, 0.0F, x)
318	  }
319	  (
320	    ((r1 + m) * 255.0F + 0.5F).to_int(),
321	    ((g1 + m) * 255.0F + 0.5F).to_int(),
322	    ((b1 + m) * 255.0F + 0.5F).to_int(),
323	  )
324	}
   	…

329	pub fn rgb_to_hsl(
330	  r : Int,
331	  g : Int,
332	  b : Int,
333	) -> (Float, Float, Float) raise @types.LoadError {
334	  // 输入验证
335	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
336	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
337	      "rgb_to_hsl: r,g,b必须在[0,255]范围内",
338	    )
339	  }
340	  let rf = Float::from_int(r) / 255.0F
341	  let gf = Float::from_int(g) / 255.0F
342	  let bf = Float::from_int(b) / 255.0F
343	  let mx = rf.max(gf).max(bf)
344	  let mn = rf.min(gf).min(bf)
345	  let delta = mx - mn
346	  let l = (mx + mn) / 2.0F
347	  let s = if delta == 0.0F {
348	    0.0F
349	  } else {
350	    delta / (1.0F - (2.0F * l - 1.0F).abs())
351	  }
352	  let h = if delta == 0.0F {
353	    0.0F
354	  } else if mx == rf {
355	    60.0F * ((gf - bf) / delta)
356	  } else if mx == gf {
357	    60.0F * ((bf - rf) / delta + 2.0F)
358	  } else {
359	    60.0F * ((rf - gf) / delta + 4.0F)
360	  }
361	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
362	  (h_norm, s, l)
363	}
   	…

368	pub fn hsl_to_rgb(
369	  h : Float,
370	  s : Float,
371	  l : Float,
372	) -> (Int, Int, Int) raise @types.LoadError {
373	  // 输入验证
374	  if s < 0.0F || s > 1.0F || l < 0.0F || l > 1.0F {
375	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
376	      "hsl_to_rgb: s,l必须在[0,1]范围内",
377	    )
378	  }
379	  let c = (1.0F - (2.0F * l - 1.0F).abs()) * s
380	  // 归一化 h 到 [0,6)
381	  let mut hp = h / 60.0F
382	  while hp >= 6.0F {
383	    hp = hp - 6.0F
384	  }
385	  while hp < 0.0F {
386	    hp = hp + 6.0F
387	  }
388	  let hp_mod2 = if hp < 2.0F {
389	    hp
390	  } else if hp < 4.0F {
391	    hp - 2.0F
392	  } else {
393	    hp - 4.0F
394	  }
395	  let x = c * (1.0F - (hp_mod2 - 1.0F).abs())
396	  let m = l - c / 2.0F
397	  let (r1, g1, b1) = if hp < 1.0F {
398	    (c, x, 0.0F)
399	  } else if hp < 2.0F {
400	    (x, c, 0.0F)
401	  } else if hp < 3.0F {
402	    (0.0F, c, x)
403	  } else if hp < 4.0F {
404	    (0.0F, x, c)
405	  } else if hp < 5.0F {
406	    (x, 0.0F, c)
407	  } else {
408	    (c, 0.0F, x)
409	  }
410	  (
411	    ((r1 + m) * 255.0F + 0.5F).to_int(),
412	    ((g1 + m) * 255.0F + 0.5F).to_int(),
413	    ((b1 + m) * 255.0F + 0.5F).to_int(),
414	  )
415	}
   	…

419	pub fn adjust_saturation(
420	  img : @types.Image,
421	  factor : Float,
422	) -> @types.Image raise @types.LoadError {
423	  // 输入验证
424	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
425	    raise @types.LoadError::DecodeFailed(
426	      "adjust_saturation: 图像尺寸必须大于0",
427	    )
428	  }
429	  if img.channels < 1 || img.channels > 4 {
430	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
431	      "adjust_saturation: 通道数必须在1-4之间",
432	    )
433	  }
434	  let expected_size = img.width * img.height * img.channels
435	  if img.data.length() != expected_size {	<-- UNCOVERED
436	    raise @types.LoadError::DecodeFailed(
437	      "adjust_saturation: 数据大小不匹配，期望 " +
438	      expected_size.to_string() +	<-- UNCOVERED
439	      "，实际 " +
440	      img.data.length().to_string(),	<-- UNCOVERED
441	    )
442	  }
443	  let n = img.width * img.height
444	  let ch = img.channels
445	  if ch < 3 {
446	    return img	<-- UNCOVERED
447	  }
448	  let out : Array[Byte] = Array::make(n * ch, b'\x00')	<-- UNCOVERED
449	  let f = factor.clamp(min=0.0F, max=10.0F)
450	  for i in 0..<n {
451	    let r = Float::from_int(img.data[i * ch].to_int()) / 255.0F	<-- UNCOVERED
452	    let g = Float::from_int(img.data[i * ch + 1].to_int()) / 255.0F
453	    let b = Float::from_int(img.data[i * ch + 2].to_int()) / 255.0F
454	    let gray = 0.299F * r + 0.587F * g + 0.114F * b
455	    let nr = (gray + (r - gray) * f).clamp(min=0.0F, max=1.0F)	<-- UNCOVERED
456	    let ng = (gray + (g - gray) * f).clamp(min=0.0F, max=1.0F)
457	    let nb = (gray + (b - gray) * f).clamp(min=0.0F, max=1.0F)
458	    out[i * ch] = (nr * 255.0F + 0.5F).to_int().clamp(min=0, max=255).to_byte()
459	    out[i * ch + 1] = (ng * 255.0F + 0.5F)
460	      .to_int()	<-- UNCOVERED
461	      .clamp(min=0, max=255)
462	      .to_byte()
463	    out[i * ch + 2] = (nb * 255.0F + 0.5F)
464	      .to_int()	<-- UNCOVERED
465	      .clamp(min=0, max=255)
466	      .to_byte()
467	    if ch == 4 {
468	      out[i * ch + 3] = img.data[i * ch + 3]	<-- UNCOVERED
469	    }
470	  }
471	  @types.Image::{
472	    width: img.width,
473	    height: img.height,
474	    channels: ch,
475	    data: Bytes::from_array(out),	<-- UNCOVERED
476	  }
477	}
   	…

481	pub fn adjust_hue(
482	  img : @types.Image,
483	  delta : Float,
484	) -> @types.Image raise @types.LoadError {
485	  // 输入验证
486	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
487	    raise @types.LoadError::DecodeFailed(
488	      "adjust_hue: 图像尺寸必须大于0",
489	    )
490	  }
491	  if img.channels < 1 || img.channels > 4 {
492	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
493	      "adjust_hue: 通道数必须在1-4之间",
494	    )
495	  }
496	  let expected_size = img.width * img.height * img.channels
497	  if img.data.length() != expected_size {	<-- UNCOVERED
498	    raise @types.LoadError::DecodeFailed(
499	      "adjust_hue: 数据大小不匹配，期望 " +
500	      expected_size.to_string() +	<-- UNCOVERED
501	      "，实际 " +
502	      img.data.length().to_string(),	<-- UNCOVERED
503	    )
504	  }
505	  let n = img.width * img.height
506	  let ch = img.channels
507	  if ch < 3 {
508	    return img	<-- UNCOVERED
509	  }
510	  let out : Array[Byte] = Array::make(n * ch, b'\x00')	<-- UNCOVERED
511	  for i in 0..<n {
512	    let r = img.data[i * ch].to_int()	<-- UNCOVERED
513	    let g = img.data[i * ch + 1].to_int()
514	    let b = img.data[i * ch + 2].to_int()
515	    let (h, s, l) = rgb_to_hsl(r, g, b)
516	    let mut nh = h + delta
517	    while nh >= 360.0F {
518	      nh = nh - 360.0F	<-- UNCOVERED
519	    }
520	    while nh < 0.0F {
521	      nh = nh + 360.0F	<-- UNCOVERED
522	    }
523	    let (nr, ng, nb) = hsl_to_rgb(nh, s, l)	<-- UNCOVERED
524	    out[i * ch] = nr.clamp(min=0, max=255).to_byte()
525	    out[i * ch + 1] = ng.clamp(min=0, max=255).to_byte()
526	    out[i * ch + 2] = nb.clamp(min=0, max=255).to_byte()
527	    if ch == 4 {
528	      out[i * ch + 3] = img.data[i * ch + 3]	<-- UNCOVERED
529	    }
530	  }
531	  @types.Image::{
532	    width: img.width,
533	    height: img.height,
534	    channels: ch,
535	    data: Bytes::from_array(out),	<-- UNCOVERED
536	  }
537	}
   	…

31 uncovered line(s) in src\process\color\color_convert.mbt:

11	pub fn to_grayscale(img : @types.Image) -> @types.Image raise @types.LoadError {
12	  // 输入验证
13	  if img.width <= 0 || img.height <= 0 {
14	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
15	      "to_grayscale: 图像尺寸必须大于0",
16	    )
17	  }
18	  if img.channels < 1 || img.channels > 4 {
19	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
20	      "to_grayscale: 通道数必须在1-4之间",
21	    )
22	  }
23	  let expected_size = img.width * img.height * img.channels
24	  if img.data.length() != expected_size {
25	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
26	      "to_grayscale: 数据大小不匹配，期望 " +
27	      expected_size.to_string() +	<-- UNCOVERED
28	      "，实际 " +
29	      img.data.length().to_string(),	<-- UNCOVERED
30	    )
31	  }
32	  if img.channels == 1 {
33	    return img
34	  }
35	  let pixel_count = img.width * img.height
36	  let ch = img.channels
37	  let out : Array[Byte] = Array::make(pixel_count, b'\x00')
38	  let data = img.data
39	  for i in 0..<pixel_count {
40	    let base = i * ch
41	    let r = data[base].to_int()
42	    let g = data[base + 1].to_int()
43	    let b = data[base + 2].to_int()
44	    let gray = (r * 299 + g * 587 + b * 114) / 1000
45	    out[i] = gray.to_byte()
46	  }
47	  @types.Image::{
48	    width: img.width,
49	    height: img.height,
50	    channels: 1,
51	    data: Bytes::from_array(out),
52	  }
53	}
  	…

57	pub fn to_rgb(img : @types.Image) -> @types.Image raise @types.LoadError {
58	  // 输入验证
59	  if img.width <= 0 || img.height <= 0 {
60	    raise @types.LoadError::DecodeFailed("to_rgb: 图像尺寸必须大于0")	<-- UNCOVERED
61	  }
62	  if img.channels < 1 || img.channels > 4 {
63	    raise @types.LoadError::DecodeFailed("to_rgb: 通道数必须在1-4之间")	<-- UNCOVERED
64	  }
65	  let expected_size = img.width * img.height * img.channels
66	  if img.data.length() != expected_size {
67	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
68	      "to_rgb: 数据大小不匹配，期望 " +
69	      expected_size.to_string() +	<-- UNCOVERED
70	      "，实际 " +
71	      img.data.length().to_string(),	<-- UNCOVERED
72	    )
73	  }
74	  if img.channels == 3 {
75	    return img
76	  }
77	  if img.channels < 3 {
78	    return img
79	  }
80	  let pixel_count = img.width * img.height
81	  let out : Array[Byte] = Array::make(pixel_count * 3, b'\x00')
82	  for i in 0..<pixel_count {
83	    out[i * 3] = img.data[i * img.channels]
84	    out[i * 3 + 1] = img.data[i * img.channels + 1]
85	    out[i * 3 + 2] = img.data[i * img.channels + 2]
86	  }
87	  @types.Image::{
88	    width: img.width,
89	    height: img.height,
90	    channels: 3,
91	    data: Bytes::from_array(out),
92	  }
93	}
  	…

 97	pub fn to_rgba(img : @types.Image) -> @types.Image raise @types.LoadError {
 98	  // 输入验证
 99	  if img.width <= 0 || img.height <= 0 {
100	    raise @types.LoadError::DecodeFailed("to_rgba: 图像尺寸必须大于0")	<-- UNCOVERED
101	  }
102	  if img.channels < 1 || img.channels > 4 {
103	    raise @types.LoadError::DecodeFailed("to_rgba: 通道数必须在1-4之间")	<-- UNCOVERED
104	  }
105	  let expected_size = img.width * img.height * img.channels
106	  if img.data.length() != expected_size {
107	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
108	      "to_rgba: 数据大小不匹配，期望 " +
109	      expected_size.to_string() +	<-- UNCOVERED
110	      "，实际 " +
111	      img.data.length().to_string(),	<-- UNCOVERED
112	    )
113	  }
114	  if img.channels == 4 {
115	    return img
116	  }
117	  let pixel_count = img.width * img.height
118	  let out : Array[Byte] = Array::make(pixel_count * 4, b'\x00')
119	  for i in 0..<pixel_count {
120	    let src_ch = img.channels
121	    out[i * 4] = img.data[i * src_ch]
122	    out[i * 4 + 1] = if src_ch >= 2 {
123	      img.data[i * src_ch + 1]
124	    } else {
125	      b'\x00'
126	    }
127	    out[i * 4 + 2] = if src_ch >= 3 {
128	      img.data[i * src_ch + 2]
129	    } else {
130	      b'\x00'
131	    }
132	    out[i * 4 + 3] = b'\xFF'
133	  }
134	  @types.Image::{
135	    width: img.width,
136	    height: img.height,
137	    channels: 4,
138	    data: Bytes::from_array(out),
139	  }
140	}
   	…

145	pub fn premultiply_alpha(
146	  img : @types.Image,
147	) -> @types.Image raise @types.LoadError {
148	  // 输入验证
149	  if img.width <= 0 || img.height <= 0 {
150	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
151	      "premultiply_alpha: 图像尺寸必须大于0",
152	    )
153	  }
154	  if img.channels != 4 {
155	    raise @types.LoadError::DecodeFailed(
156	      "premultiply_alpha 需要 channels=4 (RGBA)",
157	    )
158	  }
159	  let expected_size = img.width * img.height * img.channels
160	  if img.data.length() != expected_size {
161	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
162	      "premultiply_alpha: 数据大小不匹配，期望 " +
163	      expected_size.to_string() +	<-- UNCOVERED
164	      "，实际 " +
165	      img.data.length().to_string(),	<-- UNCOVERED
166	    )
167	  }
168	  let pixel_count = img.width * img.height
169	  let data_int : Array[Int] = Array::make(pixel_count * 4, 0)
170	  for i in 0..<(pixel_count * 4) {
171	    data_int[i] = img.data[i].to_int()
172	  }
173	  let out : Array[Byte] = Array::make(pixel_count * 4, b'\x00')
174	  for i in 0..<pixel_count {
175	    let r = data_int[i * 4]
176	    let g = data_int[i * 4 + 1]
177	    let b = data_int[i * 4 + 2]
178	    let a = data_int[i * 4 + 3]
179	    out[i * 4] = (r * a / 255).to_byte()
180	    out[i * 4 + 1] = (g * a / 255).to_byte()
181	    out[i * 4 + 2] = (b * a / 255).to_byte()
182	    out[i * 4 + 3] = a.to_byte()
183	  }
184	  @types.Image::{
185	    width: img.width,
186	    height: img.height,
187	    channels: 4,
188	    data: Bytes::from_array(out),
189	  }
190	}
   	…

195	pub fn unpremultiply_alpha(
196	  img : @types.Image,
197	) -> @types.Image raise @types.LoadError {
198	  // 输入验证
199	  if img.width <= 0 || img.height <= 0 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "unpremultiply_alpha: 图像尺寸必须大于0",
202	    )
203	  }
204	  if img.channels != 4 {
205	    raise @types.LoadError::DecodeFailed(
206	      "unpremultiply_alpha 需要 channels=4 (RGBA)",
207	    )
208	  }
209	  let expected_size = img.width * img.height * img.channels
210	  if img.data.length() != expected_size {
211	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
212	      "unpremultiply_alpha: 数据大小不匹配，期望 " +
213	      expected_size.to_string() +	<-- UNCOVERED
214	      "，实际 " +
215	      img.data.length().to_string(),	<-- UNCOVERED
216	    )
217	  }
218	  let pixel_count = img.width * img.height
219	  let data_int : Array[Int] = Array::make(pixel_count * 4, 0)
220	  for i in 0..<(pixel_count * 4) {
221	    data_int[i] = img.data[i].to_int()
222	  }
223	  let out : Array[Byte] = Array::make(pixel_count * 4, b'\x00')
224	  for i in 0..<pixel_count {
225	    let r = data_int[i * 4]
226	    let g = data_int[i * 4 + 1]
227	    let b = data_int[i * 4 + 2]
228	    let a = data_int[i * 4 + 3]
229	    if a == 0 {
230	      out[i * 4] = b'\x00'
231	      out[i * 4 + 1] = b'\x00'
232	      out[i * 4 + 2] = b'\x00'
233	      out[i * 4 + 3] = b'\x00'
234	    } else {
235	      out[i * 4] = (r * 255 / a).to_byte()
236	      out[i * 4 + 1] = (g * 255 / a).to_byte()
237	      out[i * 4 + 2] = (b * 255 / a).to_byte()
238	      out[i * 4 + 3] = a.to_byte()
239	    }
240	  }
241	  @types.Image::{
242	    width: img.width,
243	    height: img.height,
244	    channels: 4,
245	    data: Bytes::from_array(out),
246	  }
247	}
   	…

251	pub fn rgb_to_ycbcr(
252	  r : Int,
253	  g : Int,
254	  b : Int,
255	) -> (Int, Int, Int) raise @types.LoadError {
256	  // 输入验证
257	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
258	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
259	      "rgb_to_ycbcr: r,g,b必须在[0,255]范围内",
260	    )
261	  }
262	  let y = (299 * r + 587 * g + 114 * b) / 1000
263	  let cb = (-169 * r - 331 * g + 500 * b) / 1000 + 128
264	  let cr = (500 * r - 419 * g - 81 * b) / 1000 + 128
265	  (y.clamp(min=0, max=255), cb.clamp(min=0, max=255), cr.clamp(min=0, max=255))
266	}
   	…

270	pub fn ycbcr_to_rgb(
271	  y : Int,
272	  cb : Int,
273	  cr : Int,
274	) -> (Int, Int, Int) raise @types.LoadError {
275	  // 输入验证
276	  if y < 0 || y > 255 || cb < 0 || cb > 255 || cr < 0 || cr > 255 {
277	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
278	      "ycbcr_to_rgb: y,cb,cr必须在[0,255]范围内",
279	    )
280	  }
281	  let cb0 = cb - 128
282	  let cr0 = cr - 128
283	  let r = y + 1402 * cr0 / 1000
284	  let g = y - 344 * cb0 / 1000 - 714 * cr0 / 1000
285	  let b = y + 1772 * cb0 / 1000
286	  (r.clamp(min=0, max=255), g.clamp(min=0, max=255), b.clamp(min=0, max=255))
287	}
   	…

291	pub fn rgb_to_xyz(
292	  r : Int,
293	  g : Int,
294	  b : Int,
295	) -> (Float, Float, Float) raise @types.LoadError {
296	  // 输入验证
297	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
298	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
299	      "rgb_to_xyz: r,g,b必须在[0,255]范围内",
300	    )
301	  }
302	  fn linearize(c : Int) -> Float {
303	    let cf = Float::from_int(c) / 255.0F
304	    if cf <= 0.04045F {
305	      cf / 12.92F
306	    } else {
307	      @math.powf((cf + 0.055F) / 1.055F, 2.4F)
308	    }
309	  }
310	  let rl = linearize(r)
311	  let gl = linearize(g)
312	  let bl = linearize(b)
313	  let x = 0.4124F * rl + 0.3576F * gl + 0.1805F * bl
314	  let y = 0.2126F * rl + 0.7152F * gl + 0.0722F * bl
315	  let z = 0.0193F * rl + 0.1192F * gl + 0.9505F * bl
316	  (x, y, z)
317	}
   	…

321	pub fn xyz_to_rgb(
322	  x : Float,
323	  y : Float,
324	  z : Float,
325	) -> (Int, Int, Int) raise @types.LoadError {
326	  // 输入验证
327	  if x < 0.0F || x > 1.0F || y < 0.0F || y > 1.0F || z < 0.0F || z > 1.0F {
328	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
329	      "xyz_to_rgb: x,y,z必须在[0,1]范围内",
330	    )
331	  }
332	  let rl = 3.2406F * x - 1.5372F * y - 0.4986F * z
333	  let gl = -0.9689F * x + 1.8758F * y + 0.0415F * z
334	  let bl = 0.0557F * x - 0.2040F * y + 1.0570F * z
335	  fn srgb(c : Float) -> Int {
336	    let v = if c <= 0.0031308F {
337	      12.92F * c
338	    } else {
339	      1.055F * @math.powf(c, 1.0F / 2.4F) - 0.055F
340	    }
341	    (v * 255.0F + 0.5F).to_int().clamp(min=0, max=255)
342	  }
343	  (srgb(rl), srgb(gl), srgb(bl))
344	}
   	…

348	pub fn rgb_to_lab(
349	  r : Int,
350	  g : Int,
351	  b : Int,
352	) -> (Float, Float, Float) raise @types.LoadError {
353	  // 输入验证
354	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
355	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
356	      "rgb_to_lab: r,g,b必须在[0,255]范围内",
357	    )
358	  }
359	  let (x, y, z) = rgb_to_xyz(r, g, b)
360	  let xn = 0.9505F
361	  let yn = 1.0F
362	  let zn = 1.089F
363	  fn f(t : Float) -> Float {
364	    if t > 0.008856F {
365	      @math.powf(t, 1.0F / 3.0F)
366	    } else {
367	      7.787F * t + 16.0F / 116.0F
368	    }
369	  }
370	  let fx = f(x / xn)
371	  let fy = f(y / yn)
372	  let fz = f(z / zn)
373	  let l = 116.0F * fy - 16.0F
374	  let a = 500.0F * (fx - fy)
375	  let b_val = 200.0F * (fy - fz)
376	  (l, a, b_val)
377	}
   	…

381	pub fn lab_to_rgb(
382	  l : Float,
383	  a : Float,
384	  b : Float,
385	) -> (Int, Int, Int) raise @types.LoadError {
386	  // 输入验证
387	  if l < 0.0F || l > 100.0F {
388	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
389	      "lab_to_rgb: L必须在[0,100]范围内",
390	    )
391	  }
392	  let yn = 1.0F
393	  let xn = 0.9505F
394	  let zn = 1.089F
395	  let fy = (l + 16.0F) / 116.0F
396	  let fx = a / 500.0F + fy
397	  let fz = fy - b / 200.0F
398	  fn f_inv(t : Float) -> Float {
399	    if @math.powf(t, 3.0F) > 0.008856F {
400	      @math.powf(t, 3.0F)
401	    } else {
402	      (t - 16.0F / 116.0F) / 7.787F
403	    }
404	  }
405	  let x = xn * f_inv(fx)
406	  let y = yn * f_inv(fy)
407	  let z = zn * f_inv(fz)
408	  xyz_to_rgb(x, y, z)
409	}
   	…

413	pub fn rgb_to_cmyk(
414	  r : Int,
415	  g : Int,
416	  b : Int,
417	) -> (Int, Int, Int, Int) raise @types.LoadError {
418	  // 输入验证
419	  if r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 {
420	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
421	      "rgb_to_cmyk: r,g,b必须在[0,255]范围内",
422	    )
423	  }
424	  let c = 255 - r
425	  let m = 255 - g
426	  let y = 255 - b
427	  let k = if c < m { if c < y { c } else { y } } else if m < y { m } else { y }
428	  if k == 255 {
429	    (0, 0, 0, 255)
430	  } else {
431	    let denom = 255 - k
432	    ((c - k) * 255 / denom, (m - k) * 255 / denom, (y - k) * 255 / denom, k)
433	  }
434	}
   	…

438	pub fn cmyk_to_rgb(
439	  c : Int,
440	  m : Int,
441	  y : Int,
442	  k : Int,
443	) -> (Int, Int, Int) raise @types.LoadError {
444	  // 输入验证
445	  if c < 0 ||
446	    c > 255 ||
447	    m < 0 ||
448	    m > 255 ||
449	    y < 0 ||
450	    y > 255 ||
451	    k < 0 ||
452	    k > 255 {
453	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
454	      "cmyk_to_rgb: c,m,y,k必须在[0,255]范围内",
455	    )
456	  }
457	  let r = 255 - (c * (255 - k) / 255 + k)
458	  let g = 255 - (m * (255 - k) / 255 + k)
459	  let b = 255 - (y * (255 - k) / 255 + k)
460	  (r.clamp(min=0, max=255), g.clamp(min=0, max=255), b.clamp(min=0, max=255))
461	}
   	…

63 uncovered line(s) in src\process\color\color_convert_16f.mbt:

 8	fn clamp_float(v : Float) -> Float {
 9	  if v < 0.0F {
10	    0.0F	<-- UNCOVERED
11	  } else if v > 1.0F {
12	    1.0F	<-- UNCOVERED
13	  } else {
14	    v
15	  }
16	}
  	…

 68	pub fn to_rgb_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
 69	  // 输入验证
 70	  if img.width <= 0 || img.height <= 0 {
 71	    raise @types.LoadError::DecodeFailed("to_rgb_f: 图像尺寸必须大于0")	<-- UNCOVERED
 72	  }
 73	  if img.channels < 1 || img.channels > 4 {
 74	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 75	      "to_rgb_f: 通道数必须在1-4之间",
 76	    )
 77	  }
 78	  let expected_size = img.width * img.height * img.channels * 4
 79	  if img.data.length() != expected_size {
 80	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 81	      "to_rgb_f: 数据大小不匹配，期望 " +
 82	      expected_size.to_string() +	<-- UNCOVERED
 83	      "，实际 " +
 84	      img.data.length().to_string(),	<-- UNCOVERED
 85	    )
 86	  }
 87	  if img.channels == 3 {
 88	    return img	<-- UNCOVERED
 89	  }
 90	  if img.channels < 3 {
 91	    return img	<-- UNCOVERED
 92	  }
 93	  let n = img.width * img.height
 94	  let src_ch = img.channels
 95	  let src_bpp = src_ch * 4
 96	  let out_bpp = 12
 97	  let out : Array[Byte] = Array::make(n * out_bpp, b'\x00')
 98	  for i in 0..<n {
 99	    for c in 0..<3 {
100	      let src_off = i * src_bpp + c * 4
101	      let dst_off = i * out_bpp + c * 4
102	      for b in 0..<4 {
103	        out[dst_off + b] = img.data[src_off + b]
104	      }
105	    }
106	  }
107	  @types.ImageF::{
108	    width: img.width,
109	    height: img.height,
110	    channels: 3,
111	    data: Bytes::from_array(out),
112	  }
113	}
   	…

117	pub fn to_rgba_16(
118	  img : @types.Image16,
119	) -> @types.Image16 raise @types.LoadError {
120	  // 输入验证
121	  if img.width < 0 || img.height < 0 {
122	    raise @types.LoadError::DecodeFailed("to_rgba_16: 图像尺寸不能为负")	<-- UNCOVERED
123	  }
124	  if img.channels < 1 || img.channels > 4 {
125	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
126	      "to_rgba_16: 通道数必须在1-4之间",
127	    )
128	  }
129	  let expected_size = img.width * img.height * img.channels * 2
130	  if img.data.length() != expected_size {
131	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
132	      "to_rgba_16: 数据大小不匹配，期望 " +
133	      expected_size.to_string() +	<-- UNCOVERED
134	      "，实际 " +
135	      img.data.length().to_string(),	<-- UNCOVERED
136	    )
137	  }
138	  if img.channels == 4 {
139	    return img	<-- UNCOVERED
140	  }
141	  let n = img.width * img.height
142	  let src_ch = img.channels
143	  let src_bpp = src_ch * 2
144	  let out_bpp = 8
145	  let out : Array[Byte] = Array::make(n * out_bpp, b'\x00')
146	  for i in 0..<n {
147	    for c in 0..<3 {
148	      let src_off = if c < src_ch { i * src_bpp + c * 2 } else { -1 }
149	      let dst_off = i * out_bpp + c * 2
150	      if src_off >= 0 {
151	        out[dst_off] = img.data[src_off]
152	        out[dst_off + 1] = img.data[src_off + 1]
153	      }
154	    }
155	    // alpha = 65535
156	    let alpha_off = i * out_bpp + 6
157	    out[alpha_off] = b'\xFF'
158	    out[alpha_off + 1] = b'\xFF'
159	  }
160	  @types.Image16::{
161	    width: img.width,
162	    height: img.height,
163	    channels: 4,
164	    data: Bytes::from_array(out),
165	  }
166	}
   	…

170	pub fn to_rgba_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
171	  // 输入验证
172	  if img.width <= 0 || img.height <= 0 {
173	    raise @types.LoadError::DecodeFailed("to_rgba_f: 图像尺寸必须大于0")	<-- UNCOVERED
174	  }
175	  if img.channels < 1 || img.channels > 4 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "to_rgba_f: 通道数必须在1-4之间",
178	    )
179	  }
180	  let expected_size = img.width * img.height * img.channels * 4
181	  if img.data.length() != expected_size {
182	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
183	      "to_rgba_f: 数据大小不匹配，期望 " +
184	      expected_size.to_string() +	<-- UNCOVERED
185	      "，实际 " +
186	      img.data.length().to_string(),	<-- UNCOVERED
187	    )
188	  }
189	  if img.channels == 4 {
190	    return img	<-- UNCOVERED
191	  }
192	  let n = img.width * img.height
193	  let src_ch = img.channels
194	  let src_bpp = src_ch * 4
195	  let out_bpp = 16
196	  let out : Array[Byte] = Array::make(n * out_bpp, b'\x00')
197	  for i in 0..<n {
198	    for c in 0..<3 {
199	      if c < src_ch {
200	        let src_off = i * src_bpp + c * 4
201	        let dst_off = i * out_bpp + c * 4
202	        for b in 0..<4 {
203	          out[dst_off + b] = img.data[src_off + b]
204	        }
205	      }
206	    }
207	    // alpha = 1.0
208	    write_f32_le(out, i * out_bpp + 12, 1.0F)
209	  }
210	  @types.ImageF::{
211	    width: img.width,
212	    height: img.height,
213	    channels: 4,
214	    data: Bytes::from_array(out),
215	  }
216	}
   	…

220	pub fn premultiply_alpha_16(
221	  img : @types.Image16,
222	) -> @types.Image16 raise @types.LoadError {
223	  // 输入验证
224	  if img.width <= 0 || img.height <= 0 {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "premultiply_alpha_16: 图像尺寸必须大于0",
227	    )
228	  }
229	  if img.channels != 4 {
230	    raise @types.LoadError::DecodeFailed(
231	      "premultiply_alpha_16 需要 channels=4 (RGBA)",
232	    )
233	  }
234	  let expected_size = img.width * img.height * img.channels * 2
235	  if img.data.length() != expected_size {	<-- UNCOVERED
236	    raise @types.LoadError::DecodeFailed(
237	      "premultiply_alpha_16: 数据大小不匹配，期望 " +
238	      expected_size.to_string() +	<-- UNCOVERED
239	      "，实际 " +
240	      img.data.length().to_string(),	<-- UNCOVERED
241	    )
242	  }
243	  let n = img.width * img.height
244	  let bpp = 8
245	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
246	  for i in 0..<n {
247	    let r = read_u16_le_c(img.data, i * bpp)	<-- UNCOVERED
248	    let g = read_u16_le_c(img.data, i * bpp + 2)
249	    let b = read_u16_le_c(img.data, i * bpp + 4)
250	    let a = read_u16_le_c(img.data, i * bpp + 6)
251	    write_u16_le_c(out, i * bpp, r * a / 65535)
252	    write_u16_le_c(out, i * bpp + 2, g * a / 65535)
253	    write_u16_le_c(out, i * bpp + 4, b * a / 65535)
254	    write_u16_le_c(out, i * bpp + 6, a)
255	  }
256	  @types.Image16::{
257	    width: img.width,
258	    height: img.height,
259	    channels: 4,
260	    data: Bytes::from_array(out),	<-- UNCOVERED
261	  }
262	}
   	…

266	pub fn premultiply_alpha_f(
267	  img : @types.ImageF,
268	) -> @types.ImageF raise @types.LoadError {
269	  // 输入验证
270	  if img.width <= 0 || img.height <= 0 {
271	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
272	      "premultiply_alpha_f: 图像尺寸必须大于0",
273	    )
274	  }
275	  if img.channels != 4 {
276	    raise @types.LoadError::DecodeFailed(
277	      "premultiply_alpha_f 需要 channels=4 (RGBA)",
278	    )
279	  }
280	  let expected_size = img.width * img.height * img.channels * 4
281	  if img.data.length() != expected_size {	<-- UNCOVERED
282	    raise @types.LoadError::DecodeFailed(
283	      "premultiply_alpha_f: 数据大小不匹配，期望 " +
284	      expected_size.to_string() +	<-- UNCOVERED
285	      "，实际 " +
286	      img.data.length().to_string(),	<-- UNCOVERED
287	    )
288	  }
289	  let n = img.width * img.height
290	  let bpp = 16
291	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
292	  for i in 0..<n {
293	    let r = read_float_le(img.data, i * bpp)	<-- UNCOVERED
294	    let g = read_float_le(img.data, i * bpp + 4)
295	    let b = read_float_le(img.data, i * bpp + 8)
296	    let a = read_float_le(img.data, i * bpp + 12)
297	    write_f32_le(out, i * bpp, r * a)
298	    write_f32_le(out, i * bpp + 4, g * a)
299	    write_f32_le(out, i * bpp + 8, b * a)
300	    write_f32_le(out, i * bpp + 12, a)
301	  }
302	  @types.ImageF::{
303	    width: img.width,
304	    height: img.height,
305	    channels: 4,
306	    data: Bytes::from_array(out),	<-- UNCOVERED
307	  }
308	}
   	…

312	pub fn unpremultiply_alpha_16(
313	  img : @types.Image16,
314	) -> @types.Image16 raise @types.LoadError {
315	  // 输入验证
316	  if img.width <= 0 || img.height <= 0 {
317	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
318	      "unpremultiply_alpha_16: 图像尺寸必须大于0",
319	    )
320	  }
321	  if img.channels != 4 {
322	    raise @types.LoadError::DecodeFailed(
323	      "unpremultiply_alpha_16 需要 channels=4 (RGBA)",
324	    )
325	  }
326	  let expected_size = img.width * img.height * img.channels * 2
327	  if img.data.length() != expected_size {	<-- UNCOVERED
328	    raise @types.LoadError::DecodeFailed(
329	      "unpremultiply_alpha_16: 数据大小不匹配，期望 " +
330	      expected_size.to_string() +	<-- UNCOVERED
331	      "，实际 " +
332	      img.data.length().to_string(),	<-- UNCOVERED
333	    )
334	  }
335	  let n = img.width * img.height
336	  let bpp = 8
337	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
338	  for i in 0..<n {
339	    let r = read_u16_le_c(img.data, i * bpp)	<-- UNCOVERED
340	    let g = read_u16_le_c(img.data, i * bpp + 2)
341	    let b = read_u16_le_c(img.data, i * bpp + 4)
342	    let a = read_u16_le_c(img.data, i * bpp + 6)
343	    if a == 0 {
344	      write_u16_le_c(out, i * bpp, 0)	<-- UNCOVERED
345	      write_u16_le_c(out, i * bpp + 2, 0)
346	      write_u16_le_c(out, i * bpp + 4, 0)
347	      write_u16_le_c(out, i * bpp + 6, 0)
348	    } else {
349	      write_u16_le_c(out, i * bpp, r * 65535 / a)	<-- UNCOVERED
350	      write_u16_le_c(out, i * bpp + 2, g * 65535 / a)
351	      write_u16_le_c(out, i * bpp + 4, b * 65535 / a)
352	      write_u16_le_c(out, i * bpp + 6, a)
353	    }
354	  }
355	  @types.Image16::{
356	    width: img.width,
357	    height: img.height,
358	    channels: 4,
359	    data: Bytes::from_array(out),	<-- UNCOVERED
360	  }
361	}
   	…

365	pub fn unpremultiply_alpha_f(
366	  img : @types.ImageF,
367	) -> @types.ImageF raise @types.LoadError {
368	  // 输入验证
369	  if img.width <= 0 || img.height <= 0 {
370	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
371	      "unpremultiply_alpha_f: 图像尺寸必须大于0",
372	    )
373	  }
374	  if img.channels != 4 {
375	    raise @types.LoadError::DecodeFailed(
376	      "unpremultiply_alpha_f 需要 channels=4 (RGBA)",
377	    )
378	  }
379	  let expected_size = img.width * img.height * img.channels * 4
380	  if img.data.length() != expected_size {	<-- UNCOVERED
381	    raise @types.LoadError::DecodeFailed(
382	      "unpremultiply_alpha_f: 数据大小不匹配，期望 " +
383	      expected_size.to_string() +	<-- UNCOVERED
384	      "，实际 " +
385	      img.data.length().to_string(),	<-- UNCOVERED
386	    )
387	  }
388	  let n = img.width * img.height
389	  let bpp = 16
390	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
391	  for i in 0..<n {
392	    let r = read_float_le(img.data, i * bpp)	<-- UNCOVERED
393	    let g = read_float_le(img.data, i * bpp + 4)
394	    let b = read_float_le(img.data, i * bpp + 8)
395	    let a = read_float_le(img.data, i * bpp + 12)
396	    if a < 0.000001F {
397	      write_f32_le(out, i * bpp, 0.0F)	<-- UNCOVERED
398	      write_f32_le(out, i * bpp + 4, 0.0F)
399	      write_f32_le(out, i * bpp + 8, 0.0F)
400	      write_f32_le(out, i * bpp + 12, 0.0F)
401	    } else {
402	      write_f32_le(out, i * bpp, r / a)	<-- UNCOVERED
403	      write_f32_le(out, i * bpp + 4, g / a)
404	      write_f32_le(out, i * bpp + 8, b / a)
405	      write_f32_le(out, i * bpp + 12, a)
406	    }
407	  }
408	  @types.ImageF::{
409	    width: img.width,
410	    height: img.height,
411	    channels: 4,
412	    data: Bytes::from_array(out),	<-- UNCOVERED
413	  }
414	}
   	…

420	pub fn rgb_to_ycbcr_16(
421	  r : Int,
422	  g : Int,
423	  b : Int,
424	) -> (Int, Int, Int) raise @types.LoadError {
425	  // 输入验证
426	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
427	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
428	      "rgb_to_ycbcr_16: r,g,b必须在[0,65535]范围内",
429	    )
430	  }
431	  let y = (299 * r + 587 * g + 114 * b) / 1000
432	  let cb = (-169 * r - 331 * g + 500 * b) / 1000 + 32768
433	  let cr = (500 * r - 419 * g - 81 * b) / 1000 + 32768
434	  (
435	    y.clamp(min=0, max=65535),
436	    cb.clamp(min=0, max=65535),
437	    cr.clamp(min=0, max=65535),
438	  )
439	}
   	…

443	pub fn rgb_to_ycbcr_f(
444	  r : Float,
445	  g : Float,
446	  b : Float,
447	) -> (Float, Float, Float) raise @types.LoadError {
448	  // 输入验证
449	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
450	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
451	      "rgb_to_ycbcr_f: r,g,b必须在[0,1]范围内",
452	    )
453	  }
454	  let y = 0.299F * r + 0.587F * g + 0.114F * b
455	  let cb = -0.169F * r - 0.331F * g + 0.5F * b + 0.5F
456	  let cr = 0.5F * r - 0.419F * g - 0.081F * b + 0.5F
457	  (clamp_float(y), clamp_float(cb), clamp_float(cr))
458	}
   	…

462	pub fn ycbcr_to_rgb_16(
463	  y : Int,
464	  cb : Int,
465	  cr : Int,
466	) -> (Int, Int, Int) raise @types.LoadError {
467	  // 输入验证
468	  if y < 0 || y > 65535 || cb < 0 || cb > 65535 || cr < 0 || cr > 65535 {
469	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
470	      "ycbcr_to_rgb_16: y,cb,cr必须在[0,65535]范围内",
471	    )
472	  }
473	  let cb0 = cb - 32768
474	  let cr0 = cr - 32768
475	  let r = y + 1402 * cr0 / 1000
476	  let g = y - 344 * cb0 / 1000 - 714 * cr0 / 1000
477	  let b = y + 1772 * cb0 / 1000
478	  (
479	    r.clamp(min=0, max=65535),
480	    g.clamp(min=0, max=65535),
481	    b.clamp(min=0, max=65535),
482	  )
483	}
   	…

487	pub fn ycbcr_to_rgb_f(
488	  y : Float,
489	  cb : Float,
490	  cr : Float,
491	) -> (Float, Float, Float) raise @types.LoadError {
492	  // 输入验证
493	  if y < 0.0F || y > 1.0F || cb < 0.0F || cb > 1.0F || cr < 0.0F || cr > 1.0F {
494	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
495	      "ycbcr_to_rgb_f: y,cb,cr必须在[0,1]范围内",
496	    )
497	  }
498	  let cb0 = cb - 0.5F
499	  let cr0 = cr - 0.5F
500	  let r = y + 1.402F * cr0
501	  let g = y - 0.344F * cb0 - 0.714F * cr0
502	  let b = y + 1.772F * cb0
503	  (clamp_float(r), clamp_float(g), clamp_float(b))
504	}
   	…

508	pub fn rgb_to_cmyk_16(
509	  r : Int,
510	  g : Int,
511	  b : Int,
512	) -> (Int, Int, Int, Int) raise @types.LoadError {
513	  // 输入验证
514	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
515	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
516	      "rgb_to_cmyk_16: r,g,b必须在[0,65535]范围内",
517	    )
518	  }
519	  let c = 65535 - r
520	  let m = 65535 - g
521	  let y = 65535 - b
522	  let k = if c < m { if c < y { c } else { y } } else if m < y { m } else { y }	<-- UNCOVERED
523	  if k == 65535 {
524	    (0, 0, 0, 65535)
525	  } else {
526	    let denom = 65535 - k
527	    (
528	      (c - k) * 65535 / denom,
529	      (m - k) * 65535 / denom,
530	      (y - k) * 65535 / denom,
531	      k,
532	    )
533	  }
534	}
   	…

538	pub fn rgb_to_cmyk_f(
539	  r : Float,
540	  g : Float,
541	  b : Float,
542	) -> (Float, Float, Float, Float) raise @types.LoadError {
543	  // 输入验证
544	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
545	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
546	      "rgb_to_cmyk_f: r,g,b必须在[0,1]范围内",
547	    )
548	  }
549	  let c = 1.0F - r
550	  let m = 1.0F - g
551	  let y = 1.0F - b
552	  let k = if c < m { if c < y { c } else { y } } else if m < y { m } else { y }	<-- UNCOVERED
553	  if k > 0.999999F {
554	    (0.0F, 0.0F, 0.0F, 1.0F)
555	  } else {
556	    let denom = 1.0F - k
557	    ((c - k) / denom, (m - k) / denom, (y - k) / denom, k)
558	  }
559	}
   	…

563	pub fn cmyk_to_rgb_16(
564	  c : Int,
565	  m : Int,
566	  y : Int,
567	  k : Int,
568	) -> (Int, Int, Int) raise @types.LoadError {
569	  // 输入验证
570	  if c < 0 ||
571	    c > 65535 ||
572	    m < 0 ||
573	    m > 65535 ||
574	    y < 0 ||
575	    y > 65535 ||
576	    k < 0 ||
577	    k > 65535 {
578	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
579	      "cmyk_to_rgb_16: c,m,y,k必须在[0,65535]范围内",
580	    )
581	  }
582	  let r = 65535 - (c * (65535 - k) / 65535 + k)
583	  let g = 65535 - (m * (65535 - k) / 65535 + k)
584	  let b = 65535 - (y * (65535 - k) / 65535 + k)
585	  (
586	    r.clamp(min=0, max=65535),
587	    g.clamp(min=0, max=65535),
588	    b.clamp(min=0, max=65535),
589	  )
590	}
   	…

594	pub fn cmyk_to_rgb_f(
595	  c : Float,
596	  m : Float,
597	  y : Float,
598	  k : Float,
599	) -> (Float, Float, Float) raise @types.LoadError {
600	  // 输入验证
601	  if c < 0.0F ||
602	    c > 1.0F ||
603	    m < 0.0F ||
604	    m > 1.0F ||
605	    y < 0.0F ||
606	    y > 1.0F ||
607	    k < 0.0F ||
608	    k > 1.0F {
609	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
610	      "cmyk_to_rgb_f: c,m,y,k必须在[0,1]范围内",
611	    )
612	  }
613	  let r = 1.0F - (c * (1.0F - k) + k)
614	  let g = 1.0F - (m * (1.0F - k) + k)
615	  let b = 1.0F - (y * (1.0F - k) + k)
616	  (clamp_float(r), clamp_float(g), clamp_float(b))
617	}
   	…

58 uncovered line(s) in src\process\color\color_space_16f.mbt:

 7	fn clamp_float_cs(v : Float, lo : Float, hi : Float) -> Float {
 8	  if v < lo {
 9	    lo	<-- UNCOVERED
10	  } else if v > hi {
11	    hi
12	  } else {
13	    v
14	  }
15	}
  	…

18	fn clamp_int_cs(v : Int, lo : Int, hi : Int) -> Int {
19	  if v < lo {
20	    lo	<-- UNCOVERED
21	  } else if v > hi {
22	    hi
23	  } else {
24	    v
25	  }
26	}
  	…

32	pub fn rgb_to_hsl_16(
33	  r : Int,
34	  g : Int,
35	  b : Int,
36	) -> (Float, Float, Float) raise @types.LoadError {
37	  // 输入验证
38	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
39	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
40	      "rgb_to_hsl_16: r,g,b必须在[0,65535]范围内",
41	    )
42	  }
43	  let rf = Float::from_int(r) / 65535.0F
44	  let gf = Float::from_int(g) / 65535.0F
45	  let bf = Float::from_int(b) / 65535.0F
46	  let mx = rf.max(gf).max(bf)
47	  let mn = rf.min(gf).min(bf)
48	  let delta = mx - mn
49	  let l = (mx + mn) / 2.0F
50	  let s = if delta == 0.0F {
51	    0.0F	<-- UNCOVERED
52	  } else {
53	    delta / (1.0F - (2.0F * l - 1.0F).abs())
54	  }
55	  let h = if delta == 0.0F {
56	    0.0F	<-- UNCOVERED
57	  } else if mx == rf {
58	    60.0F * ((gf - bf) / delta)
59	  } else if mx == gf {
60	    60.0F * ((bf - rf) / delta + 2.0F)	<-- UNCOVERED
61	  } else {
62	    60.0F * ((rf - gf) / delta + 4.0F)
63	  }
64	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
65	  (h_norm, s, l)
66	}
  	…

 70	pub fn hsl_to_rgb_16(
 71	  h : Float,
 72	  s : Float,
 73	  l : Float,
 74	) -> (Int, Int, Int) raise @types.LoadError {
 75	  // 输入验证
 76	  if s < 0.0F || s > 1.0F || l < 0.0F || l > 1.0F {
 77	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 78	      "hsl_to_rgb_16: s,l必须在[0,1]范围内",
 79	    )
 80	  }
 81	  let c = (1.0F - (2.0F * l - 1.0F).abs()) * s
 82	  let mut hp = h / 60.0F
 83	  if hp < 0.0F {
 84	    hp = hp + 6.0F	<-- UNCOVERED
 85	  }
 86	  if hp >= 6.0F {
 87	    hp = hp - 6.0F	<-- UNCOVERED
 88	  }
 89	  let x = c * (1.0F - (hp % 2.0F - 1.0F).abs())
 90	  let m = l - c / 2.0F
 91	  let (r1, g1, b1) = if hp < 1.0F {
 92	    (c, x, 0.0F)
 93	  } else if hp < 2.0F {
 94	    (x, c, 0.0F)	<-- UNCOVERED
 95	  } else if hp < 3.0F {
 96	    (0.0F, c, x)	<-- UNCOVERED
 97	  } else if hp < 4.0F {
 98	    (0.0F, x, c)
 99	  } else if hp < 5.0F {	<-- UNCOVERED
100	    (x, 0.0F, c)
101	  } else {
102	    (c, 0.0F, x)	<-- UNCOVERED
103	  }
104	  let r = clamp_int_cs(((r1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
105	  let g = clamp_int_cs(((g1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
106	  let b = clamp_int_cs(((b1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
107	  (r, g, b)
108	}
   	…

114	pub fn rgb_to_hsl_f(
115	  r : Float,
116	  g : Float,
117	  b : Float,
118	) -> (Float, Float, Float) raise @types.LoadError {
119	  // 输入验证
120	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
121	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
122	      "rgb_to_hsl_f: r,g,b必须在[0,1]范围内",
123	    )
124	  }
125	  let rf = clamp_float_cs(r, 0.0F, 1.0F)
126	  let gf = clamp_float_cs(g, 0.0F, 1.0F)
127	  let bf = clamp_float_cs(b, 0.0F, 1.0F)
128	  let mx = rf.max(gf).max(bf)
129	  let mn = rf.min(gf).min(bf)
130	  let delta = mx - mn
131	  let l = (mx + mn) / 2.0F
132	  let s = if delta == 0.0F {
133	    0.0F	<-- UNCOVERED
134	  } else {
135	    delta / (1.0F - (2.0F * l - 1.0F).abs())
136	  }
137	  let h = if delta == 0.0F {
138	    0.0F	<-- UNCOVERED
139	  } else if mx == rf {
140	    60.0F * ((gf - bf) / delta)
141	  } else if mx == gf {	<-- UNCOVERED
142	    60.0F * ((bf - rf) / delta + 2.0F)
143	  } else {
144	    60.0F * ((rf - gf) / delta + 4.0F)	<-- UNCOVERED
145	  }
146	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
147	  (h_norm, s, l)
148	}
   	…

152	pub fn hsl_to_rgb_f(
153	  h : Float,
154	  s : Float,
155	  l : Float,
156	) -> (Float, Float, Float) raise @types.LoadError {
157	  // 输入验证
158	  if s < 0.0F || s > 1.0F || l < 0.0F || l > 1.0F {
159	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
160	      "hsl_to_rgb_f: s,l必须在[0,1]范围内",
161	    )
162	  }
163	  let c = (1.0F - (2.0F * l - 1.0F).abs()) * s
164	  let mut hp = h / 60.0F
165	  if hp < 0.0F {
166	    hp = hp + 6.0F	<-- UNCOVERED
167	  }
168	  if hp >= 6.0F {
169	    hp = hp - 6.0F	<-- UNCOVERED
170	  }
171	  let x = c * (1.0F - (hp % 2.0F - 1.0F).abs())
172	  let m = l - c / 2.0F
173	  let (r1, g1, b1) = if hp < 1.0F {
174	    (c, x, 0.0F)
175	  } else if hp < 2.0F {	<-- UNCOVERED
176	    (x, c, 0.0F)
177	  } else if hp < 3.0F {
178	    (0.0F, c, x)
179	  } else if hp < 4.0F {
180	    (0.0F, x, c)
181	  } else if hp < 5.0F {
182	    (x, 0.0F, c)
183	  } else {
184	    (c, 0.0F, x)	<-- UNCOVERED
185	  }
186	  (
187	    clamp_float_cs(r1 + m, 0.0F, 1.0F),
188	    clamp_float_cs(g1 + m, 0.0F, 1.0F),
189	    clamp_float_cs(b1 + m, 0.0F, 1.0F),
190	  )
191	}
   	…

197	pub fn rgb_to_hsv_16(
198	  r : Int,
199	  g : Int,
200	  b : Int,
201	) -> (Float, Float, Float) raise @types.LoadError {
202	  // 输入验证
203	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
204	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
205	      "rgb_to_hsv_16: r,g,b必须在[0,65535]范围内",
206	    )
207	  }
208	  let rf = Float::from_int(r) / 65535.0F
209	  let gf = Float::from_int(g) / 65535.0F
210	  let bf = Float::from_int(b) / 65535.0F
211	  let mx = rf.max(gf).max(bf)
212	  let mn = rf.min(gf).min(bf)
213	  let delta = mx - mn
214	  let v = mx
215	  let s = if mx == 0.0F { 0.0F } else { delta / mx }
216	  let h = if delta == 0.0F {
217	    0.0F	<-- UNCOVERED
218	  } else if mx == rf {
219	    60.0F * ((gf - bf) / delta)
220	  } else if mx == gf {	<-- UNCOVERED
221	    60.0F * ((bf - rf) / delta + 2.0F)
222	  } else {
223	    60.0F * ((rf - gf) / delta + 4.0F)	<-- UNCOVERED
224	  }
225	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
226	  (h_norm, s, v)
227	}
   	…

231	pub fn hsv_to_rgb_16(
232	  h : Float,
233	  s : Float,
234	  v : Float,
235	) -> (Int, Int, Int) raise @types.LoadError {
236	  // 输入验证
237	  if s < 0.0F || s > 1.0F || v < 0.0F || v > 1.0F {
238	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
239	      "hsv_to_rgb_16: s,v必须在[0,1]范围内",
240	    )
241	  }
242	  let c = v * s
243	  let mut hp = h / 60.0F
244	  if hp < 0.0F {
245	    hp = hp + 6.0F	<-- UNCOVERED
246	  }
247	  if hp >= 6.0F {
248	    hp = hp - 6.0F	<-- UNCOVERED
249	  }
250	  let x = c * (1.0F - (hp % 2.0F - 1.0F).abs())
251	  let m = v - c
252	  let (r1, g1, b1) = if hp < 1.0F {
253	    (c, x, 0.0F)
254	  } else if hp < 2.0F {	<-- UNCOVERED
255	    (x, c, 0.0F)
256	  } else if hp < 3.0F {
257	    (0.0F, c, x)
258	  } else if hp < 4.0F {
259	    (0.0F, x, c)
260	  } else if hp < 5.0F {
261	    (x, 0.0F, c)
262	  } else {
263	    (c, 0.0F, x)	<-- UNCOVERED
264	  }
265	  let r = clamp_int_cs(((r1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
266	  let g = clamp_int_cs(((g1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
267	  let b = clamp_int_cs(((b1 + m) * 65535.0F + 0.5F).to_int(), 0, 65535)
268	  (r, g, b)
269	}
   	…

275	pub fn rgb_to_hsv_f(
276	  r : Float,
277	  g : Float,
278	  b : Float,
279	) -> (Float, Float, Float) raise @types.LoadError {
280	  // 输入验证
281	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
282	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
283	      "rgb_to_hsv_f: r,g,b必须在[0,1]范围内",
284	    )
285	  }
286	  let rf = clamp_float_cs(r, 0.0F, 1.0F)
287	  let gf = clamp_float_cs(g, 0.0F, 1.0F)
288	  let bf = clamp_float_cs(b, 0.0F, 1.0F)
289	  let mx = rf.max(gf).max(bf)
290	  let mn = rf.min(gf).min(bf)
291	  let delta = mx - mn
292	  let v = mx
293	  let s = if mx == 0.0F { 0.0F } else { delta / mx }
294	  let h = if delta == 0.0F {
295	    0.0F	<-- UNCOVERED
296	  } else if mx == rf {
297	    60.0F * ((gf - bf) / delta)
298	  } else if mx == gf {
299	    60.0F * ((bf - rf) / delta + 2.0F)	<-- UNCOVERED
300	  } else {
301	    60.0F * ((rf - gf) / delta + 4.0F)
302	  }
303	  let h_norm = if h < 0.0F { h + 360.0F } else { h }
304	  (h_norm, s, v)
305	}
   	…

309	pub fn hsv_to_rgb_f(
310	  h : Float,
311	  s : Float,
312	  v : Float,
313	) -> (Float, Float, Float) raise @types.LoadError {
314	  // 输入验证
315	  if s < 0.0F || s > 1.0F || v < 0.0F || v > 1.0F {
316	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
317	      "hsv_to_rgb_f: s,v必须在[0,1]范围内",
318	    )
319	  }
320	  let c = v * s
321	  let mut hp = h / 60.0F
322	  if hp < 0.0F {
323	    hp = hp + 6.0F	<-- UNCOVERED
324	  }
325	  if hp >= 6.0F {
326	    hp = hp - 6.0F	<-- UNCOVERED
327	  }
328	  let x = c * (1.0F - (hp % 2.0F - 1.0F).abs())
329	  let m = v - c
330	  let (r1, g1, b1) = if hp < 1.0F {
331	    (c, x, 0.0F)
332	  } else if hp < 2.0F {
333	    (x, c, 0.0F)	<-- UNCOVERED
334	  } else if hp < 3.0F {
335	    (0.0F, c, x)	<-- UNCOVERED
336	  } else if hp < 4.0F {
337	    (0.0F, x, c)
338	  } else if hp < 5.0F {	<-- UNCOVERED
339	    (x, 0.0F, c)
340	  } else {
341	    (c, 0.0F, x)	<-- UNCOVERED
342	  }
343	  (
344	    clamp_float_cs(r1 + m, 0.0F, 1.0F),
345	    clamp_float_cs(g1 + m, 0.0F, 1.0F),
346	    clamp_float_cs(b1 + m, 0.0F, 1.0F),
347	  )
348	}
   	…

354	pub fn rgb_to_xyz_16(
355	  r : Int,
356	  g : Int,
357	  b : Int,
358	) -> (Float, Float, Float) raise @types.LoadError {
359	  // 输入验证
360	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
361	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
362	      "rgb_to_xyz_16: r,g,b必须在[0,65535]范围内",
363	    )
364	  }
365	  fn linearize(c : Int) -> Float {
366	    let cf = Float::from_int(c) / 65535.0F
367	    if cf <= 0.04045F {
368	      cf / 12.92F	<-- UNCOVERED
369	    } else {
370	      @math.powf((cf + 0.055F) / 1.055F, 2.4F)
371	    }
372	  }
373	  let rl = linearize(r)
374	  let gl = linearize(g)
375	  let bl = linearize(b)
376	  let x = 0.4124F * rl + 0.3576F * gl + 0.1805F * bl
377	  let y = 0.2126F * rl + 0.7152F * gl + 0.0722F * bl
378	  let z = 0.0193F * rl + 0.1192F * gl + 0.9505F * bl
379	  (x, y, z)
380	}
   	…

384	pub fn xyz_to_rgb_16(
385	  x : Float,
386	  y : Float,
387	  z : Float,
388	) -> (Int, Int, Int) raise @types.LoadError {
389	  // 输入验证（XYZ空间允许z值超过1.0）
390	  if x < 0.0F || y < 0.0F || z < 0.0F {
391	    raise @types.LoadError::DecodeFailed("xyz_to_rgb_16: x,y,z必须>=0")	<-- UNCOVERED
392	  }
393	  let rl = 3.2406F * x - 1.5372F * y - 0.4986F * z
394	  let gl = -0.9689F * x + 1.8758F * y + 0.0415F * z
395	  let bl = 0.0557F * x - 0.2040F * y + 1.0570F * z
396	  fn srgb(c : Float) -> Int {
397	    let v = if c <= 0.0031308F {
398	      c * 12.92F	<-- UNCOVERED
399	    } else {
400	      1.055F * @math.powf(c, 1.0F / 2.4F) - 0.055F
401	    }
402	    clamp_int_cs((v * 65535.0F + 0.5F).to_int(), 0, 65535)
403	  }
404	  (srgb(rl), srgb(gl), srgb(bl))
405	}
   	…

411	pub fn rgb_to_xyz_f(
412	  r : Float,
413	  g : Float,
414	  b : Float,
415	) -> (Float, Float, Float) raise @types.LoadError {
416	  // 输入验证
417	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
418	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
419	      "rgb_to_xyz_f: r,g,b必须在[0,1]范围内",
420	    )
421	  }
422	  fn linearize(c : Float) -> Float {
423	    let cf = clamp_float_cs(c, 0.0F, 1.0F)
424	    if cf <= 0.04045F {
425	      cf / 12.92F	<-- UNCOVERED
426	    } else {
427	      @math.powf((cf + 0.055F) / 1.055F, 2.4F)
428	    }
429	  }
430	  let rl = linearize(r)
431	  let gl = linearize(g)
432	  let bl = linearize(b)
433	  let x = 0.4124F * rl + 0.3576F * gl + 0.1805F * bl
434	  let y = 0.2126F * rl + 0.7152F * gl + 0.0722F * bl
435	  let z = 0.0193F * rl + 0.1192F * gl + 0.9505F * bl
436	  (x, y, z)
437	}
   	…

441	pub fn xyz_to_rgb_f(
442	  x : Float,
443	  y : Float,
444	  z : Float,
445	) -> (Float, Float, Float) raise @types.LoadError {
446	  // 输入验证（XYZ空间允许z值超过1.0）
447	  if x < 0.0F || y < 0.0F || z < 0.0F {
448	    raise @types.LoadError::DecodeFailed("xyz_to_rgb_f: x,y,z必须>=0")	<-- UNCOVERED
449	  }
450	  let rl = 3.2406F * x - 1.5372F * y - 0.4986F * z
451	  let gl = -0.9689F * x + 1.8758F * y + 0.0415F * z
452	  let bl = 0.0557F * x - 0.2040F * y + 1.0570F * z
453	  fn srgb(c : Float) -> Float {
454	    let v = if c <= 0.0031308F {
455	      c * 12.92F	<-- UNCOVERED
456	    } else {
457	      1.055F * @math.powf(c, 1.0F / 2.4F) - 0.055F
458	    }
459	    clamp_float_cs(v, 0.0F, 1.0F)
460	  }
461	  (srgb(rl), srgb(gl), srgb(bl))
462	}
   	…

468	pub fn rgb_to_lab_16(
469	  r : Int,
470	  g : Int,
471	  b : Int,
472	) -> (Float, Float, Float) raise @types.LoadError {
473	  // 输入验证
474	  if r < 0 || r > 65535 || g < 0 || g > 65535 || b < 0 || b > 65535 {
475	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
476	      "rgb_to_lab_16: r,g,b必须在[0,65535]范围内",
477	    )
478	  }
479	  let (x, y, z) = rgb_to_xyz_16(r, g, b)
480	  let xn = 0.9505F
481	  let yn = 1.0F
482	  let zn = 1.089F
483	  fn f(t : Float) -> Float {
484	    if t > 0.008856F {
485	      @math.powf(t, 1.0F / 3.0F)
486	    } else {
487	      7.787F * t + 16.0F / 116.0F	<-- UNCOVERED
488	    }
489	  }
490	  let fx = f(x / xn)
491	  let fy = f(y / yn)
492	  let fz = f(z / zn)
493	  let l = 116.0F * fy - 16.0F
494	  let a = 500.0F * (fx - fy)
495	  let b_val = 200.0F * (fy - fz)
496	  (l, a, b_val)
497	}
   	…

501	pub fn lab_to_rgb_16(
502	  l : Float,
503	  a : Float,
504	  b : Float,
505	) -> (Int, Int, Int) raise @types.LoadError {
506	  // 输入验证
507	  if l < 0.0F || l > 100.0F {
508	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
509	      "lab_to_rgb_16: L必须在[0,100]范围内",
510	    )
511	  }
512	  let xn = 0.9505F
513	  let yn = 1.0F
514	  let zn = 1.089F
515	  let fy = (l + 16.0F) / 116.0F
516	  let fx = a / 500.0F + fy
517	  let fz = fy - b / 200.0F
518	  fn finv(t : Float) -> Float {
519	    if t * t * t > 0.008856F {
520	      t * t * t
521	    } else {
522	      (t - 16.0F / 116.0F) / 7.787F	<-- UNCOVERED
523	    }
524	  }
525	  let x = xn * finv(fx)
526	  let y = yn * finv(fy)
527	  let z = zn * finv(fz)
528	  xyz_to_rgb_16(x, y, z)
529	}
   	…

535	pub fn rgb_to_lab_f(
536	  r : Float,
537	  g : Float,
538	  b : Float,
539	) -> (Float, Float, Float) raise @types.LoadError {
540	  // 输入验证
541	  if r < 0.0F || r > 1.0F || g < 0.0F || g > 1.0F || b < 0.0F || b > 1.0F {
542	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
543	      "rgb_to_lab_f: r,g,b必须在[0,1]范围内",
544	    )
545	  }
546	  let (x, y, z) = rgb_to_xyz_f(r, g, b)
547	  let xn = 0.9505F
548	  let yn = 1.0F
549	  let zn = 1.089F
550	  fn f(t : Float) -> Float {
551	    if t > 0.008856F {
552	      @math.powf(t, 1.0F / 3.0F)
553	    } else {
554	      7.787F * t + 16.0F / 116.0F	<-- UNCOVERED
555	    }
556	  }
557	  let fx = f(x / xn)
558	  let fy = f(y / yn)
559	  let fz = f(z / zn)
560	  let l = 116.0F * fy - 16.0F
561	  let a = 500.0F * (fx - fy)
562	  let b_val = 200.0F * (fy - fz)
563	  (l, a, b_val)
564	}
   	…

568	pub fn lab_to_rgb_f(
569	  l : Float,
570	  a : Float,
571	  b : Float,
572	) -> (Float, Float, Float) raise @types.LoadError {
573	  // 输入验证
574	  if l < 0.0F || l > 100.0F {
575	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
576	      "lab_to_rgb_f: L必须在[0,100]范围内",
577	    )
578	  }
579	  let xn = 0.9505F
580	  let yn = 1.0F
581	  let zn = 1.089F
582	  let fy = (l + 16.0F) / 116.0F
583	  let fx = a / 500.0F + fy
584	  let fz = fy - b / 200.0F
585	  fn finv(t : Float) -> Float {
586	    if t * t * t > 0.008856F {
587	      t * t * t
588	    } else {
589	      (t - 16.0F / 116.0F) / 7.787F	<-- UNCOVERED
590	    }
591	  }
592	  let x = xn * finv(fx)
593	  let y = yn * finv(fy)
594	  let z = zn * finv(fz)
595	  xyz_to_rgb_f(x, y, z)
596	}
   	…

5 uncovered line(s) in src\process\color\colormap.mbt:

 86	pub fn apply_colormap(
 87	  img : @types.Image,
 88	  map : Colormap,
 89	) -> @types.Image raise @types.LoadError {
 90	  // 输入验证
 91	  if img.width <= 0 || img.height <= 0 {
 92	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 93	      "apply_colormap: 图像尺寸必须大于0",
 94	    )
 95	  }
 96	  if img.channels < 1 || img.channels > 4 {
 97	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 98	      "apply_colormap: 通道数必须在1-4之间",
 99	    )
100	  }
101	  let expected_size = img.width * img.height * img.channels
102	  if img.data.length() != expected_size {
103	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
104	      "apply_colormap: 数据大小不匹配，期望 " +
105	      expected_size.to_string() +	<-- UNCOVERED
106	      "，实际 " +
107	      img.data.length().to_string(),	<-- UNCOVERED
108	    )
109	  }
110	  let lut = get_colormap_lut(map)
111	  let n = img.width * img.height
112	  let ch = img.channels
113	  let out : Array[Byte] = Array::make(n * 3, b'\x00')
114	  for i in 0..<n {
115	    let gray = if ch == 1 {
116	      img.data[i].to_int()
117	    } else {
118	      let r = img.data[i * ch].to_int()
119	      let g = img.data[i * ch + 1].to_int()
120	      let b = img.data[i * ch + 2].to_int()
121	      (r * 299 + g * 587 + b * 114) / 1000
122	    }
123	    let (cr, cg, cb) = lut[gray]
124	    out[i * 3] = cr
125	    out[i * 3 + 1] = cg
126	    out[i * 3 + 2] = cb
127	  }
128	  @types.Image::{
129	    width: img.width,
130	    height: img.height,
131	    channels: 3,
132	    data: Bytes::from_array(out),
133	  }
134	}
   	…

10 uncovered line(s) in src\process\color\dehaze.mbt:

 9	pub fn dehaze(
10	  img : @types.Image,
11	  patch_size? : Int = 7,
12	  omega? : Float = 0.95F,
13	  t0? : Float = 0.1F,
14	) -> @types.Image raise @types.LoadError {
15	  // 输入验证
16	  if img.width < 0 || img.height < 0 {
17	    raise @types.LoadError::DecodeFailed("dehaze: 图像尺寸不能为负")	<-- UNCOVERED
18	  }
19	  if img.channels != 3 && img.channels != 4 {
20	    raise @types.LoadError::DecodeFailed("dehaze 需要 RGB/RGBA 图像")
21	  }
22	  let expected_size = img.width * img.height * img.channels
23	  if img.data.length() != expected_size {
24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
25	      "dehaze: 数据大小不匹配，期望 " +
26	      expected_size.to_string() +	<-- UNCOVERED
27	      "，实际 " +
28	      img.data.length().to_string(),	<-- UNCOVERED
29	    )
30	  }
31	  if patch_size < 1 || patch_size > 30 {
32	    raise @types.LoadError::DecodeFailed("patch_size 必须在 1..30 之间")
33	  }
34	  if omega <= 0.0F || omega > 1.0F {
35	    raise @types.LoadError::DecodeFailed("omega 必须在 (0, 1] 之间")
36	  }
37	  if t0 <= 0.0F || t0 > 1.0F {
38	    raise @types.LoadError::DecodeFailed("t0 必须在 (0, 1] 之间")
39	  }
40	  let ch = img.channels
41	  let w = img.width
42	  let h = img.height
43	  let n = w * h
44	  if n == 0 {
45	    return img
46	  }
47	  // 1. 计算暗通道
48	  let dark_channel = compute_dark_channel(img, w, h, ch, patch_size)
49	  // 2. 估计大气光 A（取暗通道中最亮的 0.1% 像素对应的原图最亮像素）
50	  let a = estimate_atmospheric_light(img, dark_channel, w, h, ch)
51	  let ar = Float::from_int(a[0])
52	  let ag = Float::from_int(a[1])
53	  let ab = Float::from_int(a[2])
54	  // 3. 计算透射率 t = 1 - omega * dark(I/A)
55	  let transmission : Array[Float] = Array::make(n, 0.0F)
56	  for i in 0..<n {
57	    transmission[i] = 1.0F - omega * dark_channel[i]
58	    if transmission[i] < t0 {
59	      transmission[i] = t0
60	    }
61	  }
62	  // 4. 恢复无雾图像: J = (I - A) / t + A
63	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
64	  for i in 0..<n {
65	    for c in 0..<3 {
66	      let iv = Float::from_int(img.data[i * ch + c].to_int())
67	      let ac = if c == 0 { ar } else if c == 1 { ag } else { ab }
68	      let jv = (iv - ac) / transmission[i] + ac
69	      out[i * ch + c] = jv.to_int().clamp(min=0, max=255).to_byte()
70	    }
71	    if ch == 4 {
72	      out[i * ch + 3] = img.data[i * ch + 3]
73	    }
74	  }
75	  @types.Image::{
76	    width: w,
77	    height: h,
78	    channels: ch,
79	    data: Bytes::from_array(out),
80	  }
81	}
  	…

185	pub fn guided_filter(
186	  img : @types.Image,
187	  input : Array[Float],
188	  radius? : Int = 20,
189	  eps? : Float = 0.001F,
190	) -> Array[Float] raise @types.LoadError {
191	  // 输入验证
192	  if img.width < 0 || img.height < 0 {
193	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
194	      "guided_filter: 图像尺寸不能为负",
195	    )
196	  }
197	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
198	    raise @types.LoadError::DecodeFailed(
199	      "guided_filter 仅支持 channels=1/3/4",
200	    )
201	  }
202	  let expected_size = img.width * img.height * img.channels
203	  if img.data.length() != expected_size {
204	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
205	      "guided_filter: 数据大小不匹配，期望 " +
206	      expected_size.to_string() +	<-- UNCOVERED
207	      "，实际 " +
208	      img.data.length().to_string(),	<-- UNCOVERED
209	    )
210	  }
211	  if radius < 1 {
212	    raise @types.LoadError::DecodeFailed("guided_filter: radius必须>=1")	<-- UNCOVERED
213	  }
214	  if eps <= 0.0F {
215	    raise @types.LoadError::DecodeFailed("guided_filter: eps必须大于0")	<-- UNCOVERED
216	  }
217	  let w = img.width
218	  let h = img.height
219	  let n = w * h
220	  if n == 0 || input.length() != n {
221	    return []
222	  }
223	  // 转灰度作为引导图
224	  let guide : Array[Float] = Array::make(n, 0.0F)
225	  let ch = img.channels
226	  for i in 0..<n {
227	    let v = if ch == 1 {
228	      img.data[i].to_int()
229	    } else {
230	      let r = img.data[i * ch].to_int()
231	      let g = img.data[i * ch + 1].to_int()
232	      let b = img.data[i * ch + 2].to_int()
233	      (r * 299 + g * 587 + b * 114) / 1000
234	    }
235	    guide[i] = Float::from_int(v) / 255.0F
236	  }
237	  // 计算均值
238	  let mean_i = box_filter(guide, w, h, radius)
239	  let mean_p = box_filter(input, w, h, radius)
240	  // 计算 I*P 的均值
241	  let ip : Array[Float] = Array::make(n, 0.0F)
242	  for i in 0..<n {
243	    ip[i] = guide[i] * input[i]
244	  }
245	  let mean_ip = box_filter(ip, w, h, radius)
246	  // 协方差
247	  let cov_ip : Array[Float] = Array::make(n, 0.0F)
248	  for i in 0..<n {
249	    cov_ip[i] = mean_ip[i] - mean_i[i] * mean_p[i]
250	  }
251	  // I² 的均值
252	  let i_sq : Array[Float] = Array::make(n, 0.0F)
253	  for i in 0..<n {
254	    i_sq[i] = guide[i] * guide[i]
255	  }
256	  let mean_i_sq = box_filter(i_sq, w, h, radius)
257	  // 方差
258	  let var_i : Array[Float] = Array::make(n, 0.0F)
259	  for i in 0..<n {
260	    var_i[i] = mean_i_sq[i] - mean_i[i] * mean_i[i]
261	  }
262	  // a = cov_ip / (var_i + eps), b = mean_p - a * mean_i
263	  let a : Array[Float] = Array::make(n, 0.0F)
264	  let b : Array[Float] = Array::make(n, 0.0F)
265	  for i in 0..<n {
266	    a[i] = cov_ip[i] / (var_i[i] + eps)
267	    b[i] = mean_p[i] - a[i] * mean_i[i]
268	  }
269	  let mean_a = box_filter(a, w, h, radius)
270	  let mean_b = box_filter(b, w, h, radius)
271	  // 输出 = mean_a * guide + mean_b
272	  let out : Array[Float] = Array::make(n, 0.0F)
273	  for i in 0..<n {
274	    out[i] = mean_a[i] * guide[i] + mean_b[i]
275	  }
276	  out
277	}
   	…

25 uncovered line(s) in src\process\color\dehaze_16f.mbt:

37	fn clamp_int_dh(v : Int, lo : Int, hi : Int) -> Int {
38	  if v < lo {
39	    lo	<-- UNCOVERED
40	  } else if v > hi {
41	    hi	<-- UNCOVERED
42	  } else {
43	    v
44	  }
45	}
  	…

124	pub fn dehaze_16(
125	  img : @types.Image16,
126	  patch_size? : Int = 7,
127	  omega? : Float = 0.95F,
128	  t0? : Float = 0.1F,
129	) -> @types.Image16 raise @types.LoadError {
130	  // 输入验证
131	  if img.width <= 0 || img.height <= 0 {
132	    raise @types.LoadError::DecodeFailed("dehaze_16: 图像尺寸必须大于0")	<-- UNCOVERED
133	  }
134	  if img.channels != 3 && img.channels != 4 {
135	    raise @types.LoadError::DecodeFailed(
136	      "dehaze_16: 仅支持RGB/RGBA图像（channels=3/4）",
137	    )
138	  }
139	  let expected_size = img.width * img.height * img.channels * 2
140	  if img.data.length() != expected_size {
141	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
142	      "dehaze_16: 数据大小不匹配，期望 " +
143	      expected_size.to_string() +	<-- UNCOVERED
144	      "，实际 " +
145	      img.data.length().to_string(),	<-- UNCOVERED
146	    )
147	  }
148	  let img8 = img16_to_img8_dh(img)
149	  let out8 = dehaze(img8, patch_size~, omega~, t0~)
150	  img8_to_img16_dh(out8)
151	}
   	…

157	pub fn dehaze_f(
158	  img : @types.ImageF,
159	  patch_size? : Int = 7,
160	  omega? : Float = 0.95F,
161	  t0? : Float = 0.1F,
162	) -> @types.ImageF raise @types.LoadError {
163	  // 输入验证
164	  if img.width <= 0 || img.height <= 0 {
165	    raise @types.LoadError::DecodeFailed("dehaze_f: 图像尺寸必须大于0")	<-- UNCOVERED
166	  }
167	  if img.channels != 3 && img.channels != 4 {
168	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
169	      "dehaze_f: 仅支持RGB/RGBA图像（channels=3/4）",
170	    )
171	  }
172	  let expected_size = img.width * img.height * img.channels * 4
173	  if img.data.length() != expected_size {
174	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
175	      "dehaze_f: 数据大小不匹配，期望 " +
176	      expected_size.to_string() +	<-- UNCOVERED
177	      "，实际 " +
178	      img.data.length().to_string(),	<-- UNCOVERED
179	    )
180	  }
181	  let img8 = imgf_to_img8_dh(img)
182	  let out8 = dehaze(img8, patch_size~, omega~, t0~)
183	  img8_to_imgf_dh(out8)
184	}
   	…

190	pub fn guided_filter_16(
191	  img : @types.Image16,
192	  input : Array[Float],
193	  radius? : Int = 20,
194	  eps? : Float = 0.001F,
195	) -> Array[Float] raise @types.LoadError {
196	  // 输入验证
197	  if img.width <= 0 || img.height <= 0 {
198	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
199	      "guided_filter_16: 图像尺寸必须大于0",
200	    )
201	  }
202	  if img.channels < 1 || img.channels > 4 {
203	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
204	      "guided_filter_16: 通道数必须在1-4之间",
205	    )
206	  }
207	  let expected_size = img.width * img.height * img.channels * 2
208	  if img.data.length() != expected_size {
209	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
210	      "guided_filter_16: 数据大小不匹配，期望 " +
211	      expected_size.to_string() +	<-- UNCOVERED
212	      "，实际 " +
213	      img.data.length().to_string(),	<-- UNCOVERED
214	    )
215	  }
216	  if radius < 1 {
217	    raise @types.LoadError::DecodeFailed("guided_filter_16: radius必须>=1")	<-- UNCOVERED
218	  }
219	  if eps <= 0.0F {
220	    raise @types.LoadError::DecodeFailed("guided_filter_16: eps必须大于0")	<-- UNCOVERED
221	  }
222	  let img8 = img16_to_img8_dh(img)
223	  guided_filter(img8, input, radius~, eps~)
224	}
   	…

230	pub fn guided_filter_f(
231	  img : @types.ImageF,
232	  input : Array[Float],
233	  radius? : Int = 20,
234	  eps? : Float = 0.001F,
235	) -> Array[Float] raise @types.LoadError {
236	  // 输入验证
237	  if img.width <= 0 || img.height <= 0 {
238	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
239	      "guided_filter_f: 图像尺寸必须大于0",
240	    )
241	  }
242	  if img.channels < 1 || img.channels > 4 {
243	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
244	      "guided_filter_f: 通道数必须在1-4之间",
245	    )
246	  }
247	  let expected_size = img.width * img.height * img.channels * 4
248	  if img.data.length() != expected_size {
249	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
250	      "guided_filter_f: 数据大小不匹配，期望 " +
251	      expected_size.to_string() +	<-- UNCOVERED
252	      "，实际 " +
253	      img.data.length().to_string(),	<-- UNCOVERED
254	    )
255	  }
256	  if radius < 1 {
257	    raise @types.LoadError::DecodeFailed("guided_filter_f: radius必须>=1")	<-- UNCOVERED
258	  }
259	  if eps <= 0.0F {
260	    raise @types.LoadError::DecodeFailed("guided_filter_f: eps必须大于0")	<-- UNCOVERED
261	  }
262	  let img8 = imgf_to_img8_dh(img)
263	  guided_filter(img8, input, radius~, eps~)
264	}
   	…

2 uncovered line(s) in src\process\color\helpers.mbt:

 2	fn clamp_i(v : Int, lo : Int, hi : Int) -> Int {
 3	  if v < lo {
 4	    lo	<-- UNCOVERED
 5	  } else if v > hi {
 6	    hi	<-- UNCOVERED
 7	  } else {
 8	    v
 9	  }
10	}
  	…

13 uncovered line(s) in src\process\color\quantize.mbt:

 7	pub fn floyd_steinberg(
 8	  img : @types.Image,
 9	  levels : Int,
10	) -> @types.Image raise @types.LoadError {
11	  // 输入验证
12	  if img.width <= 0 || img.height <= 0 {
13	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
14	      "floyd_steinberg: 图像尺寸必须大于0",
15	    )
16	  }
17	  if img.channels < 1 || img.channels > 4 {
18	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
19	      "floyd_steinberg: 通道数必须在1-4之间",
20	    )
21	  }
22	  let expected_size = img.width * img.height * img.channels
23	  if img.data.length() != expected_size {
24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
25	      "floyd_steinberg: 数据大小不匹配，期望 " +
26	      expected_size.to_string() +	<-- UNCOVERED
27	      "，实际 " +
28	      img.data.length().to_string(),	<-- UNCOVERED
29	    )
30	  }
31	  if levels < 2 {
32	    raise @types.LoadError::DecodeFailed("floyd_steinberg: levels必须>=2")
33	  }
34	  let ch = img.channels
35	  let color_ch = if ch >= 3 { 3 } else { ch }
36	  let w = img.width
37	  let h = img.height
38	  let n = w * h
39	  // 可变像素缓冲
40	  let buf : Array[Int] = Array::make(n * color_ch, 0)
41	  for i in 0..<n {
42	    for c in 0..<color_ch {
43	      buf[i * color_ch + c] = img.data[i * ch + c].to_int()
44	    }
45	  }
46	  let step = 255 / (levels - 1)
47	  let half_step = step / 2
48	  // Floyd-Steinberg 抖动
49	  for y in 0..<h {
50	    for x in 0..<w {
51	      for c in 0..<color_ch {
52	        let idx = (y * w + x) * color_ch + c
53	        let old_val = buf[idx].clamp(min=0, max=255)
54	        let new_val = ((old_val + half_step) / step * step).clamp(
55	          min=0,
56	          max=255,
57	        )
58	        buf[idx] = new_val
59	        let err = old_val - new_val
60	        // 误差扩散：右 7/16, 左下 3/16, 下 5/16, 右下 1/16
61	        if x + 1 < w {
62	          buf[(y * w + x + 1) * color_ch + c] += err * 7 / 16
63	        }
64	        if y + 1 < h {
65	          if x > 0 {
66	            buf[((y + 1) * w + x - 1) * color_ch + c] += err * 3 / 16
67	          }
68	          buf[((y + 1) * w + x) * color_ch + c] += err * 5 / 16
69	          if x + 1 < w {
70	            buf[((y + 1) * w + x + 1) * color_ch + c] += err / 16
71	          }
72	        }
73	      }
74	    }
75	  }
76	  // 输出
77	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
78	  for i in 0..<n {
79	    for c in 0..<color_ch {
80	      out[i * ch + c] = buf[i * color_ch + c].clamp(min=0, max=255).to_byte()
81	    }
82	    if ch == 4 {
83	      out[i * ch + 3] = img.data[i * ch + 3]
84	    }
85	  }
86	  @types.Image::{
87	    width: w,
88	    height: h,
89	    channels: ch,
90	    data: Bytes::from_array(out),
91	  }
92	}
  	…

 98	pub fn median_cut(
 99	  img : @types.Image,
100	  num_colors : Int,
101	) -> @types.Image raise @types.LoadError {
102	  // 输入验证
103	  if img.width <= 0 || img.height <= 0 {
104	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
105	      "median_cut: 图像尺寸必须大于0",
106	    )
107	  }
108	  if img.channels < 1 || img.channels > 4 {
109	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
110	      "median_cut: 通道数必须在1-4之间",
111	    )
112	  }
113	  let expected_size = img.width * img.height * img.channels
114	  if img.data.length() != expected_size {
115	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
116	      "median_cut: 数据大小不匹配，期望 " +
117	      expected_size.to_string() +	<-- UNCOVERED
118	      "，实际 " +
119	      img.data.length().to_string(),	<-- UNCOVERED
120	    )
121	  }
122	  if num_colors < 2 {
123	    return to_rgb(img)
124	  }
125	  let w = img.width
126	  let h = img.height
127	  let n = w * h
128	  // 收集所有 RGB 像素
129	  let pixels : Array[(Int, Int, Int)] = Array::make(n, (0, 0, 0))
130	  let ch = img.channels
131	  let data_int : Array[Int] = Array::make(n * ch, 0)
132	  for i in 0..<(n * ch) {
133	    data_int[i] = img.data[i].to_int()
134	  }
135	  for i in 0..<n {
136	    let r = data_int[i * ch]
137	    let g = if ch >= 2 { data_int[i * ch + 1] } else { r }
138	    let b = if ch >= 3 { data_int[i * ch + 2] } else { r }
139	    pixels[i] = (r, g, b)
140	  }
141	  // 初始 bucket 包含所有像素索引
142	  let initial_bucket : Array[Int] = Array::make(n, 0)
143	  for i in 0..<n {
144	    initial_bucket[i] = i
145	  }
146	  // 递归分割
147	  let buckets : Array[Array[Int]] = [initial_bucket]
148	  let mut num_buckets = 1
149	  while num_buckets < num_colors {
150	    // 找到范围最大的 bucket
151	    let mut best_bucket_idx = -1
152	    let mut best_range = 0
153	    for bi in 0..<buckets.length() {
154	      if buckets[bi].length() < 2 {
155	        continue
156	      }
157	      let (range, _ch) = bucket_max_range(pixels, buckets[bi])
158	      if range > best_range {
159	        best_range = range
160	        best_bucket_idx = bi
161	      }
162	    }
163	    if best_bucket_idx < 0 {
164	      break
165	    }
166	    // 分割最佳 bucket
167	    let bucket = buckets[best_bucket_idx]
168	    let (_, split_ch) = bucket_max_range(pixels, bucket)
169	    // 按分割通道排序
170	    let sorted = bucket.copy()
171	    sorted.sort_by(fn(a, b) {
172	      let va = channel_value(pixels[a], split_ch)
173	      let vb = channel_value(pixels[b], split_ch)
174	      va.compare(vb)
175	    })
176	    let mid = sorted.length() / 2
177	    let left : Array[Int] = Array::make(mid, 0)
178	    let right : Array[Int] = Array::make(sorted.length() - mid, 0)
179	    for i in 0..<mid {
180	      left[i] = sorted[i]
181	    }
182	    for i in mid..<sorted.length() {
183	      right[i - mid] = sorted[i]
184	    }
185	    buckets[best_bucket_idx] = left
186	    buckets.push(right)
187	    num_buckets += 1
188	  }
189	  // 计算每个 bucket 的平均色作为调色板
190	  let palette : Array[(Int, Int, Int)] = Array::make(
191	    buckets.length(),
192	    (0, 0, 0),
193	  )
194	  for bi in 0..<buckets.length() {
195	    palette[bi] = bucket_average(pixels, buckets[bi])
196	  }
197	  // 映射每个像素到最近的调色板颜色
198	  let out : Array[Byte] = Array::make(n * 3, b'\x00')
199	  for i in 0..<n {
200	    let (r, g, b) = pixels[i]
201	    let mut best_idx = 0
202	    let mut best_dist = 999999
203	    for pi in 0..<palette.length() {
204	      let (pr, pg, pb) = palette[pi]
205	      let dist = (r - pr) * (r - pr) + (g - pg) * (g - pg) + (b - pb) * (b - pb)
206	      if dist < best_dist {
207	        best_dist = dist
208	        best_idx = pi
209	      }
210	    }
211	    let (pr, pg, pb) = palette[best_idx]
212	    out[i * 3] = pr.to_byte()
213	    out[i * 3 + 1] = pg.to_byte()
214	    out[i * 3 + 2] = pb.to_byte()
215	  }
216	  @types.Image::{
217	    width: w,
218	    height: h,
219	    channels: 3,
220	    data: Bytes::from_array(out),
221	  }
222	}
   	…

336	fn channel_value(pixel : (Int, Int, Int), ch : Int) -> Int {
337	  match ch {
338	    0 => pixel.0
339	    1 => pixel.1
340	    _ => pixel.2	<-- UNCOVERED
341	  }
342	}
   	…

345	fn bucket_max_range(
346	  pixels : Array[(Int, Int, Int)],
347	  bucket : Array[Int],
348	) -> (Int, Int) {
349	  let mut min_r = 255
350	  let mut max_r = 0
351	  let mut min_g = 255
352	  let mut max_g = 0
353	  let mut min_b = 255
354	  let mut max_b = 0
355	  for i in 0..<bucket.length() {
356	    let (r, g, b) = pixels[bucket[i]]
357	    if r < min_r {
358	      min_r = r
359	    }
360	    if r > max_r {
361	      max_r = r
362	    }
363	    if g < min_g {
364	      min_g = g
365	    }
366	    if g > max_g {
367	      max_g = g
368	    }
369	    if b < min_b {
370	      min_b = b
371	    }
372	    if b > max_b {
373	      max_b = b
374	    }
375	  }
376	  let range_r = max_r - min_r
377	  let range_g = max_g - min_g
378	  let range_b = max_b - min_b
379	  if range_r >= range_g && range_r >= range_b {
380	    (range_r, 0)
381	  } else if range_g >= range_b {
382	    (range_g, 1)
383	  } else {
384	    (range_b, 2)	<-- UNCOVERED
385	  }
386	}
   	…

389	fn bucket_average(
390	  pixels : Array[(Int, Int, Int)],
391	  bucket : Array[Int],
392	) -> (Int, Int, Int) {
393	  let mut sum_r = 0
394	  let mut sum_g = 0
395	  let mut sum_b = 0
396	  for i in 0..<bucket.length() {
397	    let (r, g, b) = pixels[bucket[i]]
398	    sum_r += r
399	    sum_g += g
400	    sum_b += b
401	  }
402	  let len = bucket.length()
403	  if len == 0 {
404	    (0, 0, 0)	<-- UNCOVERED
405	  } else {
406	    (sum_r / len, sum_g / len, sum_b / len)
407	  }
408	}
   	…

77 uncovered line(s) in src\process\color\quantize_16f.mbt:

 53	pub fn kmeans_quantize_16(
 54	  img : @types.Image16,
 55	  k : Int,
 56	  max_iters : Int,
 57	) -> @types.Image16 raise @types.LoadError {
 58	  // 输入验证
 59	  if img.width < 0 || img.height < 0 {
 60	    raise @types.LoadError::DecodeFailed(
 61	      "kmeans_quantize_16: 图像尺寸不能为负",
 62	    )
 63	  }
 64	  if k < 1 || k > 256 {
 65	    raise @types.LoadError::DecodeFailed("k 必须在 1..256 之间")
 66	  }
 67	  if img.channels != 3 && img.channels != 4 {
 68	    raise @types.LoadError::DecodeFailed(
 69	      "kmeans_quantize_16 需要 RGB/RGBA 图像",
 70	    )
 71	  }
 72	  let expected_size = img.width * img.height * img.channels * 2
 73	  if img.data.length() != expected_size {
 74	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 75	      "kmeans_quantize_16: 数据大小不匹配，期望 " +
 76	      expected_size.to_string() +	<-- UNCOVERED
 77	      "，实际 " +
 78	      img.data.length().to_string(),	<-- UNCOVERED
 79	    )
 80	  }
 81	  let ch = img.channels
 82	  let n = img.width * img.height
 83	  if n == 0 {
 84	    return img
 85	  }
 86	  let centroids_r : Array[Int] = Array::make(k, 0)
 87	  let centroids_g : Array[Int] = Array::make(k, 0)
 88	  let centroids_b : Array[Int] = Array::make(k, 0)
 89	  let data_int : Array[Int] = Array::make(n * ch, 0)
 90	  for i in 0..<(n * ch) {
 91	    data_int[i] = read_u16_q(img.data, i * 2)
 92	  }
 93	  let step = if n > k { n / k } else { 1 }
 94	  for i in 0..<k {
 95	    let idx = (i * step).clamp(min=0, max=n - 1)
 96	    centroids_r[i] = data_int[idx * ch]
 97	    centroids_g[i] = data_int[idx * ch + 1]
 98	    centroids_b[i] = data_int[idx * ch + 2]
 99	  }
100	  let labels : Array[Int] = Array::make(n, 0)
101	  let iters = if max_iters < 1 { 20 } else { max_iters }
102	  for _ in 0..<iters {
103	    let mut changed = false
104	    for i in 0..<n {
105	      let r = data_int[i * ch]
106	      let g = data_int[i * ch + 1]
107	      let b = data_int[i * ch + 2]
108	      let mut best = 0
109	      let mut best_dist = (r - centroids_r[0]) * (r - centroids_r[0]) +
110	        (g - centroids_g[0]) * (g - centroids_g[0]) +
111	        (b - centroids_b[0]) * (b - centroids_b[0])
112	      for j in 1..<k {
113	        let d = (r - centroids_r[j]) * (r - centroids_r[j]) +
114	          (g - centroids_g[j]) * (g - centroids_g[j]) +
115	          (b - centroids_b[j]) * (b - centroids_b[j])
116	        if d < best_dist {
117	          best_dist = d	<-- UNCOVERED
118	          best = j
119	        }
120	      }
121	      if labels[i] != best {
122	        changed = true	<-- UNCOVERED
123	        labels[i] = best
124	      }
125	    }
126	    if !changed {
127	      break
128	    }
129	    let sum_r : Array[Int] = Array::make(k, 0)	<-- UNCOVERED
130	    let sum_g : Array[Int] = Array::make(k, 0)
131	    let sum_b : Array[Int] = Array::make(k, 0)
132	    let cnt : Array[Int] = Array::make(k, 0)
133	    for i in 0..<n {
134	      let label = labels[i]	<-- UNCOVERED
135	      sum_r[label] += data_int[i * ch]
136	      sum_g[label] += data_int[i * ch + 1]
137	      sum_b[label] += data_int[i * ch + 2]
138	      cnt[label] += 1
139	    }
140	    for j in 0..<k {
141	      if cnt[j] > 0 {	<-- UNCOVERED
142	        centroids_r[j] = sum_r[j] / cnt[j]
143	        centroids_g[j] = sum_g[j] / cnt[j]
144	        centroids_b[j] = sum_b[j] / cnt[j]
145	      }
146	    }
147	  }
148	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
149	  for i in 0..<n {
150	    let label = labels[i]
151	    write_u16_q(out, i * ch * 2, centroids_r[label])
152	    write_u16_q(out, (i * ch + 1) * 2, centroids_g[label])
153	    write_u16_q(out, (i * ch + 2) * 2, centroids_b[label])
154	    if ch == 4 {
155	      let v = read_u16_q(img.data, (i * ch + 3) * 2)
156	      write_u16_q(out, (i * ch + 3) * 2, v)
157	    }
158	  }
159	  @types.Image16::{
160	    width: img.width,
161	    height: img.height,
162	    channels: ch,
163	    data: Bytes::from_array(out),
164	  }
165	}
   	…

171	pub fn kmeans_quantize_f(
172	  img : @types.ImageF,
173	  k : Int,
174	  max_iters : Int,
175	) -> @types.ImageF raise @types.LoadError {
176	  // 输入验证
177	  if img.width < 0 || img.height < 0 {
178	    raise @types.LoadError::DecodeFailed(
179	      "kmeans_quantize_f: 图像尺寸不能为负",
180	    )
181	  }
182	  if k < 1 || k > 256 {
183	    raise @types.LoadError::DecodeFailed("k 必须在 1..256 之间")
184	  }
185	  if img.channels != 3 && img.channels != 4 {
186	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
187	      "kmeans_quantize_f 需要 RGB/RGBA 图像",
188	    )
189	  }
190	  let expected_size = img.width * img.height * img.channels * 4
191	  if img.data.length() != expected_size {
192	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
193	      "kmeans_quantize_f: 数据大小不匹配，期望 " +
194	      expected_size.to_string() +	<-- UNCOVERED
195	      "，实际 " +
196	      img.data.length().to_string(),	<-- UNCOVERED
197	    )
198	  }
199	  let ch = img.channels
200	  let n = img.width * img.height
201	  if n == 0 {
202	    return img	<-- UNCOVERED
203	  }
204	  let centroids_r : Array[Float] = Array::make(k, 0.0F)
205	  let centroids_g : Array[Float] = Array::make(k, 0.0F)
206	  let centroids_b : Array[Float] = Array::make(k, 0.0F)
207	  let data_f : Array[Float] = Array::make(n * ch, 0.0F)
208	  for i in 0..<(n * ch) {
209	    data_f[i] = read_f32_q(img.data, i * 4)
210	  }
211	  let step = if n > k { n / k } else { 1 }
212	  for i in 0..<k {
213	    let idx = (i * step).clamp(min=0, max=n - 1)
214	    centroids_r[i] = data_f[idx * ch]
215	    centroids_g[i] = data_f[idx * ch + 1]
216	    centroids_b[i] = data_f[idx * ch + 2]
217	  }
218	  let labels : Array[Int] = Array::make(n, 0)
219	  let iters = if max_iters < 1 { 20 } else { max_iters }
220	  for _ in 0..<iters {
221	    let mut changed = false
222	    for i in 0..<n {
223	      let r = data_f[i * ch]
224	      let g = data_f[i * ch + 1]
225	      let b = data_f[i * ch + 2]
226	      let mut best = 0
227	      let mut best_dist = dist_sq_f(
228	        r,
229	        g,
230	        b,
231	        centroids_r[0],
232	        centroids_g[0],
233	        centroids_b[0],
234	      )
235	      for j in 1..<k {
236	        let d = dist_sq_f(
237	          r,
238	          g,
239	          b,
240	          centroids_r[j],
241	          centroids_g[j],
242	          centroids_b[j],
243	        )
244	        if d < best_dist {
245	          best_dist = d	<-- UNCOVERED
246	          best = j
247	        }
248	      }
249	      if labels[i] != best {
250	        changed = true	<-- UNCOVERED
251	        labels[i] = best
252	      }
253	    }
254	    if !changed {
255	      break
256	    }
257	    let sum_r : Array[Float] = Array::make(k, 0.0F)	<-- UNCOVERED
258	    let sum_g : Array[Float] = Array::make(k, 0.0F)
259	    let sum_b : Array[Float] = Array::make(k, 0.0F)
260	    let cnt : Array[Int] = Array::make(k, 0)
261	    for i in 0..<n {
262	      let label = labels[i]	<-- UNCOVERED
263	      sum_r[label] += data_f[i * ch]
264	      sum_g[label] += data_f[i * ch + 1]
265	      sum_b[label] += data_f[i * ch + 2]
266	      cnt[label] += 1
267	    }
268	    for j in 0..<k {
269	      if cnt[j] > 0 {	<-- UNCOVERED
270	        centroids_r[j] = sum_r[j] / Float::from_int(cnt[j])
271	        centroids_g[j] = sum_g[j] / Float::from_int(cnt[j])
272	        centroids_b[j] = sum_b[j] / Float::from_int(cnt[j])
273	      }
274	    }
275	  }
276	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
277	  for i in 0..<n {
278	    let label = labels[i]
279	    write_f32_q(out, i * ch * 4, centroids_r[label])
280	    write_f32_q(out, (i * ch + 1) * 4, centroids_g[label])
281	    write_f32_q(out, (i * ch + 2) * 4, centroids_b[label])
282	    if ch == 4 {
283	      let v = read_f32_q(img.data, (i * ch + 3) * 4)
284	      write_f32_q(out, (i * ch + 3) * 4, v)
285	    }
286	  }
287	  @types.ImageF::{
288	    width: img.width,
289	    height: img.height,
290	    channels: ch,
291	    data: Bytes::from_array(out),
292	  }
293	}
   	…

299	pub fn floyd_steinberg_16(
300	  img : @types.Image16,
301	  levels : Int,
302	) -> @types.Image16 raise @types.LoadError {
303	  // 输入验证
304	  if img.width <= 0 || img.height <= 0 {
305	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
306	      "floyd_steinberg_16: 图像尺寸必须大于0",
307	    )
308	  }
309	  if img.channels < 1 || img.channels > 4 {
310	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
311	      "floyd_steinberg_16: 通道数必须在1-4之间",
312	    )
313	  }
314	  let expected_size = img.width * img.height * img.channels * 2
315	  if img.data.length() != expected_size {
316	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
317	      "floyd_steinberg_16: 数据大小不匹配，期望 " +
318	      expected_size.to_string() +	<-- UNCOVERED
319	      "，实际 " +
320	      img.data.length().to_string(),	<-- UNCOVERED
321	    )
322	  }
323	  if levels < 2 {
324	    raise @types.LoadError::DecodeFailed("floyd_steinberg_16: levels必须>=2")	<-- UNCOVERED
325	  }
326	  let ch = img.channels
327	  let color_ch = if ch >= 3 { 3 } else { ch }
328	  let w = img.width
329	  let h = img.height
330	  let n = w * h
331	  let buf : Array[Int] = Array::make(n * color_ch, 0)
332	  for i in 0..<n {
333	    for c in 0..<color_ch {
334	      buf[i * color_ch + c] = read_u16_q(img.data, (i * ch + c) * 2)
335	    }
336	  }
337	  let step = 65535 / (levels - 1)
338	  let half_step = step / 2
339	  for y in 0..<h {
340	    for x in 0..<w {
341	      for c in 0..<color_ch {
342	        let idx = (y * w + x) * color_ch + c
343	        let old_val = if buf[idx] < 0 {
344	          0	<-- UNCOVERED
345	        } else if buf[idx] > 65535 {
346	          65535	<-- UNCOVERED
347	        } else {
348	          buf[idx]
349	        }
350	        let new_val = (old_val + half_step) / step * step
351	        let new_val_clamped = if new_val < 0 {
352	          0	<-- UNCOVERED
353	        } else if new_val > 65535 {
354	          65535	<-- UNCOVERED
355	        } else {
356	          new_val
357	        }
358	        buf[idx] = new_val_clamped
359	        let err = old_val - new_val_clamped
360	        if x + 1 < w {
361	          buf[(y * w + x + 1) * color_ch + c] += err * 7 / 16
362	        }
363	        if y + 1 < h {
364	          if x > 0 {
365	            buf[((y + 1) * w + x - 1) * color_ch + c] += err * 3 / 16
366	          }
367	          buf[((y + 1) * w + x) * color_ch + c] += err * 5 / 16
368	          if x + 1 < w {
369	            buf[((y + 1) * w + x + 1) * color_ch + c] += err / 16
370	          }
371	        }
372	      }
373	    }
374	  }
375	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
376	  for i in 0..<n {
377	    for c in 0..<color_ch {
378	      let v = if buf[i * color_ch + c] < 0 {
379	        0	<-- UNCOVERED
380	      } else if buf[i * color_ch + c] > 65535 {
381	        65535	<-- UNCOVERED
382	      } else {
383	        buf[i * color_ch + c]
384	      }
385	      write_u16_q(out, (i * ch + c) * 2, v)
386	    }
387	    if ch == 4 {
388	      let v = read_u16_q(img.data, (i * ch + 3) * 2)
389	      write_u16_q(out, (i * ch + 3) * 2, v)
390	    }
391	  }
392	  @types.Image16::{
393	    width: w,
394	    height: h,
395	    channels: ch,
396	    data: Bytes::from_array(out),
397	  }
398	}
   	…

404	pub fn floyd_steinberg_f(
405	  img : @types.ImageF,
406	  levels : Int,
407	) -> @types.ImageF raise @types.LoadError {
408	  // 输入验证
409	  if img.width <= 0 || img.height <= 0 {
410	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
411	      "floyd_steinberg_f: 图像尺寸必须大于0",
412	    )
413	  }
414	  if img.channels < 1 || img.channels > 4 {
415	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
416	      "floyd_steinberg_f: 通道数必须在1-4之间",
417	    )
418	  }
419	  let expected_size = img.width * img.height * img.channels * 4
420	  if img.data.length() != expected_size {
421	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
422	      "floyd_steinberg_f: 数据大小不匹配，期望 " +
423	      expected_size.to_string() +	<-- UNCOVERED
424	      "，实际 " +
425	      img.data.length().to_string(),	<-- UNCOVERED
426	    )
427	  }
428	  if levels < 2 {
429	    return img	<-- UNCOVERED
430	  }
431	  let ch = img.channels
432	  let color_ch = if ch >= 3 { 3 } else { ch }
433	  let w = img.width
434	  let h = img.height
435	  let n = w * h
436	  let buf : Array[Float] = Array::make(n * color_ch, 0.0F)
437	  for i in 0..<n {
438	    for c in 0..<color_ch {
439	      buf[i * color_ch + c] = read_f32_q(img.data, (i * ch + c) * 4)
440	    }
441	  }
442	  let step = 1.0F / Float::from_int(levels - 1)
443	  let half_step = step / 2.0F
444	  for y in 0..<h {
445	    for x in 0..<w {
446	      for c in 0..<color_ch {
447	        let idx = (y * w + x) * color_ch + c
448	        let old_val = if buf[idx] < 0.0F {
449	          0.0F	<-- UNCOVERED
450	        } else if buf[idx] > 1.0F {
451	          1.0F	<-- UNCOVERED
452	        } else {
453	          buf[idx]
454	        }
455	        let new_val = Float::from_int(((old_val + half_step) / step).to_int()) *
456	          step
457	        let new_val_clamped = if new_val < 0.0F {
458	          0.0F	<-- UNCOVERED
459	        } else if new_val > 1.0F {
460	          1.0F	<-- UNCOVERED
461	        } else {
462	          new_val
463	        }
464	        buf[idx] = new_val_clamped
465	        let err = old_val - new_val_clamped
466	        if x + 1 < w {
467	          buf[(y * w + x + 1) * color_ch + c] += err * 7.0F / 16.0F
468	        }
469	        if y + 1 < h {
470	          if x > 0 {
471	            buf[((y + 1) * w + x - 1) * color_ch + c] += err * 3.0F / 16.0F
472	          }
473	          buf[((y + 1) * w + x) * color_ch + c] += err * 5.0F / 16.0F
474	          if x + 1 < w {
475	            buf[((y + 1) * w + x + 1) * color_ch + c] += err / 16.0F
476	          }
477	        }
478	      }
479	    }
480	  }
481	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
482	  for i in 0..<n {
483	    for c in 0..<color_ch {
484	      let v = if buf[i * color_ch + c] < 0.0F {
485	        0.0F	<-- UNCOVERED
486	      } else if buf[i * color_ch + c] > 1.0F {
487	        1.0F	<-- UNCOVERED
488	      } else {
489	        buf[i * color_ch + c]
490	      }
491	      write_f32_q(out, (i * ch + c) * 4, v)
492	    }
493	    if ch == 4 {
494	      let v = read_f32_q(img.data, (i * ch + 3) * 4)	<-- UNCOVERED
495	      write_f32_q(out, (i * ch + 3) * 4, v)
496	    }
497	  }
498	  @types.ImageF::{
499	    width: w,
500	    height: h,
501	    channels: ch,
502	    data: Bytes::from_array(out),
503	  }
504	}
   	…

509	fn channel_value_mc16(pixel : (Int, Int, Int), ch : Int) -> Int {
510	  match ch {	<-- UNCOVERED
511	    0 => pixel.0
512	    1 => pixel.1
513	    _ => pixel.2
514	  }
515	}
   	…

518	fn bucket_max_range_mc16(
519	  pixels : Array[(Int, Int, Int)],
520	  bucket : Array[Int],
521	) -> (Int, Int) {
522	  let mut min_r = 65535
523	  let mut max_r = 0
524	  let mut min_g = 65535
525	  let mut max_g = 0
526	  let mut min_b = 65535
527	  let mut max_b = 0
528	  for i in 0..<bucket.length() {
529	    let (r, g, b) = pixels[bucket[i]]
530	    if r < min_r {
531	      min_r = r
532	    }
533	    if r > max_r {
534	      max_r = r
535	    }
536	    if g < min_g {
537	      min_g = g
538	    }
539	    if g > max_g {
540	      max_g = g
541	    }
542	    if b < min_b {
543	      min_b = b
544	    }
545	    if b > max_b {
546	      max_b = b
547	    }
548	  }
549	  let range_r = max_r - min_r
550	  let range_g = max_g - min_g
551	  let range_b = max_b - min_b
552	  if range_r >= range_g && range_r >= range_b {
553	    (range_r, 0)
554	  } else if range_g >= range_b {	<-- UNCOVERED
555	    (range_g, 1)
556	  } else {
557	    (range_b, 2)	<-- UNCOVERED
558	  }
559	}
   	…

562	fn bucket_average_mc16(
563	  pixels : Array[(Int, Int, Int)],
564	  bucket : Array[Int],
565	) -> (Int, Int, Int) {
566	  let mut sum_r = 0
567	  let mut sum_g = 0
568	  let mut sum_b = 0
569	  for i in 0..<bucket.length() {
570	    let (r, g, b) = pixels[bucket[i]]
571	    sum_r += r
572	    sum_g += g
573	    sum_b += b
574	  }
575	  let len = bucket.length()
576	  if len == 0 {
577	    (0, 0, 0)	<-- UNCOVERED
578	  } else {
579	    (sum_r / len, sum_g / len, sum_b / len)
580	  }
581	}
   	…

587	pub fn median_cut_16(
588	  img : @types.Image16,
589	  num_colors : Int,
590	) -> @types.Image16 raise @types.LoadError {
591	  // 输入验证
592	  if img.width <= 0 || img.height <= 0 {
593	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
594	      "median_cut_16: 图像尺寸必须大于0",
595	    )
596	  }
597	  if img.channels < 1 || img.channels > 4 {
598	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
599	      "median_cut_16: 通道数必须在1-4之间",
600	    )
601	  }
602	  let expected_size = img.width * img.height * img.channels * 2
603	  if img.data.length() != expected_size {
604	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
605	      "median_cut_16: 数据大小不匹配，期望 " +
606	      expected_size.to_string() +	<-- UNCOVERED
607	      "，实际 " +
608	      img.data.length().to_string(),	<-- UNCOVERED
609	    )
610	  }
611	  if num_colors < 2 {
612	    raise @types.LoadError::DecodeFailed("median_cut_16: num_colors必须>=2")	<-- UNCOVERED
613	  }
614	  let w = img.width
615	  let h = img.height
616	  let n = w * h
617	  let ch = img.channels
618	  let pixels : Array[(Int, Int, Int)] = Array::make(n, (0, 0, 0))
619	  for i in 0..<n {
620	    let r = read_u16_q(img.data, i * ch * 2)
621	    let g = if ch >= 2 { read_u16_q(img.data, (i * ch + 1) * 2) } else { r }
622	    let b = if ch >= 3 { read_u16_q(img.data, (i * ch + 2) * 2) } else { r }
623	    pixels[i] = (r, g, b)
624	  }
625	  let initial_bucket : Array[Int] = Array::make(n, 0)
626	  for i in 0..<n {
627	    initial_bucket[i] = i
628	  }
629	  let buckets : Array[Array[Int]] = [initial_bucket]
630	  let mut num_buckets = 1
631	  while num_buckets < num_colors {
632	    let mut best_bucket_idx = -1
633	    let mut best_range = 0
634	    for bi in 0..<buckets.length() {
635	      if buckets[bi].length() < 2 {
636	        continue	<-- UNCOVERED
637	      }
638	      let (range, _ch) = bucket_max_range_mc16(pixels, buckets[bi])
639	      if range > best_range {
640	        best_range = range	<-- UNCOVERED
641	        best_bucket_idx = bi
642	      }
643	    }
644	    if best_bucket_idx < 0 {
645	      break
646	    }
647	    let bucket = buckets[best_bucket_idx]
648	    let (_, split_ch) = bucket_max_range_mc16(pixels, bucket)	<-- UNCOVERED
649	    let sorted = bucket.copy()
650	    sorted.sort_by(fn(a, b) {
651	      let va = channel_value_mc16(pixels[a], split_ch)
652	      let vb = channel_value_mc16(pixels[b], split_ch)
653	      va.compare(vb)
654	    })
655	    let mid = sorted.length() / 2	<-- UNCOVERED
656	    let left : Array[Int] = Array::make(mid, 0)
657	    let right : Array[Int] = Array::make(sorted.length() - mid, 0)
658	    for i in 0..<mid {
659	      left[i] = sorted[i]	<-- UNCOVERED
660	    }
661	    for i in mid..<sorted.length() {	<-- UNCOVERED
662	      right[i - mid] = sorted[i]
663	    }
664	    buckets[best_bucket_idx] = left
665	    buckets.push(right)	<-- UNCOVERED
666	    num_buckets += 1
667	  }
668	  let palette : Array[(Int, Int, Int)] = Array::make(
669	    buckets.length(),
670	    (0, 0, 0),
671	  )
672	  for bi in 0..<buckets.length() {
673	    palette[bi] = bucket_average_mc16(pixels, buckets[bi])
674	  }
675	  let out : Array[Byte] = Array::make(n * 3 * 2, b'\x00')
676	  for i in 0..<n {
677	    let (r, g, b) = pixels[i]
678	    let mut best_idx = 0
679	    let mut best_dist = 999999999
680	    for pi in 0..<palette.length() {
681	      let (pr, pg, pb) = palette[pi]
682	      let dist = (r - pr) * (r - pr) + (g - pg) * (g - pg) + (b - pb) * (b - pb)
683	      if dist < best_dist {
684	        best_dist = dist
685	        best_idx = pi
686	      }
687	    }
688	    let (pr, pg, pb) = palette[best_idx]
689	    write_u16_q(out, i * 6, pr)
690	    write_u16_q(out, i * 6 + 2, pg)
691	    write_u16_q(out, i * 6 + 4, pb)
692	  }
693	  @types.Image16::{
694	    width: w,
695	    height: h,
696	    channels: 3,
697	    data: Bytes::from_array(out),
698	  }
699	}
   	…

704	fn channel_value_mcf(pixel : (Float, Float, Float), ch : Int) -> Float {
705	  match ch {	<-- UNCOVERED
706	    0 => pixel.0
707	    1 => pixel.1
708	    _ => pixel.2
709	  }
710	}
   	…

713	fn bucket_max_range_mcf(
714	  pixels : Array[(Float, Float, Float)],
715	  bucket : Array[Int],
716	) -> (Float, Int) {
717	  let mut min_r = 1.0F
718	  let mut max_r = 0.0F
719	  let mut min_g = 1.0F
720	  let mut max_g = 0.0F
721	  let mut min_b = 1.0F
722	  let mut max_b = 0.0F
723	  for i in 0..<bucket.length() {
724	    let (r, g, b) = pixels[bucket[i]]
725	    if r < min_r {
726	      min_r = r
727	    }
728	    if r > max_r {
729	      max_r = r
730	    }
731	    if g < min_g {
732	      min_g = g
733	    }
734	    if g > max_g {
735	      max_g = g
736	    }
737	    if b < min_b {
738	      min_b = b
739	    }
740	    if b > max_b {
741	      max_b = b
742	    }
743	  }
744	  let range_r = max_r - min_r
745	  let range_g = max_g - min_g
746	  let range_b = max_b - min_b
747	  if range_r >= range_g && range_r >= range_b {
748	    (range_r, 0)
749	  } else if range_g >= range_b {	<-- UNCOVERED
750	    (range_g, 1)
751	  } else {
752	    (range_b, 2)	<-- UNCOVERED
753	  }
754	}
   	…

757	fn bucket_average_mcf(
758	  pixels : Array[(Float, Float, Float)],
759	  bucket : Array[Int],
760	) -> (Float, Float, Float) {
761	  let mut sum_r = 0.0F
762	  let mut sum_g = 0.0F
763	  let mut sum_b = 0.0F
764	  for i in 0..<bucket.length() {
765	    let (r, g, b) = pixels[bucket[i]]
766	    sum_r += r
767	    sum_g += g
768	    sum_b += b
769	  }
770	  let len = Float::from_int(bucket.length())
771	  if len == 0.0F {
772	    (0.0F, 0.0F, 0.0F)	<-- UNCOVERED
773	  } else {
774	    (sum_r / len, sum_g / len, sum_b / len)
775	  }
776	}
   	…

782	pub fn median_cut_f(
783	  img : @types.ImageF,
784	  num_colors : Int,
785	) -> @types.ImageF raise @types.LoadError {
786	  // 输入验证
787	  if img.width <= 0 || img.height <= 0 {
788	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
789	      "median_cut_f: 图像尺寸必须大于0",
790	    )
791	  }
792	  if img.channels < 1 || img.channels > 4 {
793	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
794	      "median_cut_f: 通道数必须在1-4之间",
795	    )
796	  }
797	  let expected_size = img.width * img.height * img.channels * 4
798	  if img.data.length() != expected_size {
799	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
800	      "median_cut_f: 数据大小不匹配，期望 " +
801	      expected_size.to_string() +	<-- UNCOVERED
802	      "，实际 " +
803	      img.data.length().to_string(),	<-- UNCOVERED
804	    )
805	  }
806	  if num_colors < 2 {
807	    return img	<-- UNCOVERED
808	  }
809	  let w = img.width
810	  let h = img.height
811	  let n = w * h
812	  let ch = img.channels
813	  let pixels : Array[(Float, Float, Float)] = Array::make(n, (0.0F, 0.0F, 0.0F))
814	  for i in 0..<n {
815	    let r = read_f32_q(img.data, i * ch * 4)
816	    let g = if ch >= 2 { read_f32_q(img.data, (i * ch + 1) * 4) } else { r }
817	    let b = if ch >= 3 { read_f32_q(img.data, (i * ch + 2) * 4) } else { r }
818	    pixels[i] = (r, g, b)
819	  }
820	  let initial_bucket : Array[Int] = Array::make(n, 0)
821	  for i in 0..<n {
822	    initial_bucket[i] = i
823	  }
824	  let buckets : Array[Array[Int]] = [initial_bucket]
825	  let mut num_buckets = 1
826	  while num_buckets < num_colors {
827	    let mut best_bucket_idx = -1
828	    let mut best_range = 0.0F
829	    for bi in 0..<buckets.length() {
830	      if buckets[bi].length() < 2 {
831	        continue	<-- UNCOVERED
832	      }
833	      let (range, _ch) = bucket_max_range_mcf(pixels, buckets[bi])
834	      if range > best_range {
835	        best_range = range	<-- UNCOVERED
836	        best_bucket_idx = bi
837	      }
838	    }
839	    if best_bucket_idx < 0 {
840	      break
841	    }
842	    let bucket = buckets[best_bucket_idx]
843	    let (_, split_ch) = bucket_max_range_mcf(pixels, bucket)	<-- UNCOVERED
844	    let sorted = bucket.copy()
845	    sorted.sort_by(fn(a, b) {
846	      let va = channel_value_mcf(pixels[a], split_ch)
847	      let vb = channel_value_mcf(pixels[b], split_ch)
848	      va.compare(vb)
849	    })
850	    let mid = sorted.length() / 2	<-- UNCOVERED
851	    let left : Array[Int] = Array::make(mid, 0)
852	    let right : Array[Int] = Array::make(sorted.length() - mid, 0)
853	    for i in 0..<mid {
854	      left[i] = sorted[i]	<-- UNCOVERED
855	    }
856	    for i in mid..<sorted.length() {	<-- UNCOVERED
857	      right[i - mid] = sorted[i]
858	    }
859	    buckets[best_bucket_idx] = left
860	    buckets.push(right)	<-- UNCOVERED
861	    num_buckets += 1
862	  }
863	  let palette : Array[(Float, Float, Float)] = Array::make(
864	    buckets.length(),
865	    (0.0F, 0.0F, 0.0F),
866	  )
867	  for bi in 0..<buckets.length() {
868	    palette[bi] = bucket_average_mcf(pixels, buckets[bi])
869	  }
870	  let out : Array[Byte] = Array::make(n * 3 * 4, b'\x00')
871	  for i in 0..<n {
872	    let (r, g, b) = pixels[i]
873	    let mut best_idx = 0
874	    let mut best_dist = 999.0F
875	    for pi in 0..<palette.length() {
876	      let (pr, pg, pb) = palette[pi]
877	      let dist = (r - pr) * (r - pr) + (g - pg) * (g - pg) + (b - pb) * (b - pb)
878	      if dist < best_dist {
879	        best_dist = dist
880	        best_idx = pi
881	      }
882	    }
883	    let (pr, pg, pb) = palette[best_idx]
884	    write_f32_q(out, i * 12, pr)
885	    write_f32_q(out, i * 12 + 4, pg)
886	    write_f32_q(out, i * 12 + 8, pb)
887	  }
888	  @types.ImageF::{
889	    width: w,
890	    height: h,
891	    channels: 3,
892	    data: Bytes::from_array(out),
893	  }
894	}
   	…

21 uncovered line(s) in src\process\color\retinex.mbt:

15	pub fn ssr(
16	  img : @types.Image,
17	  sigma? : Float = 80.0F,
18	  gain? : Float = 1.0F,
19	  offset? : Float = 0.0F,
20	) -> @types.Image raise @types.LoadError {
21	  // 输入验证
22	  if img.width == 0 || img.height == 0 {
23	    return img
24	  }
25	  if img.width < 0 || img.height < 0 {
26	    raise @types.LoadError::DecodeFailed("ssr: 图像尺寸不能为负")	<-- UNCOVERED
27	  }
28	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
29	    raise @types.LoadError::DecodeFailed("ssr 仅支持 channels=1/3/4")
30	  }
31	  let expected_size = img.width * img.height * img.channels
32	  if img.data.length() != expected_size {
33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
34	      "ssr: 数据大小不匹配，期望 " +
35	      expected_size.to_string() +	<-- UNCOVERED
36	      "，实际 " +
37	      img.data.length().to_string(),	<-- UNCOVERED
38	    )
39	  }
40	  if sigma <= 0.0F {
41	    raise @types.LoadError::DecodeFailed("sigma 必须 > 0")
42	  }
43	  let ch = img.channels
44	  let w = img.width
45	  let h = img.height
46	  let n = w * h
47	  if n == 0 {
48	    return img	<-- UNCOVERED
49	  }
50	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
51	  for c in 0..<ch {
52	    if ch == 4 && c == 3 {
53	      for i in 0..<n {
54	        out[i * ch + 3] = img.data[i * ch + 3]
55	      }
56	      continue
57	    }
58	    let channel : Array[Float] = Array::make(n, 0.0F)
59	    for i in 0..<n {
60	      let v = img.data[i * ch + c].to_int()
61	      channel[i] = Float::from_int(if v == 0 { 1 } else { v })
62	    }
63	    let blurred = gaussian_blur_channel(channel, w, h, sigma)
64	    for i in 0..<n {
65	      let log_i = logf(channel[i])
66	      let log_b = logf(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
67	      let retinex = (log_i - log_b) * gain + offset
68	      let val = (retinex * 30.0F + 128.0F).to_int()
69	      out[i * ch + c] = val.clamp(min=0, max=255).to_byte()
70	    }
71	  }
72	  @types.Image::{
73	    width: w,
74	    height: h,
75	    channels: ch,
76	    data: Bytes::from_array(out),
77	  }
78	}
  	…

 86	pub fn msr(
 87	  img : @types.Image,
 88	  sigmas? : Array[Float] = [15.0F, 80.0F, 250.0F],
 89	  gain? : Float = 1.0F,
 90	  offset? : Float = 0.0F,
 91	) -> @types.Image raise @types.LoadError {
 92	  // 输入验证
 93	  if img.width == 0 || img.height == 0 {
 94	    return img
 95	  }
 96	  if img.width < 0 || img.height < 0 {
 97	    raise @types.LoadError::DecodeFailed("msr: 图像尺寸不能为负")	<-- UNCOVERED
 98	  }
 99	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
100	    raise @types.LoadError::DecodeFailed("msr 仅支持 channels=1/3/4")
101	  }
102	  let expected_size = img.width * img.height * img.channels
103	  if img.data.length() != expected_size {
104	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
105	      "msr: 数据大小不匹配，期望 " +
106	      expected_size.to_string() +	<-- UNCOVERED
107	      "，实际 " +
108	      img.data.length().to_string(),	<-- UNCOVERED
109	    )
110	  }
111	  if sigmas.length() == 0 {
112	    raise @types.LoadError::DecodeFailed("sigmas 不能为空")
113	  }
114	  for s in sigmas {
115	    if s <= 0.0F {
116	      raise @types.LoadError::DecodeFailed("sigma 必须 > 0")
117	    }
118	  }
119	  let ch = img.channels
120	  let w = img.width
121	  let h = img.height
122	  let n = w * h
123	  if n == 0 {
124	    return img	<-- UNCOVERED
125	  }
126	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
127	  let num_scales = sigmas.length()
128	  for c in 0..<ch {
129	    if ch == 4 && c == 3 {
130	      for i in 0..<n {
131	        out[i * ch + 3] = img.data[i * ch + 3]
132	      }
133	      continue
134	    }
135	    let channel : Array[Float] = Array::make(n, 0.0F)
136	    for i in 0..<n {
137	      let v = img.data[i * ch + c].to_int()
138	      channel[i] = Float::from_int(if v == 0 { 1 } else { v })
139	    }
140	    // 预计算 log(channel[i])，避免在每个 sigma 循环中重复计算
141	    let log_channel : Array[Float] = Array::make(n, 0.0F)
142	    for i in 0..<n {
143	      log_channel[i] = logf(channel[i])
144	    }
145	    let retinex_sum : Array[Float] = Array::make(n, 0.0F)
146	    for sigma in sigmas {
147	      let blurred = gaussian_blur_channel(channel, w, h, sigma)
148	      for i in 0..<n {
149	        let log_b = logf(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
150	        retinex_sum[i] += log_channel[i] - log_b
151	      }
152	    }
153	    let inv_scales = gain / Float::from_int(num_scales)
154	    for i in 0..<n {
155	      let retinex = retinex_sum[i] * inv_scales + offset
156	      let val = (retinex * 30.0F + 128.0F).to_int()
157	      out[i * ch + c] = val.clamp(min=0, max=255).to_byte()
158	    }
159	  }
160	  @types.Image::{
161	    width: w,
162	    height: h,
163	    channels: ch,
164	    data: Bytes::from_array(out),
165	  }
166	}
   	…

255	fn gaussian_blur_channel(
256	  channel : Array[Float],
257	  w : Int,
258	  h : Int,
259	  sigma : Float,
260	) -> Array[Float] {
261	  let n = w * h
262	  let out : Array[Float] = Array::make(n, 0.0F)
263	  let radius = (Float::from_int(3) * sigma).to_int()
264	  let radius = if radius < 1 { 1 } else { radius }
265	  let kernel_size = 2 * radius + 1
266	  let kernel : Array[Float] = Array::make(kernel_size, 0.0F)
267	  let two_sigma_sq = 2.0F * sigma * sigma
268	  let mut kernel_sum = 0.0F
269	  for i in 0..<kernel_size {
270	    let x = Float::from_int(i - radius)
271	    kernel[i] = @math.expf(-(x * x) / two_sigma_sq)
272	    kernel_sum += kernel[i]
273	  }
274	  for i in 0..<kernel_size {
275	    kernel[i] = kernel[i] / kernel_sum
276	  }
277	  // kernel prefix sum，用于大 radius 时 O(w) 替代 O(kernel_size)
278	  let k_prefix : Array[Float] = Array::make(kernel_size + 1, 0.0F)
279	  for i in 0..<kernel_size {
280	    k_prefix[i + 1] = k_prefix[i] + kernel[i]
281	  }
282	  let total_k = k_prefix[kernel_size]
283	  // 水平模糊
284	  let temp : Array[Float] = Array::make(n, 0.0F)
285	  if radius >= w {
286	    // 大 radius：所有像素都是边界，用 prefix sum 将 O(kernel_size) 降为 O(w)
287	    for y in 0..<h {
288	      let row_base = y * w
289	      let c0 = channel[row_base]
290	      let cw1 = channel[row_base + w - 1]
291	      for x in 0..<w {
292	        let left_end = radius - x
293	        let right_start = left_end + w
294	        let mid_start = if left_end > 0 { left_end } else { 0 }
295	        let mid_end = if right_start < kernel_size {
296	          right_start
297	        } else {
298	          kernel_size	<-- UNCOVERED
299	        }
300	        let mut sum = c0 * k_prefix[mid_start] +
301	          cw1 * (total_k - k_prefix[mid_end])
302	        for k in mid_start..<mid_end {
303	          sum += channel[row_base + k - left_end] * kernel[k]
304	        }
305	        temp[row_base + x] = sum
306	      }
307	    }
308	  } else {
309	    // 正常情况：分离内部/边界区域
310	    for y in 0..<h {	<-- UNCOVERED
311	      let row_base = y * w
312	      for x in radius..<(w - radius) {
313	        let mut sum = 0.0F	<-- UNCOVERED
314	        let base = row_base + x - radius
315	        for k in 0..<kernel_size {
316	          sum += channel[base + k] * kernel[k]	<-- UNCOVERED
317	        }
318	        temp[row_base + x] = sum
319	      }
320	      for x in 0..<w {
321	        if x >= radius && x < w - radius {	<-- UNCOVERED
322	          continue
323	        }
324	        let mut sum = 0.0F
325	        for k in -radius..<=radius {
326	          let px = (x + k).clamp(min=0, max=w - 1)	<-- UNCOVERED
327	          sum += channel[row_base + px] * kernel[k + radius]
328	        }
329	        temp[row_base + x] = sum
330	      }
331	    }
332	  }
333	  // 垂直模糊
334	  if radius >= h {
335	    // 大 radius：用 prefix sum 优化
336	    for x in 0..<w {
337	      let c0 = temp[x]
338	      let ch1 = temp[(h - 1) * w + x]
339	      for y in 0..<h {
340	        let left_end = radius - y
341	        let right_start = left_end + h
342	        let mid_start = if left_end > 0 { left_end } else { 0 }
343	        let mid_end = if right_start < kernel_size {
344	          right_start
345	        } else {
346	          kernel_size	<-- UNCOVERED
347	        }
348	        let mut sum = c0 * k_prefix[mid_start] +
349	          ch1 * (total_k - k_prefix[mid_end])
350	        for k in mid_start..<mid_end {
351	          sum += temp[(k - left_end) * w + x] * kernel[k]
352	        }
353	        out[y * w + x] = sum
354	      }
355	    }
356	  } else {
357	    for x in 0..<w {	<-- UNCOVERED
358	      for y in radius..<(h - radius) {
359	        let mut sum = 0.0F
360	        let base = (y - radius) * w + x
361	        for k in 0..<kernel_size {
362	          sum += temp[base + k * w] * kernel[k]	<-- UNCOVERED
363	        }
364	        out[y * w + x] = sum
365	      }
366	      for y in 0..<h {
367	        if y >= radius && y < h - radius {	<-- UNCOVERED
368	          continue
369	        }
370	        let mut sum = 0.0F
371	        for k in -radius..<=radius {
372	          let py = (y + k).clamp(min=0, max=h - 1)	<-- UNCOVERED
373	          sum += temp[py * w + x] * kernel[k + radius]
374	        }
375	        out[y * w + x] = sum
376	      }
377	    }
378	  }
379	  out
380	}
   	…

47 uncovered line(s) in src\process\color\retinex_16f.mbt:

 43	pub fn ssr_16(
 44	  img : @types.Image16,
 45	  sigma? : Float = 80.0F,
 46	  gain? : Float = 1.0F,
 47	  offset? : Float = 0.0F,
 48	) -> @types.Image16 raise @types.LoadError {
 49	  // 输入验证
 50	  if img.width == 0 || img.height == 0 {
 51	    return img	<-- UNCOVERED
 52	  }
 53	  if img.width < 0 || img.height < 0 {
 54	    raise @types.LoadError::DecodeFailed("ssr_16: 图像尺寸不能为负")	<-- UNCOVERED
 55	  }
 56	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 57	    raise @types.LoadError::DecodeFailed("ssr_16 仅支持 channels=1/3/4")	<-- UNCOVERED
 58	  }
 59	  let expected_size = img.width * img.height * img.channels * 2
 60	  if img.data.length() != expected_size {
 61	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 62	      "ssr_16: 数据大小不匹配，期望 " +
 63	      expected_size.to_string() +	<-- UNCOVERED
 64	      "，实际 " +
 65	      img.data.length().to_string(),	<-- UNCOVERED
 66	    )
 67	  }
 68	  if sigma <= 0.0F {
 69	    raise @types.LoadError::DecodeFailed("sigma 必须 > 0")	<-- UNCOVERED
 70	  }
 71	  let ch = img.channels
 72	  let w = img.width
 73	  let h = img.height
 74	  let n = w * h
 75	  if n == 0 {
 76	    return img	<-- UNCOVERED
 77	  }
 78	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
 79	  for c in 0..<ch {
 80	    if ch == 4 && c == 3 {
 81	      for i in 0..<n {	<-- UNCOVERED
 82	        let v = read_u16_r(img.data, (i * ch + 3) * 2)
 83	        write_u16_r(out, (i * ch + 3) * 2, v)
 84	      }
 85	      continue
 86	    }
 87	    let channel : Array[Float] = Array::make(n, 0.0F)
 88	    for i in 0..<n {
 89	      let v = read_u16_r(img.data, (i * ch + c) * 2)
 90	      channel[i] = Float::from_int(if v == 0 { 1 } else { v })
 91	    }
 92	    let blurred = gaussian_blur_channel(channel, w, h, sigma)
 93	    for i in 0..<n {
 94	      let log_i = logf_r(channel[i])
 95	      let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
 96	      let retinex = (log_i - log_b) * gain + offset
 97	      let val = (retinex * 30.0F + 32768.0F).to_int()
 98	      let clamped = if val < 0 { 0 } else if val > 65535 { 65535 } else { val }	<-- UNCOVERED
 99	      write_u16_r(out, (i * ch + c) * 2, clamped)
100	    }
101	  }
102	  @types.Image16::{
103	    width: w,
104	    height: h,
105	    channels: ch,
106	    data: Bytes::from_array(out),
107	  }
108	}
   	…

114	pub fn ssr_f(
115	  img : @types.ImageF,
116	  sigma? : Float = 80.0F,
117	  gain? : Float = 1.0F,
118	  offset? : Float = 0.0F,
119	) -> @types.ImageF raise @types.LoadError {
120	  // 输入验证
121	  if img.width == 0 || img.height == 0 {
122	    return img	<-- UNCOVERED
123	  }
124	  if img.width < 0 || img.height < 0 {
125	    raise @types.LoadError::DecodeFailed("ssr_f: 图像尺寸不能为负")	<-- UNCOVERED
126	  }
127	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
128	    raise @types.LoadError::DecodeFailed("ssr_f 仅支持 channels=1/3/4")	<-- UNCOVERED
129	  }
130	  let expected_size = img.width * img.height * img.channels * 4
131	  if img.data.length() != expected_size {
132	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
133	      "ssr_f: 数据大小不匹配，期望 " +
134	      expected_size.to_string() +	<-- UNCOVERED
135	      "，实际 " +
136	      img.data.length().to_string(),	<-- UNCOVERED
137	    )
138	  }
139	  if sigma <= 0.0F {
140	    raise @types.LoadError::DecodeFailed("sigma 必须 > 0")	<-- UNCOVERED
141	  }
142	  let ch = img.channels
143	  let w = img.width
144	  let h = img.height
145	  let n = w * h
146	  if n == 0 {
147	    return img	<-- UNCOVERED
148	  }
149	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
150	  for c in 0..<ch {
151	    if ch == 4 && c == 3 {
152	      for i in 0..<n {
153	        let v = read_f32_r(img.data, (i * ch + 3) * 4)
154	        write_f32_r(out, (i * ch + 3) * 4, v)
155	      }
156	      continue
157	    }
158	    let channel : Array[Float] = Array::make(n, 0.0F)
159	    for i in 0..<n {
160	      let v = read_f32_r(img.data, (i * ch + c) * 4)
161	      channel[i] = if v <= 0.0F { 0.0001F } else { v }
162	    }
163	    let blurred = gaussian_blur_channel(channel, w, h, sigma)
164	    for i in 0..<n {
165	      let log_i = logf_r(channel[i])
166	      let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
167	      let retinex = (log_i - log_b) * gain + offset
168	      write_f32_r(out, (i * ch + c) * 4, retinex)
169	    }
170	  }
171	  @types.ImageF::{
172	    width: w,
173	    height: h,
174	    channels: ch,
175	    data: Bytes::from_array(out),
176	  }
177	}
   	…

183	pub fn msr_16(
184	  img : @types.Image16,
185	  sigmas? : Array[Float] = [15.0F, 80.0F, 250.0F],
186	  gain? : Float = 1.0F,
187	  offset? : Float = 0.0F,
188	) -> @types.Image16 raise @types.LoadError {
189	  // 输入验证
190	  if img.width <= 0 || img.height <= 0 {
191	    raise @types.LoadError::DecodeFailed("msr_16: 图像尺寸必须大于0")	<-- UNCOVERED
192	  }
193	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
194	    raise @types.LoadError::DecodeFailed("msr_16 仅支持 channels=1/3/4")	<-- UNCOVERED
195	  }
196	  let expected_size = img.width * img.height * img.channels * 2
197	  if img.data.length() != expected_size {
198	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
199	      "msr_16: 数据大小不匹配，期望 " +
200	      expected_size.to_string() +	<-- UNCOVERED
201	      "，实际 " +
202	      img.data.length().to_string(),	<-- UNCOVERED
203	    )
204	  }
205	  if sigmas.length() == 0 {
206	    raise @types.LoadError::DecodeFailed("sigmas 不能为空")	<-- UNCOVERED
207	  }
208	  for s in sigmas {
209	    if s <= 0.0F {
210	      raise @types.LoadError::DecodeFailed("sigma 必须 > 0")	<-- UNCOVERED
211	    }
212	  }
213	  let ch = img.channels
214	  let w = img.width
215	  let h = img.height
216	  let n = w * h
217	  if n == 0 {
218	    return img	<-- UNCOVERED
219	  }
220	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
221	  let num_scales = sigmas.length()
222	  for c in 0..<ch {
223	    if ch == 4 && c == 3 {
224	      for i in 0..<n {	<-- UNCOVERED
225	        let v = read_u16_r(img.data, (i * ch + 3) * 2)
226	        write_u16_r(out, (i * ch + 3) * 2, v)
227	      }
228	      continue
229	    }
230	    let channel : Array[Float] = Array::make(n, 0.0F)
231	    for i in 0..<n {
232	      let v = read_u16_r(img.data, (i * ch + c) * 2)
233	      channel[i] = Float::from_int(if v == 0 { 1 } else { v })
234	    }
235	    let log_channel : Array[Float] = Array::make(n, 0.0F)
236	    for i in 0..<n {
237	      log_channel[i] = logf_r(channel[i])
238	    }
239	    let retinex_sum : Array[Float] = Array::make(n, 0.0F)
240	    for sigma in sigmas {
241	      let blurred = gaussian_blur_channel(channel, w, h, sigma)
242	      for i in 0..<n {
243	        let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
244	        retinex_sum[i] += log_channel[i] - log_b
245	      }
246	    }
247	    let inv_scales = gain / Float::from_int(num_scales)
248	    for i in 0..<n {
249	      let retinex = retinex_sum[i] * inv_scales + offset
250	      let val = (retinex * 30.0F + 32768.0F).to_int()
251	      let clamped = if val < 0 { 0 } else if val > 65535 { 65535 } else { val }	<-- UNCOVERED
252	      write_u16_r(out, (i * ch + c) * 2, clamped)
253	    }
254	  }
255	  @types.Image16::{
256	    width: w,
257	    height: h,
258	    channels: ch,
259	    data: Bytes::from_array(out),
260	  }
261	}
   	…

267	pub fn msr_f(
268	  img : @types.ImageF,
269	  sigmas? : Array[Float] = [15.0F, 80.0F, 250.0F],
270	  gain? : Float = 1.0F,
271	  offset? : Float = 0.0F,
272	) -> @types.ImageF raise @types.LoadError {
273	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
274	    raise @types.LoadError::DecodeFailed("msr_f 仅支持 channels=1/3/4")	<-- UNCOVERED
275	  }
276	  if sigmas.length() == 0 {
277	    raise @types.LoadError::DecodeFailed("sigmas 不能为空")	<-- UNCOVERED
278	  }
279	  for s in sigmas {
280	    if s <= 0.0F {
281	      raise @types.LoadError::DecodeFailed("sigma 必须 > 0")	<-- UNCOVERED
282	    }
283	  }
284	  let ch = img.channels
285	  let w = img.width
286	  let h = img.height
287	  let n = w * h
288	  if n == 0 {
289	    return img	<-- UNCOVERED
290	  }
291	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
292	  let num_scales = sigmas.length()
293	  for c in 0..<ch {
294	    if ch == 4 && c == 3 {
295	      for i in 0..<n {	<-- UNCOVERED
296	        let v = read_f32_r(img.data, (i * ch + 3) * 4)
297	        write_f32_r(out, (i * ch + 3) * 4, v)
298	      }
299	      continue
300	    }
301	    let channel : Array[Float] = Array::make(n, 0.0F)
302	    for i in 0..<n {
303	      let v = read_f32_r(img.data, (i * ch + c) * 4)
304	      channel[i] = if v <= 0.0F { 0.0001F } else { v }
305	    }
306	    let log_channel : Array[Float] = Array::make(n, 0.0F)
307	    for i in 0..<n {
308	      log_channel[i] = logf_r(channel[i])
309	    }
310	    let retinex_sum : Array[Float] = Array::make(n, 0.0F)
311	    for sigma in sigmas {
312	      let blurred = gaussian_blur_channel(channel, w, h, sigma)
313	      for i in 0..<n {
314	        let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
315	        retinex_sum[i] += log_channel[i] - log_b
316	      }
317	    }
318	    let inv_scales = gain / Float::from_int(num_scales)
319	    for i in 0..<n {
320	      let retinex = retinex_sum[i] * inv_scales + offset
321	      write_f32_r(out, (i * ch + c) * 4, retinex)
322	    }
323	  }
324	  @types.ImageF::{
325	    width: w,
326	    height: h,
327	    channels: ch,
328	    data: Bytes::from_array(out),
329	  }
330	}
   	…

336	pub fn msrcr_16(
337	  img : @types.Image16,
338	  sigmas? : Array[Float] = [15.0F, 80.0F, 250.0F],
339	  gain? : Float = 1.0F,
340	  offset? : Float = 0.0F,
341	  alpha? : Float = 125.0F,
342	  beta? : Float = 46.0F,
343	) -> @types.Image16 raise @types.LoadError {
344	  // 输入验证
345	  if img.width <= 0 || img.height <= 0 {
346	    raise @types.LoadError::DecodeFailed("msrcr_16: 图像尺寸必须大于0")	<-- UNCOVERED
347	  }
348	  if img.channels != 3 && img.channels != 4 {
349	    raise @types.LoadError::DecodeFailed("msrcr_16 需要 RGB/RGBA 图像")	<-- UNCOVERED
350	  }
351	  let expected_size = img.width * img.height * img.channels * 2
352	  if img.data.length() != expected_size {
353	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
354	      "msrcr_16: 数据大小不匹配，期望 " +
355	      expected_size.to_string() +	<-- UNCOVERED
356	      "，实际 " +
357	      img.data.length().to_string(),	<-- UNCOVERED
358	    )
359	  }
360	  if sigmas.length() == 0 {
361	    raise @types.LoadError::DecodeFailed("sigmas 不能为空")	<-- UNCOVERED
362	  }
363	  let ch = img.channels
364	  let w = img.width
365	  let h = img.height
366	  let n = w * h
367	  if n == 0 {
368	    return img	<-- UNCOVERED
369	  }
370	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
371	  let num_scales = sigmas.length()
372	  let channels : Array[Array[Float]] = [
373	    Array::make(n, 0.0F),
374	    Array::make(n, 0.0F),
375	    Array::make(n, 0.0F),
376	  ]
377	  for c in 0..<3 {
378	    for i in 0..<n {
379	      let v = read_u16_r(img.data, (i * ch + c) * 2)
380	      channels[c][i] = Float::from_int(if v == 0 { 1 } else { v })
381	    }
382	  }
383	  let msr_results : Array[Array[Float]] = [
384	    Array::make(n, 0.0F),
385	    Array::make(n, 0.0F),
386	    Array::make(n, 0.0F),
387	  ]
388	  for c in 0..<3 {
389	    let log_channel : Array[Float] = Array::make(n, 0.0F)
390	    for i in 0..<n {
391	      log_channel[i] = logf_r(channels[c][i])
392	    }
393	    let retinex_sum : Array[Float] = Array::make(n, 0.0F)
394	    for sigma in sigmas {
395	      let blurred = gaussian_blur_channel(channels[c], w, h, sigma)
396	      for i in 0..<n {
397	        let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
398	        retinex_sum[i] += log_channel[i] - log_b
399	      }
400	    }
401	    let inv_scales = 1.0F / Float::from_int(num_scales)
402	    for i in 0..<n {
403	      msr_results[c][i] = retinex_sum[i] * inv_scales
404	    }
405	  }
406	  for i in 0..<n {
407	    let sum = channels[0][i] + channels[1][i] + channels[2][i]
408	    for c in 0..<3 {
409	      let crf = logf_r(alpha * channels[c][i] / (sum + 1.0F)) * beta
410	      let val = msr_results[c][i] * gain + offset + crf
411	      let mapped = (val * 30.0F + 32768.0F).to_int()
412	      let clamped = if mapped < 0 {
413	        0	<-- UNCOVERED
414	      } else if mapped > 65535 {
415	        65535	<-- UNCOVERED
416	      } else {
417	        mapped
418	      }
419	      write_u16_r(out, (i * ch + c) * 2, clamped)
420	    }
421	    if ch == 4 {
422	      let v = read_u16_r(img.data, (i * ch + 3) * 2)	<-- UNCOVERED
423	      write_u16_r(out, (i * ch + 3) * 2, v)
424	    }
425	  }
426	  @types.Image16::{
427	    width: w,
428	    height: h,
429	    channels: ch,
430	    data: Bytes::from_array(out),
431	  }
432	}
   	…

438	pub fn msrcr_f(
439	  img : @types.ImageF,
440	  sigmas? : Array[Float] = [15.0F, 80.0F, 250.0F],
441	  gain? : Float = 1.0F,
442	  offset? : Float = 0.0F,
443	  alpha? : Float = 125.0F,
444	  beta? : Float = 46.0F,
445	) -> @types.ImageF raise @types.LoadError {
446	  if img.channels != 3 && img.channels != 4 {
447	    raise @types.LoadError::DecodeFailed("msrcr_f 需要 RGB/RGBA 图像")	<-- UNCOVERED
448	  }
449	  if sigmas.length() == 0 {
450	    raise @types.LoadError::DecodeFailed("sigmas 不能为空")	<-- UNCOVERED
451	  }
452	  let ch = img.channels
453	  let w = img.width
454	  let h = img.height
455	  let n = w * h
456	  if n == 0 {
457	    return img	<-- UNCOVERED
458	  }
459	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
460	  let num_scales = sigmas.length()
461	  let channels : Array[Array[Float]] = [
462	    Array::make(n, 0.0F),
463	    Array::make(n, 0.0F),
464	    Array::make(n, 0.0F),
465	  ]
466	  for c in 0..<3 {
467	    for i in 0..<n {
468	      let v = read_f32_r(img.data, (i * ch + c) * 4)
469	      channels[c][i] = if v <= 0.0F { 0.0001F } else { v }
470	    }
471	  }
472	  let msr_results : Array[Array[Float]] = [
473	    Array::make(n, 0.0F),
474	    Array::make(n, 0.0F),
475	    Array::make(n, 0.0F),
476	  ]
477	  for c in 0..<3 {
478	    let log_channel : Array[Float] = Array::make(n, 0.0F)
479	    for i in 0..<n {
480	      log_channel[i] = logf_r(channels[c][i])
481	    }
482	    let retinex_sum : Array[Float] = Array::make(n, 0.0F)
483	    for sigma in sigmas {
484	      let blurred = gaussian_blur_channel(channels[c], w, h, sigma)
485	      for i in 0..<n {
486	        let log_b = logf_r(if blurred[i] > 0.0F { blurred[i] } else { 1.0F })
487	        retinex_sum[i] += log_channel[i] - log_b
488	      }
489	    }
490	    let inv_scales = 1.0F / Float::from_int(num_scales)
491	    for i in 0..<n {
492	      msr_results[c][i] = retinex_sum[i] * inv_scales
493	    }
494	  }
495	  for i in 0..<n {
496	    let sum = channels[0][i] + channels[1][i] + channels[2][i]
497	    for c in 0..<3 {
498	      let crf = logf_r(alpha * channels[c][i] / (sum + 1.0F)) * beta
499	      let val = msr_results[c][i] * gain + offset + crf
500	      write_f32_r(out, (i * ch + c) * 4, val)
501	    }
502	    if ch == 4 {
503	      let v = read_f32_r(img.data, (i * ch + 3) * 4)	<-- UNCOVERED
504	      write_f32_r(out, (i * ch + 3) * 4, v)
505	    }
506	  }
507	  @types.ImageF::{
508	    width: w,
509	    height: h,
510	    channels: ch,
511	    data: Bytes::from_array(out),
512	  }
513	}
   	…

12 uncovered line(s) in src\process\color\tonemap.mbt:

23	pub fn reinhard_tonemap(
24	  imgf : @types.ImageF,
25	  key? : Float = 0.18F,
26	) -> @types.Image raise @types.LoadError {
27	  // 输入验证
28	  if imgf.width <= 0 || imgf.height <= 0 {
29	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
30	      "reinhard_tonemap: 图像尺寸必须大于0",
31	    )
32	  }
33	  if imgf.channels < 1 || imgf.channels > 4 {
34	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
35	      "reinhard_tonemap: 通道数必须在1-4之间",
36	    )
37	  }
38	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
39	  if imgf.data.length() != expected_size {
40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
41	      "reinhard_tonemap: 数据大小不匹配，期望 " +
42	      expected_size.to_string() +	<-- UNCOVERED
43	      "，实际 " +
44	      imgf.data.length().to_string(),	<-- UNCOVERED
45	    )
46	  }
47	  if key <= 0.0F {
48	    raise @types.LoadError::DecodeFailed("reinhard_tonemap: key必须大于0")	<-- UNCOVERED
49	  }
50	  let w = imgf.width
51	  let h = imgf.height
52	  let ch = imgf.channels
53	  let n = w * h
54	  let color_ch = if ch >= 3 { 3 } else { ch }
55	  // 计算对数平均亮度
56	  let mut log_sum = 0.0F
57	  for i in 0..<n {
58	    let r = read_float_le(imgf.data, i * ch * 4)
59	    let g = read_float_le(imgf.data, i * ch * 4 + 4)
60	    let b = read_float_le(imgf.data, i * ch * 4 + 8)
61	    let lum = 0.2126F * r + 0.7152F * g + 0.0722F * b
62	    let lum_safe = if lum > 0.0F { lum } else { 0.0001F }
63	    log_sum += log_f(lum_safe)
64	  }
65	  let log_avg = @math.expf(log_sum / Float::from_int(n))
66	  let scale = key / log_avg
67	  // 应用 Reinhard 映射
68	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
69	  for i in 0..<n {
70	    for c in 0..<color_ch {
71	      let v = read_float_le(imgf.data, (i * ch + c) * 4)
72	      let scaled = v * scale
73	      let mapped = scaled / (1.0F + scaled)
74	      let val = (mapped * 255.0F + 0.5F).to_int().clamp(min=0, max=255)
75	      out[i * ch + c] = val.to_byte()
76	    }
77	    if ch == 4 {
78	      out[i * ch + 3] = b'\xFF'
79	    }
80	  }
81	  @types.Image::{
82	    width: w,
83	    height: h,
84	    channels: ch,
85	    data: Bytes::from_array(out),
86	  }
87	}
  	…

 93	pub fn gamma_tonemap(
 94	  imgf : @types.ImageF,
 95	  gamma : Float,
 96	) -> @types.Image raise @types.LoadError {
 97	  // 输入验证
 98	  if imgf.width <= 0 || imgf.height <= 0 {
 99	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
100	      "gamma_tonemap: 图像尺寸必须大于0",
101	    )
102	  }
103	  if imgf.channels < 1 || imgf.channels > 4 {
104	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
105	      "gamma_tonemap: 通道数必须在1-4之间",
106	    )
107	  }
108	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
109	  if imgf.data.length() != expected_size {
110	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
111	      "gamma_tonemap: 数据大小不匹配，期望 " +
112	      expected_size.to_string() +	<-- UNCOVERED
113	      "，实际 " +
114	      imgf.data.length().to_string(),	<-- UNCOVERED
115	    )
116	  }
117	  if gamma <= 0.0F {
118	    raise @types.LoadError::DecodeFailed("gamma_tonemap: gamma必须大于0")	<-- UNCOVERED
119	  }
120	  let w = imgf.width
121	  let h = imgf.height
122	  let ch = imgf.channels
123	  let n = w * h
124	  let color_ch = if ch >= 3 { 3 } else { ch }
125	  // 找最大值用于归一化
126	  let mut max_val = 0.0F
127	  for i in 0..<n {
128	    for c in 0..<color_ch {
129	      let v = read_float_le(imgf.data, (i * ch + c) * 4)
130	      if v > max_val {
131	        max_val = v
132	      }
133	    }
134	  }
135	  if max_val <= 0.0F {
136	    return @types.Image::{
137	      width: w,
138	      height: h,
139	      channels: ch,
140	      data: Bytes::from_array(Array::make(n * ch, b'\x00')),
141	    }
142	  }
143	  let inv_gamma = 1.0F / gamma
144	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
145	  for i in 0..<n {
146	    for c in 0..<color_ch {
147	      let v = read_float_le(imgf.data, (i * ch + c) * 4)
148	      let normalized = v / max_val
149	      let mapped = if normalized > 0.0F {
150	        @math.powf(normalized, inv_gamma)
151	      } else {
152	        0.0F
153	      }
154	      let val = (mapped * 255.0F + 0.5F).to_int().clamp(min=0, max=255)
155	      out[i * ch + c] = val.to_byte()
156	    }
157	    if ch == 4 {
158	      out[i * ch + 3] = b'\xFF'
159	    }
160	  }
161	  @types.Image::{
162	    width: w,
163	    height: h,
164	    channels: ch,
165	    data: Bytes::from_array(out),
166	  }
167	}
   	…

32 uncovered line(s) in src\process\color\tonemap_16f.mbt:

 40	pub fn gamma_tonemap_f(
 41	  imgf : @types.ImageF,
 42	  gamma : Float,
 43	) -> @types.ImageF raise @types.LoadError {
 44	  // 输入验证
 45	  if imgf.width <= 0 || imgf.height <= 0 {
 46	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 47	      "gamma_tonemap_f: 图像尺寸必须大于0",
 48	    )
 49	  }
 50	  if imgf.channels < 1 || imgf.channels > 4 {
 51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 52	      "gamma_tonemap_f: 通道数必须在1-4之间",
 53	    )
 54	  }
 55	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
 56	  if imgf.data.length() != expected_size {
 57	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 58	      "gamma_tonemap_f: 数据大小不匹配，期望 " +
 59	      expected_size.to_string() +	<-- UNCOVERED
 60	      "，实际 " +
 61	      imgf.data.length().to_string(),	<-- UNCOVERED
 62	    )
 63	  }
 64	  if gamma <= 0.0F {
 65	    raise @types.LoadError::DecodeFailed("gamma_tonemap_f: gamma必须大于0")	<-- UNCOVERED
 66	  }
 67	  let w = imgf.width
 68	  let h = imgf.height
 69	  let ch = imgf.channels
 70	  let n = w * h
 71	  let color_ch = if ch >= 3 { 3 } else { ch }
 72	  // 找最大值用于归一化
 73	  let mut max_val = 0.0F
 74	  for i in 0..<n {
 75	    for c in 0..<color_ch {
 76	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
 77	      if v > max_val {
 78	        max_val = v
 79	      }
 80	    }
 81	  }
 82	  if max_val <= 0.0F {
 83	    return @types.ImageF::{
 84	      width: w,
 85	      height: h,
 86	      channels: ch,
 87	      data: Bytes::from_array(Array::make(n * ch * 4, b'\x00')),
 88	    }
 89	  }
 90	  let inv_gamma = 1.0F / gamma
 91	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
 92	  for i in 0..<n {
 93	    for c in 0..<color_ch {
 94	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
 95	      let normalized = v / max_val
 96	      let mapped = if normalized > 0.0F {
 97	        @math.powf(normalized, inv_gamma)
 98	      } else {
 99	        0.0F	<-- UNCOVERED
100	      }
101	      write_float_le_tm(out, (i * ch + c) * 4, mapped)
102	    }
103	    if ch == 4 {
104	      write_float_le_tm(out, (i * ch + 3) * 4, 1.0F)	<-- UNCOVERED
105	    }
106	  }
107	  @types.ImageF::{
108	    width: w,
109	    height: h,
110	    channels: ch,
111	    data: Bytes::from_array(out),
112	  }
113	}
   	…

121	pub fn reinhard_tonemap_f(
122	  imgf : @types.ImageF,
123	  key? : Float = 0.18F,
124	) -> @types.ImageF raise @types.LoadError {
125	  // 输入验证
126	  if imgf.width <= 0 || imgf.height <= 0 {
127	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
128	      "reinhard_tonemap_f: 图像尺寸必须大于0",
129	    )
130	  }
131	  if imgf.channels < 1 || imgf.channels > 4 {
132	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
133	      "reinhard_tonemap_f: 通道数必须在1-4之间",
134	    )
135	  }
136	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
137	  if imgf.data.length() != expected_size {
138	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
139	      "reinhard_tonemap_f: 数据大小不匹配，期望 " +
140	      expected_size.to_string() +	<-- UNCOVERED
141	      "，实际 " +
142	      imgf.data.length().to_string(),	<-- UNCOVERED
143	    )
144	  }
145	  if key <= 0.0F {
146	    raise @types.LoadError::DecodeFailed("reinhard_tonemap_f: key必须大于0")	<-- UNCOVERED
147	  }
148	  let w = imgf.width
149	  let h = imgf.height
150	  let ch = imgf.channels
151	  let n = w * h
152	  let color_ch = if ch >= 3 { 3 } else { ch }
153	  // 计算对数平均亮度
154	  let mut log_sum = 0.0F
155	  for i in 0..<n {
156	    let r = read_float_le_tm(imgf.data, i * ch * 4)
157	    let g = read_float_le_tm(imgf.data, i * ch * 4 + 4)
158	    let b = read_float_le_tm(imgf.data, i * ch * 4 + 8)
159	    let lum = 0.2126F * r + 0.7152F * g + 0.0722F * b
160	    let lum_safe = if lum > 0.0F { lum } else { 0.0001F }
161	    log_sum += log_f_tm(lum_safe)
162	  }
163	  let log_avg = @math.expf(log_sum / Float::from_int(n))
164	  let scale = key / log_avg
165	  // 应用 Reinhard 映射
166	  let out : Array[Byte] = Array::make(n * ch * 4, b'\x00')
167	  for i in 0..<n {
168	    for c in 0..<color_ch {
169	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
170	      let scaled = v * scale
171	      let mapped = scaled / (1.0F + scaled)
172	      write_float_le_tm(out, (i * ch + c) * 4, mapped)
173	    }
174	    if ch == 4 {
175	      write_float_le_tm(out, (i * ch + 3) * 4, 1.0F)	<-- UNCOVERED
176	    }
177	  }
178	  @types.ImageF::{
179	    width: w,
180	    height: h,
181	    channels: ch,
182	    data: Bytes::from_array(out),
183	  }
184	}
   	…

191	pub fn gamma_tonemap_16(
192	  imgf : @types.ImageF,
193	  gamma : Float,
194	) -> @types.Image16 raise @types.LoadError {
195	  // 输入验证
196	  if imgf.width <= 0 || imgf.height <= 0 {
197	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
198	      "gamma_tonemap_16: 图像尺寸必须大于0",
199	    )
200	  }
201	  if imgf.channels < 1 || imgf.channels > 4 {
202	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
203	      "gamma_tonemap_16: 通道数必须在1-4之间",
204	    )
205	  }
206	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
207	  if imgf.data.length() != expected_size {
208	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
209	      "gamma_tonemap_16: 数据大小不匹配，期望 " +
210	      expected_size.to_string() +	<-- UNCOVERED
211	      "，实际 " +
212	      imgf.data.length().to_string(),	<-- UNCOVERED
213	    )
214	  }
215	  if gamma <= 0.0F {
216	    raise @types.LoadError::DecodeFailed("gamma_tonemap_16: gamma必须大于0")	<-- UNCOVERED
217	  }
218	  let w = imgf.width
219	  let h = imgf.height
220	  let ch = imgf.channels
221	  let n = w * h
222	  let color_ch = if ch >= 3 { 3 } else { ch }
223	  // 找最大值用于归一化
224	  let mut max_val = 0.0F
225	  for i in 0..<n {
226	    for c in 0..<color_ch {
227	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
228	      if v > max_val {
229	        max_val = v
230	      }
231	    }
232	  }
233	  if max_val <= 0.0F {
234	    return @types.Image16::{
235	      width: w,
236	      height: h,
237	      channels: ch,
238	      data: Bytes::from_array(Array::make(n * ch * 2, b'\x00')),
239	    }
240	  }
241	  let inv_gamma = 1.0F / gamma
242	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
243	  for i in 0..<n {
244	    for c in 0..<color_ch {
245	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
246	      let normalized = v / max_val
247	      let mapped = if normalized > 0.0F {
248	        @math.powf(normalized, inv_gamma)
249	      } else {
250	        0.0F	<-- UNCOVERED
251	      }
252	      let val = (mapped * 65535.0F + 0.5F).to_int()
253	      let clamped = if val < 0 { 0 } else if val > 65535 { 65535 } else { val }	<-- UNCOVERED
254	      write_u16_le_tm(out, (i * ch + c) * 2, clamped)
255	    }
256	    if ch == 4 {
257	      write_u16_le_tm(out, (i * ch + 3) * 2, 65535)	<-- UNCOVERED
258	    }
259	  }
260	  @types.Image16::{
261	    width: w,
262	    height: h,
263	    channels: ch,
264	    data: Bytes::from_array(out),
265	  }
266	}
   	…

273	pub fn reinhard_tonemap_16(
274	  imgf : @types.ImageF,
275	  key? : Float = 0.18F,
276	) -> @types.Image16 raise @types.LoadError {
277	  // 输入验证
278	  if imgf.width <= 0 || imgf.height <= 0 {
279	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
280	      "reinhard_tonemap_16: 图像尺寸必须大于0",
281	    )
282	  }
283	  if imgf.channels < 1 || imgf.channels > 4 {
284	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
285	      "reinhard_tonemap_16: 通道数必须在1-4之间",
286	    )
287	  }
288	  let expected_size = imgf.width * imgf.height * imgf.channels * 4
289	  if imgf.data.length() != expected_size {
290	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
291	      "reinhard_tonemap_16: 数据大小不匹配，期望 " +
292	      expected_size.to_string() +	<-- UNCOVERED
293	      "，实际 " +
294	      imgf.data.length().to_string(),	<-- UNCOVERED
295	    )
296	  }
297	  if key <= 0.0F {
298	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
299	      "reinhard_tonemap_16: key必须大于0",
300	    )
301	  }
302	  let w = imgf.width
303	  let h = imgf.height
304	  let ch = imgf.channels
305	  let n = w * h
306	  let color_ch = if ch >= 3 { 3 } else { ch }
307	  // 计算对数平均亮度
308	  let mut log_sum = 0.0F
309	  for i in 0..<n {
310	    let r = read_float_le_tm(imgf.data, i * ch * 4)
311	    let g = read_float_le_tm(imgf.data, i * ch * 4 + 4)
312	    let b = read_float_le_tm(imgf.data, i * ch * 4 + 8)
313	    let lum = 0.2126F * r + 0.7152F * g + 0.0722F * b
314	    let lum_safe = if lum > 0.0F { lum } else { 0.0001F }
315	    log_sum += log_f_tm(lum_safe)
316	  }
317	  let log_avg = @math.expf(log_sum / Float::from_int(n))
318	  let scale = key / log_avg
319	  // 应用 Reinhard 映射
320	  let out : Array[Byte] = Array::make(n * ch * 2, b'\x00')
321	  for i in 0..<n {
322	    for c in 0..<color_ch {
323	      let v = read_float_le_tm(imgf.data, (i * ch + c) * 4)
324	      let scaled = v * scale
325	      let mapped = scaled / (1.0F + scaled)
326	      let val = (mapped * 65535.0F + 0.5F).to_int()
327	      let clamped = if val < 0 { 0 } else if val > 65535 { 65535 } else { val }	<-- UNCOVERED
328	      write_u16_le_tm(out, (i * ch + c) * 2, clamped)
329	    }
330	    if ch == 4 {
331	      write_u16_le_tm(out, (i * ch + 3) * 2, 65535)	<-- UNCOVERED
332	    }
333	  }
334	  @types.Image16::{
335	    width: w,
336	    height: h,
337	    channels: ch,
338	    data: Bytes::from_array(out),
339	  }
340	}
   	…

23 uncovered line(s) in src\process\compression\image_compression.mbt:

171	pub fn quantize_image(img : @types.Image, levels : Int) -> @types.Image {
172	  let w = img.width
173	  let h = img.height
174	  let total = w * h
175	  let out : Array[Byte] = []
176	  let step = 256 / levels
177	
178	  for i in 0..<total {
179	    let pixel = img.data[i].to_int()
180	    let quantized = pixel / step * step + step / 2
181	    let clamped = if quantized < 0 {
182	      0	<-- UNCOVERED
183	    } else if quantized > 255 {
184	      255	<-- UNCOVERED
185	    } else {
186	      quantized
187	    }
188	    out.push(clamped.to_byte())
189	  }
190	
191	  @types.Image::{
192	    width: w,
193	    height: h,
194	    channels: img.channels,
195	    data: Bytes::from_array(out),
196	  }
197	}
   	…

235	pub fn calculate_compression_ratio(
236	  original_size : Int,
237	  compressed_size : Int,
238	) -> Double {
239	  if compressed_size > 0 {
240	    original_size.to_double() / compressed_size.to_double()
241	  } else {
242	    0.0	<-- UNCOVERED
243	  }
244	}
   	…

248	pub fn calculate_bits_per_pixel(
249	  compressed_size : Int,
250	  width : Int,
251	  height : Int,
252	) -> Double {
253	  let total_pixels = width * height
254	  if total_pixels > 0 {
255	    compressed_size.to_double() * 8.0 / total_pixels.to_double()
256	  } else {
257	    0.0	<-- UNCOVERED
258	  }
259	}
   	…

263	pub fn calculate_compression_quality(
264	  original : @types.Image,
265	  compressed : @types.Image,
266	  original_size : Int,
267	  compressed_size : Int,
268	) -> CompressionQualityMetrics {
269	  let w = original.width
270	  let h = original.height
271	  let total = w * h
272	
273	  // MSE
274	  let mut mse = 0.0
275	  for i in 0..<total {
276	    let diff = original.data[i].to_double() - compressed.data[i].to_double()
277	    mse += diff * diff
278	  }
279	  mse /= total.to_double()
280	
281	  // PSNR
282	  let psnr = if mse > 0.0 {
283	    10.0 * (65025.0 / mse) * 0.1 // 简化的 log10
284	  } else {
285	    100.0
286	  }
287	
288	  // SSIM（简化版）
289	  let mut mean1 = 0.0
290	  let mut mean2 = 0.0
291	  for i in 0..<total {
292	    mean1 += original.data[i].to_double()
293	    mean2 += compressed.data[i].to_double()
294	  }
295	  mean1 /= total.to_double()
296	  mean2 /= total.to_double()
297	
298	  let mut var1 = 0.0
299	  let mut var2 = 0.0
300	  let mut cov = 0.0
301	  for i in 0..<total {
302	    let p1 = original.data[i].to_double()
303	    let p2 = compressed.data[i].to_double()
304	    var1 += (p1 - mean1) * (p1 - mean1)
305	    var2 += (p2 - mean2) * (p2 - mean2)
306	    cov += (p1 - mean1) * (p2 - mean2)
307	  }
308	  var1 /= total.to_double()
309	  var2 /= total.to_double()
310	  cov /= total.to_double()
311	
312	  let c1 = 6.5025
313	  let c2 = 58.5225
314	  let ssim = if var1 + var2 + c2 > 0.0 {
315	    (2.0 * mean1 * mean2 + c1) *
316	    (2.0 * cov + c2) /
317	    ((mean1 * mean1 + mean2 * mean2 + c1) * (var1 + var2 + c2))
318	  } else {
319	    1.0	<-- UNCOVERED
320	  }
321	
322	  let compression_ratio = calculate_compression_ratio(
323	    original_size, compressed_size,
324	  )
325	  let bpp = calculate_bits_per_pixel(compressed_size, w, h)
326	
327	  // 综合质量评分（PSNR 40% + SSIM 40% + 压缩比 20%）
328	  let psnr_score = if psnr > 40.0 {
329	    1.0
330	  } else if psnr > 30.0 {
331	    0.8	<-- UNCOVERED
332	  } else if psnr > 20.0 {
333	    0.6	<-- UNCOVERED
334	  } else {
335	    0.4
336	  }
337	  let ssim_score = if ssim > 0.95 {
338	    1.0
339	  } else if ssim > 0.9 {
340	    0.85	<-- UNCOVERED
341	  } else if ssim > 0.8 {
342	    0.7	<-- UNCOVERED
343	  } else {
344	    0.5
345	  }
346	  let ratio_score = if compression_ratio > 20.0 {
347	    1.0	<-- UNCOVERED
348	  } else if compression_ratio > 10.0 {
349	    0.8	<-- UNCOVERED
350	  } else if compression_ratio > 5.0 {
351	    0.6	<-- UNCOVERED
352	  } else {
353	    0.4
354	  }
355	  let overall_quality = psnr_score * 0.4 + ssim_score * 0.4 + ratio_score * 0.2
356	
357	  CompressionQualityMetrics::{
358	    psnr,
359	    ssim,
360	    mse,
361	    compression_ratio,
362	    bits_per_pixel: bpp,
363	    structural_similarity: ssim,
364	    overall_quality,
365	  }
366	}
   	…

370	pub fn compress_jpeg_simple(
371	  img : @types.Image,
372	  quality : Double,
373	) -> CompressionResult {
374	  let w = img.width
375	  let h = img.height
376	  let original_size = w * h
377	
378	  // 量化级别根据质量调整
379	  let levels = if quality > 90.0 {
380	    64	<-- UNCOVERED
381	  } else if quality > 75.0 {
382	    32	<-- UNCOVERED
383	  } else if quality > 50.0 {
384	    16	<-- UNCOVERED
385	  } else {
386	    8
387	  }
388	
389	  // 量化
390	  let quantized = quantize_image(img, levels)
391	
392	  // 预测编码
393	  let predicted = predictive_encode(quantized)
394	
395	  // RLE 编码
396	  let compressed = run_length_encode(predicted)
397	
398	  let compressed_size = compressed.length()
399	  let ratio = calculate_compression_ratio(original_size, compressed_size)
400	  let bpp = calculate_bits_per_pixel(compressed_size, w, h)
401	
402	  CompressionResult::{
403	    compressed_data: compressed,
404	    original_size,
405	    compressed_size,
406	    compression_ratio: ratio,
407	    bits_per_pixel: bpp,
408	    algorithm: CompressionAlgorithm::JPEGBaseline,
409	    quality,
410	    processing_time_ms: 0.0,
411	  }
412	}
   	…

445	pub fn compress_image(
446	  img : @types.Image,
447	  config : CompressionConfig,
448	) -> CompressionResult {
449	  if config.algorithm == CompressionAlgorithm::JPEGBaseline {
450	    compress_jpeg_simple(img, config.quality)	<-- UNCOVERED
451	  } else if config.algorithm == CompressionAlgorithm::PNG {
452	    compress_png_simple(img)	<-- UNCOVERED
453	  } else if config.algorithm == CompressionAlgorithm::RunLengthEncoding {
454	    let compressed = run_length_encode(img.data)
455	    let original_size = img.width * img.height
456	    let compressed_size = compressed.length()
457	    CompressionResult::{
458	      compressed_data: compressed,
459	      original_size,
460	      compressed_size,
461	      compression_ratio: calculate_compression_ratio(
462	        original_size, compressed_size,
463	      ),
464	      bits_per_pixel: calculate_bits_per_pixel(
465	        compressed_size,
466	        img.width,
467	        img.height,
468	      ),
469	      algorithm: CompressionAlgorithm::RunLengthEncoding,
470	      quality: 100.0,
471	      processing_time_ms: 0.0,
472	    }
473	  } else {
474	    compress_jpeg_simple(img, config.quality)
475	  }
476	}
   	…

480	pub fn compression_algorithm_to_string(
481	  algorithm : CompressionAlgorithm,
482	) -> String {
483	  if algorithm == CompressionAlgorithm::RunLengthEncoding {
484	    "游程编码(RLE)"
485	  } else if algorithm == CompressionAlgorithm::HuffmanCoding {
486	    "霍夫曼编码"
487	  } else if algorithm == CompressionAlgorithm::ArithmeticCoding {
488	    "算术编码"	<-- UNCOVERED
489	  } else if algorithm == CompressionAlgorithm::LZW {
490	    "LZW"	<-- UNCOVERED
491	  } else if algorithm == CompressionAlgorithm::Deflate {
492	    "Deflate"
493	  } else if algorithm == CompressionAlgorithm::JPEGBaseline {
494	    "JPEG 基线"
495	  } else if algorithm == CompressionAlgorithm::JPEG2000 {
496	    "JPEG 2000"	<-- UNCOVERED
497	  } else if algorithm == CompressionAlgorithm::WebP {
498	    "WebP"	<-- UNCOVERED
499	  } else if algorithm == CompressionAlgorithm::AVIF {
500	    "AVIF"	<-- UNCOVERED
501	  } else if algorithm == CompressionAlgorithm::PNG {
502	    "PNG"
503	  } else {
504	    "未知"	<-- UNCOVERED
505	  }
506	}
   	…

14 uncovered line(s) in src\process\computational_imaging\computational_imaging.mbt:

234	pub fn lightfield_refocus(
235	  lightfield : LightFieldData,
236	  focus_depth : Double,
237	  aperture_size : Double,
238	) -> RefocusResult {
239	  if lightfield.views.length() == 0 {
240	    return RefocusResult::{	<-- UNCOVERED
241	      image: @types.Image::{
242	        width: 1,
243	        height: 1,
244	        channels: 1,
245	        data: Bytes::make(1, b'\x00'),	<-- UNCOVERED
246	      },
247	      focus_depth,
248	      aperture_size,
249	      num_views_used: 0,
250	    }
251	  }
252	
253	  let first = lightfield.views[0]
254	  let w = first.width
255	  let h = first.height
256	  let channels = first.channels
257	  let total = w * h * channels
258	  let accumulator : Array[Double] = []
259	  for _ in 0..<total {
260	    accumulator.push(0.0)
261	  }
262	
263	  let mut num_views_used = 0
264	  let center_x = lightfield.num_views_x / 2
265	  let center_y = lightfield.num_views_y / 2
266	
267	  for vy in 0..<lightfield.num_views_y {
268	    for vx in 0..<lightfield.num_views_x {
269	      let dx = (vx - center_x).to_double()
270	      let dy = (vy - center_y).to_double()
271	      let dist = (dx * dx + dy * dy).sqrt()
272	
273	      if dist <= aperture_size {
274	        let view_idx = vy * lightfield.num_views_x + vx
275	        let view = lightfield.views[view_idx]
276	
277	        // 简化的重聚焦：根据深度偏移像素
278	        let shift_x = (dx * focus_depth * 0.1).to_int()
279	        let shift_y = (dy * focus_depth * 0.1).to_int()
280	
281	        for y in 0..<h {
282	          for x in 0..<w {
283	            let src_x = x + shift_x
284	            let src_y = y + shift_y
285	            if src_x >= 0 && src_x < w && src_y >= 0 && src_y < h {
286	              for c in 0..<channels {
287	                let dst_idx = (y * w + x) * channels + c
288	                let src_idx = (src_y * w + src_x) * channels + c
289	                accumulator[dst_idx] += view.data[src_idx].to_double()
290	              }
291	            }
292	          }
293	        }
294	        num_views_used += 1
295	      }
296	    }
297	  }
298	
299	  // 归一化
300	  let out : Array[Byte] = []
301	  for i in 0..<total {
302	    let value = if num_views_used > 0 {
303	      accumulator[i] / num_views_used.to_double()
304	    } else {
305	      0.0	<-- UNCOVERED
306	    }
307	    let clamped = if value < 0.0 {
308	      0.0	<-- UNCOVERED
309	    } else if value > 255.0 {
310	      255.0	<-- UNCOVERED
311	    } else {
312	      value
313	    }
314	    out.push(clamped.to_int().to_byte())
315	  }
316	
317	  RefocusResult::{
318	    image: @types.Image::{
319	      width: w,
320	      height: h,
321	      channels,
322	      data: Bytes::from_array(out),
323	    },
324	    focus_depth,
325	    aperture_size,
326	    num_views_used,
327	  }
328	}
   	…

332	pub fn synthesize_view(
333	  lightfield : LightFieldData,
334	  target_x : Double,
335	  target_y : Double,
336	) -> ViewSynthesisResult {
337	  if lightfield.views.length() < 4 {
338	    return ViewSynthesisResult::{	<-- UNCOVERED
339	      image: @types.Image::{
340	        width: 1,
341	        height: 1,
342	        channels: 1,
343	        data: Bytes::make(1, b'\x00'),	<-- UNCOVERED
344	      },
345	      view_position: [target_x, target_y],
346	      num_source_views: 0,
347	      interpolation_method: "none",
348	    }
349	  }
350	
351	  let first = lightfield.views[0]
352	  let w = first.width
353	  let h = first.height
354	  let channels = first.channels
355	  let total = w * h * channels
356	
357	  // 找到最近的 4 个视角进行双线性插值
358	  let fx = target_x * (lightfield.num_views_x - 1).to_double()
359	  let fy = target_y * (lightfield.num_views_y - 1).to_double()
360	  let x0 = fx.to_int()
361	  let y0 = fy.to_int()
362	  let x1 = if x0 + 1 < lightfield.num_views_x { x0 + 1 } else { x0 }
363	  let y1 = if y0 + 1 < lightfield.num_views_y { y0 + 1 } else { y0 }
364	  let wx = fx - x0.to_double()
365	  let wy = fy - y0.to_double()
366	
367	  let idx00 = y0 * lightfield.num_views_x + x0
368	  let idx01 = y0 * lightfield.num_views_x + x1
369	  let idx10 = y1 * lightfield.num_views_x + x0
370	  let idx11 = y1 * lightfield.num_views_x + x1
371	
372	  let v00 = lightfield.views[idx00]
373	  let v01 = lightfield.views[idx01]
374	  let v10 = lightfield.views[idx10]
375	  let v11 = lightfield.views[idx11]
376	
377	  let out : Array[Byte] = []
378	  for i in 0..<total {
379	    let value = v00.data[i].to_double() * (1.0 - wx) * (1.0 - wy) +
380	      v01.data[i].to_double() * wx * (1.0 - wy) +
381	      v10.data[i].to_double() * (1.0 - wx) * wy +
382	      v11.data[i].to_double() * wx * wy
383	    let clamped = if value < 0.0 {
384	      0.0	<-- UNCOVERED
385	    } else if value > 255.0 {
386	      255.0	<-- UNCOVERED
387	    } else {
388	      value
389	    }
390	    out.push(clamped.to_int().to_byte())
391	  }
392	
393	  ViewSynthesisResult::{
394	    image: @types.Image::{
395	      width: w,
396	      height: h,
397	      channels,
398	      data: Bytes::from_array(out),
399	    },
400	    view_position: [target_x, target_y],
401	    num_source_views: 4,
402	    interpolation_method: "bilinear",
403	  }
404	}
   	…

465	pub fn reconstruct_hologram(hologram : Hologram) -> ImagingReconstructionResult {
466	  let w = hologram.intensity.width
467	  let h = hologram.intensity.height
468	  let _ = w * h
469	  let out : Array[Byte] = []
470	
471	  // 简化的重建：直接使用强度图的逆变换
472	  for y in 0..<h {
473	    for x in 0..<w {
474	      let idx = y * w + x
475	      let value = hologram.intensity.data[idx].to_double()
476	      // 简化的重建：对比度增强
477	      let reconstructed = (value - 128.0) * 1.5 + 128.0
478	      let clamped = if reconstructed < 0.0 {
479	        0.0
480	      } else if reconstructed > 255.0 {
481	        255.0	<-- UNCOVERED
482	      } else {
483	        reconstructed
484	      }
485	      out.push(clamped.to_int().to_byte())
486	    }
487	  }
488	
489	  ImagingReconstructionResult::{
490	    image: @types.Image::{
491	      width: w,
492	      height: h,
493	      channels: 1,
494	      data: Bytes::from_array(out),
495	    },
496	    algorithm: ComputationalImagingAlgorithm::Holography,
497	    num_iterations: 1,
498	    convergence_error: 0.0,
499	    reconstruction_time_ms: 0.0,
500	    psnr: 0.0,
501	    ssim: 0.0,
502	  }
503	}
   	…

569	pub fn phase_retrieval(
570	  magnitude : @types.Image,
571	  config : PhaseRetrievalConfig,
572	) -> ImagingReconstructionResult {
573	  let w = magnitude.width
574	  let h = magnitude.height
575	  let total = w * h
576	
577	  // 简化的相位恢复：迭代优化
578	  let phase : Array[Double] = []
579	  for _ in 0..<total {
580	    phase.push(0.0)
581	  }
582	
583	  let mut error = 0.0
584	  for iter in 0..<config.num_iterations {
585	    // 简化的迭代：随机扰动相位
586	    let mut state = iter * 11035 + 12345
587	    for i in 0..<total {
588	      state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
589	      let perturbation = (state % 1000).to_double() / 1000.0 - 0.5
590	      phase[i] += perturbation * 0.01
591	    }
592	    error = 1.0 / (iter + 1).to_double()
593	  }
594	
595	  // 生成重建图像
596	  let out : Array[Byte] = []
597	  for i in 0..<total {
598	    let value = magnitude.data[i].to_double() *
599	      (0.5 + 0.5 * cos_approx(phase[i]))
600	    let clamped = if value < 0.0 {
601	      0.0	<-- UNCOVERED
602	    } else if value > 255.0 {
603	      255.0	<-- UNCOVERED
604	    } else {
605	      value
606	    }
607	    out.push(clamped.to_int().to_byte())
608	  }
609	
610	  ImagingReconstructionResult::{
611	    image: @types.Image::{
612	      width: w,
613	      height: h,
614	      channels: 1,
615	      data: Bytes::from_array(out),
616	    },
617	    algorithm: ComputationalImagingAlgorithm::PhaseRetrieval,
618	    num_iterations: config.num_iterations,
619	    convergence_error: error,
620	    reconstruction_time_ms: 0.0,
621	    psnr: 0.0,
622	    ssim: 0.0,
623	  }
624	}
   	…

628	pub fn coded_aperture_reconstruct(
629	  measurement : @types.Image,
630	  pattern : CodedAperturePattern,
631	) -> ImagingReconstructionResult {
632	  let w = measurement.width
633	  let h = measurement.height
634	  let _ = w * h
635	  let out : Array[Byte] = []
636	
637	  // 简化的反卷积重建
638	  for y in 0..<h {
639	    for x in 0..<w {
640	      let _ = y * w + x
641	      let mut sum = 0.0
642	      let mut count = 0
643	
644	      // 局部反卷积
645	      for dy in -2..<3 {
646	        for dx in -2..<3 {
647	          let src_x = x + dx
648	          let src_y = y + dy
649	          if src_x >= 0 && src_x < w && src_y >= 0 && src_y < h {
650	            let pat_idx = src_y % pattern.pattern_size * pattern.pattern_size +
651	              src_x % pattern.pattern_size
652	            if pattern.pattern.data[pat_idx].to_int() > 128 {
653	              sum += measurement.data[src_y * w + src_x].to_double()
654	              count += 1
655	            }
656	          }
657	        }
658	      }
659	
660	      let value = if count > 0 { sum / count.to_double() } else { 0.0 }
661	      let clamped = if value < 0.0 {
662	        0.0	<-- UNCOVERED
663	      } else if value > 255.0 {
664	        255.0	<-- UNCOVERED
665	      } else {
666	        value
667	      }
668	      out.push(clamped.to_int().to_byte())
669	    }
670	  }
671	
672	  ImagingReconstructionResult::{
673	    image: @types.Image::{
674	      width: w,
675	      height: h,
676	      channels: 1,
677	      data: Bytes::from_array(out),
678	    },
679	    algorithm: ComputationalImagingAlgorithm::CodedAperture,
680	    num_iterations: 1,
681	    convergence_error: 0.0,
682	    reconstruction_time_ms: 0.0,
683	    psnr: 0.0,
684	    ssim: 0.0,
685	  }
686	}
   	…

16 uncovered line(s) in src\process\computational_photography_advanced\computational_photography_advanced.mbt:

234	pub fn create_hdr_image(
235	  exposures : Array[Array[Double]],
236	  exposure_values : Array[Double],
237	  width : Int,
238	  height : Int,
239	  config : ComputationalPhotographyConfig,
240	) -> HDRImage {
241	  ignore(config)
242	  let hdr_data : Array[Double] = []
243	  let num_pixels = width * height * 3
244	
245	  for i in 0..<num_pixels {
246	    let mut hdr_value = 0.0
247	    let mut weight_sum = 0.0
248	
249	    for j in 0..<exposures.length() {
250	      if i < exposures[j].length() {
251	        let value = exposures[j][i]
252	        let ev = if j < exposure_values.length() {
253	          exposure_values[j]
254	        } else {
255	          0.0	<-- UNCOVERED
256	        }
257	        // 简化的响应函数和权重
258	        let weight = if value > 0.01 && value < 0.99 { 1.0 } else { 0.1 }
259	        let irradiance = value * power_approx(2.0, ev.to_int().to_double())
260	        hdr_value += irradiance * weight
261	        weight_sum += weight
262	      }
263	    }
264	
265	    let final_value = if weight_sum > 0.0 {
266	      hdr_value / weight_sum
267	    } else {
268	      0.0	<-- UNCOVERED
269	    }
270	    hdr_data.push(final_value)
271	  }
272	
273	  HDRImage::{
274	    hdr_data,
275	    width,
276	    height,
277	    num_channels: 3,
278	    min_luminance: 0.001,
279	    max_luminance: 1000.0,
280	    exposure_values,
281	    num_exposures: exposures.length(),
282	    tone_mapped: false,
283	  }
284	}
   	…

306	pub fn stitch_panorama(
307	  images : Array[Array[Double]],
308	  width : Int,
309	  height : Int,
310	  config : ComputationalPhotographyConfig,
311	) -> PanoramaImage {
312	  let panorama_width = width * images.length()
313	  let panorama_data : Array[Double] = []
314	
315	  // 简化的拼接：直接水平排列
316	  for y in 0..<height {
317	    for img_idx in 0..<images.length() {
318	      for x in 0..<width {
319	        let src_idx = y * width + x
320	        if src_idx < images[img_idx].length() {
321	          panorama_data.push(images[img_idx][src_idx])
322	        } else {
323	          panorama_data.push(0.0)	<-- UNCOVERED
324	        }
325	      }
326	    }
327	  }
328	
329	  PanoramaImage::{
330	    panorama_data,
331	    width: panorama_width,
332	    height,
333	    num_channels: 3,
334	    num_input_images: images.length(),
335	    stitching_method: config.panorama_stitching,
336	    focal_length: 500.0,
337	    projection_type: config.panorama_projection,
338	    blending_method: "multiband",
339	    seam_line: [],
340	  }
341	}
   	…

345	pub fn focus_stack(
346	  images : Array[Array[Double]],
347	  width : Int,
348	  height : Int,
349	  config : ComputationalPhotographyConfig,
350	) -> FocusStackingResult {
351	  let focused_image : Array[Double] = []
352	  let focus_map : Array[Array[Int]] = []
353	  let sharpness_map : Array[Array[Double]] = []
354	
355	  for y in 0..<height {
356	    let focus_row : Array[Int] = []
357	    let sharpness_row : Array[Double] = []
358	    for x in 0..<width {
359	      let mut best_sharpness = 0.0
360	      let mut best_idx = 0
361	
362	      for img_idx in 0..<images.length() {
363	        // 简化的锐度估计：局部方差
364	        let idx = y * width + x
365	        if idx < images[img_idx].length() {
366	          let value = images[img_idx][idx]
367	          let sharpness = value.abs() // 简化
368	          if sharpness > best_sharpness {
369	            best_sharpness = sharpness
370	            best_idx = img_idx
371	          }
372	        }
373	      }
374	
375	      let idx = y * width + x
376	      if idx < images[best_idx].length() {
377	        focused_image.push(images[best_idx][idx])
378	      } else {
379	        focused_image.push(0.0)	<-- UNCOVERED
380	      }
381	      focus_row.push(best_idx)
382	      sharpness_row.push(best_sharpness)
383	    }
384	    focus_map.push(focus_row)
385	    sharpness_map.push(sharpness_row)
386	  }
387	
388	  FocusStackingResult::{
389	    focused_image,
390	    width,
391	    height,
392	    num_channels: 3,
393	    num_input_images: images.length(),
394	    focus_map,
395	    sharpness_map,
396	    stacking_method: config.focus_stacking_method,
397	    depth_estimation: [],
398	  }
399	}
   	…

454	fn log_approx_cp(x : Double) -> Double {
455	  if x <= 0.0 {
456	    -100.0	<-- UNCOVERED
457	  } else {
458	    let mut result = 0.0
459	    let mut y = x
460	    while y > 1.0 {
461	      y = y / 2.71828
462	      result = result + 1.0
463	    }
464	    result + (y - 1.0) / y
465	  }
466	}
   	…

470	pub fn deblur_image(
471	  image : Array[Double],
472	  width : Int,
473	  height : Int,
474	  config : ComputationalPhotographyConfig,
475	) -> DeblurringResult {
476	  let deblurred : Array[Double] = []
477	  let kernel_size = 5
478	  let kernel : Array[Array[Double]] = []
479	
480	  // 简化的高斯核
481	  for i in 0..<kernel_size {
482	    let row : Array[Double] = []
483	    for j in 0..<kernel_size {
484	      let dist = ((i - 2) * (i - 2) + (j - 2) * (j - 2)).to_double()
485	      let value = 1.0 / (1.0 + dist)
486	      row.push(value)
487	    }
488	    kernel.push(row)
489	  }
490	
491	  // 简化的去模糊：锐化
492	  for y in 0..<height {
493	    for x in 0..<width {
494	      let idx = y * width + x
495	      if idx < image.length() {
496	        let mut value = image[idx] * 2.0
497	        // 减去邻域平均值
498	        let mut neighbor_sum = 0.0
499	        let mut neighbor_count = 0
500	        for dy in -1..<2 {
501	          for dx in -1..<2 {
502	            let ny = y + dy
503	            let nx = x + dx
504	            if ny >= 0 && ny < height && nx >= 0 && nx < width {
505	              let nidx = ny * width + nx
506	              if nidx < image.length() {
507	                neighbor_sum += image[nidx]
508	                neighbor_count += 1
509	              }
510	            }
511	          }
512	        }
513	        if neighbor_count > 0 {
514	          value -= neighbor_sum / neighbor_count.to_double()
515	        }
516	        if value < 0.0 {
517	          value = 0.0
518	        }
519	        if value > 255.0 {
520	          value = 255.0	<-- UNCOVERED
521	        }
522	        deblurred.push(value)
523	      } else {
524	        deblurred.push(0.0)	<-- UNCOVERED
525	      }
526	    }
527	  }
528	
529	  DeblurringResult::{
530	    deblurred_image: deblurred,
531	    width,
532	    height,
533	    num_channels: 3,
534	    deblurring_method: config.deblurring_method,
535	    kernel_size,
536	    kernel_estimate: kernel,
537	    psnr_improvement: 3.0,
538	    ssim_improvement: 0.1,
539	    iterations: 10,
540	    is_blind: false,
541	  }
542	}
   	…

546	pub fn dehaze_image(
547	  image : Array[Double],
548	  width : Int,
549	  height : Int,
550	  config : ComputationalPhotographyConfig,
551	) -> DehazingResult {
552	  let dehazed : Array[Double] = []
553	  let transmission : Array[Double] = []
554	  let atmospheric : Array[Double] = []
555	  atmospheric.push(240.0)
556	  atmospheric.push(240.0)
557	  atmospheric.push(240.0)
558	
559	  for y in 0..<height {
560	    for x in 0..<width {
561	      let idx = y * width + x
562	      if idx < image.length() {
563	        // 简化的暗通道先验
564	        let value = image[idx]
565	        let dark_channel = value // 简化
566	        let t = 1.0 - 0.95 * (dark_channel / 255.0)
567	        transmission.push(t)
568	
569	        // 去雾公式：J = (I - A) / t + A
570	        let a = atmospheric[idx % 3]
571	        let dehazed_value = if t > 0.1 { (value - a) / t + a } else { value }
572	        if dehazed_value < 0.0 {
573	          dehazed.push(0.0)	<-- UNCOVERED
574	        } else if dehazed_value > 255.0 {
575	          dehazed.push(255.0)	<-- UNCOVERED
576	        } else {
577	          dehazed.push(dehazed_value)
578	        }
579	      } else {
580	        dehazed.push(0.0)	<-- UNCOVERED
581	        transmission.push(1.0)
582	      }
583	    }
584	  }
585	
586	  DehazingResult::{
587	    dehazed_image: dehazed,
588	    width,
589	    height,
590	    num_channels: 3,
591	    dehazing_method: config.dehazing_method,
592	    transmission_map: transmission,
593	    atmospheric_light: atmospheric,
594	    haze_density: 0.5,
595	    visibility_improvement: 50.0,
596	    contrast_improvement: 20.0,
597	  }
598	}
   	…

602	pub fn computational_photography_report_to_string(
603	  task_type : ComputationalPhotographyTaskType,
604	  config : ComputationalPhotographyConfig,
605	  result_description : String,
606	) -> String {
607	  let mut report = "## 计算摄影高级报告\n\n"
608	
609	  report = report + "### 任务信息\n\n"
610	  report = report + "| 指标 | 值 |\n|------|------|\n"
611	  report = report +
612	    "| 任务类型 | " +
613	    (match task_type {
614	      ComputationalPhotographyTaskType::HDRImaging => "HDR成像"
615	      ComputationalPhotographyTaskType::PanoramaStitching => "全景拼接"	<-- UNCOVERED
616	      ComputationalPhotographyTaskType::FocusStacking => "景深合成"
617	      ComputationalPhotographyTaskType::MultiFrameDenoising => "多帧降噪"
618	      ComputationalPhotographyTaskType::SuperResolution => "超分辨率"
619	      ComputationalPhotographyTaskType::Deblurring => "去模糊"
620	      ComputationalPhotographyTaskType::Dehazing => "去雾"
621	      ComputationalPhotographyTaskType::ColorManagement => "色彩管理"
622	      _ => "未知"
623	    }) +
624	    " |\n"
625	  report = report +
626	    "| HDR色调映射 | " +
627	    (match config.hdr_tone_mapping {
628	      HDRToneMappingMethod::Reinhard => "Reinhard"
629	      HDRToneMappingMethod::Durand => "Durand"	<-- UNCOVERED
630	      HDRToneMappingMethod::Mantiuk => "Mantiuk"
631	      _ => "未知"
632	    }) +
633	    " |\n"
634	  report = report +
635	    "| 全景拼接 | " +
636	    (match config.panorama_stitching {
637	      PanoramaStitchingMethod::SIFT => "SIFT"
638	      PanoramaStitchingMethod::SURF => "SURF"	<-- UNCOVERED
639	      PanoramaStitchingMethod::ORB => "ORB"
640	      _ => "未知"
641	    }) +
642	    " |\n"
643	  report = report +
644	    "| 去模糊方法 | " +
645	    (match config.deblurring_method {
646	      DeblurringMethod::Wiener => "Wiener"	<-- UNCOVERED
647	      DeblurringMethod::RichardsonLucy => "Richardson-Lucy"
648	      DeblurringMethod::BlindDeconvolution => "盲去卷积"	<-- UNCOVERED
649	      _ => "未知"
650	    }) +
651	    " |\n"
652	  report = report +
653	    "| 去雾方法 | " +
654	    (match config.dehazing_method {
655	      DehazingMethod::DarkChannelPrior => "暗通道先验"
656	      DehazingMethod::ColorAttenuation => "颜色衰减"	<-- UNCOVERED
657	      _ => "未知"
658	    }) +
659	    " |\n"
660	  report = report + "| Gamma | " + config.gamma.to_string() + " |\n"
661	  report = report +
662	    "| 白平衡温度 | " +
663	    config.white_balance_temperature.to_string() +
664	    " K |\n"
665	  report = report +
666	    "| 超分倍率 | " +
667	    config.sr_scale_factor.to_string() +
668	    "x |\n"
669	
670	  report = report + "\n### 结果\n\n"
671	  report = report + result_description + "\n"
672	
673	  report
674	}
   	…

8 uncovered line(s) in src\process\continuous_learning\continuous_learning.mbt:

246	pub fn update_fisher_information(
247	  ewc_config : EWCConfig,
248	  gradients : Array[Double],
249	) -> EWCConfig {
250	  let new_fisher : Array[Double] = []
251	
252	  for i in 0..<ewc_config.num_params {
253	    let grad_sq = if i < gradients.length() {
254	      gradients[i] * gradients[i]
255	    } else {
256	      0.0
257	    }
258	
259	    let updated = if ewc_config.online {
260	      ewc_config.gamma * ewc_config.fisher_information[i] +	<-- UNCOVERED
261	      (1.0 - ewc_config.gamma) * grad_sq
262	    } else {
263	      ewc_config.fisher_information[i] + grad_sq
264	    }
265	
266	    new_fisher.push(updated)
267	  }
268	
269	  EWCConfig::{
270	    lambda: ewc_config.lambda,
271	    fisher_information: new_fisher,
272	    optimal_params: ewc_config.optimal_params,
273	    num_params: ewc_config.num_params,
274	    online: ewc_config.online,
275	    gamma: ewc_config.gamma,
276	  }
277	}
   	…

281	pub fn sample_from_memory(
282	  buffer : MemoryBuffer,
283	  num_samples : Int,
284	) -> (Array[Array[Double]], Array[Int], Array[Int]) {
285	  let sampled_samples : Array[Array[Double]] = []
286	  let sampled_labels : Array[Int] = []
287	  let sampled_task_ids : Array[Int] = []
288	
289	  let actual_samples = if num_samples < buffer.current_size {
290	    num_samples	<-- UNCOVERED
291	  } else {
292	    buffer.current_size
293	  }
294	
295	  for i in 0..<actual_samples {
296	    // 简化的随机采样
297	    let idx = (i * 7 + 13) % buffer.current_size
298	    sampled_samples.push(buffer.samples[idx])
299	    sampled_labels.push(buffer.labels[idx])
300	    sampled_task_ids.push(buffer.task_ids[idx])
301	  }
302	
303	  (sampled_samples, sampled_labels, sampled_task_ids)
304	}
   	…

308	pub fn add_to_memory(
309	  buffer : MemoryBuffer,
310	  sample : Array[Double],
311	  label : Int,
312	  task_id : Int,
313	) -> MemoryBuffer {
314	  if buffer.current_size < buffer.buffer_size {
315	    let new_samples = buffer.samples
316	    let new_labels = buffer.labels
317	    let new_task_ids = buffer.task_ids
318	
319	    new_samples.push(sample)
320	    new_labels.push(label)
321	    new_task_ids.push(task_id)
322	
323	    MemoryBuffer::{
324	      buffer_size: buffer.buffer_size,
325	      current_size: buffer.current_size + 1,
326	      samples: new_samples,
327	      labels: new_labels,
328	      task_ids: new_task_ids,
329	      sampling_strategy: buffer.sampling_strategy,
330	    }
331	  } else {
332	    // 缓冲区满，替换最旧的样本
333	    let idx = 0	<-- UNCOVERED
334	    buffer.samples[idx] = sample
335	    buffer.labels[idx] = label
336	    buffer.task_ids[idx] = task_id
337	    buffer
338	  }
339	}
   	…

343	pub fn simulate_continual_learning(
344	  tasks : Array[LearningTask],
345	  config : ContinualLearningConfig,
346	  initial_accuracy : Double,
347	) -> ContinualLearningMetrics {
348	  let num_tasks = tasks.length()
349	  let task_accuracies : Array[Double] = []
350	  let learning_accuracy : Array[Double] = []
351	  let mut final_accuracy = initial_accuracy
352	  let mut total_accuracy = 0.0
353	  let mut forgetting = 0.0
354	
355	  for task_idx in 0..<num_tasks {
356	    let task = tasks[task_idx]
357	
358	    // 模拟任务学习
359	    let task_accuracy = initial_accuracy +
360	      (1.0 - initial_accuracy) * (1.0 - task.difficulty * 0.5)
361	    task_accuracies.push(task_accuracy)
362	    learning_accuracy.push(task_accuracy)
363	
364	    // 模拟对之前任务的遗忘
365	    if task_idx > 0 {
366	      let forgetting_rate = match config.regularization_method {
367	        RegularizationMethod::EWC => 0.02
368	        RegularizationMethod::SI => 0.03	<-- UNCOVERED
369	        RegularizationMethod::MAS => 0.025
370	        RegularizationMethod::LwF => 0.04
371	        _ => 0.05
372	      }
373	
374	      for prev_idx in 0..<task_idx {
375	        let prev_accuracy = task_accuracies[prev_idx]
376	        let new_accuracy = prev_accuracy * (1.0 - forgetting_rate)
377	        task_accuracies[prev_idx] = new_accuracy
378	        forgetting += prev_accuracy - new_accuracy
379	      }
380	    }
381	
382	    // 计算平均精度
383	    let mut sum = 0.0
384	    for i in 0..<(task_idx + 1) {
385	      sum += task_accuracies[i]
386	    }
387	    final_accuracy = sum / (task_idx + 1).to_double()
388	    total_accuracy += final_accuracy
389	  }
390	
391	  let average_accuracy = total_accuracy / num_tasks.to_double()
392	  let backward_transfer = -forgetting / (num_tasks - 1).to_double()
393	  let forward_transfer = 0.05
394	  let forgetting_measure = forgetting / (num_tasks - 1).to_double()
395	
396	  ContinualLearningMetrics::{
397	    average_accuracy,
398	    final_accuracy,
399	    backward_transfer,
400	    forward_transfer,
401	    forgetting_measure,
402	    learning_accuracy,
403	    task_accuracies,
404	    num_tasks_completed: num_tasks,
405	    memory_usage_mb: config.memory_size.to_double() * 0.001,
406	    training_time_s: num_tasks.to_double() *
407	    config.num_epochs.to_double() *
408	    10.0,
409	  }
410	}
   	…

414	pub fn continual_learning_report_to_string(
415	  metrics : ContinualLearningMetrics,
416	  config : ContinualLearningConfig,
417	) -> String {
418	  let mut report = "## 持续学习与增量学习报告\n\n"
419	
420	  report = report + "### 配置信息\n\n"
421	  report = report + "| 指标 | 值 |\n|------|------|\n"
422	  report = report +
423	    "| 策略 | " +
424	    (match config.strategy {
425	      ContinualLearningStrategy::RegularizationBased => "基于正则化"
426	      ContinualLearningStrategy::ReplayBased => "基于回放"	<-- UNCOVERED
427	      ContinualLearningStrategy::ArchitectureBased => "基于架构"
428	      ContinualLearningStrategy::MetaLearningBased => "基于元学习"
429	      _ => "混合"
430	    }) +
431	    " |\n"
432	  report = report +
433	    "| 正则化方法 | " +
434	    (match config.regularization_method {
435	      RegularizationMethod::EWC => "EWC"
436	      RegularizationMethod::SI => "SI"	<-- UNCOVERED
437	      RegularizationMethod::MAS => "MAS"
438	      RegularizationMethod::LwF => "LwF"
439	      _ => "未知"
440	    }) +
441	    " |\n"
442	  report = report +
443	    "| 场景 | " +
444	    (match config.scenario {
445	      IncrementalLearningScenario::TaskIncremental => "任务增量"	<-- UNCOVERED
446	      IncrementalLearningScenario::DomainIncremental => "域增量"
447	      IncrementalLearningScenario::ClassIncremental => "类增量"
448	      _ => "未知"	<-- UNCOVERED
449	    }) +
450	    " |\n"
451	  report = report + "| 任务数 | " + config.num_tasks.to_string() + " |\n"
452	  report = report + "| EWC Lambda | " + config.ewc_lambda.to_string() + " |\n"
453	  report = report +
454	    "| 记忆大小 | " +
455	    config.memory_size.to_string() +
456	    " |\n"
457	
458	  report = report + "\n### 性能指标\n\n"
459	  report = report + "| 指标 | 值 |\n|------|------|\n"
460	  report = report +
461	    "| 平均精度 | " +
462	    (metrics.average_accuracy * 100.0).to_string() +
463	    "% |\n"
464	  report = report +
465	    "| 最终精度 | " +
466	    (metrics.final_accuracy * 100.0).to_string() +
467	    "% |\n"
468	  report = report +
469	    "| 反向迁移 | " +
470	    (metrics.backward_transfer * 100.0).to_string() +
471	    "% |\n"
472	  report = report +
473	    "| 正向迁移 | " +
474	    (metrics.forward_transfer * 100.0).to_string() +
475	    "% |\n"
476	  report = report +
477	    "| 遗忘度量 | " +
478	    (metrics.forgetting_measure * 100.0).to_string() +
479	    "% |\n"
480	  report = report +
481	    "| 完成任务数 | " +
482	    metrics.num_tasks_completed.to_string() +
483	    " |\n"
484	  report = report +
485	    "| 内存使用 | " +
486	    metrics.memory_usage_mb.to_string() +
487	    " MB |\n"
488	  report = report +
489	    "| 训练时间 | " +
490	    metrics.training_time_s.to_string() +
491	    " s |\n"
492	
493	  report = report + "\n### 各任务精度\n\n"
494	  report = report + "| 任务 | 精度 |\n|------|------|\n"
495	  for i in 0..<metrics.task_accuracies.length() {
496	    report = report +
497	      "| 任务 " +
498	      (i + 1).to_string() +
499	      " | " +
500	      (metrics.task_accuracies[i] * 100.0).to_string() +
501	      "% |\n"
502	  }
503	
504	  report
505	}
   	…

10 uncovered line(s) in src\process\cv_advanced\cv_advanced.mbt:

171	pub fn calculate_iou(box1 : Array[Int], box2 : Array[Int]) -> Double {
172	  let x1 = if box1[0] > box2[0] { box1[0] } else { box2[0] }
173	  let y1 = if box1[1] > box2[1] { box1[1] } else { box2[1] }
174	  let x2 = if box1[2] < box2[2] { box1[2] } else { box2[2] }
175	  let y2 = if box1[3] < box2[3] { box1[3] } else { box2[3] }
176	
177	  let intersection = if x2 > x1 && y2 > y1 {
178	    (x2 - x1).to_double() * (y2 - y1).to_double()
179	  } else {
180	    0.0	<-- UNCOVERED
181	  }
182	
183	  let area1 = (box1[2] - box1[0]).to_double() * (box1[3] - box1[1]).to_double()
184	  let area2 = (box2[2] - box2[0]).to_double() * (box2[3] - box2[1]).to_double()
185	  let union = area1 + area2 - intersection
186	
187	  if union > 0.0 {
188	    intersection / union
189	  } else {
190	    0.0	<-- UNCOVERED
191	  }
192	}
   	…

318	pub fn visual_question_answering(
319	  image_id : String,
320	  question : String,
321	) -> VQAResult {
322	  // 简化的VQA：基于问题关键词生成答案
323	  let answer = if question.contains("color") {
324	    "red"	<-- UNCOVERED
325	  } else if question.contains("how many") {
326	    "3"	<-- UNCOVERED
327	  } else if question.contains("where") {
328	    "in the center"	<-- UNCOVERED
329	  } else if question.contains("what") {
330	    "a person"	<-- UNCOVERED
331	  } else {
332	    "yes"
333	  }
334	
335	  let reasoning_steps : Array[String] = []
336	  reasoning_steps.push("Analyze question")
337	  reasoning_steps.push("Extract visual features")
338	  reasoning_steps.push("Fuse multimodal information")
339	  reasoning_steps.push("Generate answer")
340	
341	  VQAResult::{
342	    image_id,
343	    question,
344	    answer,
345	    confidence: 0.75,
346	    answer_type: "open",
347	    reasoning_steps,
348	    num_steps: reasoning_steps.length(),
349	  }
350	}
   	…

381	pub fn cv_advanced_report_to_string(
382	  task_type : CVAdvancedTaskType,
383	  config : CVAdvancedConfig,
384	  result_description : String,
385	) -> String {
386	  let mut report = "## 计算机视觉高级报告\n\n"
387	
388	  report = report + "### 任务信息\n\n"
389	  report = report + "| 指标 | 值 |\n|------|------|\n"
390	  report = report +
391	    "| 任务类型 | " +
392	    (match task_type {
393	      CVAdvancedTaskType::ObjectTracking => "目标跟踪"
394	      CVAdvancedTaskType::ActionRecognition => "动作识别"	<-- UNCOVERED
395	      CVAdvancedTaskType::SceneParsing => "场景解析"
396	      CVAdvancedTaskType::VisualQuestionAnswering => "视觉问答"
397	      CVAdvancedTaskType::ImageCaptioning => "图像描述"
398	      CVAdvancedTaskType::VisualReasoning => "视觉推理"
399	      CVAdvancedTaskType::VideoUnderstanding => "视频理解"
400	      _ => "未知"
401	    }) +
402	    " |\n"
403	  report = report +
404	    "| 跟踪算法 | " +
405	    (match config.tracking_algorithm {
406	      TrackingAlgorithm::KCF => "KCF"
407	      TrackingAlgorithm::MOSSE => "MOSSE"	<-- UNCOVERED
408	      TrackingAlgorithm::CSRT => "CSRT"
409	      TrackingAlgorithm::SiamRPN => "SiamRPN"
410	      _ => "未知"
411	    }) +
412	    " |\n"
413	  report = report +
414	    "| 动作识别算法 | " +
415	    (match config.action_algorithm {
416	      ActionRecognitionAlgorithm::C3D => "C3D"	<-- UNCOVERED
417	      ActionRecognitionAlgorithm::I3D => "I3D"
418	      ActionRecognitionAlgorithm::SlowFast => "SlowFast"
419	      ActionRecognitionAlgorithm::TimeSformer => "TimeSformer"	<-- UNCOVERED
420	      _ => "未知"
421	    }) +
422	    " |\n"
423	  report = report +
424	    "| 置信度阈值 | " +
425	    config.confidence_threshold.to_string() +
426	    " |\n"
427	  report = report + "| IoU阈值 | " + config.iou_threshold.to_string() + " |\n"
428	
429	  report = report + "\n### 结果\n\n"
430	  report = report + result_description + "\n"
431	
432	  report
433	}
   	…

22 uncovered line(s) in src\process\denoise\advanced_denoise_enhance.mbt:

104	pub fn mean_filter_denoise(
105	  img : @types.Image,
106	  kernel_size : Int,
107	) -> @types.Image {
108	  let w = img.width
109	  let h = img.height
110	  let _ = w * h
111	  let out : Array[Byte] = []
112	  let radius = kernel_size / 2
113	
114	  for y in 0..<h {
115	    for x in 0..<w {
116	      let mut sum = 0.0
117	      let mut count = 0
118	      for dy in -radius..<(radius + 1) {
119	        for dx in -radius..<(radius + 1) {
120	          let nx = x + dx
121	          let ny = y + dy
122	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
123	            sum += img.data[ny * w + nx].to_double()
124	            count += 1
125	          }
126	        }
127	      }
128	      let avg = if count > 0 {
129	        sum / count.to_double()
130	      } else {
131	        img.data[y * w + x].to_double()	<-- UNCOVERED
132	      }
133	      let clamped = if avg < 0.0 {
134	        0.0	<-- UNCOVERED
135	      } else if avg > 255.0 {
136	        255.0	<-- UNCOVERED
137	      } else {
138	        avg
139	      }
140	      out.push(clamped.to_int().to_byte())
141	    }
142	  }
143	
144	  @types.Image::{
145	    width: w,
146	    height: h,
147	    channels: img.channels,
148	    data: Bytes::from_array(out),
149	  }
150	}
   	…

201	pub fn gaussian_filter_denoise(
202	  img : @types.Image,
203	  kernel_size : Int,
204	  sigma : Double,
205	) -> @types.Image {
206	  let w = img.width
207	  let h = img.height
208	  let _ = w * h
209	  let out : Array[Byte] = []
210	  let radius = kernel_size / 2
211	
212	  // 生成高斯核
213	  let kernel : Array[Double] = []
214	  let mut kernel_sum = 0.0
215	  for dy in -radius..<(radius + 1) {
216	    for dx in -radius..<(radius + 1) {
217	      let dist = (dx * dx + dy * dy).to_double()
218	      let weight = (0.0 - dist / (2.0 * sigma * sigma)) * 0.1 // 简化的 exp
219	      kernel.push(weight)
220	      kernel_sum += weight
221	    }
222	  }
223	  // 归一化
224	  for i in 0..<kernel.length() {
225	    kernel[i] = kernel[i] / kernel_sum
226	  }
227	
228	  for y in 0..<h {
229	    for x in 0..<w {
230	      let mut sum = 0.0
231	      let mut idx = 0
232	      for dy in -radius..<(radius + 1) {
233	        for dx in -radius..<(radius + 1) {
234	          let nx = x + dx
235	          let ny = y + dy
236	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
237	            sum += img.data[ny * w + nx].to_double() * kernel[idx]
238	          }
239	          idx += 1
240	        }
241	      }
242	      let clamped = if sum < 0.0 {
243	        0.0	<-- UNCOVERED
244	      } else if sum > 255.0 {
245	        255.0	<-- UNCOVERED
246	      } else {
247	        sum
248	      }
249	      out.push(clamped.to_int().to_byte())
250	    }
251	  }
252	
253	  @types.Image::{
254	    width: w,
255	    height: h,
256	    channels: img.channels,
257	    data: Bytes::from_array(out),
258	  }
259	}
   	…

263	pub fn bilateral_filter_denoise(
264	  img : @types.Image,
265	  kernel_size : Int,
266	  sigma_color : Double,
267	  sigma_space : Double,
268	) -> @types.Image {
269	  let w = img.width
270	  let h = img.height
271	  let _ = w * h
272	  let out : Array[Byte] = []
273	  let radius = kernel_size / 2
274	
275	  for y in 0..<h {
276	    for x in 0..<w {
277	      let center = img.data[y * w + x].to_double()
278	      let mut sum = 0.0
279	      let mut weight_sum = 0.0
280	      for dy in -radius..<(radius + 1) {
281	        for dx in -radius..<(radius + 1) {
282	          let nx = x + dx
283	          let ny = y + dy
284	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
285	            let current = img.data[ny * w + nx].to_double()
286	            let color_diff = (current - center).abs()
287	            let space_dist = (dx * dx + dy * dy).to_double().sqrt()
288	            // 简化的高斯权重
289	            let color_weight = 1.0 /
290	              (1.0 + color_diff * color_diff / (sigma_color * sigma_color))
291	            let space_weight = 1.0 /
292	              (1.0 + space_dist * space_dist / (sigma_space * sigma_space))
293	            let weight = color_weight * space_weight
294	            sum += current * weight
295	            weight_sum += weight
296	          }
297	        }
298	      }
299	      let result = if weight_sum > 0.0 { sum / weight_sum } else { center }
300	      let clamped = if result < 0.0 {
301	        0.0	<-- UNCOVERED
302	      } else if result > 255.0 {
303	        255.0	<-- UNCOVERED
304	      } else {
305	        result
306	      }
307	      out.push(clamped.to_int().to_byte())
308	    }
309	  }
310	
311	  @types.Image::{
312	    width: w,
313	    height: h,
314	    channels: img.channels,
315	    data: Bytes::from_array(out),
316	  }
317	}
   	…

348	pub fn histogram_equalization_enhance(img : @types.Image) -> @types.Image {
349	  let w = img.width
350	  let h = img.height
351	  let total = w * h
352	
353	  // 计算直方图
354	  let histogram : Array[Int] = []
355	  for _ in 0..<256 {
356	    histogram.push(0)
357	  }
358	  for i in 0..<total {
359	    histogram[img.data[i].to_int()] += 1
360	  }
361	
362	  // 累积分布函数
363	  let cdf : Array[Double] = []
364	  let mut cdf_min = total.to_double()
365	  let mut sum = 0.0
366	  for i in 0..<256 {
367	    sum += histogram[i].to_double()
368	    cdf.push(sum)
369	    if histogram[i] > 0 && sum < cdf_min {
370	      cdf_min = sum
371	    }
372	  }
373	
374	  // 直方图均衡化
375	  let out : Array[Byte] = []
376	  for i in 0..<total {
377	    let pixel = img.data[i].to_int()
378	    let equalized = if total > 1 {
379	      ((cdf[pixel] - cdf_min) / (total.to_double() - cdf_min) * 255.0).to_int()
380	    } else {
381	      pixel	<-- UNCOVERED
382	    }
383	    let clamped = if equalized < 0 {
384	      0	<-- UNCOVERED
385	    } else if equalized > 255 {
386	      255	<-- UNCOVERED
387	    } else {
388	      equalized
389	    }
390	    out.push(clamped.to_byte())
391	  }
392	
393	  @types.Image::{
394	    width: w,
395	    height: h,
396	    channels: img.channels,
397	    data: Bytes::from_array(out),
398	  }
399	}
   	…

403	pub fn gamma_correction_enhance(
404	  img : @types.Image,
405	  gamma : Double,
406	) -> @types.Image {
407	  let w = img.width
408	  let h = img.height
409	  let total = w * h
410	  let out : Array[Byte] = []
411	
412	  for i in 0..<total {
413	    let pixel = img.data[i].to_double() / 255.0
414	    // 简化的幂运算
415	    let corrected = if gamma > 0.0 {
416	      if gamma == 1.0 {
417	        pixel
418	      } else if gamma > 1.0 {
419	        pixel * pixel // 简化
420	      } else {
421	        pixel.sqrt() // 简化	<-- UNCOVERED
422	      }
423	    } else {
424	      pixel	<-- UNCOVERED
425	    }
426	    let value = corrected * 255.0
427	    let clamped = if value < 0.0 {
428	      0.0	<-- UNCOVERED
429	    } else if value > 255.0 {
430	      255.0	<-- UNCOVERED
431	    } else {
432	      value
433	    }
434	    out.push(clamped.to_int().to_byte())
435	  }
436	
437	  @types.Image::{
438	    width: w,
439	    height: h,
440	    channels: img.channels,
441	    data: Bytes::from_array(out),
442	  }
443	}
   	…

530	pub fn denoise_image(
531	  img : @types.Image,
532	  config : DenoiseConfig,
533	) -> DenoiseResult {
534	  let noise_before = calculate_noise_level(img)
535	
536	  let result = if config.algorithm == DenoiseAlgorithm::MeanFilter {
537	    mean_filter_denoise(img, config.kernel_size)
538	  } else if config.algorithm == DenoiseAlgorithm::MedianFilter {
539	    median_filter_denoise(img, config.kernel_size)
540	  } else if config.algorithm == DenoiseAlgorithm::GaussianFilter {
541	    gaussian_filter_denoise(img, config.kernel_size, config.sigma_space)
542	  } else if config.algorithm == DenoiseAlgorithm::BilateralFilter {
543	    bilateral_filter_denoise(
544	      img,
545	      config.kernel_size,
546	      config.sigma_color,
547	      config.sigma_space,
548	    )
549	  } else {
550	    bilateral_filter_denoise(	<-- UNCOVERED
551	      img,
552	      config.kernel_size,
553	      config.sigma_color,
554	      config.sigma_space,
555	    )
556	  }
557	
558	  let noise_after = calculate_noise_level(result)
559	
560	  // 简化的 PSNR 计算
561	  let w = img.width
562	  let h = img.height
563	  let total = w * h
564	  let mut mse = 0.0
565	  for i in 0..<total {
566	    let diff = img.data[i].to_double() - result.data[i].to_double()
567	    mse += diff * diff
568	  }
569	  mse /= total.to_double()
570	  let psnr = if mse > 0.0 { 10.0 * (65025.0 / mse) * 0.1 } else { 100.0 }
571	
572	  DenoiseResult::{
573	    image: result,
574	    algorithm: config.algorithm,
575	    noise_level_before: noise_before,
576	    noise_level_after: noise_after,
577	    psnr,
578	    ssim: 0.9,
579	    processing_time_ms: 0.0,
580	    strength: config.strength,
581	  }
582	}
   	…

586	pub fn enhance_image_advanced(
587	  img : @types.Image,
588	  algorithm : EnhancementAlgorithm,
589	) -> EnhancementResult {
590	  let brightness_before = calculate_brightness(img)
591	  let contrast_before = calculate_contrast(img)
592	  let sharpness_before = calculate_sharpness(img)
593	
594	  let result = if algorithm == EnhancementAlgorithm::HistogramEqualization {
595	    histogram_equalization_enhance(img)
596	  } else if algorithm == EnhancementAlgorithm::GammaCorrection {
597	    gamma_correction_enhance(img, 1.5)
598	  } else if algorithm == EnhancementAlgorithm::UnsharpMask {
599	    unsharp_mask_enhance(img, 0.5, 2)
600	  } else {
601	    histogram_equalization_enhance(img)	<-- UNCOVERED
602	  }
603	
604	  let brightness_after = calculate_brightness(result)
605	  let contrast_after = calculate_contrast(result)
606	  let sharpness_after = calculate_sharpness(result)
607	
608	  EnhancementResult::{
609	    image: result,
610	    algorithm,
611	    brightness_before,
612	    brightness_after,
613	    contrast_before,
614	    contrast_after,
615	    sharpness_before,
616	    sharpness_after,
617	    processing_time_ms: 0.0,
618	  }
619	}
   	…

623	pub fn denoise_algorithm_to_string(algorithm : DenoiseAlgorithm) -> String {
624	  if algorithm == DenoiseAlgorithm::MeanFilter {
625	    "均值滤波"
626	  } else if algorithm == DenoiseAlgorithm::MedianFilter {
627	    "中值滤波"
628	  } else if algorithm == DenoiseAlgorithm::GaussianFilter {
629	    "高斯滤波"
630	  } else if algorithm == DenoiseAlgorithm::BilateralFilter {
631	    "双边滤波"
632	  } else if algorithm == DenoiseAlgorithm::NonLocalMeans {
633	    "非局部均值"	<-- UNCOVERED
634	  } else if algorithm == DenoiseAlgorithm::WienerFilter {
635	    "维纳滤波"	<-- UNCOVERED
636	  } else if algorithm == DenoiseAlgorithm::TotalVariation {
637	    "全变分"	<-- UNCOVERED
638	  } else if algorithm == DenoiseAlgorithm::WaveletDenoise {
639	    "小波降噪"	<-- UNCOVERED
640	  } else if algorithm == DenoiseAlgorithm::BM3D {
641	    "BM3D"
642	  } else if algorithm == DenoiseAlgorithm::DeepLearningDenoise {	<-- UNCOVERED
643	    "深度学习降噪"
644	  } else {
645	    "未知"	<-- UNCOVERED
646	  }
647	}
   	…

18 uncovered line(s) in src\process\detection\object_detection.mbt:

17	pub fn bbox_new(
18	  x : Int,
19	  y : Int,
20	  width : Int,
21	  height : Int,
22	  score? : Double = 0.0,
23	  class_id? : Int = 0,
24	) -> BoundingBox raise @types.LoadError {
25	  // 输入验证
26	  if width <= 0 {
27	    raise @types.LoadError::DecodeFailed("bbox_new: width必须大于0")	<-- UNCOVERED
28	  }
29	  if height <= 0 {
30	    raise @types.LoadError::DecodeFailed("bbox_new: height必须大于0")	<-- UNCOVERED
31	  }
32	  if score < 0.0 || score > 1.0 {
33	    raise @types.LoadError::DecodeFailed("bbox_new: score必须在0-1之间")	<-- UNCOVERED
34	  }
35	  if class_id < 0 {
36	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
37	      "bbox_new: class_id必须大于等于0",
38	    )
39	  }
40	
41	  BoundingBox::{ x, y, width, height, score, class_id }
42	}
  	…

46	pub fn bbox_iou(
47	  a : BoundingBox,
48	  b : BoundingBox,
49	) -> Double raise @types.LoadError {
50	  // 输入验证
51	  if a.width <= 0 || a.height <= 0 {
52	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
53	      "bbox_iou: 检测框a的尺寸必须大于0",
54	    )
55	  }
56	  if b.width <= 0 || b.height <= 0 {
57	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
58	      "bbox_iou: 检测框b的尺寸必须大于0",
59	    )
60	  }
61	
62	  let x1 = a.x.max(b.x)
63	  let y1 = a.y.max(b.y)
64	  let x2 = (a.x + a.width).min(b.x + b.width)
65	  let y2 = (a.y + a.height).min(b.y + b.height)
66	  let intersection_w = (x2 - x1).max(0)
67	  let intersection_h = (y2 - y1).max(0)
68	  let intersection = intersection_w.to_double() * intersection_h.to_double()
69	  let area_a = a.width.to_double() * a.height.to_double()
70	  let area_b = b.width.to_double() * b.height.to_double()
71	  let union = area_a + area_b - intersection
72	  if union > 0.0 {
73	    intersection / union
74	  } else {
75	    0.0	<-- UNCOVERED
76	  }
77	}
  	…

 81	pub fn nms(
 82	  boxes : Array[BoundingBox],
 83	  iou_threshold? : Double = 0.5,
 84	) -> Array[BoundingBox] raise @types.LoadError {
 85	  // 输入验证
 86	  if boxes.length() == 0 {
 87	    raise @types.LoadError::DecodeFailed("nms: 检测框列表不能为空")	<-- UNCOVERED
 88	  }
 89	  if iou_threshold < 0.0 || iou_threshold > 1.0 {
 90	    raise @types.LoadError::DecodeFailed("nms: iou_threshold必须在0-1之间")	<-- UNCOVERED
 91	  }
 92	
 93	  let threshold = iou_threshold
 94	  // 按置信度降序排序（选择排序）
 95	  let sorted = boxes
 96	  let n = sorted.length()
 97	  for i in 0..<n {
 98	    let mut max_idx = i
 99	    for j in (i + 1)..<n {
100	      if sorted[j].score > sorted[max_idx].score {
101	        max_idx = j	<-- UNCOVERED
102	      }
103	    }
104	    if max_idx != i {
105	      let tmp = sorted[i]	<-- UNCOVERED
106	      sorted[i] = sorted[max_idx]
107	      sorted[max_idx] = tmp
108	    }
109	  }
110	  // NMS
111	  let suppressed : Array[Bool] = Array::make(n, false)
112	  let result : Array[BoundingBox] = []
113	  for i in 0..<n {
114	    if suppressed[i] {
115	      continue
116	    }
117	    result.push(sorted[i])
118	    for j in (i + 1)..<n {
119	      if suppressed[j] {
120	        continue	<-- UNCOVERED
121	      }
122	      if bbox_iou(sorted[i], sorted[j]) > threshold {
123	        suppressed[j] = true
124	      }
125	    }
126	  }
127	  result
128	}
   	…

132	pub fn draw_detections(
133	  img : @types.Image,
134	  boxes : Array[BoundingBox],
135	) -> @types.Image raise @types.LoadError {
136	  // 输入验证
137	  if img.width <= 0 || img.height <= 0 {
138	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
139	      "draw_detections: 图像尺寸必须大于0",
140	    )
141	  }
142	  if img.channels < 1 || img.channels > 4 {
143	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
144	      "draw_detections: 通道数必须在1-4之间",
145	    )
146	  }
147	  let expected_size = img.width * img.height * img.channels
148	  if img.data.length() != expected_size {
149	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
150	      "draw_detections: 数据大小不匹配，期望 " +
151	      expected_size.to_string() +	<-- UNCOVERED
152	      "，实际 " +
153	      img.data.length().to_string(),	<-- UNCOVERED
154	    )
155	  }
156	
157	  let w = img.width
158	  let h = img.height
159	  let channels = img.channels
160	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
161	  // 复制原图
162	  for i in 0..<(w * h * channels) {
163	    out_data[i] = img.data[i]
164	  }
165	  // 绘制检测框（红色，2像素宽）
166	  for bbox in boxes {
167	    let color = if channels >= 3 {
168	      [b'\xFF', b'\x00', b'\x00']
169	    } else {
170	      [b'\xFF']	<-- UNCOVERED
171	    }
172	    // 上下边
173	    for x in bbox.x..<(bbox.x + bbox.width) {
174	      for dy in 0..<2 {
175	        let y_top = bbox.y + dy
176	        let y_bottom = bbox.y + bbox.height - 1 - dy
177	        if y_top >= 0 && y_top < h && x >= 0 && x < w {
178	          let idx = (y_top * w + x) * channels
179	          for c in 0..<channels.min(color.length()) {
180	            out_data[idx + c] = color[c]
181	          }
182	        }
183	        if y_bottom >= 0 && y_bottom < h && x >= 0 && x < w {
184	          let idx = (y_bottom * w + x) * channels
185	          for c in 0..<channels.min(color.length()) {
186	            out_data[idx + c] = color[c]
187	          }
188	        }
189	      }
190	    }
191	    // 左右边
192	    for y in bbox.y..<(bbox.y + bbox.height) {
193	      for dx in 0..<2 {
194	        let x_left = bbox.x + dx
195	        let x_right = bbox.x + bbox.width - 1 - dx
196	        if x_left >= 0 && x_left < w && y >= 0 && y < h {
197	          let idx = (y * w + x_left) * channels
198	          for c in 0..<channels.min(color.length()) {
199	            out_data[idx + c] = color[c]
200	          }
201	        }
202	        if x_right >= 0 && x_right < w && y >= 0 && y < h {
203	          let idx = (y * w + x_right) * channels
204	          for c in 0..<channels.min(color.length()) {
205	            out_data[idx + c] = color[c]
206	          }
207	        }
208	      }
209	    }
210	  }
211	  @types.Image::{
212	    width: w,
213	    height: h,
214	    channels,
215	    data: Bytes::from_array(out_data),
216	  }
217	}
   	…

68 uncovered line(s) in src\process\detection\yolo_detection.mbt:

138	pub fn calculate_iou(
139	  box1 : YoloBoundingBox,
140	  box2 : YoloBoundingBox,
141	) -> Double raise @types.LoadError {
142	  // 输入验证
143	  if box1.width <= 0.0 || box1.height <= 0.0 {
144	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
145	      "calculate_iou: 检测框1的尺寸必须大于0",
146	    )
147	  }
148	  if box2.width <= 0.0 || box2.height <= 0.0 {
149	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
150	      "calculate_iou: 检测框2的尺寸必须大于0",
151	    )
152	  }
153	
154	  let x1_min = box1.x - box1.width / 2.0
155	  let x1_max = box1.x + box1.width / 2.0
156	  let y1_min = box1.y - box1.height / 2.0
157	  let y1_max = box1.y + box1.height / 2.0
158	
159	  let x2_min = box2.x - box2.width / 2.0
160	  let x2_max = box2.x + box2.width / 2.0
161	  let y2_min = box2.y - box2.height / 2.0
162	  let y2_max = box2.y + box2.height / 2.0
163	
164	  let inter_x_min = if x1_min > x2_min { x1_min } else { x2_min }
165	  let inter_x_max = if x1_max < x2_max { x1_max } else { x2_max }
166	  let inter_y_min = if y1_min > y2_min { y1_min } else { y2_min }
167	  let inter_y_max = if y1_max < y2_max { y1_max } else { y2_max }
168	
169	  let inter_width = if inter_x_max > inter_x_min {
170	    inter_x_max - inter_x_min
171	  } else {
172	    0.0
173	  }
174	  let inter_height = if inter_y_max > inter_y_min {
175	    inter_y_max - inter_y_min
176	  } else {
177	    0.0
178	  }
179	  let inter_area = inter_width * inter_height
180	
181	  let area1 = box1.width * box1.height
182	  let area2 = box2.width * box2.height
183	  let union_area = area1 + area2 - inter_area
184	
185	  if union_area > 0.0 {
186	    inter_area / union_area
187	  } else {
188	    0.0	<-- UNCOVERED
189	  }
190	}
   	…

194	pub fn non_max_suppression(
195	  boxes : Array[YoloBoundingBox],
196	  iou_threshold : Double,
197	  max_detections : Int,
198	) -> Array[YoloBoundingBox] raise @types.LoadError {
199	  // 输入验证
200	  if boxes.length() == 0 {
201	    raise @types.LoadError::DecodeFailed(
202	      "non_max_suppression: 检测框列表不能为空",
203	    )
204	  }
205	  if iou_threshold < 0.0 || iou_threshold > 1.0 {
206	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
207	      "non_max_suppression: iou_threshold必须在0-1之间",
208	    )
209	  }
210	  if max_detections <= 0 {
211	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
212	      "non_max_suppression: max_detections必须大于0",
213	    )
214	  }
215	
216	  if boxes.length() == 0 {
217	    return []	<-- UNCOVERED
218	  }
219	
220	  // 按置信度排序（冒泡排序，降序）
221	  let sorted = boxes
222	  for i in 0..<sorted.length() {
223	    for j in 0..<(sorted.length() - i - 1) {
224	      if sorted[j].confidence < sorted[j + 1].confidence {
225	        let temp = sorted[j]	<-- UNCOVERED
226	        sorted[j] = sorted[j + 1]
227	        sorted[j + 1] = temp
228	      }
229	    }
230	  }
231	
232	  // NMS
233	  let result : Array[YoloBoundingBox] = []
234	  let suppressed : Array[Bool] = []
235	  for _ in 0..<sorted.length() {
236	    suppressed.push(false)
237	  }
238	
239	  for i in 0..<sorted.length() {
240	    if suppressed[i] {
241	      continue
242	    }
243	    if result.length() >= max_detections {
244	      break	<-- UNCOVERED
245	    }
246	    result.push(sorted[i])
247	
248	    for j in (i + 1)..<sorted.length() {
249	      if suppressed[j] {
250	        continue	<-- UNCOVERED
251	      }
252	      let iou = calculate_iou(sorted[i], sorted[j])
253	      if iou > iou_threshold && sorted[i].class_id == sorted[j].class_id {
254	        suppressed[j] = true
255	      }
256	    }
257	  }
258	
259	  result
260	}
   	…

264	pub fn refine_bounding_box(
265	  box : YoloBoundingBox,
266	  dx : Double,
267	  dy : Double,
268	  dw : Double,
269	  dh : Double,
270	) -> YoloBoundingBox raise @types.LoadError {
271	  // 输入验证
272	  if box.width <= 0.0 || box.height <= 0.0 {
273	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
274	      "refine_bounding_box: 检测框尺寸必须大于0",
275	    )
276	  }
277	
278	  let new_x = box.x + dx * box.width
279	  let new_y = box.y + dy * box.height
280	  let new_width = box.width * (dw * 1.0)
281	  let new_height = box.height * (dh * 1.0)
282	
283	  YoloBoundingBox::{
284	    x: new_x,
285	    y: new_y,
286	    width: new_width,
287	    height: new_height,
288	    confidence: box.confidence,
289	    class_id: box.class_id,
290	    class_name: box.class_name,
291	  }
292	}
   	…

296	pub fn detect_objects_simple(
297	  img : @types.Image,
298	  config : YoloConfig,
299	) -> YoloDetectionResult raise @types.LoadError {
300	  // 输入验证
301	  if img.width <= 0 || img.height <= 0 {
302	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
303	      "detect_objects_simple: 图像尺寸必须大于0",
304	    )
305	  }
306	  if img.channels < 1 || img.channels > 4 {
307	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
308	      "detect_objects_simple: 通道数必须在1-4之间",
309	    )
310	  }
311	  let expected_size = img.width * img.height * img.channels
312	  if img.data.length() != expected_size {
313	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
314	      "detect_objects_simple: 数据大小不匹配，期望 " +
315	      expected_size.to_string() +	<-- UNCOVERED
316	      "，实际 " +
317	      img.data.length().to_string(),	<-- UNCOVERED
318	    )
319	  }
320	  if config.input_size <= 0 {
321	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
322	      "detect_objects_simple: input_size必须大于0",
323	    )
324	  }
325	  if config.num_classes <= 0 {
326	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
327	      "detect_objects_simple: num_classes必须大于0",
328	    )
329	  }
330	  if config.confidence_threshold < 0.0 || config.confidence_threshold > 1.0 {
331	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
332	      "detect_objects_simple: confidence_threshold必须在0-1之间",
333	    )
334	  }
335	  if config.iou_threshold < 0.0 || config.iou_threshold > 1.0 {
336	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
337	      "detect_objects_simple: iou_threshold必须在0-1之间",
338	    )
339	  }
340	  if config.max_detections <= 0 {
341	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
342	      "detect_objects_simple: max_detections必须大于0",
343	    )
344	  }
345	
346	  let w = img.width
347	  let h = img.height
348	  let boxes : Array[YoloBoundingBox] = []
349	
350	  // 简化版：滑动窗口检测
351	  let window_sizes = [64, 128, 256]
352	  let stride = 32
353	
354	  for window_size in window_sizes {
355	    let mut y = 0
356	    while y + window_size <= h {
357	      let mut x = 0
358	      while x + window_size <= w {
359	        // 计算窗口内的平均颜色方差作为检测分数
360	        let mut sum = 0.0
361	        let mut sum_sq = 0.0
362	        let mut count = 0
363	        for py in y..<(y + window_size) {
364	          for px in x..<(x + window_size) {
365	            let pixel = img.data[py * w + px].to_double()
366	            sum += pixel
367	            sum_sq += pixel * pixel
368	            count += 1
369	          }
370	        }
371	        if count > 0 {
372	          let mean = sum / count.to_double()
373	          let variance = sum_sq / count.to_double() - mean * mean
374	          let confidence = if variance > 100.0 {
375	            variance / 1000.0	<-- UNCOVERED
376	          } else {
377	            0.0
378	          }
379	          if confidence > config.confidence_threshold {
380	            boxes.push(YoloBoundingBox::{	<-- UNCOVERED
381	              x: (x + window_size / 2).to_double(),
382	              y: (y + window_size / 2).to_double(),
383	              width: window_size.to_double(),
384	              height: window_size.to_double(),
385	              confidence,
386	              class_id: 0,
387	              class_name: "object",
388	            })
389	          }
390	        }
391	        x += stride
392	      }
393	      y += stride
394	    }
395	  }
396	
397	  // NMS
398	  let final_boxes = non_max_suppression(
399	    boxes,
400	    config.iou_threshold,
401	    config.max_detections,
402	  )
403	
404	  YoloDetectionResult::{
405	    boxes: final_boxes,
406	    num_detections: final_boxes.length(),	<-- UNCOVERED
407	    inference_time_ms: 0.0,
408	    input_width: w,
409	    input_height: h,
410	  }
411	}
   	…

415	pub fn initialize_object_tracking(
416	  detection : YoloDetectionResult,
417	) -> Array[YoloTrackedObject] raise @types.LoadError {
418	  // 输入验证
419	  if detection.boxes.length() == 0 {	<-- UNCOVERED
420	    raise @types.LoadError::DecodeFailed(
421	      "initialize_object_tracking: 检测框列表不能为空",
422	    )
423	  }
424	
425	  let objects : Array[YoloTrackedObject] = []
426	  let colors = [
427	    (255, 0, 0),
428	    (0, 255, 0),
429	    (0, 0, 255),
430	    (255, 255, 0),
431	    (255, 0, 255),
432	    (0, 255, 255),
433	  ]
434	
435	  for i in 0..<detection.boxes.length() {	<-- UNCOVERED
436	    let box = detection.boxes[i]
437	    let trajectory : Array[(Double, Double)] = [(box.x, box.y)]
438	    let color_idx = i % colors.length()	<-- UNCOVERED
439	    objects.push(YoloTrackedObject::{
440	      id: i,
441	      class_id: box.class_id,
442	      class_name: box.class_name,
443	      bbox: box,
444	      age: 0,
445	      visible_count: 1,
446	      invisible_count: 0,
447	      velocity_x: 0.0,
448	      velocity_y: 0.0,
449	      trajectory,
450	      color: colors[color_idx],
451	    })
452	  }
453	
454	  objects
455	}
   	…

459	pub fn update_object_tracking(
460	  tracked_objects : Array[YoloTrackedObject],
461	  detection : YoloDetectionResult,
462	  max_age : Int,
463	) -> Array[YoloTrackedObject] raise @types.LoadError {
464	  // 输入验证
465	  if tracked_objects.length() == 0 {	<-- UNCOVERED
466	    raise @types.LoadError::DecodeFailed(
467	      "update_object_tracking: 跟踪目标列表不能为空",
468	    )
469	  }
470	  if max_age <= 0 {
471	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
472	      "update_object_tracking: max_age必须大于0",
473	    )
474	  }
475	
476	  let updated : Array[YoloTrackedObject] = []
477	  let matched_detections : Array[Bool] = []
478	  for _ in 0..<detection.boxes.length() {	<-- UNCOVERED
479	    matched_detections.push(false)
480	  }
481	
482	  // 匹配已有目标
483	  for obj in tracked_objects {
484	    let mut best_iou = 0.0	<-- UNCOVERED
485	    let mut best_idx = -1
486	
487	    for i in 0..<detection.boxes.length() {	<-- UNCOVERED
488	      if matched_detections[i] {
489	        continue
490	      }
491	      let iou = calculate_iou(obj.bbox, detection.boxes[i])	<-- UNCOVERED
492	      if iou > best_iou && iou > 0.3 {
493	        best_iou = iou	<-- UNCOVERED
494	        best_idx = i
495	      }
496	    }
497	
498	    if best_idx >= 0 {
499	      // 匹配成功，更新目标
500	      matched_detections[best_idx] = true	<-- UNCOVERED
501	      let new_box = detection.boxes[best_idx]
502	      let velocity_x = new_box.x - obj.bbox.x
503	      let velocity_y = new_box.y - obj.bbox.y
504	      let mut new_trajectory = obj.trajectory
505	      new_trajectory.push((new_box.x, new_box.y))	<-- UNCOVERED
506	      if new_trajectory.length() > 50 {
507	        let trimmed : Array[(Double, Double)] = []
508	        for i in (new_trajectory.length() - 50)..<new_trajectory.length() {
509	          trimmed.push(new_trajectory[i])
510	        }
511	        new_trajectory = trimmed
512	      }
513	
514	      updated.push(YoloTrackedObject::{	<-- UNCOVERED
515	        id: obj.id,
516	        class_id: new_box.class_id,
517	        class_name: new_box.class_name,
518	        bbox: new_box,
519	        age: obj.age + 1,
520	        visible_count: obj.visible_count + 1,
521	        invisible_count: 0,
522	        velocity_x,
523	        velocity_y,
524	        trajectory: new_trajectory,
525	        color: obj.color,
526	      })
527	      // 匹配失败，目标不可见
528	    } else if obj.invisible_count < max_age {	<-- UNCOVERED
529	      updated.push(YoloTrackedObject::{
530	        id: obj.id,
531	        class_id: obj.class_id,
532	        class_name: obj.class_name,
533	        bbox: obj.bbox,
534	        age: obj.age + 1,
535	        visible_count: obj.visible_count,
536	        invisible_count: obj.invisible_count + 1,
537	        velocity_x: obj.velocity_x,
538	        velocity_y: obj.velocity_y,
539	        trajectory: obj.trajectory,
540	        color: obj.color,
541	      })
542	    }
543	  }
544	
545	  // 添加新目标
546	  let mut next_id = tracked_objects.length()	<-- UNCOVERED
547	  for i in 0..<detection.boxes.length() {
548	    if !matched_detections[i] {
549	      let box = detection.boxes[i]
550	      let trajectory : Array[(Double, Double)] = [(box.x, box.y)]
551	      let colors = [
552	        (255, 0, 0),
553	        (0, 255, 0),
554	        (0, 0, 255),
555	        (255, 255, 0),
556	        (255, 0, 255),
557	        (0, 255, 255),
558	      ]
559	      updated.push(YoloTrackedObject::{	<-- UNCOVERED
560	        id: next_id,
561	        class_id: box.class_id,
562	        class_name: box.class_name,
563	        bbox: box,
564	        age: 0,
565	        visible_count: 1,
566	        invisible_count: 0,
567	        velocity_x: 0.0,
568	        velocity_y: 0.0,
569	        trajectory,
570	        color: colors[next_id % colors.length()],	<-- UNCOVERED
571	      })
572	      next_id += 1
573	    }
574	  }
575	
576	  updated
577	}
   	…

581	pub fn calculate_detection_metrics(
582	  predictions : Array[YoloBoundingBox],
583	  ground_truth : Array[YoloBoundingBox],
584	  iou_threshold : Double,
585	) -> YoloDetectionMetrics raise @types.LoadError {
586	  // 输入验证
587	  if predictions.length() == 0 {
588	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
589	      "calculate_detection_metrics: 预测框列表不能为空",
590	    )
591	  }
592	  if ground_truth.length() == 0 {
593	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
594	      "calculate_detection_metrics: 真实框列表不能为空",
595	    )
596	  }
597	  if iou_threshold < 0.0 || iou_threshold > 1.0 {
598	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
599	      "calculate_detection_metrics: iou_threshold必须在0-1之间",
600	    )
601	  }
602	
603	  let mut tp = 0
604	  let mut fp = 0
605	  let matched_gt : Array[Bool] = []
606	  for _ in 0..<ground_truth.length() {
607	    matched_gt.push(false)
608	  }
609	
610	  // 按置信度排序
611	  let sorted_pred = predictions
612	  for i in 0..<sorted_pred.length() {
613	    for j in 0..<(sorted_pred.length() - i - 1) {
614	      if sorted_pred[j].confidence < sorted_pred[j + 1].confidence {	<-- UNCOVERED
615	        let temp = sorted_pred[j]
616	        sorted_pred[j] = sorted_pred[j + 1]
617	        sorted_pred[j + 1] = temp
618	      }
619	    }
620	  }
621	
622	  for pred in sorted_pred {
623	    let mut best_iou = 0.0
624	    let mut best_gt_idx = -1
625	
626	    for i in 0..<ground_truth.length() {
627	      if matched_gt[i] {
628	        continue	<-- UNCOVERED
629	      }
630	      let iou = calculate_iou(pred, ground_truth[i])
631	      if iou > best_iou && pred.class_id == ground_truth[i].class_id {
632	        best_iou = iou
633	        best_gt_idx = i
634	      }
635	    }
636	
637	    if best_gt_idx >= 0 && best_iou >= iou_threshold {
638	      tp += 1
639	      matched_gt[best_gt_idx] = true
640	    } else {
641	      fp += 1	<-- UNCOVERED
642	    }
643	  }
644	
645	  let false_neg = ground_truth.length() - tp
646	
647	  let precision = if tp + fp > 0 {
648	    tp.to_double() / (tp + fp).to_double()
649	  } else {
650	    0.0	<-- UNCOVERED
651	  }
652	  let recall = if tp + false_neg > 0 {
653	    tp.to_double() / (tp + false_neg).to_double()
654	  } else {
655	    0.0	<-- UNCOVERED
656	  }
657	  let f1 = if precision + recall > 0 {
658	    2.0 * precision * recall / (precision + recall)
659	  } else {
660	    0.0	<-- UNCOVERED
661	  }
662	
663	  YoloDetectionMetrics::{
664	    precision,
665	    recall,
666	    f1_score: f1,
667	    mAP: f1,
668	    mAP50: f1,
669	    mAP50_95: f1 * 0.8,
670	    true_positives: tp,
671	    false_positives: fp,
672	    false_negatives: false_neg,
673	  }
674	}
   	…

678	pub fn detection_report_to_string(
679	  result : YoloDetectionResult,
680	  metrics : YoloDetectionMetrics,
681	) -> String {
682	  let mut report = "## 目标检测报告\n\n"	<-- UNCOVERED
683	  report = report + "### 检测结果\n\n"
684	  report = report + "| 指标 | 值 |\n|------|------|\n"
685	  report = report +
686	    "| 检测目标数 | " +
687	    result.num_detections.to_string() +	<-- UNCOVERED
688	    " |\n"
689	  report = report +
690	    "| 输入尺寸 | " +
691	    result.input_width.to_string() +	<-- UNCOVERED
692	    "x" +
693	    result.input_height.to_string() +	<-- UNCOVERED
694	    " |\n"
695	  report = report +
696	    "| 推理耗时 | " +
697	    result.inference_time_ms.to_string() +	<-- UNCOVERED
698	    "ms |\n\n"
699	
700	  if result.boxes.length() > 0 {	<-- UNCOVERED
701	    report = report + "### 检测目标列表\n\n"
702	    report = report +
703	      "| ID | 类别 | 置信度 | 位置(X,Y) | 尺寸(WxH) |\n"
704	    report = report + "|----|------|--------|-----------|------------|\n"
705	    for i in 0..<result.boxes.length() {	<-- UNCOVERED
706	      let box = result.boxes[i]
707	      report = report +
708	        "| " +
709	        i.to_string() +	<-- UNCOVERED
710	        " | " +
711	        box.class_name +
712	        " | " +
713	        (box.confidence * 100.0).to_string() +	<-- UNCOVERED
714	        "% | " +
715	        box.x.to_string() +	<-- UNCOVERED
716	        "," +
717	        box.y.to_string() +	<-- UNCOVERED
718	        " | " +
719	        box.width.to_string() +	<-- UNCOVERED
720	        "x" +
721	        box.height.to_string() +	<-- UNCOVERED
722	        " |\n"
723	    }
724	  }
725	
726	  report = report + "\n### 评估指标\n\n"
727	  report = report + "| 指标 | 值 |\n|------|------|\n"
728	  report = report +
729	    "| Precision | " +
730	    (metrics.precision * 100.0).to_string() +	<-- UNCOVERED
731	    "% |\n"
732	  report = report +
733	    "| Recall | " +
734	    (metrics.recall * 100.0).to_string() +	<-- UNCOVERED
735	    "% |\n"
736	  report = report +
737	    "| F1 Score | " +
738	    (metrics.f1_score * 100.0).to_string() +	<-- UNCOVERED
739	    "% |\n"
740	  report = report +
741	    "| mAP@0.5 | " +
742	    (metrics.mAP50 * 100.0).to_string() +	<-- UNCOVERED
743	    "% |\n"
744	  report = report +
745	    "| mAP@0.5:0.95 | " +
746	    (metrics.mAP50_95 * 100.0).to_string() +	<-- UNCOVERED
747	    "% |\n"
748	  report = report + "| TP | " + metrics.true_positives.to_string() + " |\n"	<-- UNCOVERED
749	  report = report + "| FP | " + metrics.false_positives.to_string() + " |\n"
750	  report = report + "| FN | " + metrics.false_negatives.to_string() + " |\n"
751	
752	  report
753	}
   	…

7 uncovered line(s) in src\process\dl_advanced\dl_advanced.mbt:

284	pub fn transformer_forward(
285	  input_embeddings : Array[Array[Double]],
286	  config : TransformerConfig,
287	) -> TransformerLayerOutput {
288	  let seq_length = input_embeddings.length()
289	  let hidden_dim = config.hidden_dim
290	
291	  // 简化的前向传播
292	  let hidden_states : Array[Array[Double]] = []
293	  for _ in 0..<seq_length {
294	    let state : Array[Double] = []
295	    for _ in 0..<hidden_dim {
296	      state.push(0.1)
297	    }
298	    hidden_states.push(state)
299	  }
300	
301	  let attention = compute_self_attention(
302	    input_embeddings,
303	    input_embeddings,
304	    input_embeddings,
305	    seq_length,
306	    if hidden_dim > 64 {
307	      64
308	    } else {
309	      hidden_dim	<-- UNCOVERED
310	    },
311	  )
312	
313	  TransformerLayerOutput::{
314	    hidden_states,
315	    attention_weights: attention,
316	    layer_norm_output: hidden_states,
317	    seq_length,
318	    hidden_dim,
319	  }
320	}
   	…

324	pub fn simulate_finetuning(
325	  config : FineTuningConfig,
326	  initial_accuracy : Double,
327	) -> TransferLearningResult {
328	  let mut accuracy = initial_accuracy
329	  let mut convergence_epoch = config.num_epochs
330	
331	  for epoch in 0..<config.num_epochs {
332	    let progress = (epoch + 1).to_double() / config.num_epochs.to_double()
333	    accuracy = initial_accuracy + (1.0 - initial_accuracy) * progress * 0.8
334	
335	    if accuracy > 0.95 && convergence_epoch == config.num_epochs {
336	      convergence_epoch = epoch + 1
337	    }
338	  }
339	
340	  let transfer_gain = accuracy - initial_accuracy
341	  let num_trainable_params = if config.use_lora {
342	    config.lora_r * config.lora_r.to_double().to_int() * 2	<-- UNCOVERED
343	  } else {
344	    110000000 / (config.freeze_layers + 1)
345	  }
346	
347	  TransferLearningResult::{
348	    source_model: config.model_name,
349	    target_task: config.task_type,
350	    source_accuracy: initial_accuracy,
351	    target_accuracy: accuracy,
352	    transfer_gain,
353	    num_frozen_layers: config.freeze_layers,
354	    num_trainable_params,
355	    training_time_s: config.num_epochs.to_double() * 100.0,
356	    convergence_epoch,
357	  }
358	}
   	…

362	pub fn dl_advanced_report_to_string(
363	  task_type : DLAdvancedTaskType,
364	  config : TransformerConfig,
365	  result_description : String,
366	) -> String {
367	  let mut report = "## 深度学习高级报告\n\n"
368	
369	  report = report + "### 任务信息\n\n"
370	  report = report + "| 指标 | 值 |\n|------|------|\n"
371	  report = report +
372	    "| 任务类型 | " +
373	    (match task_type {
374	      DLAdvancedTaskType::Transformer => "Transformer"
375	      DLAdvancedTaskType::AttentionMechanism => "注意力机制"	<-- UNCOVERED
376	      DLAdvancedTaskType::PretrainedModel => "预训练模型"
377	      DLAdvancedTaskType::FineTuning => "微调"
378	      DLAdvancedTaskType::TransferLearning => "迁移学习"	<-- UNCOVERED
379	      DLAdvancedTaskType::SelfSupervisedLearning => "自监督学习"
380	      DLAdvancedTaskType::ContrastiveLearning => "对比学习"
381	      _ => "未知"
382	    }) +
383	    " |\n"
384	  report = report +
385	    "| 架构 | " +
386	    (match config.architecture {
387	      TransformerArchitecture::EncoderOnly => "仅编码器"
388	      TransformerArchitecture::DecoderOnly => "仅解码器"	<-- UNCOVERED
389	      TransformerArchitecture::EncoderDecoder => "编码器-解码器"
390	      TransformerArchitecture::VisionTransformer => "视觉Transformer"
391	      TransformerArchitecture::SwinTransformer => "Swin Transformer"
392	      _ => "未知"
393	    }) +
394	    " |\n"
395	  report = report +
396	    "| 注意力类型 | " +
397	    (match config.attention_type {
398	      AttentionType::SelfAttention => "自注意力"	<-- UNCOVERED
399	      AttentionType::MultiHeadAttention => "多头注意力"
400	      AttentionType::CrossAttention => "交叉注意力"	<-- UNCOVERED
401	      AttentionType::SparseAttention => "稀疏注意力"
402	      AttentionType::LinearAttention => "线性注意力"
403	      AttentionType::FlashAttention => "FlashAttention"
404	      _ => "未知"
405	    }) +
406	    " |\n"
407	  report = report + "| 层数 | " + config.num_layers.to_string() + " |\n"
408	  report = report + "| 隐藏维度 | " + config.hidden_dim.to_string() + " |\n"
409	  report = report +
410	    "| 注意力头数 | " +
411	    config.num_heads.to_string() +
412	    " |\n"
413	  report = report +
414	    "| 中间维度 | " +
415	    config.intermediate_dim.to_string() +
416	    " |\n"
417	  report = report + "| Dropout率 | " + config.dropout_rate.to_string() + " |\n"
418	  report = report +
419	    "| 最大位置嵌入 | " +
420	    config.max_position_embeddings.to_string() +
421	    " |\n"
422	
423	  report = report + "\n### 结果\n\n"
424	  report = report + result_description + "\n"
425	
426	  report
427	}
   	…

7 uncovered line(s) in src\process\edge\canny.mbt:

  8	pub fn canny_edge(
  9	  img : @types.Image,
 10	  low_threshold? : Int = 50,
 11	  high_threshold? : Int = 100,
 12	) -> @types.Image raise @types.LoadError {
 13	  // 输入验证
 14	  if img.width <= 0 || img.height <= 0 {
 15	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 16	      "canny_edge: 图像尺寸必须大于0",
 17	    )
 18	  }
 19	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 20	    raise @types.LoadError::DecodeFailed("canny_edge: 仅支持 channels=1/3/4")
 21	  }
 22	  let expected_size = img.width * img.height * img.channels
 23	  if img.data.length() != expected_size {
 24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 25	      "canny_edge: 数据大小不匹配，期望 " +
 26	      expected_size.to_string() +	<-- UNCOVERED
 27	      "，实际 " +
 28	      img.data.length().to_string(),	<-- UNCOVERED
 29	    )
 30	  }
 31	  if low_threshold < 0 || low_threshold > 255 {
 32	    raise @types.LoadError::DecodeFailed(
 33	      "low_threshold 必须在 0..255 之间",
 34	    )
 35	  }
 36	  if high_threshold < 0 || high_threshold > 255 {
 37	    raise @types.LoadError::DecodeFailed(
 38	      "high_threshold 必须在 0..255 之间",
 39	    )
 40	  }
 41	  if low_threshold > high_threshold {
 42	    raise @types.LoadError::DecodeFailed("low_threshold 应 <= high_threshold")
 43	  }
 44	  let w = img.width
 45	  let h = img.height
 46	  let n = w * h
 47	  if n == 0 {
 48	    return img	<-- UNCOVERED
 49	  }
 50	  // 转灰度
 51	  let gray : Array[Int] = Array::make(n, 0)
 52	  let ch = img.channels
 53	  for i in 0..<n {
 54	    if ch == 1 {
 55	      gray[i] = img.data[i].to_int()
 56	    } else {
 57	      let r = img.data[i * ch].to_int()
 58	      let g = img.data[i * ch + 1].to_int()
 59	      let b = img.data[i * ch + 2].to_int()
 60	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
 61	    }
 62	  }
 63	  // 1. 高斯模糊 (sigma=1.0, 5x5 核)
 64	  let blurred = canny_gaussian_blur(gray, w, h)
 65	  // 2. Sobel 梯度
 66	  let gradient : Array[Int] = Array::make(n, 0)
 67	  let direction : Array[Int] = Array::make(n, 0) // 0=0°, 1=45°, 2=90°, 3=135°
 68	  for y in 1..<(h - 1) {
 69	    for x in 1..<(w - 1) {
 70	      let idx = y * w + x
 71	      // Sobel X
 72	      let gx = -blurred[(y - 1) * w + (x - 1)] -
 73	        2 * blurred[y * w + (x - 1)] -
 74	        blurred[(y + 1) * w + (x - 1)] +
 75	        blurred[(y - 1) * w + (x + 1)] +
 76	        2 * blurred[y * w + (x + 1)] +
 77	        blurred[(y + 1) * w + (x + 1)]
 78	      // Sobel Y
 79	      let gy = -blurred[(y - 1) * w + (x - 1)] -
 80	        2 * blurred[(y - 1) * w + x] -
 81	        blurred[(y - 1) * w + (x + 1)] +
 82	        blurred[(y + 1) * w + (x - 1)] +
 83	        2 * blurred[(y + 1) * w + x] +
 84	        blurred[(y + 1) * w + (x + 1)]
 85	      // 梯度幅值（sqrt 比 powf(x, 0.5) 更快）
 86	      let mag = Float::from_int(gx * gx + gy * gy).sqrt().to_int()
 87	      gradient[idx] = mag
 88	      // 梯度方向量化到 4 个方向
 89	      let angle = @math.atan2f(Float::from_int(gy), Float::from_int(gx)) *
 90	        180.0F /
 91	        3.14159265F
 92	      let norm_angle = if angle < 0.0F { angle + 180.0F } else { angle }
 93	      direction[idx] = if norm_angle < 22.5F || norm_angle >= 157.5F {
 94	        0 // 水平
 95	      } else if norm_angle < 67.5F {
 96	        1 // 45°
 97	      } else if norm_angle < 112.5F {
 98	        2 // 垂直
 99	      } else {
100	        3 // 135°
101	      }
102	    }
103	  }
104	  // 3. 非极大值抑制
105	  let nms : Array[Int] = Array::make(n, 0)
106	  for y in 1..<(h - 1) {
107	    for x in 1..<(w - 1) {
108	      let idx = y * w + x
109	      let mag = gradient[idx]
110	      if mag == 0 {
111	        continue
112	      }
113	      let dir = direction[idx]
114	      let (n1, n2) = match dir {
115	        0 => (gradient[idx - 1], gradient[idx + 1]) // 水平：比较左右
116	        1 => (gradient[(y - 1) * w + (x + 1)], gradient[(y + 1) * w + (x - 1)]) // 45°
117	        2 => (gradient[(y - 1) * w + x], gradient[(y + 1) * w + x]) // 垂直：比较上下
118	        _ => (gradient[(y - 1) * w + (x - 1)], gradient[(y + 1) * w + (x + 1)]) // 135°
119	      }
120	      if mag >= n1 && mag >= n2 {
121	        nms[idx] = mag
122	      }
123	    }
124	  }
125	  // 4. 双阈值 + 滞后连接
126	  let out : Array[Byte] = Array::make(n, b'\x00')
127	  let strong : Array[Bool] = Array::make(n, false)
128	  let weak : Array[Bool] = Array::make(n, false)
129	  for i in 0..<n {
130	    if nms[i] >= high_threshold {
131	      strong[i] = true
132	    } else if nms[i] >= low_threshold {
133	      weak[i] = true
134	    }
135	  }
136	  // 滞后连接：弱边缘如果与强边缘连通则保留
137	  let stack : Array[Int] = []
138	  for i in 0..<n {
139	    if strong[i] {
140	      out[i] = b'\xFF'
141	      stack.push(i)
142	    }
143	  }
144	  while stack.length() > 0 {
145	    let idx = stack.remove(stack.length() - 1)
146	    let x = idx % w
147	    let y = idx / w
148	    // 8 邻域
149	    for dy in -1..<=1 {
150	      for dx in -1..<=1 {
151	        if dx == 0 && dy == 0 {
152	          continue
153	        }
154	        let nx = x + dx
155	        let ny = y + dy
156	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
157	          let nidx = ny * w + nx
158	          if weak[nidx] && !strong[nidx] {
159	            strong[nidx] = true	<-- UNCOVERED
160	            out[nidx] = b'\xFF'
161	            stack.push(nidx)	<-- UNCOVERED
162	          }
163	        }
164	      }
165	    }
166	  }
167	  @types.Image::{
168	    width: w,
169	    height: h,
170	    channels: 1,
171	    data: Bytes::from_array(out),
172	  }
173	}
   	…

39 uncovered line(s) in src\process\edge\canny_16f.mbt:

12	fn write_u16_le_c(buf : Array[Byte], offset : Int, val : Int) -> Unit {
13	  buf[offset] = (val & 0xFF).to_byte()	<-- UNCOVERED
14	  buf[offset + 1] = ((val >> 8) & 0xFF).to_byte()
15	}
  	…

28	fn write_f32_le_c(buf : Array[Byte], offset : Int, val : Float) -> Unit {
29	  let bits = val.reinterpret_as_int()	<-- UNCOVERED
30	  buf[offset] = (bits & 0xFF).to_byte()
31	  buf[offset + 1] = ((bits >> 8) & 0xFF).to_byte()
32	  buf[offset + 2] = ((bits >> 16) & 0xFF).to_byte()
33	  buf[offset + 3] = ((bits >> 24) & 0xFF).to_byte()
34	}
  	…

 96	pub fn canny_edge_16(
 97	  img : @types.Image16,
 98	  low_threshold? : Int = 12800,
 99	  high_threshold? : Int = 25600,
100	) -> @types.Image16 raise @types.LoadError {
101	  // 输入验证
102	  if img.width <= 0 || img.height <= 0 {
103	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
104	      "canny_edge_16: 图像尺寸必须大于0",
105	    )
106	  }
107	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
108	    raise @types.LoadError::DecodeFailed(
109	      "canny_edge_16: 仅支持 channels=1/3/4",
110	    )
111	  }
112	  let expected_size = img.width * img.height * img.channels * 2
113	  if img.data.length() != expected_size {
114	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
115	      "canny_edge_16: 数据大小不匹配，期望 " +
116	      expected_size.to_string() +	<-- UNCOVERED
117	      "，实际 " +
118	      img.data.length().to_string(),	<-- UNCOVERED
119	    )
120	  }
121	  if low_threshold < 0 || low_threshold > 65535 {
122	    raise @types.LoadError::DecodeFailed(
123	      "low_threshold 必须在 0..65535 之间",
124	    )
125	  }
126	  if high_threshold < 0 || high_threshold > 65535 {
127	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
128	      "high_threshold 必须在 0..65535 之间",
129	    )
130	  }
131	  if low_threshold > high_threshold {
132	    raise @types.LoadError::DecodeFailed("low_threshold 应 <= high_threshold")	<-- UNCOVERED
133	  }
134	  let w = img.width
135	  let h = img.height
136	  let n = w * h
137	  if n == 0 {
138	    return img	<-- UNCOVERED
139	  }
140	  let ch = img.channels
141	  let bpp = ch * 2
142	  // 转灰度
143	  let gray : Array[Int] = Array::make(n, 0)
144	  for i in 0..<n {
145	    if ch == 1 {
146	      gray[i] = read_u16_le_c(img.data, i * 2)
147	    } else {
148	      let r = read_u16_le_c(img.data, i * bpp)
149	      let g = read_u16_le_c(img.data, i * bpp + 2)
150	      let b = read_u16_le_c(img.data, i * bpp + 4)
151	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
152	    }
153	  }
154	  // 1. 高斯模糊
155	  let blurred = canny_gaussian_blur_16(gray, w, h)
156	  // 2. Sobel 梯度
157	  let gradient : Array[Int] = Array::make(n, 0)
158	  let direction : Array[Int] = Array::make(n, 0)
159	  for y in 1..<(h - 1) {
160	    for x in 1..<(w - 1) {
161	      let idx = y * w + x
162	      let gx = -blurred[(y - 1) * w + (x - 1)] -
163	        2 * blurred[y * w + (x - 1)] -
164	        blurred[(y + 1) * w + (x - 1)] +
165	        blurred[(y - 1) * w + (x + 1)] +
166	        2 * blurred[y * w + (x + 1)] +
167	        blurred[(y + 1) * w + (x + 1)]
168	      let gy = -blurred[(y - 1) * w + (x - 1)] -
169	        2 * blurred[(y - 1) * w + x] -
170	        blurred[(y - 1) * w + (x + 1)] +
171	        blurred[(y + 1) * w + (x - 1)] +
172	        2 * blurred[(y + 1) * w + x] +
173	        blurred[(y + 1) * w + (x + 1)]
174	      let mag = Float::from_int(gx * gx + gy * gy).sqrt().to_int()
175	      gradient[idx] = mag
176	      let angle = @math.atan2f(Float::from_int(gy), Float::from_int(gx)) *
177	        180.0F /
178	        3.14159265F
179	      let norm_angle = if angle < 0.0F { angle + 180.0F } else { angle }
180	      direction[idx] = if norm_angle < 22.5F || norm_angle >= 157.5F {
181	        0
182	      } else if norm_angle < 67.5F {	<-- UNCOVERED
183	        1
184	      } else if norm_angle < 112.5F {
185	        2
186	      } else {
187	        3	<-- UNCOVERED
188	      }
189	    }
190	  }
191	  // 3. 非极大值抑制
192	  let nms : Array[Int] = Array::make(n, 0)
193	  for y in 1..<(h - 1) {
194	    for x in 1..<(w - 1) {
195	      let idx = y * w + x
196	      let mag = gradient[idx]
197	      if mag == 0 {
198	        continue
199	      }
200	      let dir = direction[idx]
201	      let (n1, n2) = match dir {	<-- UNCOVERED
202	        0 => (gradient[idx - 1], gradient[idx + 1])
203	        1 => (gradient[(y - 1) * w + (x + 1)], gradient[(y + 1) * w + (x - 1)])
204	        2 => (gradient[(y - 1) * w + x], gradient[(y + 1) * w + x])
205	        _ => (gradient[(y - 1) * w + (x - 1)], gradient[(y + 1) * w + (x + 1)])
206	      }
207	      if mag >= n1 && mag >= n2 {
208	        nms[idx] = mag	<-- UNCOVERED
209	      }
210	    }
211	  }
212	  // 4. 双阈值 + 滞后连接
213	  let out : Array[Byte] = Array::make(n * 2, b'\x00')
214	  let strong : Array[Bool] = Array::make(n, false)
215	  let weak : Array[Bool] = Array::make(n, false)
216	  for i in 0..<n {
217	    if nms[i] >= high_threshold {
218	      strong[i] = true	<-- UNCOVERED
219	    } else if nms[i] >= low_threshold {
220	      weak[i] = true	<-- UNCOVERED
221	    }
222	  }
223	  let stack : Array[Int] = []
224	  for i in 0..<n {
225	    if strong[i] {
226	      write_u16_le_c(out, i * 2, 65535)	<-- UNCOVERED
227	      stack.push(i)
228	    }
229	  }
230	  while stack.length() > 0 {
231	    let idx = stack.remove(stack.length() - 1)	<-- UNCOVERED
232	    let x = idx % w
233	    let y = idx / w
234	    for dy in -1..<=1 {
235	      for dx in -1..<=1 {	<-- UNCOVERED
236	        if dx == 0 && dy == 0 {
237	          continue
238	        }
239	        let nx = x + dx
240	        let ny = y + dy
241	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
242	          let nidx = ny * w + nx	<-- UNCOVERED
243	          if weak[nidx] && !strong[nidx] {
244	            strong[nidx] = true	<-- UNCOVERED
245	            write_u16_le_c(out, nidx * 2, 65535)
246	            stack.push(nidx)
247	          }
248	        }
249	      }
250	    }
251	  }
252	  @types.Image16::{
253	    width: w,
254	    height: h,
255	    channels: 1,
256	    data: Bytes::from_array(out),
257	  }
258	}
   	…

264	pub fn canny_edge_f(
265	  img : @types.ImageF,
266	  low_threshold? : Float = 0.2F,
267	  high_threshold? : Float = 0.4F,
268	) -> @types.ImageF raise @types.LoadError {
269	  // 输入验证
270	  if img.width <= 0 || img.height <= 0 {
271	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
272	      "canny_edge_f: 图像尺寸必须大于0",
273	    )
274	  }
275	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
276	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
277	      "canny_edge_f: 仅支持 channels=1/3/4",
278	    )
279	  }
280	  let expected_size = img.width * img.height * img.channels * 4
281	  if img.data.length() != expected_size {
282	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
283	      "canny_edge_f: 数据大小不匹配，期望 " +
284	      expected_size.to_string() +	<-- UNCOVERED
285	      "，实际 " +
286	      img.data.length().to_string(),	<-- UNCOVERED
287	    )
288	  }
289	  if low_threshold < 0.0F || low_threshold > 1.0F {
290	    raise @types.LoadError::DecodeFailed(
291	      "low_threshold 必须在 0.0..1.0 之间",
292	    )
293	  }
294	  if high_threshold < 0.0F || high_threshold > 1.0F {
295	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
296	      "high_threshold 必须在 0.0..1.0 之间",
297	    )
298	  }
299	  if low_threshold > high_threshold {
300	    raise @types.LoadError::DecodeFailed("low_threshold 应 <= high_threshold")	<-- UNCOVERED
301	  }
302	  let w = img.width
303	  let h = img.height
304	  let n = w * h
305	  if n == 0 {
306	    return img	<-- UNCOVERED
307	  }
308	  let ch = img.channels
309	  let bpp = ch * 4
310	  // 转灰度
311	  let gray : Array[Float] = Array::make(n, 0.0F)
312	  for i in 0..<n {
313	    if ch == 1 {
314	      gray[i] = read_float_le_c(img.data, i * 4)
315	    } else {
316	      let r = read_float_le_c(img.data, i * bpp)
317	      let g = read_float_le_c(img.data, i * bpp + 4)
318	      let b = read_float_le_c(img.data, i * bpp + 8)
319	      gray[i] = r * 0.299F + g * 0.587F + b * 0.114F
320	    }
321	  }
322	  // 1. 高斯模糊 (5x5, sigma=1.0)
323	  let blurred : Array[Float] = Array::make(n, 0.0F)
324	  let kernel : Array[Float] = [
325	    2.0F, 4.0F, 5.0F, 4.0F, 2.0F, 4.0F, 9.0F, 12.0F, 9.0F, 4.0F, 5.0F, 12.0F, 15.0F,
326	    12.0F, 5.0F, 4.0F, 9.0F, 12.0F, 9.0F, 4.0F, 2.0F, 4.0F, 5.0F, 4.0F, 2.0F,
327	  ]
328	  let kernel_sum = 159.0F
329	  for y in 2..<(h - 2) {
330	    for x in 2..<(w - 2) {
331	      let mut sum = 0.0F
332	      let base = (y - 2) * w + (x - 2)
333	      for ky in 0..<5 {
334	        let row = base + ky * w
335	        let krow = ky * 5
336	        for kx in 0..<5 {
337	          sum += gray[row + kx] * kernel[krow + kx]
338	        }
339	      }
340	      blurred[y * w + x] = sum / kernel_sum
341	    }
342	  }
343	  for y in 0..<h {
344	    let y_inner = y >= 2 && y < h - 2
345	    for x in 0..<w {
346	      if y_inner && x >= 2 && x < w - 2 {
347	        continue
348	      }
349	      let mut sum = 0.0F
350	      for ky in 0..<5 {
351	        for kx in 0..<5 {
352	          let px = clamp_int_c(x + kx - 2, 0, w - 1)
353	          let py = clamp_int_c(y + ky - 2, 0, h - 1)
354	          sum += gray[py * w + px] * kernel[ky * 5 + kx]
355	        }
356	      }
357	      blurred[y * w + x] = sum / kernel_sum
358	    }
359	  }
360	  // 2. Sobel 梯度
361	  let gradient : Array[Float] = Array::make(n, 0.0F)
362	  let direction : Array[Int] = Array::make(n, 0)
363	  for y in 1..<(h - 1) {
364	    for x in 1..<(w - 1) {
365	      let idx = y * w + x
366	      let gx = -blurred[(y - 1) * w + (x - 1)] -
367	        2.0F * blurred[y * w + (x - 1)] -
368	        blurred[(y + 1) * w + (x - 1)] +
369	        blurred[(y - 1) * w + (x + 1)] +
370	        2.0F * blurred[y * w + (x + 1)] +
371	        blurred[(y + 1) * w + (x + 1)]
372	      let gy = -blurred[(y - 1) * w + (x - 1)] -
373	        2.0F * blurred[(y - 1) * w + x] -
374	        blurred[(y - 1) * w + (x + 1)] +
375	        blurred[(y + 1) * w + (x - 1)] +
376	        2.0F * blurred[(y + 1) * w + x] +
377	        blurred[(y + 1) * w + (x + 1)]
378	      let mag = (gx * gx + gy * gy).sqrt()
379	      gradient[idx] = mag
380	      let angle = @math.atan2f(gy, gx) * 180.0F / 3.14159265F
381	      let norm_angle = if angle < 0.0F { angle + 180.0F } else { angle }
382	      direction[idx] = if norm_angle < 22.5F || norm_angle >= 157.5F {
383	        0
384	      } else if norm_angle < 67.5F {	<-- UNCOVERED
385	        1
386	      } else if norm_angle < 112.5F {
387	        2
388	      } else {
389	        3	<-- UNCOVERED
390	      }
391	    }
392	  }
393	  // 3. 非极大值抑制
394	  let nms : Array[Float] = Array::make(n, 0.0F)
395	  for y in 1..<(h - 1) {
396	    for x in 1..<(w - 1) {
397	      let idx = y * w + x
398	      let mag = gradient[idx]
399	      if mag == 0.0F {
400	        continue
401	      }
402	      let dir = direction[idx]
403	      let (n1, n2) = match dir {	<-- UNCOVERED
404	        0 => (gradient[idx - 1], gradient[idx + 1])
405	        1 => (gradient[(y - 1) * w + (x + 1)], gradient[(y + 1) * w + (x - 1)])
406	        2 => (gradient[(y - 1) * w + x], gradient[(y + 1) * w + x])
407	        _ => (gradient[(y - 1) * w + (x - 1)], gradient[(y + 1) * w + (x + 1)])
408	      }
409	      if mag >= n1 && mag >= n2 {
410	        nms[idx] = mag	<-- UNCOVERED
411	      }
412	    }
413	  }
414	  // 4. 双阈值 + 滞后连接
415	  let out : Array[Byte] = Array::make(n * 4, b'\x00')
416	  let strong : Array[Bool] = Array::make(n, false)
417	  let weak : Array[Bool] = Array::make(n, false)
418	  for i in 0..<n {
419	    if nms[i] >= high_threshold {
420	      strong[i] = true	<-- UNCOVERED
421	    } else if nms[i] >= low_threshold {
422	      weak[i] = true	<-- UNCOVERED
423	    }
424	  }
425	  let stack : Array[Int] = []
426	  for i in 0..<n {
427	    if strong[i] {
428	      write_f32_le_c(out, i * 4, 1.0F)	<-- UNCOVERED
429	      stack.push(i)
430	    }
431	  }
432	  while stack.length() > 0 {
433	    let idx = stack.remove(stack.length() - 1)	<-- UNCOVERED
434	    let x = idx % w
435	    let y = idx / w
436	    for dy in -1..<=1 {
437	      for dx in -1..<=1 {	<-- UNCOVERED
438	        if dx == 0 && dy == 0 {
439	          continue
440	        }
441	        let nx = x + dx
442	        let ny = y + dy
443	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
444	          let nidx = ny * w + nx	<-- UNCOVERED
445	          if weak[nidx] && !strong[nidx] {
446	            strong[nidx] = true	<-- UNCOVERED
447	            write_f32_le_c(out, nidx * 4, 1.0F)
448	            stack.push(nidx)
449	          }
450	        }
451	      }
452	    }
453	  }
454	  @types.ImageF::{
455	    width: w,
456	    height: h,
457	    channels: 1,
458	    data: Bytes::from_array(out),
459	  }
460	}
   	…

12 uncovered line(s) in src\process\edge\contour.mbt:

21	pub fn find_contours(
22	  img : @types.Image,
23	) -> Array[Contour] raise @types.LoadError {
24	  // 输入验证
25	  if img.width <= 0 || img.height <= 0 {
26	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
27	      "find_contours: 图像尺寸必须大于0",
28	    )
29	  }
30	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
31	    raise @types.LoadError::DecodeFailed(
32	      "find_contours: 仅支持 channels=1/3/4",
33	    )
34	  }
35	  let expected_size = img.width * img.height * img.channels
36	  if img.data.length() != expected_size {
37	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
38	      "find_contours: 数据大小不匹配，期望 " +
39	      expected_size.to_string() +	<-- UNCOVERED
40	      "，实际 " +
41	      img.data.length().to_string(),	<-- UNCOVERED
42	    )
43	  }
44	  let w = img.width
45	  let h = img.height
46	  let ch = img.channels
47	  // 转为二值（直接从首通道计算，消除中间数组）
48	  let binary : Array[Bool] = Array::make(w * h, false)
49	  for i in 0..<(w * h) {
50	    binary[i] = img.data[i * ch].to_int() > 0
51	  }
52	  let visited : Array[Bool] = Array::make(w * h, false)
53	  let contours : Array[Contour] = []
54	  for y in 0..<h {
55	    for x in 0..<w {
56	      let idx = y * w + x
57	      if binary[idx] && !visited[idx] {
58	        // 检查是否为外轮廓起点（上方为背景或边界）
59	        let is_outer = y == 0 || !binary[idx - w]
60	        let contour = trace_boundary(binary, visited, x, y, w, h, is_outer)
61	        if contour.points.length() > 0 {
62	          contours.push(contour)
63	        }
64	      }
65	    }
66	  }
67	  contours
68	}
  	…

132	pub fn draw_contours(
133	  img : @types.Image,
134	  contours : Array[Contour],
135	  color : Array[Byte],
136	) -> @types.Image raise @types.LoadError {
137	  // 输入验证
138	  if img.width <= 0 || img.height <= 0 {
139	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
140	      "draw_contours: 图像尺寸必须大于0",
141	    )
142	  }
143	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
144	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
145	      "draw_contours: 仅支持 channels=1/3/4",
146	    )
147	  }
148	  let expected_size = img.width * img.height * img.channels
149	  if img.data.length() != expected_size {
150	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
151	      "draw_contours: 数据大小不匹配，期望 " +
152	      expected_size.to_string() +	<-- UNCOVERED
153	      "，实际 " +
154	      img.data.length().to_string(),	<-- UNCOVERED
155	    )
156	  }
157	  if color.length() == 0 {
158	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
159	      "draw_contours: color数组不能为空",
160	    )
161	  }
162	  let ch = img.channels
163	  let w = img.width
164	  let h = img.height
165	  let out : Array[Byte] = Array::make(w * h * ch, b'\x00')
166	  for i in 0..<(w * h * ch) {
167	    out[i] = img.data[i]
168	  }
169	  let draw_ch = if ch >= 3 { 3 } else { 1 }
170	  for contour in contours {
171	    for pt in contour.points {
172	      if pt.x >= 0 && pt.x < w && pt.y >= 0 && pt.y < h {
173	        for c in 0..<draw_ch {
174	          if c < color.length() {
175	            out[(pt.y * w + pt.x) * ch + c] = color[c]
176	          }
177	        }
178	      }
179	    }
180	  }
181	  @types.Image::{
182	    width: w,
183	    height: h,
184	    channels: ch,
185	    data: Bytes::from_array(out),
186	  }
187	}
   	…

191	pub fn contour_perimeter(contour : Contour) -> Float {
192	  let pts = contour.points
193	  if pts.length() < 2 {
194	    return 0.0F	<-- UNCOVERED
195	  }
196	  let mut perim = 0.0F
197	  for i in 0..<pts.length() {
198	    let j = (i + 1) % pts.length()
199	    let dx = pts[i].x - pts[j].x
200	    let dy = pts[i].y - pts[j].y
201	    perim += Float::from_int(dx * dx + dy * dy).sqrt()
202	  }
203	  perim
204	}
   	…

208	pub fn contour_area(contour : Contour) -> Float {
209	  let pts = contour.points
210	  if pts.length() < 3 {
211	    return 0.0F	<-- UNCOVERED
212	  }
213	  let mut area = 0.0F
214	  for i in 0..<pts.length() {
215	    let j = (i + 1) % pts.length()
216	    area += Float::from_int(pts[i].x * pts[j].y - pts[j].x * pts[i].y)
217	  }
218	  (area / 2.0F).abs()
219	}
   	…

25 uncovered line(s) in src\process\edge\contour_16f.mbt:

 5	pub fn find_contours_16(
 6	  img : @types.Image16,
 7	) -> Array[Contour] raise @types.LoadError {
 8	  // 输入验证
 9	  if img.width <= 0 || img.height <= 0 {
10	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
11	      "find_contours_16: 图像尺寸必须大于0",
12	    )
13	  }
14	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
15	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
16	      "find_contours_16: 仅支持 channels=1/3/4",
17	    )
18	  }
19	  let expected_size = img.width * img.height * img.channels * 2
20	  if img.data.length() != expected_size {
21	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
22	      "find_contours_16: 数据大小不匹配，期望 " +
23	      expected_size.to_string() +	<-- UNCOVERED
24	      "，实际 " +
25	      img.data.length().to_string(),	<-- UNCOVERED
26	    )
27	  }
28	  let w = img.width
29	  let h = img.height
30	  let ch = img.channels
31	  let binary : Array[Bool] = Array::make(w * h, false)
32	  for i in 0..<(w * h) {
33	    let v = img.data[i * ch * 2].to_int() |
34	      (img.data[i * ch * 2 + 1].to_int() << 8)
35	    binary[i] = v > 0
36	  }
37	  let visited : Array[Bool] = Array::make(w * h, false)
38	  let contours : Array[Contour] = []
39	  for y in 0..<h {
40	    for x in 0..<w {
41	      let idx = y * w + x
42	      if binary[idx] && !visited[idx] {
43	        let is_outer = y == 0 || !binary[idx - w]
44	        let contour = trace_boundary_16f(binary, visited, x, y, w, h, is_outer)
45	        if contour.points.length() > 0 {
46	          contours.push(contour)
47	        }
48	      }
49	    }
50	  }
51	  contours
52	}
  	…

 56	pub fn find_contours_f(
 57	  img : @types.ImageF,
 58	) -> Array[Contour] raise @types.LoadError {
 59	  // 输入验证
 60	  if img.width <= 0 || img.height <= 0 {
 61	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 62	      "find_contours_f: 图像尺寸必须大于0",
 63	    )
 64	  }
 65	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 66	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 67	      "find_contours_f: 仅支持 channels=1/3/4",
 68	    )
 69	  }
 70	  let expected_size = img.width * img.height * img.channels * 4
 71	  if img.data.length() != expected_size {
 72	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 73	      "find_contours_f: 数据大小不匹配，期望 " +
 74	      expected_size.to_string() +	<-- UNCOVERED
 75	      "，实际 " +
 76	      img.data.length().to_string(),	<-- UNCOVERED
 77	    )
 78	  }
 79	  let w = img.width
 80	  let h = img.height
 81	  let ch = img.channels
 82	  let binary : Array[Bool] = Array::make(w * h, false)
 83	  for i in 0..<(w * h) {
 84	    let bits = img.data[i * ch * 4].to_int() |
 85	      (img.data[i * ch * 4 + 1].to_int() << 8) |
 86	      (img.data[i * ch * 4 + 2].to_int() << 16) |
 87	      (img.data[i * ch * 4 + 3].to_int() << 24)
 88	    binary[i] = Float::reinterpret_from_int(bits) > 0.0F
 89	  }
 90	  let visited : Array[Bool] = Array::make(w * h, false)
 91	  let contours : Array[Contour] = []
 92	  for y in 0..<h {
 93	    for x in 0..<w {
 94	      let idx = y * w + x
 95	      if binary[idx] && !visited[idx] {
 96	        let is_outer = y == 0 || !binary[idx - w]
 97	        let contour = trace_boundary_16f(binary, visited, x, y, w, h, is_outer)
 98	        if contour.points.length() > 0 {
 99	          contours.push(contour)
100	        }
101	      }
102	    }
103	  }
104	  contours
105	}
   	…

109	fn trace_boundary_16f(
110	  binary : Array[Bool],
111	  visited : Array[Bool],
112	  start_x : Int,
113	  start_y : Int,
114	  w : Int,
115	  h : Int,
116	  is_outer : Bool,
117	) -> Contour {
118	  let points : Array[ContourPoint] = []
119	  let mut cx = start_x
120	  let mut cy = start_y
121	  let mut dir_idx = 0
122	  let mut count = 0
123	  let max_count = w * h * 8 + 1
124	  while count < max_count {
125	    let idx = cy * w + cx
126	    if !visited[idx] {
127	      visited[idx] = true
128	      points.push(ContourPoint::{ x: cx, y: cy })
129	    }
130	    count += 1
131	    let mut found = false
132	    for i in 0..<8 {
133	      let d = (dir_idx + i) % 8
134	      let (dx, dy) = match d {
135	        0 => (1, 0)
136	        1 => (1, 1)
137	        2 => (0, 1)
138	        3 => (-1, 1)
139	        4 => (-1, 0)
140	        5 => (-1, -1)
141	        6 => (0, -1)
142	        _ => (1, -1)
143	      }
144	      let nx = cx + dx
145	      let ny = cy + dy
146	      if nx >= 0 && nx < w && ny >= 0 && ny < h && binary[ny * w + nx] {
147	        cx = nx
148	        cy = ny
149	        dir_idx = (d + 6) % 8
150	        found = true
151	        break
152	      }
153	    }
154	    if !found {
155	      break	<-- UNCOVERED
156	    }
157	    if cx == start_x && cy == start_y {
158	      break
159	    }
160	  }
161	  Contour::{ points, is_outer }
162	}
   	…

166	pub fn draw_contours_16(
167	  img : @types.Image16,
168	  contours : Array[Contour],
169	  color : Array[Int],
170	) -> @types.Image16 raise @types.LoadError {
171	  // 输入验证
172	  if img.width <= 0 || img.height <= 0 {
173	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
174	      "draw_contours_16: 图像尺寸必须大于0",
175	    )
176	  }
177	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
178	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
179	      "draw_contours_16: 仅支持 channels=1/3/4",
180	    )
181	  }
182	  let expected_size = img.width * img.height * img.channels * 2
183	  if img.data.length() != expected_size {
184	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
185	      "draw_contours_16: 数据大小不匹配，期望 " +
186	      expected_size.to_string() +	<-- UNCOVERED
187	      "，实际 " +
188	      img.data.length().to_string(),	<-- UNCOVERED
189	    )
190	  }
191	  if color.length() == 0 {
192	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
193	      "draw_contours_16: color数组不能为空",
194	    )
195	  }
196	  let ch = img.channels
197	  let w = img.width
198	  let h = img.height
199	  let out : Array[Byte] = Array::make(w * h * ch * 2, b'\x00')
200	  for i in 0..<(w * h * ch) {
201	    out[i * 2] = img.data[i * 2]
202	    out[i * 2 + 1] = img.data[i * 2 + 1]
203	  }
204	  let draw_ch = if ch >= 3 { 3 } else { 1 }
205	  for contour in contours {
206	    for pt in contour.points {	<-- UNCOVERED
207	      if pt.x >= 0 && pt.x < w && pt.y >= 0 && pt.y < h {
208	        for c in 0..<draw_ch {
209	          if c < color.length() {
210	            let v = color[c]
211	            out[((pt.y * w + pt.x) * ch + c) * 2] = (v & 0xFF).to_byte()
212	            out[((pt.y * w + pt.x) * ch + c) * 2 + 1] = ((v >> 8) & 0xFF).to_byte()
213	          }
214	        }
215	      }
216	    }
217	  }
218	  @types.Image16::{
219	    width: w,
220	    height: h,
221	    channels: ch,
222	    data: Bytes::from_array(out),
223	  }
224	}
   	…

228	pub fn draw_contours_f(
229	  img : @types.ImageF,
230	  contours : Array[Contour],
231	  color : Array[Float],
232	) -> @types.ImageF raise @types.LoadError {
233	  // 输入验证
234	  if img.width <= 0 || img.height <= 0 {
235	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
236	      "draw_contours_f: 图像尺寸必须大于0",
237	    )
238	  }
239	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
240	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
241	      "draw_contours_f: 仅支持 channels=1/3/4",
242	    )
243	  }
244	  let expected_size = img.width * img.height * img.channels * 4
245	  if img.data.length() != expected_size {
246	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
247	      "draw_contours_f: 数据大小不匹配，期望 " +
248	      expected_size.to_string() +	<-- UNCOVERED
249	      "，实际 " +
250	      img.data.length().to_string(),	<-- UNCOVERED
251	    )
252	  }
253	  if color.length() == 0 {
254	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
255	      "draw_contours_f: color数组不能为空",
256	    )
257	  }
258	  let ch = img.channels
259	  let w = img.width
260	  let h = img.height
261	  let out : Array[Byte] = Array::make(w * h * ch * 4, b'\x00')
262	  for i in 0..<(w * h * ch) {
263	    out[i * 4] = img.data[i * 4]
264	    out[i * 4 + 1] = img.data[i * 4 + 1]
265	    out[i * 4 + 2] = img.data[i * 4 + 2]
266	    out[i * 4 + 3] = img.data[i * 4 + 3]
267	  }
268	  let draw_ch = if ch >= 3 { 3 } else { 1 }
269	  for contour in contours {
270	    for pt in contour.points {	<-- UNCOVERED
271	      if pt.x >= 0 && pt.x < w && pt.y >= 0 && pt.y < h {
272	        for c in 0..<draw_ch {
273	          if c < color.length() {
274	            let bits = color[c].reinterpret_as_int()
275	            out[((pt.y * w + pt.x) * ch + c) * 4] = (bits & 0xFF).to_byte()
276	            out[((pt.y * w + pt.x) * ch + c) * 4 + 1] = ((bits >> 8) & 0xFF).to_byte()
277	            out[((pt.y * w + pt.x) * ch + c) * 4 + 2] = ((bits >> 16) & 0xFF).to_byte()
278	            out[((pt.y * w + pt.x) * ch + c) * 4 + 3] = ((bits >> 24) & 0xFF).to_byte()
279	          }
280	        }
281	      }
282	    }
283	  }
284	  @types.ImageF::{
285	    width: w,
286	    height: h,
287	    channels: ch,
288	    data: Bytes::from_array(out),
289	  }
290	}
   	…

10 uncovered line(s) in src\process\edge\contour_analysis.mbt:

30	pub fn convex_hull(points : Array[ContourPoint]) -> Array[ContourPoint] {
31	  // 空数组返回空数组
32	  if points.length() == 0 {
33	    return []	<-- UNCOVERED
34	  }
35	
36	  let n = points.length()
37	  if n <= 2 {
38	    let out : Array[ContourPoint] = []	<-- UNCOVERED
39	    for p in points {
40	      out.push(p)	<-- UNCOVERED
41	    }
42	    return out
43	  }
44	  // 找最下方的点（y 最大，x 最小）
45	  let mut pivot = 0
46	  for i in 1..<n {
47	    if points[i].y > points[pivot].y ||
48	      (points[i].y == points[pivot].y && points[i].x < points[pivot].x) {
49	      pivot = i
50	    }
51	  }
52	  // 按极角排序
53	  let p0 = points[pivot]
54	  let sorted : Array[ContourPoint] = []
55	  for i in 0..<n {
56	    if i != pivot {
57	      sorted.push(points[i])
58	    }
59	  }
60	  sorted.sort_by(fn(a, b) {
61	    let cross = (a.x - p0.x) * (b.y - p0.y) - (a.y - p0.y) * (b.x - p0.x)
62	    (0 - cross).compare(0)
63	  })
64	  // Graham 扫描
65	  let hull : Array[ContourPoint] = [p0]
66	  for p in sorted {
67	    while hull.length() >= 2 {
68	      let m = hull.length()
69	      let p1 = hull[m - 2]
70	      let p2 = hull[m - 1]
71	      let cross = (p2.x - p1.x) * (p.y - p1.y) - (p2.y - p1.y) * (p.x - p1.x)
72	      if cross <= 0 {
73	        ignore(hull.pop())
74	      } else {
75	        break
76	      }
77	    }
78	    hull.push(p)
79	  }
80	  hull
81	}
  	…

 85	pub fn approx_poly_dp(
 86	  points : Array[ContourPoint],
 87	  epsilon : Float,
 88	  closed? : Bool = true,
 89	) -> Array[ContourPoint] raise @types.LoadError {
 90	  // 参数验证
 91	  if epsilon < 0.0F {
 92	    raise @types.LoadError::DecodeFailed("approx_poly_dp: epsilon必须 >= 0")	<-- UNCOVERED
 93	  }
 94	  // 空数组返回空数组
 95	  if points.length() == 0 {
 96	    return []
 97	  }
 98	
 99	  let n = points.length()
100	  if n <= 2 {
101	    let out : Array[ContourPoint] = []	<-- UNCOVERED
102	    for p in points {
103	      out.push(p)	<-- UNCOVERED
104	    }
105	    return out
106	  }
107	  // 标记保留的点
108	  let keep : Array[Bool] = Array::make(n, false)
109	  keep[0] = true
110	  keep[n - 1] = true
111	  approx_dp_recursive(points, 0, n - 1, epsilon, keep)
112	  if closed {
113	    // 对闭合轮廓，还需处理首尾之间的段
114	    approx_dp_recursive(points, n - 1, 0, epsilon, keep)
115	  }
116	  let out : Array[ContourPoint] = []
117	  for i in 0..<n {
118	    if keep[i] {
119	      out.push(points[i])
120	    }
121	  }
122	  out
123	}
   	…

126	fn approx_dp_recursive(
127	  points : Array[ContourPoint],
128	  start : Int,
129	  end : Int,
130	  epsilon : Float,
131	  keep : Array[Bool],
132	) -> Unit {
133	  let n = points.length()
134	  let mut max_dist = 0.0F
135	  let mut max_idx = -1
136	  let p_start = points[start]
137	  let p_end = points[end]
138	  let dx = Float::from_int(p_end.x - p_start.x)
139	  let dy = Float::from_int(p_end.y - p_start.y)
140	  let len_sq = dx * dx + dy * dy
141	  let mut i = (start + 1) % n
142	  while i != end {
143	    let p = points[i]
144	    let px = Float::from_int(p.x - p_start.x)
145	    let py = Float::from_int(p.y - p_start.y)
146	    let dist = if len_sq == 0.0F {
147	      (px * px + py * py).sqrt()
148	    } else {
149	      let t = (px * dx + py * dy) / len_sq
150	      let proj_x = t * dx
151	      let proj_y = t * dy
152	      ((px - proj_x) * (px - proj_x) + (py - proj_y) * (py - proj_y)).sqrt()
153	    }
154	    if dist > max_dist {
155	      max_dist = dist
156	      max_idx = i
157	    }
158	    i = (i + 1) % n
159	    if i == start {
160	      break	<-- UNCOVERED
161	    }
162	  }
163	  if max_idx >= 0 && max_dist > epsilon {
164	    keep[max_idx] = true
165	    approx_dp_recursive(points, start, max_idx, epsilon, keep)
166	    approx_dp_recursive(points, max_idx, end, epsilon, keep)
167	  }
168	}
   	…

172	pub fn image_moments(points : Array[ContourPoint]) -> Moments {
173	  // 空数组返回零矩
174	  if points.length() == 0 {
175	    return Moments::{	<-- UNCOVERED
176	      m00: 0.0,
177	      m10: 0.0,
178	      m01: 0.0,
179	      m20: 0.0,
180	      m11: 0.0,
181	      m02: 0.0,
182	      m30: 0.0,
183	      m21: 0.0,
184	      m12: 0.0,
185	      m03: 0.0,
186	      mu20: 0.0,
187	      mu11: 0.0,
188	      mu02: 0.0,
189	      mu30: 0.0,
190	      mu21: 0.0,
191	      mu12: 0.0,
192	      mu03: 0.0,
193	      nu20: 0.0,
194	      nu11: 0.0,
195	      nu02: 0.0,
196	    }
197	  }
198	
199	  let mut m00 = 0.0
200	  let mut m10 = 0.0
201	  let mut m01 = 0.0
202	  let mut m20 = 0.0
203	  let mut m11 = 0.0
204	  let mut m02 = 0.0
205	  let mut m30 = 0.0
206	  let mut m21 = 0.0
207	  let mut m12 = 0.0
208	  let mut m03 = 0.0
209	  for p in points {
210	    let x = Double::from_int(p.x)
211	    let y = Double::from_int(p.y)
212	    m00 += 1.0
213	    m10 += x
214	    m01 += y
215	    m20 += x * x
216	    m11 += x * y
217	    m02 += y * y
218	    m30 += x * x * x
219	    m21 += x * x * y
220	    m12 += x * y * y
221	    m03 += y * y * y
222	  }
223	  // 中心矩
224	  let x_bar = if m00 > 0.0 { m10 / m00 } else { 0.0 }
225	  let y_bar = if m00 > 0.0 { m01 / m00 } else { 0.0 }
226	  let mu20 = m20 - x_bar * m10
227	  let mu11 = m11 - x_bar * m01
228	  let mu02 = m02 - y_bar * m01
229	  let mu30 = m30 - 3.0 * x_bar * m20 + 2.0 * x_bar * x_bar * m10
230	  let mu21 = m21 - 2.0 * x_bar * m11 - y_bar * m20 + 2.0 * x_bar * x_bar * m01
231	  let mu12 = m12 - 2.0 * y_bar * m11 - x_bar * m02 + 2.0 * y_bar * y_bar * m10
232	  let mu03 = m03 - 3.0 * y_bar * m02 + 2.0 * y_bar * y_bar * m01
233	  // 归一化矩
234	  let nu20 = if m00 > 0.0 { mu20 / (m00 * m00) } else { 0.0 }
235	  let nu11 = if m00 > 0.0 { mu11 / (m00 * m00) } else { 0.0 }
236	  let nu02 = if m00 > 0.0 { mu02 / (m00 * m00) } else { 0.0 }
237	  Moments::{
238	    m00,
239	    m10,
240	    m01,
241	    m20,
242	    m11,
243	    m02,
244	    m30,
245	    m21,
246	    m12,
247	    m03,
248	    mu20,
249	    mu11,
250	    mu02,
251	    mu30,
252	    mu21,
253	    mu12,
254	    mu03,
255	    nu20,
256	    nu11,
257	    nu02,
258	  }
259	}
   	…

263	pub fn hu_moments(m : Moments) -> Array[Double] raise @types.LoadError {
264	  // 参数验证
265	  if m.m00 == 0.0 {
266	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
267	      "hu_moments: m00不能为0（空轮廓）",
268	    )
269	  }
270	
271	  let hu : Array[Double] = Array::make(7, 0.0)
272	  let n20 = m.nu20
273	  let n11 = m.nu11
274	  let n02 = m.nu02
275	  let n30 = m.mu30 / (m.m00 * m.m00 * m.m00).sqrt()
276	  let n21 = m.mu21 / (m.m00 * m.m00 * m.m00).sqrt()
277	  let n12 = m.mu12 / (m.m00 * m.m00 * m.m00).sqrt()
278	  let n03 = m.mu03 / (m.m00 * m.m00 * m.m00).sqrt()
279	  hu[0] = n20 + n02
280	  hu[1] = (n20 - n02) * (n20 - n02) + 4.0 * n11 * n11
281	  hu[2] = (n30 - 3.0 * n12) * (n30 - 3.0 * n12) +
282	    (3.0 * n21 - n03) * (3.0 * n21 - n03)
283	  hu[3] = (n30 + n12) * (n30 + n12) + (n21 + n03) * (n21 + n03)
284	  hu[4] = (n30 - 3.0 * n12) *
285	    (n30 + n12) *
286	    ((n30 + n12) * (n30 + n12) - 3.0 * (n21 + n03) * (n21 + n03)) +
287	    (3.0 * n21 - n03) *
288	    (n21 + n03) *
289	    (3.0 * (n30 + n12) * (n30 + n12) - (n21 + n03) * (n21 + n03))
290	  hu[5] = (n20 - n02) * ((n30 + n12) * (n30 + n12) - (n21 + n03) * (n21 + n03)) +
291	    4.0 * n11 * (n30 + n12) * (n21 + n03)
292	  hu[6] = (3.0 * n21 - n03) *
293	    (n30 + n12) *
294	    ((n30 + n12) * (n30 + n12) - 3.0 * (n21 + n03) * (n21 + n03)) -
295	    (n30 - 3.0 * n12) *
296	    (n21 + n03) *
297	    (3.0 * (n30 + n12) * (n30 + n12) - (n21 + n03) * (n21 + n03))
298	  hu
299	}
   	…

303	pub fn min_enclosing_circle(
304	  points : Array[ContourPoint],
305	) -> (ContourPoint, Float) {
306	  // 空数组返回默认值
307	  if points.length() == 0 {
308	    return (ContourPoint::{ x: 0, y: 0 }, 0.0F)	<-- UNCOVERED
309	  }
310	
311	  let n = points.length()
312	  if n == 1 {
313	    return (points[0], 0.0F)
314	  }
315	  // 简化：用质心作为圆心，最大距离作为半径
316	  let mut cx = 0.0F
317	  let mut cy = 0.0F
318	  for p in points {
319	    cx += Float::from_int(p.x)
320	    cy += Float::from_int(p.y)
321	  }
322	  cx = cx / Float::from_int(n)
323	  cy = cy / Float::from_int(n)
324	  // 迭代优化：移动圆心向最远点
325	  let mut center_x = cx
326	  let mut center_y = cy
327	  let mut radius = 0.0F
328	  for _iter in 0..<20 {
329	    let mut far_x = 0
330	    let mut far_y = 0
331	    let mut max_dist = 0.0F
332	    for p in points {
333	      let dx = Float::from_int(p.x) - center_x
334	      let dy = Float::from_int(p.y) - center_y
335	      let dist = (dx * dx + dy * dy).sqrt()
336	      if dist > max_dist {
337	        max_dist = dist
338	        far_x = p.x
339	        far_y = p.y
340	      }
341	    }
342	    radius = max_dist
343	    // 向最远点移动一小步
344	    let step = 0.1F
345	    center_x += (Float::from_int(far_x) - center_x) * step
346	    center_y += (Float::from_int(far_y) - center_y) * step
347	  }
348	  (ContourPoint::{ x: center_x.to_int(), y: center_y.to_int() }, radius)
349	}
   	…

15 uncovered line(s) in src\process\edge\edge_detect.mbt:

  7	pub fn edge_detect_sobel(
  8	  img : @types.Image,
  9	) -> @types.Image raise @types.LoadError {
 10	  // 输入验证
 11	  if img.width <= 0 || img.height <= 0 {
 12	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 13	      "edge_detect_sobel: 图像尺寸必须大于0",
 14	    )
 15	  }
 16	  if img.channels < 1 || img.channels > 4 {
 17	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 18	      "edge_detect_sobel: 通道数必须在1-4之间",
 19	    )
 20	  }
 21	  let expected_size = img.width * img.height * img.channels
 22	  if img.data.length() != expected_size {
 23	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 24	      "edge_detect_sobel: 数据大小不匹配，期望 " +
 25	      expected_size.to_string() +	<-- UNCOVERED
 26	      "，实际 " +
 27	      img.data.length().to_string(),	<-- UNCOVERED
 28	    )
 29	  }
 30	
 31	  let w = img.width
 32	  let h = img.height
 33	  let ch = img.channels
 34	  let n = w * h
 35	  // 先转灰度
 36	  let gray : Array[Int] = Array::make(n, 0)
 37	  for i in 0..<n {
 38	    if ch == 1 {
 39	      gray[i] = img.data[i].to_int()
 40	    } else {
 41	      let r = img.data[i * ch].to_int()
 42	      let g = img.data[i * ch + 1].to_int()
 43	      let b = img.data[i * ch + 2].to_int()
 44	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
 45	    }
 46	  }
 47	  // Sobel 卷积
 48	  let out : Array[Byte] = Array::make(n, b'\x00')
 49	  // 内部区域：1 <= x < w-1 && 1 <= y < h-1，无需 clamp
 50	  for y in 1..<(h - 1) {
 51	    for x in 1..<(w - 1) {
 52	      let idx = y * w + x
 53	      let idx_l = idx - 1
 54	      let idx_r = idx + 1
 55	      let idx_t = idx - w
 56	      let idx_b = idx + w
 57	      // Gx = [-1 0 1; -2 0 2; -1 0 1]
 58	      let gx = -gray[idx_t - 1] +
 59	        gray[idx_t + 1] -
 60	        2 * gray[idx_l] +
 61	        2 * gray[idx_r] -
 62	        gray[idx_b - 1] +
 63	        gray[idx_b + 1]
 64	      // Gy = [-1 -2 -1; 0 0 0; 1 2 1]
 65	      let gy = -gray[idx_t - 1] -
 66	        2 * gray[idx_t] -
 67	        gray[idx_t + 1] +
 68	        gray[idx_b - 1] +
 69	        2 * gray[idx_b] +
 70	        gray[idx_b + 1]
 71	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
 72	      out[idx] = (mag + 0.5F).to_int().clamp(min=0, max=255).to_byte()
 73	    }
 74	  }
 75	  // 边界区域
 76	  for y in 0..<h {
 77	    for x in 0..<w {
 78	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
 79	        continue
 80	      }
 81	      let x_l = clamp_i(x - 1, 0, w - 1)
 82	      let x_r = clamp_i(x + 1, 0, w - 1)
 83	      let y_t = clamp_i(y - 1, 0, h - 1)
 84	      let y_b = clamp_i(y + 1, 0, h - 1)
 85	      let gx = -gray[y_t * w + x_l] +
 86	        gray[y_t * w + x_r] -
 87	        2 * gray[y * w + x_l] +
 88	        2 * gray[y * w + x_r] -
 89	        gray[y_b * w + x_l] +
 90	        gray[y_b * w + x_r]
 91	      let gy = -gray[y_t * w + x_l] -
 92	        2 * gray[y_t * w + x] -
 93	        gray[y_t * w + x_r] +
 94	        gray[y_b * w + x_l] +
 95	        2 * gray[y_b * w + x] +
 96	        gray[y_b * w + x_r]
 97	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
 98	      out[y * w + x] = (mag + 0.5F).to_int().clamp(min=0, max=255).to_byte()
 99	    }
100	  }
101	  @types.Image::{
102	    width: w,
103	    height: h,
104	    channels: 1,
105	    data: Bytes::from_array(out),
106	  }
107	}
   	…

113	pub fn edge_detect_laplacian(
114	  img : @types.Image,
115	) -> @types.Image raise @types.LoadError {
116	  // 输入验证
117	  if img.width <= 0 || img.height <= 0 {
118	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
119	      "edge_detect_laplacian: 图像尺寸必须大于0",
120	    )
121	  }
122	  if img.channels < 1 || img.channels > 4 {
123	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
124	      "edge_detect_laplacian: 通道数必须在1-4之间",
125	    )
126	  }
127	  let expected_size = img.width * img.height * img.channels
128	  if img.data.length() != expected_size {
129	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
130	      "edge_detect_laplacian: 数据大小不匹配，期望 " +
131	      expected_size.to_string() +	<-- UNCOVERED
132	      "，实际 " +
133	      img.data.length().to_string(),	<-- UNCOVERED
134	    )
135	  }
136	
137	  let ch = img.channels
138	  let w = img.width
139	  let h = img.height
140	  let n = w * h
141	  let out : Array[Byte] = Array::make(n, b'\x00')
142	  // 预计算灰度 Int 数组
143	  let gray : Array[Int] = Array::make(n, 0)
144	  for i in 0..<n {
145	    if ch >= 3 {
146	      let r = img.data[i * ch].to_int()
147	      let g = img.data[i * ch + 1].to_int()
148	      let b = img.data[i * ch + 2].to_int()
149	      gray[i] = (r * 77 + g * 150 + b * 29) / 256
150	    } else {
151	      gray[i] = img.data[i * ch].to_int()
152	    }
153	  }
154	  // 内部区域：x in [1, w-1), y in [1, h-1)，直接访问 4 邻域
155	  for y in 1..<(h - 1) {
156	    for x in 1..<(w - 1) {
157	      let idx = y * w + x
158	      let center = gray[idx]
159	      let u = gray[idx - w]
160	      let d = gray[idx + w]
161	      let l = gray[idx - 1]
162	      let r = gray[idx + 1]
163	      let lap = u + d + l + r - 4 * center
164	      out[idx] = clamp_b(lap.abs())
165	    }
166	  }
167	  // 边界区域
168	  for y in 0..<h {
169	    let y_inner = y >= 1 && y < h - 1
170	    for x in 0..<w {
171	      if y_inner && x >= 1 && x < w - 1 {
172	        continue
173	      }
174	      let cx = clamp_i(x, 0, w - 1)
175	      let cy = clamp_i(y, 0, h - 1)
176	      let up = clamp_i(y - 1, 0, h - 1)
177	      let down = clamp_i(y + 1, 0, h - 1)
178	      let left = clamp_i(x - 1, 0, w - 1)
179	      let right = clamp_i(x + 1, 0, w - 1)
180	      let center = gray[cy * w + cx]
181	      let u = gray[up * w + cx]
182	      let d = gray[down * w + cx]
183	      let l = gray[cy * w + left]
184	      let r = gray[cy * w + right]
185	      let lap = u + d + l + r - 4 * center
186	      out[y * w + x] = clamp_b(lap.abs())
187	    }
188	  }
189	  @types.Image::{
190	    width: w,
191	    height: h,
192	    channels: 1,
193	    data: Bytes::from_array(out),
194	  }
195	}
   	…

202	pub fn edge_detect_prewitt(
203	  img : @types.Image,
204	) -> @types.Image raise @types.LoadError {
205	  // 输入验证
206	  if img.width <= 0 || img.height <= 0 {
207	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
208	      "edge_detect_prewitt: 图像尺寸必须大于0",
209	    )
210	  }
211	  if img.channels < 1 || img.channels > 4 {
212	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
213	      "edge_detect_prewitt: 通道数必须在1-4之间",
214	    )
215	  }
216	  let expected_size = img.width * img.height * img.channels
217	  if img.data.length() != expected_size {
218	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
219	      "edge_detect_prewitt: 数据大小不匹配，期望 " +
220	      expected_size.to_string() +	<-- UNCOVERED
221	      "，实际 " +
222	      img.data.length().to_string(),	<-- UNCOVERED
223	    )
224	  }
225	
226	  let ch = img.channels
227	  let w = img.width
228	  let h = img.height
229	  let n = w * h
230	  let out : Array[Byte] = Array::make(n, b'\x00')
231	  // 预计算灰度 Int 数组
232	  let gray : Array[Int] = Array::make(n, 0)
233	  for i in 0..<n {
234	    if ch >= 3 {
235	      let r = img.data[i * ch].to_int()
236	      let g = img.data[i * ch + 1].to_int()
237	      let b = img.data[i * ch + 2].to_int()
238	      gray[i] = (r * 77 + g * 150 + b * 29) / 256
239	    } else {
240	      gray[i] = img.data[i * ch].to_int()
241	    }
242	  }
243	  // 内部区域：x in [1, w-1), y in [1, h-1)
244	  for y in 1..<(h - 1) {
245	    for x in 1..<(w - 1) {
246	      let idx = y * w + x
247	      let ul = gray[idx - w - 1]
248	      let ur = gray[idx - w + 1]
249	      let ll = gray[idx + w - 1]
250	      let lr = gray[idx + w + 1]
251	      let l = gray[idx - 1]
252	      let r = gray[idx + 1]
253	      let u = gray[idx - w]
254	      let d = gray[idx + w]
255	      let gx = ur + r + lr - ul - l - ll
256	      let gy = ll + d + lr - ul - u - ur
257	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
258	      out[idx] = clamp_b(mag.to_int())
259	    }
260	  }
261	  // 边界区域
262	  for y in 0..<h {
263	    let y_inner = y >= 1 && y < h - 1
264	    for x in 0..<w {
265	      if y_inner && x >= 1 && x < w - 1 {
266	        continue
267	      }
268	      let up = clamp_i(y - 1, 0, h - 1)
269	      let down = clamp_i(y + 1, 0, h - 1)
270	      let left = clamp_i(x - 1, 0, w - 1)
271	      let right = clamp_i(x + 1, 0, w - 1)
272	      let ul = gray[up * w + left]
273	      let ur = gray[up * w + right]
274	      let ll = gray[down * w + left]
275	      let lr = gray[down * w + right]
276	      let l = gray[y * w + left]
277	      let r = gray[y * w + right]
278	      let u = gray[up * w + x]
279	      let d = gray[down * w + x]
280	      let gx = ur + r + lr - ul - l - ll
281	      let gy = ll + d + lr - ul - u - ur
282	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
283	      out[y * w + x] = clamp_b(mag.to_int())
284	    }
285	  }
286	  @types.Image::{
287	    width: w,
288	    height: h,
289	    channels: 1,
290	    data: Bytes::from_array(out),
291	  }
292	}
   	…

34 uncovered line(s) in src\process\edge\edge_detect_16f.mbt:

 37	pub fn edge_detect_sobel_16(
 38	  img : @types.Image16,
 39	) -> @types.Image16 raise @types.LoadError {
 40	  // 输入验证
 41	  if img.width <= 0 || img.height <= 0 {
 42	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 43	      "edge_detect_sobel_16: 图像尺寸必须大于0",
 44	    )
 45	  }
 46	  if img.channels < 1 || img.channels > 4 {
 47	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 48	      "edge_detect_sobel_16: 通道数必须在1-4之间",
 49	    )
 50	  }
 51	  let expected_size = img.width * img.height * img.channels * 2
 52	  if img.data.length() != expected_size {
 53	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 54	      "edge_detect_sobel_16: 数据大小不匹配，期望 " +
 55	      expected_size.to_string() +	<-- UNCOVERED
 56	      "，实际 " +
 57	      img.data.length().to_string(),	<-- UNCOVERED
 58	    )
 59	  }
 60	
 61	  let w = img.width
 62	  let h = img.height
 63	  let ch = img.channels
 64	  let n = w * h
 65	  let bpp = ch * 2
 66	  // 先转灰度
 67	  let gray : Array[Int] = Array::make(n, 0)
 68	  for i in 0..<n {
 69	    if ch == 1 {
 70	      gray[i] = read_u16_le_e(img.data, i * bpp)
 71	    } else {
 72	      let r = read_u16_le_e(img.data, i * bpp)
 73	      let g = read_u16_le_e(img.data, i * bpp + 2)
 74	      let b = read_u16_le_e(img.data, i * bpp + 4)
 75	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
 76	    }
 77	  }
 78	  // Sobel 卷积
 79	  let out : Array[Byte] = Array::make(n * 2, b'\x00')
 80	  // 内部区域
 81	  for y in 1..<(h - 1) {
 82	    for x in 1..<(w - 1) {
 83	      let idx = y * w + x
 84	      let idx_l = idx - 1
 85	      let idx_r = idx + 1
 86	      let idx_t = idx - w
 87	      let idx_b = idx + w
 88	      let gx = -gray[idx_t - 1] +
 89	        gray[idx_t + 1] -
 90	        2 * gray[idx_l] +
 91	        2 * gray[idx_r] -
 92	        gray[idx_b - 1] +
 93	        gray[idx_b + 1]
 94	      let gy = -gray[idx_t - 1] -
 95	        2 * gray[idx_t] -
 96	        gray[idx_t + 1] +
 97	        gray[idx_b - 1] +
 98	        2 * gray[idx_b] +
 99	        gray[idx_b + 1]
100	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
101	      let v = (mag + 0.5F).to_int().clamp(min=0, max=65535)
102	      write_u16_le_e(out, idx * 2, v)
103	    }
104	  }
105	  // 边界区域
106	  for y in 0..<h {
107	    for x in 0..<w {
108	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
109	        continue
110	      }
111	      let x_l = clamp_i(x - 1, 0, w - 1)
112	      let x_r = clamp_i(x + 1, 0, w - 1)
113	      let y_t = clamp_i(y - 1, 0, h - 1)
114	      let y_b = clamp_i(y + 1, 0, h - 1)
115	      let gx = -gray[y_t * w + x_l] +
116	        gray[y_t * w + x_r] -
117	        2 * gray[y * w + x_l] +
118	        2 * gray[y * w + x_r] -
119	        gray[y_b * w + x_l] +
120	        gray[y_b * w + x_r]
121	      let gy = -gray[y_t * w + x_l] -
122	        2 * gray[y_t * w + x] -
123	        gray[y_t * w + x_r] +
124	        gray[y_b * w + x_l] +
125	        2 * gray[y_b * w + x] +
126	        gray[y_b * w + x_r]
127	      let mag = Float::from_int(gx * gx + gy * gy).sqrt()
128	      let v = (mag + 0.5F).to_int().clamp(min=0, max=65535)
129	      write_u16_le_e(out, (y * w + x) * 2, v)
130	    }
131	  }
132	  @types.Image16::{
133	    width: w,
134	    height: h,
135	    channels: 1,
136	    data: Bytes::from_array(out),
137	  }
138	}
   	…

143	pub fn edge_detect_sobel_f(
144	  img : @types.ImageF,
145	) -> @types.ImageF raise @types.LoadError {
146	  // 输入验证
147	  if img.width <= 0 || img.height <= 0 {
148	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
149	      "edge_detect_sobel_f: 图像尺寸必须大于0",
150	    )
151	  }
152	  if img.channels < 1 || img.channels > 4 {
153	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
154	      "edge_detect_sobel_f: 通道数必须在1-4之间",
155	    )
156	  }
157	  let expected_size = img.width * img.height * img.channels * 4
158	  if img.data.length() != expected_size {
159	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
160	      "edge_detect_sobel_f: 数据大小不匹配，期望 " +
161	      expected_size.to_string() +	<-- UNCOVERED
162	      "，实际 " +
163	      img.data.length().to_string(),	<-- UNCOVERED
164	    )
165	  }
166	
167	  let w = img.width
168	  let h = img.height
169	  let ch = img.channels
170	  let n = w * h
171	  let bpp = ch * 4
172	  // 先转灰度
173	  let gray : Array[Float] = Array::make(n, 0.0F)
174	  for i in 0..<n {
175	    if ch == 1 {
176	      gray[i] = read_f32_le_e(img.data, i * bpp)
177	    } else {
178	      let r = read_f32_le_e(img.data, i * bpp)
179	      let g = read_f32_le_e(img.data, i * bpp + 4)
180	      let b = read_f32_le_e(img.data, i * bpp + 8)
181	      gray[i] = r * 0.299F + g * 0.587F + b * 0.114F
182	    }
183	  }
184	  // Sobel 卷积
185	  let out : Array[Byte] = Array::make(n * 4, b'\x00')
186	  // 内部区域
187	  for y in 1..<(h - 1) {
188	    for x in 1..<(w - 1) {
189	      let idx = y * w + x
190	      let idx_l = idx - 1
191	      let idx_r = idx + 1
192	      let idx_t = idx - w
193	      let idx_b = idx + w
194	      let gx = -gray[idx_t - 1] +
195	        gray[idx_t + 1] -
196	        2.0F * gray[idx_l] +
197	        2.0F * gray[idx_r] -
198	        gray[idx_b - 1] +
199	        gray[idx_b + 1]
200	      let gy = -gray[idx_t - 1] -
201	        2.0F * gray[idx_t] -
202	        gray[idx_t + 1] +
203	        gray[idx_b - 1] +
204	        2.0F * gray[idx_b] +
205	        gray[idx_b + 1]
206	      let mag = (gx * gx + gy * gy).sqrt()
207	      write_f32_le_e(out, idx * 4, mag)
208	    }
209	  }
210	  // 边界区域
211	  for y in 0..<h {
212	    for x in 0..<w {
213	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
214	        continue
215	      }
216	      let x_l = clamp_i(x - 1, 0, w - 1)
217	      let x_r = clamp_i(x + 1, 0, w - 1)
218	      let y_t = clamp_i(y - 1, 0, h - 1)
219	      let y_b = clamp_i(y + 1, 0, h - 1)
220	      let gx = -gray[y_t * w + x_l] +
221	        gray[y_t * w + x_r] -
222	        2.0F * gray[y * w + x_l] +
223	        2.0F * gray[y * w + x_r] -
224	        gray[y_b * w + x_l] +
225	        gray[y_b * w + x_r]
226	      let gy = -gray[y_t * w + x_l] -
227	        2.0F * gray[y_t * w + x] -
228	        gray[y_t * w + x_r] +
229	        gray[y_b * w + x_l] +
230	        2.0F * gray[y_b * w + x] +
231	        gray[y_b * w + x_r]
232	      let mag = (gx * gx + gy * gy).sqrt()
233	      write_f32_le_e(out, (y * w + x) * 4, mag)
234	    }
235	  }
236	  @types.ImageF::{
237	    width: w,
238	    height: h,
239	    channels: 1,
240	    data: Bytes::from_array(out),
241	  }
242	}
   	…

248	pub fn edge_detect_laplacian_16(
249	  img : @types.Image16,
250	) -> @types.Image16 raise @types.LoadError {
251	  // 输入验证
252	  if img.width <= 0 || img.height <= 0 {
253	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
254	      "edge_detect_laplacian_16: 图像尺寸必须大于0",
255	    )
256	  }
257	  if img.channels < 1 || img.channels > 4 {
258	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
259	      "edge_detect_laplacian_16: 通道数必须在1-4之间",
260	    )
261	  }
262	  let expected_size = img.width * img.height * img.channels * 2
263	  if img.data.length() != expected_size {
264	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
265	      "edge_detect_laplacian_16: 数据大小不匹配，期望 " +
266	      expected_size.to_string() +	<-- UNCOVERED
267	      "，实际 " +
268	      img.data.length().to_string(),	<-- UNCOVERED
269	    )
270	  }
271	
272	  let w = img.width
273	  let h = img.height
274	  let ch = img.channels
275	  let n = w * h
276	  let bpp = ch * 2
277	  // 先转灰度
278	  let gray : Array[Int] = Array::make(n, 0)
279	  for i in 0..<n {
280	    if ch == 1 {
281	      gray[i] = read_u16_le_e(img.data, i * bpp)	<-- UNCOVERED
282	    } else {
283	      let r = read_u16_le_e(img.data, i * bpp)
284	      let g = read_u16_le_e(img.data, i * bpp + 2)
285	      let b = read_u16_le_e(img.data, i * bpp + 4)
286	      gray[i] = (r * 77 + g * 150 + b * 29) / 256
287	    }
288	  }
289	  let out : Array[Byte] = Array::make(n * 2, b'\x00')
290	  // 内部区域
291	  for y in 1..<(h - 1) {
292	    for x in 1..<(w - 1) {
293	      let idx = y * w + x
294	      let center = gray[idx]
295	      let u = gray[idx - w]
296	      let d = gray[idx + w]
297	      let l = gray[idx - 1]
298	      let r = gray[idx + 1]
299	      let lap = u + d + l + r - 4 * center
300	      let val = lap.abs()
301	      let clamped = if val > 65535 { 65535 } else { val }
302	      write_u16_le_e(out, idx * 2, clamped)
303	    }
304	  }
305	  // 边界区域
306	  for y in 0..<h {
307	    for x in 0..<w {
308	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
309	        continue
310	      }
311	      let cx = clamp_i(x, 0, w - 1)
312	      let cy = clamp_i(y, 0, h - 1)
313	      let up = clamp_i(y - 1, 0, h - 1)
314	      let down = clamp_i(y + 1, 0, h - 1)
315	      let left = clamp_i(x - 1, 0, w - 1)
316	      let right = clamp_i(x + 1, 0, w - 1)
317	      let center = gray[cy * w + cx]
318	      let u = gray[up * w + cx]
319	      let d = gray[down * w + cx]
320	      let l = gray[cy * w + left]
321	      let r = gray[cy * w + right]
322	      let lap = u + d + l + r - 4 * center
323	      let val = lap.abs()
324	      let clamped = if val > 65535 { 65535 } else { val }
325	      write_u16_le_e(out, (y * w + x) * 2, clamped)
326	    }
327	  }
328	  @types.Image16::{
329	    width: w,
330	    height: h,
331	    channels: 1,
332	    data: Bytes::from_array(out),
333	  }
334	}
   	…

340	pub fn edge_detect_laplacian_f(
341	  img : @types.ImageF,
342	) -> @types.ImageF raise @types.LoadError {
343	  // 输入验证
344	  if img.width <= 0 || img.height <= 0 {
345	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
346	      "edge_detect_laplacian_f: 图像尺寸必须大于0",
347	    )
348	  }
349	  if img.channels < 1 || img.channels > 4 {
350	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
351	      "edge_detect_laplacian_f: 通道数必须在1-4之间",
352	    )
353	  }
354	  let expected_size = img.width * img.height * img.channels * 4
355	  if img.data.length() != expected_size {
356	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
357	      "edge_detect_laplacian_f: 数据大小不匹配，期望 " +
358	      expected_size.to_string() +	<-- UNCOVERED
359	      "，实际 " +
360	      img.data.length().to_string(),	<-- UNCOVERED
361	    )
362	  }
363	
364	  let w = img.width
365	  let h = img.height
366	  let ch = img.channels
367	  let n = w * h
368	  let bpp = ch * 4
369	  // 先转灰度
370	  let gray : Array[Float] = Array::make(n, 0.0F)
371	  for i in 0..<n {
372	    if ch == 1 {
373	      gray[i] = read_f32_le_e(img.data, i * bpp)	<-- UNCOVERED
374	    } else {
375	      let r = read_f32_le_e(img.data, i * bpp)
376	      let g = read_f32_le_e(img.data, i * bpp + 4)
377	      let b = read_f32_le_e(img.data, i * bpp + 8)
378	      gray[i] = r * 0.299F + g * 0.587F + b * 0.114F
379	    }
380	  }
381	  let out : Array[Byte] = Array::make(n * 4, b'\x00')
382	  // 内部区域
383	  for y in 1..<(h - 1) {
384	    for x in 1..<(w - 1) {
385	      let idx = y * w + x
386	      let center = gray[idx]
387	      let u = gray[idx - w]
388	      let d = gray[idx + w]
389	      let l = gray[idx - 1]
390	      let r = gray[idx + 1]
391	      let lap = u + d + l + r - 4.0F * center
392	      write_f32_le_e(out, idx * 4, lap.abs())
393	    }
394	  }
395	  // 边界区域
396	  for y in 0..<h {
397	    for x in 0..<w {
398	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
399	        continue
400	      }
401	      let cx = clamp_i(x, 0, w - 1)
402	      let cy = clamp_i(y, 0, h - 1)
403	      let up = clamp_i(y - 1, 0, h - 1)
404	      let down = clamp_i(y + 1, 0, h - 1)
405	      let left = clamp_i(x - 1, 0, w - 1)
406	      let right = clamp_i(x + 1, 0, w - 1)
407	      let center = gray[cy * w + cx]
408	      let u = gray[up * w + cx]
409	      let d = gray[down * w + cx]
410	      let l = gray[cy * w + left]
411	      let r = gray[cy * w + right]
412	      let lap = u + d + l + r - 4.0F * center
413	      write_f32_le_e(out, (y * w + x) * 4, lap.abs())
414	    }
415	  }
416	  @types.ImageF::{
417	    width: w,
418	    height: h,
419	    channels: 1,
420	    data: Bytes::from_array(out),
421	  }
422	}
   	…

429	pub fn edge_detect_prewitt_16(
430	  img : @types.Image16,
431	) -> @types.Image16 raise @types.LoadError {
432	  // 输入验证
433	  if img.width <= 0 || img.height <= 0 {
434	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
435	      "edge_detect_prewitt_16: 图像尺寸必须大于0",
436	    )
437	  }
438	  if img.channels < 1 || img.channels > 4 {
439	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
440	      "edge_detect_prewitt_16: 通道数必须在1-4之间",
441	    )
442	  }
443	  let expected_size = img.width * img.height * img.channels * 2
444	  if img.data.length() != expected_size {
445	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
446	      "edge_detect_prewitt_16: 数据大小不匹配，期望 " +
447	      expected_size.to_string() +	<-- UNCOVERED
448	      "，实际 " +
449	      img.data.length().to_string(),	<-- UNCOVERED
450	    )
451	  }
452	
453	  let w = img.width
454	  let h = img.height
455	  let ch = img.channels
456	  let n = w * h
457	  let bpp = ch * 2
458	  // 先转灰度
459	  let gray : Array[Int] = Array::make(n, 0)
460	  for i in 0..<n {
461	    if ch == 1 {
462	      gray[i] = read_u16_le_e(img.data, i * bpp)	<-- UNCOVERED
463	    } else {
464	      let r = read_u16_le_e(img.data, i * bpp)
465	      let g = read_u16_le_e(img.data, i * bpp + 2)
466	      let b = read_u16_le_e(img.data, i * bpp + 4)
467	      gray[i] = (r * 77 + g * 150 + b * 29) / 256
468	    }
469	  }
470	  let out : Array[Byte] = Array::make(n * 2, b'\x00')
471	  // 内部区域
472	  for y in 1..<(h - 1) {
473	    for x in 1..<(w - 1) {
474	      let idx = y * w + x
475	      let ul = gray[idx - w - 1]
476	      let ur = gray[idx - w + 1]
477	      let ll = gray[idx + w - 1]
478	      let lr = gray[idx + w + 1]
479	      let l = gray[idx - 1]
480	      let r = gray[idx + 1]
481	      let u = gray[idx - w]
482	      let d = gray[idx + w]
483	      let gx = ur + r + lr - ul - l - ll
484	      let gy = ll + d + lr - ul - u - ur
485	      let mag = Float::from_int(gx * gx + gy * gy).sqrt().to_int()
486	      let clamped = if mag > 65535 { 65535 } else { mag }
487	      write_u16_le_e(out, idx * 2, clamped)
488	    }
489	  }
490	  // 边界区域
491	  for y in 0..<h {
492	    for x in 0..<w {
493	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
494	        continue
495	      }
496	      let up = clamp_i(y - 1, 0, h - 1)
497	      let down = clamp_i(y + 1, 0, h - 1)
498	      let left = clamp_i(x - 1, 0, w - 1)
499	      let right = clamp_i(x + 1, 0, w - 1)
500	      let ul = gray[up * w + left]
501	      let ur = gray[up * w + right]
502	      let ll = gray[down * w + left]
503	      let lr = gray[down * w + right]
504	      let l = gray[y * w + left]
505	      let r = gray[y * w + right]
506	      let u = gray[up * w + x]
507	      let d = gray[down * w + x]
508	      let gx = ur + r + lr - ul - l - ll
509	      let gy = ll + d + lr - ul - u - ur
510	      let mag = Float::from_int(gx * gx + gy * gy).sqrt().to_int()
511	      let clamped = if mag > 65535 { 65535 } else { mag }
512	      write_u16_le_e(out, (y * w + x) * 2, clamped)
513	    }
514	  }
515	  @types.Image16::{
516	    width: w,
517	    height: h,
518	    channels: 1,
519	    data: Bytes::from_array(out),
520	  }
521	}
   	…

526	pub fn edge_detect_prewitt_f(
527	  img : @types.ImageF,
528	) -> @types.ImageF raise @types.LoadError {
529	  // 输入验证
530	  if img.width <= 0 || img.height <= 0 {
531	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
532	      "edge_detect_prewitt_f: 图像尺寸必须大于0",
533	    )
534	  }
535	  if img.channels < 1 || img.channels > 4 {
536	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
537	      "edge_detect_prewitt_f: 通道数必须在1-4之间",
538	    )
539	  }
540	  let expected_size = img.width * img.height * img.channels * 4
541	  if img.data.length() != expected_size {
542	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
543	      "edge_detect_prewitt_f: 数据大小不匹配，期望 " +
544	      expected_size.to_string() +	<-- UNCOVERED
545	      "，实际 " +
546	      img.data.length().to_string(),	<-- UNCOVERED
547	    )
548	  }
549	
550	  let w = img.width
551	  let h = img.height
552	  let ch = img.channels
553	  let n = w * h
554	  let bpp = ch * 4
555	  // 先转灰度
556	  let gray : Array[Float] = Array::make(n, 0.0F)
557	  for i in 0..<n {
558	    if ch == 1 {
559	      gray[i] = read_f32_le_e(img.data, i * bpp)	<-- UNCOVERED
560	    } else {
561	      let r = read_f32_le_e(img.data, i * bpp)
562	      let g = read_f32_le_e(img.data, i * bpp + 4)
563	      let b = read_f32_le_e(img.data, i * bpp + 8)
564	      gray[i] = r * 0.299F + g * 0.587F + b * 0.114F
565	    }
566	  }
567	  let out : Array[Byte] = Array::make(n * 4, b'\x00')
568	  // 内部区域
569	  for y in 1..<(h - 1) {
570	    for x in 1..<(w - 1) {
571	      let idx = y * w + x
572	      let ul = gray[idx - w - 1]
573	      let ur = gray[idx - w + 1]
574	      let ll = gray[idx + w - 1]
575	      let lr = gray[idx + w + 1]
576	      let l = gray[idx - 1]
577	      let r = gray[idx + 1]
578	      let u = gray[idx - w]
579	      let d = gray[idx + w]
580	      let gx = ur + r + lr - ul - l - ll
581	      let gy = ll + d + lr - ul - u - ur
582	      let mag = (gx * gx + gy * gy).sqrt()
583	      write_f32_le_e(out, idx * 4, mag)
584	    }
585	  }
586	  // 边界区域
587	  for y in 0..<h {
588	    for x in 0..<w {
589	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
590	        continue
591	      }
592	      let up = clamp_i(y - 1, 0, h - 1)
593	      let down = clamp_i(y + 1, 0, h - 1)
594	      let left = clamp_i(x - 1, 0, w - 1)
595	      let right = clamp_i(x + 1, 0, w - 1)
596	      let ul = gray[up * w + left]
597	      let ur = gray[up * w + right]
598	      let ll = gray[down * w + left]
599	      let lr = gray[down * w + right]
600	      let l = gray[y * w + left]
601	      let r = gray[y * w + right]
602	      let u = gray[up * w + x]
603	      let d = gray[down * w + x]
604	      let gx = ur + r + lr - ul - l - ll
605	      let gy = ll + d + lr - ul - u - ur
606	      let mag = (gx * gx + gy * gy).sqrt()
607	      write_f32_le_e(out, (y * w + x) * 4, mag)
608	    }
609	  }
610	  @types.ImageF::{
611	    width: w,
612	    height: h,
613	    channels: 1,
614	    data: Bytes::from_array(out),
615	  }
616	}
   	…

1 uncovered line(s) in src\process\edge\helpers.mbt:

 2	fn clamp_b(v : Int) -> Byte {
 3	  if v < 0 {
 4	    b'\x00'	<-- UNCOVERED
 5	  } else if v > 255 {
 6	    b'\xFF'
 7	  } else {
 8	    v.to_byte()
 9	  }
10	}
  	…

12 uncovered line(s) in src\process\edge\hough.mbt:

 15	pub fn hough_lines(
 16	  img : @types.Image,
 17	  threshold : Int,
 18	  theta_resolution? : Float = 1.0F,
 19	  rho_resolution? : Float = 1.0F,
 20	) -> Array[HoughLine] raise @types.LoadError {
 21	  // 输入验证
 22	  if img.width <= 0 || img.height <= 0 {
 23	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 24	      "hough_lines: 图像尺寸必须大于0",
 25	    )
 26	  }
 27	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 28	    raise @types.LoadError::DecodeFailed(
 29	      "hough_lines: 仅支持 channels=1/3/4",
 30	    )
 31	  }
 32	  let expected_size = img.width * img.height * img.channels
 33	  if img.data.length() != expected_size {
 34	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 35	      "hough_lines: 数据大小不匹配，期望 " +
 36	      expected_size.to_string() +	<-- UNCOVERED
 37	      "，实际 " +
 38	      img.data.length().to_string(),	<-- UNCOVERED
 39	    )
 40	  }
 41	  if threshold <= 0 {
 42	    raise @types.LoadError::DecodeFailed("hough_lines: threshold必须大于0")	<-- UNCOVERED
 43	  }
 44	  if theta_resolution <= 0.0F {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "hough_lines: theta_resolution必须大于0",
 47	    )
 48	  }
 49	  if rho_resolution <= 0.0F {
 50	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 51	      "hough_lines: rho_resolution必须大于0",
 52	    )
 53	  }
 54	  let w = img.width
 55	  let h = img.height
 56	  let ch = img.channels
 57	  // 角度范围 0~180 度
 58	  let n_theta = (180.0F / theta_resolution).to_int()
 59	  let diag = Float::from_int(w * w + h * h).sqrt()
 60	  let max_rho = (diag / rho_resolution).to_int() + 1
 61	  let n_rho = 2 * max_rho + 1
 62	  // 累加器（一维数组，提高缓存友好性）
 63	  let accumulator : Array[Int] = Array::make(n_rho * n_theta, 0)
 64	  // 预计算 sin/cos 表
 65	  let cos_table : Array[Float] = Array::make(n_theta, 0.0F)
 66	  let sin_table : Array[Float] = Array::make(n_theta, 0.0F)
 67	  let angle_scale = theta_resolution * 3.141592653589793F / 180.0F
 68	  for t in 0..<n_theta {
 69	    let angle = Float::from_int(t) * angle_scale
 70	    cos_table[t] = @math.cosf(angle)
 71	    sin_table[t] = @math.sinf(angle)
 72	  }
 73	  // 预计算 1/rho_resolution，用乘法替代除法
 74	  let inv_rho_res = 1.0F / rho_resolution
 75	  let rho_max_idx = n_rho - 1
 76	  // 预计算首通道 to_int（只看第一个通道判断边缘）
 77	  let data_int : Array[Int] = Array::make(w * h, 0)
 78	  for i in 0..<(w * h) {
 79	    data_int[i] = img.data[i * ch].to_int()
 80	  }
 81	  // 遍历边缘点
 82	  for y in 0..<h {
 83	    let fy = Float::from_int(y)
 84	    let row_base = y * w
 85	    for x in 0..<w {
 86	      if data_int[row_base + x] == 0 {
 87	        continue
 88	      }
 89	      let fx = Float::from_int(x)
 90	      for t in 0..<n_theta {
 91	        let rho = fx * cos_table[t] + fy * sin_table[t]
 92	        let raw_idx = (rho * inv_rho_res).to_int() + max_rho
 93	        // 内联 clamp，避免方法调用开销
 94	        let rho_idx = if raw_idx < 0 {
 95	          0	<-- UNCOVERED
 96	        } else if raw_idx > rho_max_idx {
 97	          rho_max_idx	<-- UNCOVERED
 98	        } else {
 99	          raw_idx
100	        }
101	        accumulator[rho_idx * n_theta + t] += 1
102	      }
103	    }
104	  }
105	  // 收集结果
106	  let lines : Array[HoughLine] = []
107	  let max_rho_f = Float::from_int(max_rho)
108	  for r in 0..<n_rho {
109	    let row_base = r * n_theta
110	    for t in 0..<n_theta {
111	      let votes = accumulator[row_base + t]
112	      if votes >= threshold {
113	        lines.push(HoughLine::{
114	          rho: (Float::from_int(r) - max_rho_f) * rho_resolution,
115	          theta: Float::from_int(t) * angle_scale,
116	          votes,
117	        })
118	      }
119	    }
120	  }
121	  // 按票数降序排序
122	  lines.sort_by(fn(a, b) { b.votes - a.votes })
123	  lines
124	}
   	…

128	pub fn hough_lines_nms(
129	  lines : Array[HoughLine],
130	  rho_threshold? : Float = 5.0F,
131	  theta_threshold? : Float = 0.1F,
132	) -> Array[HoughLine] raise @types.LoadError {
133	  // 参数验证
134	  if rho_threshold <= 0.0F {
135	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
136	      "hough_lines_nms: rho_threshold必须大于0",
137	    )
138	  }
139	  if theta_threshold <= 0.0F {
140	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
141	      "hough_lines_nms: theta_threshold必须大于0",
142	    )
143	  }
144	
145	  let result : Array[HoughLine] = []
146	  let suppressed : Array[Bool] = Array::make(lines.length(), false)
147	  for i in 0..<lines.length() {
148	    if suppressed[i] {
149	      continue
150	    }
151	    result.push(lines[i])
152	    for j in (i + 1)..<lines.length() {
153	      if suppressed[j] {
154	        continue	<-- UNCOVERED
155	      }
156	      let dr = (lines[i].rho - lines[j].rho).abs()
157	      let dt = (lines[i].theta - lines[j].theta).abs()
158	      if dr < rho_threshold && dt < theta_threshold {
159	        suppressed[j] = true
160	      }
161	    }
162	  }
163	  result
164	}
   	…

20 uncovered line(s) in src\process\edge\hough_16f.mbt:

  6	pub fn hough_lines_16(
  7	  img : @types.Image16,
  8	  threshold : Int,
  9	  theta_resolution? : Float = 1.0F,
 10	  rho_resolution? : Float = 1.0F,
 11	) -> Array[HoughLine] raise @types.LoadError {
 12	  // 输入验证
 13	  if img.width <= 0 || img.height <= 0 {
 14	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 15	      "hough_lines_16: 图像尺寸必须大于0",
 16	    )
 17	  }
 18	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 19	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 20	      "hough_lines_16: 仅支持 channels=1/3/4",
 21	    )
 22	  }
 23	  let expected_size = img.width * img.height * img.channels * 2
 24	  if img.data.length() != expected_size {
 25	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 26	      "hough_lines_16: 数据大小不匹配，期望 " +
 27	      expected_size.to_string() +	<-- UNCOVERED
 28	      "，实际 " +
 29	      img.data.length().to_string(),	<-- UNCOVERED
 30	    )
 31	  }
 32	  if threshold <= 0 {
 33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 34	      "hough_lines_16: threshold必须大于0",
 35	    )
 36	  }
 37	  if theta_resolution <= 0.0F {
 38	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 39	      "hough_lines_16: theta_resolution必须大于0",
 40	    )
 41	  }
 42	  if rho_resolution <= 0.0F {
 43	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 44	      "hough_lines_16: rho_resolution必须大于0",
 45	    )
 46	  }
 47	  let w = img.width
 48	  let h = img.height
 49	  let ch = img.channels
 50	  let n_theta = (180.0F / theta_resolution).to_int()
 51	  let diag = Float::from_int(w * w + h * h).sqrt()
 52	  let max_rho = (diag / rho_resolution).to_int() + 1
 53	  let n_rho = 2 * max_rho + 1
 54	  let accumulator : Array[Int] = Array::make(n_rho * n_theta, 0)
 55	  let cos_table : Array[Float] = Array::make(n_theta, 0.0F)
 56	  let sin_table : Array[Float] = Array::make(n_theta, 0.0F)
 57	  let angle_scale = theta_resolution * 3.141592653589793F / 180.0F
 58	  for t in 0..<n_theta {
 59	    let angle = Float::from_int(t) * angle_scale
 60	    cos_table[t] = @math.cosf(angle)
 61	    sin_table[t] = @math.sinf(angle)
 62	  }
 63	  let inv_rho_res = 1.0F / rho_resolution
 64	  let rho_max_idx = n_rho - 1
 65	  let data_int : Array[Int] = Array::make(w * h, 0)
 66	  for i in 0..<(w * h) {
 67	    data_int[i] = img.data[i * ch * 2].to_int() |
 68	      (img.data[i * ch * 2 + 1].to_int() << 8)
 69	  }
 70	  for y in 0..<h {
 71	    let fy = Float::from_int(y)
 72	    let row_base = y * w
 73	    for x in 0..<w {
 74	      if data_int[row_base + x] == 0 {
 75	        continue
 76	      }
 77	      let fx = Float::from_int(x)
 78	      for t in 0..<n_theta {
 79	        let rho = fx * cos_table[t] + fy * sin_table[t]
 80	        let raw_idx = (rho * inv_rho_res).to_int() + max_rho
 81	        let rho_idx = if raw_idx < 0 {
 82	          0	<-- UNCOVERED
 83	        } else if raw_idx > rho_max_idx {
 84	          rho_max_idx	<-- UNCOVERED
 85	        } else {
 86	          raw_idx
 87	        }
 88	        accumulator[rho_idx * n_theta + t] += 1
 89	      }
 90	    }
 91	  }
 92	  let lines : Array[HoughLine] = []
 93	  let max_rho_f = Float::from_int(max_rho)
 94	  for r in 0..<n_rho {
 95	    let row_base = r * n_theta
 96	    for t in 0..<n_theta {
 97	      let votes = accumulator[row_base + t]
 98	      if votes >= threshold {
 99	        let rho = (Float::from_int(r) - max_rho_f) * rho_resolution
100	        let theta = Float::from_int(t) * angle_scale
101	        lines.push(HoughLine::{ rho, theta, votes })
102	      }
103	    }
104	  }
105	  lines.sort_by(fn(a, b) { b.votes.compare(a.votes) })
106	  lines
107	}
   	…

111	pub fn hough_lines_f(
112	  img : @types.ImageF,
113	  threshold : Int,
114	  theta_resolution? : Float = 1.0F,
115	  rho_resolution? : Float = 1.0F,
116	) -> Array[HoughLine] raise @types.LoadError {
117	  // 输入验证
118	  if img.width <= 0 || img.height <= 0 {
119	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
120	      "hough_lines_f: 图像尺寸必须大于0",
121	    )
122	  }
123	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
124	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
125	      "hough_lines_f: 仅支持 channels=1/3/4",
126	    )
127	  }
128	  let expected_size = img.width * img.height * img.channels * 4
129	  if img.data.length() != expected_size {
130	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
131	      "hough_lines_f: 数据大小不匹配，期望 " +
132	      expected_size.to_string() +	<-- UNCOVERED
133	      "，实际 " +
134	      img.data.length().to_string(),	<-- UNCOVERED
135	    )
136	  }
137	  if threshold <= 0 {
138	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
139	      "hough_lines_f: threshold必须大于0",
140	    )
141	  }
142	  if theta_resolution <= 0.0F {
143	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
144	      "hough_lines_f: theta_resolution必须大于0",
145	    )
146	  }
147	  if rho_resolution <= 0.0F {
148	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
149	      "hough_lines_f: rho_resolution必须大于0",
150	    )
151	  }
152	  let w = img.width
153	  let h = img.height
154	  let ch = img.channels
155	  let n_theta = (180.0F / theta_resolution).to_int()
156	  let diag = Float::from_int(w * w + h * h).sqrt()
157	  let max_rho = (diag / rho_resolution).to_int() + 1
158	  let n_rho = 2 * max_rho + 1
159	  let accumulator : Array[Int] = Array::make(n_rho * n_theta, 0)
160	  let cos_table : Array[Float] = Array::make(n_theta, 0.0F)
161	  let sin_table : Array[Float] = Array::make(n_theta, 0.0F)
162	  let angle_scale = theta_resolution * 3.141592653589793F / 180.0F
163	  for t in 0..<n_theta {
164	    let angle = Float::from_int(t) * angle_scale
165	    cos_table[t] = @math.cosf(angle)
166	    sin_table[t] = @math.sinf(angle)
167	  }
168	  let inv_rho_res = 1.0F / rho_resolution
169	  let rho_max_idx = n_rho - 1
170	  let data_f : Array[Float] = Array::make(w * h, 0.0F)
171	  for i in 0..<(w * h) {
172	    let bits = img.data[i * ch * 4].to_int() |
173	      (img.data[i * ch * 4 + 1].to_int() << 8) |
174	      (img.data[i * ch * 4 + 2].to_int() << 16) |
175	      (img.data[i * ch * 4 + 3].to_int() << 24)
176	    data_f[i] = Float::reinterpret_from_int(bits)
177	  }
178	  for y in 0..<h {
179	    let fy = Float::from_int(y)
180	    let row_base = y * w
181	    for x in 0..<w {
182	      if data_f[row_base + x] == 0.0F {
183	        continue
184	      }
185	      let fx = Float::from_int(x)
186	      for t in 0..<n_theta {
187	        let rho = fx * cos_table[t] + fy * sin_table[t]
188	        let raw_idx = (rho * inv_rho_res).to_int() + max_rho
189	        let rho_idx = if raw_idx < 0 {
190	          0	<-- UNCOVERED
191	        } else if raw_idx > rho_max_idx {
192	          rho_max_idx	<-- UNCOVERED
193	        } else {
194	          raw_idx
195	        }
196	        accumulator[rho_idx * n_theta + t] += 1
197	      }
198	    }
199	  }
200	  let lines : Array[HoughLine] = []
201	  let max_rho_f = Float::from_int(max_rho)
202	  for r in 0..<n_rho {
203	    let row_base = r * n_theta
204	    for t in 0..<n_theta {
205	      let votes = accumulator[row_base + t]
206	      if votes >= threshold {
207	        let rho = (Float::from_int(r) - max_rho_f) * rho_resolution
208	        let theta = Float::from_int(t) * angle_scale
209	        lines.push(HoughLine::{ rho, theta, votes })
210	      }
211	    }
212	  }
213	  lines.sort_by(fn(a, b) { b.votes.compare(a.votes) })
214	  lines
215	}
   	…

13 uncovered line(s) in src\process\edge\hough_circles.mbt:

 17	pub fn hough_circles(
 18	  img : @types.Image,
 19	  dp? : Int = 1,
 20	  min_dist? : Int = 10,
 21	  param1? : Int = 100,
 22	  param2? : Int = 30,
 23	  min_radius? : Int = 0,
 24	  max_radius? : Int = 0,
 25	) -> Array[Circle] raise @types.LoadError {
 26	  // 输入验证
 27	  if img.width <= 0 || img.height <= 0 {
 28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 29	      "hough_circles: 图像尺寸必须大于0",
 30	    )
 31	  }
 32	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 34	      "hough_circles: 仅支持 channels=1/3/4",
 35	    )
 36	  }
 37	  let expected_size = img.width * img.height * img.channels
 38	  if img.data.length() != expected_size {
 39	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 40	      "hough_circles: 数据大小不匹配，期望 " +
 41	      expected_size.to_string() +	<-- UNCOVERED
 42	      "，实际 " +
 43	      img.data.length().to_string(),	<-- UNCOVERED
 44	    )
 45	  }
 46	  if dp < 1 {
 47	    raise @types.LoadError::DecodeFailed("hough_circles: dp必须 >= 1")	<-- UNCOVERED
 48	  }
 49	  if min_dist < 1 {
 50	    raise @types.LoadError::DecodeFailed("hough_circles: min_dist必须 >= 1")	<-- UNCOVERED
 51	  }
 52	  if param1 <= 0 {
 53	    raise @types.LoadError::DecodeFailed("hough_circles: param1必须大于0")	<-- UNCOVERED
 54	  }
 55	  if param2 <= 0 {
 56	    raise @types.LoadError::DecodeFailed("hough_circles: param2必须大于0")	<-- UNCOVERED
 57	  }
 58	  if min_radius < 0 {
 59	    raise @types.LoadError::DecodeFailed("hough_circles: min_radius必须 >= 0")	<-- UNCOVERED
 60	  }
 61	  if max_radius < 0 {
 62	    raise @types.LoadError::DecodeFailed("hough_circles: max_radius必须 >= 0")	<-- UNCOVERED
 63	  }
 64	  let w = img.width
 65	  let h = img.height
 66	  let n = w * h
 67	  if n == 0 {
 68	    return []	<-- UNCOVERED
 69	  }
 70	  ignore(dp)
 71	  // 转灰度
 72	  let gray : Array[Int] = Array::make(n, 0)
 73	  let ch = img.channels
 74	  for i in 0..<n {
 75	    gray[i] = if ch == 1 {
 76	      img.data[i].to_int()
 77	    } else {
 78	      let r = img.data[i * ch].to_int()
 79	      let g = img.data[i * ch + 1].to_int()
 80	      let b = img.data[i * ch + 2].to_int()
 81	      (r * 299 + g * 587 + b * 114) / 1000
 82	    }
 83	  }
 84	  // Sobel 梯度
 85	  let gx : Array[Int] = Array::make(n, 0)
 86	  let gy : Array[Int] = Array::make(n, 0)
 87	  for y in 1..<(h - 1) {
 88	    for x in 1..<(w - 1) {
 89	      let idx = y * w + x
 90	      gx[idx] = gray[(y - 1) * w + (x + 1)] +
 91	        2 * gray[y * w + (x + 1)] +
 92	        gray[(y + 1) * w + (x + 1)] -
 93	        gray[(y - 1) * w + (x - 1)] -
 94	        2 * gray[y * w + (x - 1)] -
 95	        gray[(y + 1) * w + (x - 1)]
 96	      gy[idx] = gray[(y + 1) * w + (x - 1)] +
 97	        2 * gray[(y + 1) * w + x] +
 98	        gray[(y + 1) * w + (x + 1)] -
 99	        gray[(y - 1) * w + (x - 1)] -
100	        2 * gray[(y - 1) * w + x] -
101	        gray[(y - 1) * w + (x + 1)]
102	    }
103	  }
104	  // 边缘点（梯度幅值 > param1）
105	  let r_max = if max_radius > 0 { max_radius } else { (w + h) / 4 }
106	  let r_min = min_radius
107	  if r_max <= r_min {
108	    return []
109	  }
110	  // 累加器：3D [w * dp][h * dp][r_max - r_min + 1]
111	  let acc_w = w
112	  let acc_h = h
113	  let num_r = r_max - r_min + 1
114	  let acc : Array[Int] = Array::make(acc_w * acc_h * num_r, 0)
115	  // 对每个边缘点投票
116	  for y in 1..<(h - 1) {
117	    for x in 1..<(w - 1) {
118	      let idx = y * w + x
119	      let mag = gx[idx] * gx[idx] + gy[idx] * gy[idx]
120	      if mag < param1 * param1 {
121	        continue
122	      }
123	      // 沿梯度方向投票
124	      let dx = gx[idx]
125	      let dy = gy[idx]
126	      let mag_sqrt = Float::from_int(mag).sqrt().to_int()
127	      if mag_sqrt == 0 {
128	        continue	<-- UNCOVERED
129	      }
130	      for r in r_min..<=r_max {
131	        let cx_pos = x + dx * r / mag_sqrt
132	        let cy_pos = y + dy * r / mag_sqrt
133	        let cx_neg = x - dx * r / mag_sqrt
134	        let cy_neg = y - dy * r / mag_sqrt
135	        if cx_pos >= 0 && cx_pos < acc_w && cy_pos >= 0 && cy_pos < acc_h {
136	          acc[(cy_pos * acc_w + cx_pos) * num_r + (r - r_min)] += 1
137	        }
138	        if cx_neg >= 0 && cx_neg < acc_w && cy_neg >= 0 && cy_neg < acc_h {
139	          acc[(cy_neg * acc_w + cx_neg) * num_r + (r - r_min)] += 1
140	        }
141	      }
142	    }
143	  }
144	  // 找峰值
145	  let candidates : Array[Circle] = []
146	  for r in r_min..<=r_max {
147	    for y in 0..<acc_h {
148	      for x in 0..<acc_w {
149	        let votes = acc[(y * acc_w + x) * num_r + (r - r_min)]
150	        if votes >= param2 {
151	          candidates.push(Circle::{ x, y, radius: r, votes })
152	        }
153	      }
154	    }
155	  }
156	  // 按投票数排序（降序）
157	  candidates.sort_by(fn(a, b) { b.votes.compare(a.votes) })
158	  // 距离过滤
159	  let result : Array[Circle] = []
160	  for c in candidates {
161	    let mut ok = true
162	    for r in result {
163	      let dx = c.x - r.x
164	      let dy = c.y - r.y
165	      if dx * dx + dy * dy < min_dist * min_dist {
166	        ok = false
167	        break
168	      }
169	    }
170	    if ok {
171	      result.push(c)
172	    }
173	  }
174	  result
175	}
   	…

48 uncovered line(s) in src\process\edge\hough_circles_16f.mbt:

  5	pub fn hough_circles_16(
  6	  img : @types.Image16,
  7	  dp? : Int = 1,
  8	  min_dist? : Int = 10,
  9	  param1? : Int = 100,
 10	  param2? : Int = 30,
 11	  min_radius? : Int = 0,
 12	  max_radius? : Int = 0,
 13	) -> Array[Circle] raise @types.LoadError {
 14	  // 输入验证
 15	  if img.width <= 0 || img.height <= 0 {
 16	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 17	      "hough_circles_16: 图像尺寸必须大于0",
 18	    )
 19	  }
 20	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 21	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 22	      "hough_circles_16: 仅支持 channels=1/3/4",
 23	    )
 24	  }
 25	  let expected_size = img.width * img.height * img.channels * 2
 26	  if img.data.length() != expected_size {
 27	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 28	      "hough_circles_16: 数据大小不匹配，期望 " +
 29	      expected_size.to_string() +	<-- UNCOVERED
 30	      "，实际 " +
 31	      img.data.length().to_string(),	<-- UNCOVERED
 32	    )
 33	  }
 34	  if dp < 1 {
 35	    raise @types.LoadError::DecodeFailed("hough_circles_16: dp必须 >= 1")	<-- UNCOVERED
 36	  }
 37	  if min_dist < 1 {
 38	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 39	      "hough_circles_16: min_dist必须 >= 1",
 40	    )
 41	  }
 42	  if param1 <= 0 {
 43	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 44	      "hough_circles_16: param1必须大于0",
 45	    )
 46	  }
 47	  if param2 <= 0 {
 48	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 49	      "hough_circles_16: param2必须大于0",
 50	    )
 51	  }
 52	  if min_radius < 0 {
 53	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 54	      "hough_circles_16: min_radius必须 >= 0",
 55	    )
 56	  }
 57	  if max_radius < 0 {
 58	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 59	      "hough_circles_16: max_radius必须 >= 0",
 60	    )
 61	  }
 62	  let w = img.width
 63	  let h = img.height
 64	  let n = w * h
 65	  if n == 0 {
 66	    return []	<-- UNCOVERED
 67	  }
 68	  ignore(dp)
 69	  let ch = img.channels
 70	  let gray : Array[Int] = Array::make(n, 0)
 71	  for i in 0..<n {
 72	    gray[i] = if ch == 1 {
 73	      img.data[i * 2].to_int() | (img.data[i * 2 + 1].to_int() << 8)
 74	    } else {
 75	      let r = img.data[i * ch * 2].to_int() |
 76	        (img.data[i * ch * 2 + 1].to_int() << 8)
 77	      let g = img.data[(i * ch + 1) * 2].to_int() |
 78	        (img.data[(i * ch + 1) * 2 + 1].to_int() << 8)
 79	      let b = img.data[(i * ch + 2) * 2].to_int() |
 80	        (img.data[(i * ch + 2) * 2 + 1].to_int() << 8)
 81	      (r * 299 + g * 587 + b * 114) / 1000
 82	    }
 83	  }
 84	  let gx : Array[Int] = Array::make(n, 0)
 85	  let gy : Array[Int] = Array::make(n, 0)
 86	  for y in 1..<(h - 1) {
 87	    for x in 1..<(w - 1) {
 88	      let idx = y * w + x
 89	      gx[idx] = gray[(y - 1) * w + (x + 1)] +
 90	        2 * gray[y * w + (x + 1)] +
 91	        gray[(y + 1) * w + (x + 1)] -
 92	        gray[(y - 1) * w + (x - 1)] -
 93	        2 * gray[y * w + (x - 1)] -
 94	        gray[(y + 1) * w + (x - 1)]
 95	      gy[idx] = gray[(y + 1) * w + (x - 1)] +
 96	        2 * gray[(y + 1) * w + x] +
 97	        gray[(y + 1) * w + (x + 1)] -
 98	        gray[(y - 1) * w + (x - 1)] -
 99	        2 * gray[(y - 1) * w + x] -
100	        gray[(y - 1) * w + (x + 1)]
101	    }
102	  }
103	  let r_max = if max_radius > 0 { max_radius } else { (w + h) / 4 }
104	  let r_min = min_radius
105	  if r_max <= r_min {
106	    return []	<-- UNCOVERED
107	  }
108	  let acc_w = w
109	  let acc_h = h
110	  let num_r = r_max - r_min + 1
111	  let acc : Array[Int] = Array::make(acc_w * acc_h * num_r, 0)
112	  for y in 1..<(h - 1) {
113	    for x in 1..<(w - 1) {
114	      let idx = y * w + x
115	      let mag = gx[idx] * gx[idx] + gy[idx] * gy[idx]
116	      if mag < param1 * param1 {
117	        continue
118	      }
119	      let dx = gx[idx]
120	      let dy = gy[idx]
121	      let mag_sqrt = Float::from_int(mag).sqrt().to_int()	<-- UNCOVERED
122	      if mag_sqrt == 0 {
123	        continue	<-- UNCOVERED
124	      }
125	      for r in r_min..<=r_max {
126	        let cx_pos = x + dx * r / mag_sqrt	<-- UNCOVERED
127	        let cy_pos = y + dy * r / mag_sqrt
128	        let cx_neg = x - dx * r / mag_sqrt
129	        let cy_neg = y - dy * r / mag_sqrt
130	        if cx_pos >= 0 && cx_pos < acc_w && cy_pos >= 0 && cy_pos < acc_h {
131	          acc[(cy_pos * acc_w + cx_pos) * num_r + (r - r_min)] += 1	<-- UNCOVERED
132	        }
133	        if cx_neg >= 0 && cx_neg < acc_w && cy_neg >= 0 && cy_neg < acc_h {
134	          acc[(cy_neg * acc_w + cx_neg) * num_r + (r - r_min)] += 1	<-- UNCOVERED
135	        }
136	      }
137	    }
138	  }
139	  let candidates : Array[Circle] = []
140	  for r in r_min..<=r_max {
141	    for y in 0..<acc_h {
142	      for x in 0..<acc_w {
143	        let votes = acc[(y * acc_w + x) * num_r + (r - r_min)]
144	        if votes >= param2 {
145	          candidates.push(Circle::{ x, y, radius: r, votes })	<-- UNCOVERED
146	        }
147	      }
148	    }
149	  }
150	  candidates.sort_by(fn(a, b) { b.votes.compare(a.votes) })	<-- UNCOVERED
151	  let result : Array[Circle] = []
152	  for c in candidates {
153	    let mut ok = true	<-- UNCOVERED
154	    for r in result {
155	      let dx = c.x - r.x	<-- UNCOVERED
156	      let dy = c.y - r.y
157	      if dx * dx + dy * dy < min_dist * min_dist {
158	        ok = false	<-- UNCOVERED
159	        break
160	      }
161	    }
162	    if ok {
163	      result.push(c)	<-- UNCOVERED
164	    }
165	  }
166	  result
167	}
   	…

171	pub fn hough_circles_f(
172	  img : @types.ImageF,
173	  dp? : Int = 1,
174	  min_dist? : Int = 10,
175	  param1? : Int = 100,
176	  param2? : Int = 30,
177	  min_radius? : Int = 0,
178	  max_radius? : Int = 0,
179	) -> Array[Circle] raise @types.LoadError {
180	  // 输入验证
181	  if img.width <= 0 || img.height <= 0 {
182	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
183	      "hough_circles_f: 图像尺寸必须大于0",
184	    )
185	  }
186	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
187	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
188	      "hough_circles_f: 仅支持 channels=1/3/4",
189	    )
190	  }
191	  let expected_size = img.width * img.height * img.channels * 4
192	  if img.data.length() != expected_size {
193	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
194	      "hough_circles_f: 数据大小不匹配，期望 " +
195	      expected_size.to_string() +	<-- UNCOVERED
196	      "，实际 " +
197	      img.data.length().to_string(),	<-- UNCOVERED
198	    )
199	  }
200	  if dp < 1 {
201	    raise @types.LoadError::DecodeFailed("hough_circles_f: dp必须 >= 1")	<-- UNCOVERED
202	  }
203	  if min_dist < 1 {
204	    raise @types.LoadError::DecodeFailed("hough_circles_f: min_dist必须 >= 1")	<-- UNCOVERED
205	  }
206	  if param1 <= 0 {
207	    raise @types.LoadError::DecodeFailed("hough_circles_f: param1必须大于0")	<-- UNCOVERED
208	  }
209	  if param2 <= 0 {
210	    raise @types.LoadError::DecodeFailed("hough_circles_f: param2必须大于0")	<-- UNCOVERED
211	  }
212	  if min_radius < 0 {
213	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
214	      "hough_circles_f: min_radius必须 >= 0",
215	    )
216	  }
217	  if max_radius < 0 {
218	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
219	      "hough_circles_f: max_radius必须 >= 0",
220	    )
221	  }
222	  let w = img.width
223	  let h = img.height
224	  let n = w * h
225	  if n == 0 {
226	    return []	<-- UNCOVERED
227	  }
228	  ignore(dp)
229	  let ch = img.channels
230	  let gray : Array[Float] = Array::make(n, 0.0F)
231	  for i in 0..<n {
232	    gray[i] = if ch == 1 {
233	      let bits = img.data[i * 4].to_int() |
234	        (img.data[i * 4 + 1].to_int() << 8) |
235	        (img.data[i * 4 + 2].to_int() << 16) |
236	        (img.data[i * 4 + 3].to_int() << 24)
237	      Float::reinterpret_from_int(bits)
238	    } else {
239	      let bits_r = img.data[i * ch * 4].to_int() |
240	        (img.data[i * ch * 4 + 1].to_int() << 8) |
241	        (img.data[i * ch * 4 + 2].to_int() << 16) |
242	        (img.data[i * ch * 4 + 3].to_int() << 24)
243	      let bits_g = img.data[(i * ch + 1) * 4].to_int() |
244	        (img.data[(i * ch + 1) * 4 + 1].to_int() << 8) |
245	        (img.data[(i * ch + 1) * 4 + 2].to_int() << 16) |
246	        (img.data[(i * ch + 1) * 4 + 3].to_int() << 24)
247	      let bits_b = img.data[(i * ch + 2) * 4].to_int() |
248	        (img.data[(i * ch + 2) * 4 + 1].to_int() << 8) |
249	        (img.data[(i * ch + 2) * 4 + 2].to_int() << 16) |
250	        (img.data[(i * ch + 2) * 4 + 3].to_int() << 24)
251	      Float::reinterpret_from_int(bits_r) * 0.299F +
252	      Float::reinterpret_from_int(bits_g) * 0.587F +
253	      Float::reinterpret_from_int(bits_b) * 0.114F
254	    }
255	  }
256	  let gx : Array[Float] = Array::make(n, 0.0F)
257	  let gy : Array[Float] = Array::make(n, 0.0F)
258	  for y in 1..<(h - 1) {
259	    for x in 1..<(w - 1) {
260	      let idx = y * w + x
261	      gx[idx] = gray[(y - 1) * w + (x + 1)] +
262	        2.0F * gray[y * w + (x + 1)] +
263	        gray[(y + 1) * w + (x + 1)] -
264	        gray[(y - 1) * w + (x - 1)] -
265	        2.0F * gray[y * w + (x - 1)] -
266	        gray[(y + 1) * w + (x - 1)]
267	      gy[idx] = gray[(y + 1) * w + (x - 1)] +
268	        2.0F * gray[(y + 1) * w + x] +
269	        gray[(y + 1) * w + (x + 1)] -
270	        gray[(y - 1) * w + (x - 1)] -
271	        2.0F * gray[(y - 1) * w + x] -
272	        gray[(y - 1) * w + (x + 1)]
273	    }
274	  }
275	  let r_max = if max_radius > 0 { max_radius } else { (w + h) / 4 }
276	  let r_min = min_radius
277	  if r_max <= r_min {
278	    return []	<-- UNCOVERED
279	  }
280	  let acc_w = w
281	  let acc_h = h
282	  let num_r = r_max - r_min + 1
283	  let acc : Array[Int] = Array::make(acc_w * acc_h * num_r, 0)
284	  let param1_f = Float::from_int(param1) / 255.0F
285	  for y in 1..<(h - 1) {
286	    for x in 1..<(w - 1) {
287	      let idx = y * w + x
288	      let mag = gx[idx] * gx[idx] + gy[idx] * gy[idx]
289	      if mag < param1_f * param1_f {
290	        continue
291	      }
292	      let dx = gx[idx]
293	      let dy = gy[idx]
294	      let mag_sqrt = mag.sqrt()	<-- UNCOVERED
295	      if mag_sqrt == 0.0F {
296	        continue	<-- UNCOVERED
297	      }
298	      for r in r_min..<=r_max {
299	        let cx_pos = x + (dx * Float::from_int(r) / mag_sqrt).to_int()	<-- UNCOVERED
300	        let cy_pos = y + (dy * Float::from_int(r) / mag_sqrt).to_int()
301	        let cx_neg = x - (dx * Float::from_int(r) / mag_sqrt).to_int()
302	        let cy_neg = y - (dy * Float::from_int(r) / mag_sqrt).to_int()
303	        if cx_pos >= 0 && cx_pos < acc_w && cy_pos >= 0 && cy_pos < acc_h {
304	          acc[(cy_pos * acc_w + cx_pos) * num_r + (r - r_min)] += 1	<-- UNCOVERED
305	        }
306	        if cx_neg >= 0 && cx_neg < acc_w && cy_neg >= 0 && cy_neg < acc_h {
307	          acc[(cy_neg * acc_w + cx_neg) * num_r + (r - r_min)] += 1	<-- UNCOVERED
308	        }
309	      }
310	    }
311	  }
312	  let candidates : Array[Circle] = []
313	  for r in r_min..<=r_max {
314	    for y in 0..<acc_h {
315	      for x in 0..<acc_w {
316	        let votes = acc[(y * acc_w + x) * num_r + (r - r_min)]
317	        if votes >= param2 {
318	          candidates.push(Circle::{ x, y, radius: r, votes })	<-- UNCOVERED
319	        }
320	      }
321	    }
322	  }
323	  candidates.sort_by(fn(a, b) { b.votes.compare(a.votes) })	<-- UNCOVERED
324	  let result : Array[Circle] = []
325	  for c in candidates {
326	    let mut ok = true	<-- UNCOVERED
327	    for r in result {
328	      let dx = c.x - r.x	<-- UNCOVERED
329	      let dy = c.y - r.y
330	      if dx * dx + dy * dy < min_dist * min_dist {
331	        ok = false	<-- UNCOVERED
332	        break
333	      }
334	    }
335	    if ok {
336	      result.push(c)	<-- UNCOVERED
337	    }
338	  }
339	  result
340	}
   	…

5 uncovered line(s) in src\process\encryption\encryption_watermark.mbt:

540	pub fn encryption_report_to_string(
541	  encryption : EncryptionResult,
542	  watermark : WatermarkInfo,
543	  quality : ImageEncryptionQuality,
544	) -> String {
545	  let mut report = "## 图像加密与水印报告\n\n"
546	  report = report + "### 加密信息\n\n"
547	  report = report + "| 指标 | 值 |\n|------|------|\n"
548	  let algo_str = if encryption.algorithm == EncryptionAlgorithm::XOR {
549	    "XOR"
550	  } else if encryption.algorithm == EncryptionAlgorithm::AES_Simplified {	<-- UNCOVERED
551	    "AES(简化)"
552	  } else if encryption.algorithm == EncryptionAlgorithm::Chaotic {
553	    "混沌加密"
554	  } else if encryption.algorithm == EncryptionAlgorithm::Arnold {
555	    "Arnold置乱"
556	  } else if encryption.algorithm == EncryptionAlgorithm::Hill {
557	    "Hill密码"
558	  } else if encryption.algorithm == EncryptionAlgorithm::RSA_Simplified {
559	    "RSA(简化)"
560	  } else {
561	    "未知"	<-- UNCOVERED
562	  }
563	  report = report + "| 加密算法 | " + algo_str + " |\n"
564	  report = report +
565	    "| 图像尺寸 | " +
566	    encryption.width.to_string() +
567	    "x" +
568	    encryption.height.to_string() +
569	    " |\n"
570	  report = report +
571	    "| 原始大小 | " +
572	    encryption.file_size_original.to_string() +
573	    " bytes |\n"
574	  report = report +
575	    "| 加密大小 | " +
576	    encryption.file_size_encrypted.to_string() +
577	    " bytes |\n"
578	  report = report +
579	    "| 加密耗时 | " +
580	    encryption.encryption_time_ms.to_string() +
581	    "ms |\n\n"
582	
583	  report = report + "### 水印信息\n\n"
584	  report = report + "| 指标 | 值 |\n|------|------|\n"
585	  let wm_str = if watermark.watermark_type == WatermarkType::Visible {
586	    "可见水印"	<-- UNCOVERED
587	  } else if watermark.watermark_type == WatermarkType::Invisible_LSB {
588	    "LSB不可见水印"
589	  } else if watermark.watermark_type == WatermarkType::Invisible_DCT {	<-- UNCOVERED
590	    "DCT不可见水印"
591	  } else if watermark.watermark_type == WatermarkType::Invisible_DWT {
592	    "DWT不可见水印"
593	  } else if watermark.watermark_type == WatermarkType::Robust {
594	    "鲁棒水印"
595	  } else if watermark.watermark_type == WatermarkType::Fragile {
596	    "脆弱水印"
597	  } else {
598	    "未知"	<-- UNCOVERED
599	  }
600	  report = report + "| 水印类型 | " + wm_str + " |\n"
601	  report = report +
602	    "| 嵌入强度 | " +
603	    watermark.strength.to_string() +
604	    " |\n"
605	  report = report +
606	    "| 已嵌入 | " +
607	    (if watermark.embedded { "是" } else { "否" }) +
608	    " |\n"
609	  report = report +
610	    "| 嵌入位置 | " +
611	    watermark.embedding_position +
612	    " |\n\n"
613	
614	  report = report + "### 加密质量评估\n\n"
615	  report = report + "| 指标 | 值 |\n|------|------|\n"
616	  report = report + "| 信息熵 | " + quality.entropy.to_string() + " |\n"
617	  report = report +
618	    "| 水平相关性 | " +
619	    quality.correlation_horizontal.to_string() +
620	    " |\n"
621	  report = report +
622	    "| 垂直相关性 | " +
623	    quality.correlation_vertical.to_string() +
624	    " |\n"
625	  report = report +
626	    "| 对角相关性 | " +
627	    quality.correlation_diagonal.to_string() +
628	    " |\n"
629	  report = report + "| NPCR | " + quality.npcr.to_string() + "% |\n"
630	  report = report + "| UACI | " + quality.uaci.to_string() + "% |\n"
631	  report = report + "| PSNR | " + quality.psnr.to_string() + " dB |\n"
632	  report = report +
633	    "| 密钥敏感度 | " +
634	    (quality.key_sensitivity * 100.0).to_string() +
635	    "% |\n"
636	
637	  report
638	}
   	…

5 uncovered line(s) in src\process\encryption\image_encryption.mbt:

178	pub fn adjacent_correlation(
179	  img : @types.Image,
180	  direction? : String = "horizontal",
181	) -> Double {
182	  let dir = direction
183	  let w = img.width
184	  let h = img.height
185	  let channels = img.channels
186	  // 采样点数量
187	  let sample_count = if 1000 < w * h { 1000 } else { w * h }
188	  let mut sum_x = 0.0
189	  let mut sum_y = 0.0
190	  let mut sum_xy = 0.0
191	  let mut sum_x2 = 0.0
192	  let mut sum_y2 = 0.0
193	  let mut count = 0
194	  let mut seed = 12345
195	  for _ in 0..<sample_count {
196	    // 随机选择像素位置
197	    let (next, value) = lcg_next(seed)
198	    seed = next
199	    let x = value % w
200	    let (next2, value2) = lcg_next(seed)
201	    seed = next2
202	    let y = value2 % h
203	    let idx = (y * w + x) * channels
204	    let pixel1 = img.data[idx].to_double()
205	    // 相邻像素
206	    let pixel2 = match dir {
207	      "horizontal" =>
208	        if x + 1 < w {
209	          img.data[(y * w + x + 1) * channels].to_double()
210	        } else {
211	          img.data[idx].to_double()	<-- UNCOVERED
212	        }
213	      "vertical" =>
214	        if y + 1 < h {
215	          img.data[((y + 1) * w + x) * channels].to_double()
216	        } else {
217	          img.data[idx].to_double()
218	        }
219	      "diagonal" =>
220	        if x + 1 < w && y + 1 < h {
221	          img.data[((y + 1) * w + x + 1) * channels].to_double()
222	        } else {
223	          img.data[idx].to_double()
224	        }
225	      _ => img.data[idx].to_double()	<-- UNCOVERED
226	    }
227	    sum_x += pixel1
228	    sum_y += pixel2
229	    sum_xy += pixel1 * pixel2
230	    sum_x2 += pixel1 * pixel1
231	    sum_y2 += pixel2 * pixel2
232	    count += 1
233	  }
234	  if count == 0 {
235	    return 0.0	<-- UNCOVERED
236	  }
237	  let n = count.to_double()
238	  let mean_x = sum_x / n
239	  let mean_y = sum_y / n
240	  let cov_xy = sum_xy / n - mean_x * mean_y
241	  let var_x = sum_x2 / n - mean_x * mean_x
242	  let var_y = sum_y2 / n - mean_y * mean_y
243	  let denom = (var_x * var_y).sqrt()
244	  if denom < 0.000001 {
245	    return 0.0
246	  }
247	  cov_xy / denom
248	}
   	…

263	fn compute_npcr(img1 : @types.Image, img2 : @types.Image) -> Double {
264	  if img1.width != img2.width || img1.height != img2.height {
265	    return 0.0	<-- UNCOVERED
266	  }
267	  let w = img1.width
268	  let h = img1.height
269	  let channels = img1.channels.min(img2.channels)
270	  let total = w * h * channels
271	  let mut changed = 0
272	  for i in 0..<total {
273	    if img1.data[i] != img2.data[i] {
274	      changed += 1
275	    }
276	  }
277	  changed.to_double() / total.to_double() * 100.0
278	}
   	…

282	fn compute_uaci(img1 : @types.Image, img2 : @types.Image) -> Double {
283	  if img1.width != img2.width || img1.height != img2.height {
284	    return 0.0	<-- UNCOVERED
285	  }
286	  let w = img1.width
287	  let h = img1.height
288	  let channels = img1.channels.min(img2.channels)
289	  let total = w * h * channels
290	  let mut sum = 0.0
291	  for i in 0..<total {
292	    let diff = (img1.data[i].to_int() - img2.data[i].to_int()).abs().to_double()
293	    sum += diff / 255.0
294	  }
295	  sum / total.to_double() * 100.0
296	}
   	…

42 uncovered line(s) in src\process\enhance\dehaze.mbt:

 31	pub fn dark_channel(
 32	  img : @types.Image,
 33	  patch_size? : Int = 15,
 34	) -> @types.Image raise @types.LoadError {
 35	  // 输入验证
 36	  if img.width <= 0 || img.height <= 0 {
 37	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 38	      "dark_channel: 图像尺寸必须大于0",
 39	    )
 40	  }
 41	  if img.channels < 1 || img.channels > 4 {
 42	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 43	      "dark_channel: 通道数必须在1-4之间",
 44	    )
 45	  }
 46	  let expected_size = img.width * img.height * img.channels
 47	  if img.data.length() != expected_size {
 48	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 49	      "dark_channel: 数据大小不匹配，期望 " +
 50	      expected_size.to_string() +	<-- UNCOVERED
 51	      "，实际 " +
 52	      img.data.length().to_string(),	<-- UNCOVERED
 53	    )
 54	  }
 55	  if patch_size <= 0 || patch_size % 2 == 0 {
 56	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 57	      "dark_channel: patch_size必须是正奇数",
 58	    )
 59	  }
 60	
 61	  let ps = patch_size
 62	  let w = img.width
 63	  let h = img.height
 64	  let channels = img.channels
 65	  let data = img.data
 66	  // 1. 计算每个像素的最小通道值
 67	  let min_channel : Array[Byte] = []
 68	  for i in 0..<(w * h) {
 69	    let idx = i * channels
 70	    let r = data[idx].to_int()
 71	    let g = if channels >= 2 { data[idx + 1].to_int() } else { r }
 72	    let b = if channels >= 3 { data[idx + 2].to_int() } else { r }
 73	    let min_val = r.min(g).min(b)
 74	    min_channel.push(min_val.to_byte())
 75	  }
 76	  // 2. 计算局部最小值（最小值滤波）
 77	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
 78	  let half = ps / 2
 79	  for y in 0..<h {
 80	    for x in 0..<w {
 81	      let mut min_val = 255
 82	      for dy in -half..<(half + 1) {
 83	        for dx in -half..<(half + 1) {
 84	          let nx = x + dx
 85	          let ny = y + dy
 86	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
 87	            let val = min_channel[ny * w + nx].to_int()
 88	            if val < min_val {
 89	              min_val = val
 90	            }
 91	          }
 92	        }
 93	      }
 94	      out_data[y * w + x] = min_val.to_byte()
 95	    }
 96	  }
 97	  @types.Image::{
 98	    width: w,
 99	    height: h,
100	    channels: 1,
101	    data: Bytes::from_array(out_data),
102	  }
103	}
   	…

108	pub fn estimate_atmospheric_light(
109	  img : @types.Image,
110	  dark : @types.Image,
111	  top_percent? : Int = 1000,
112	) -> Array[Int] raise @types.LoadError {
113	  // 输入验证
114	  if img.width <= 0 || img.height <= 0 {
115	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
116	      "estimate_atmospheric_light: 图像尺寸必须大于0",
117	    )
118	  }
119	  if img.channels < 1 || img.channels > 4 {
120	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
121	      "estimate_atmospheric_light: 通道数必须在1-4之间",
122	    )
123	  }
124	  if dark.width != img.width || dark.height != img.height {
125	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
126	      "estimate_atmospheric_light: 暗通道图像尺寸必须与原图一致",
127	    )
128	  }
129	  if dark.channels != 1 {
130	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
131	      "estimate_atmospheric_light: 暗通道图像必须是单通道",
132	    )
133	  }
134	  if top_percent <= 0 {
135	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
136	      "estimate_atmospheric_light: top_percent必须大于0",
137	    )
138	  }
139	
140	  let top = top_percent
141	  let w = img.width
142	  let h = img.height
143	  let channels = img.channels
144	  let img_data = img.data
145	  let dark_data = dark.data
146	  // 收集所有像素的暗通道值和坐标
147	  let pixels : Array[(Int, Int)] = []
148	  for y in 0..<h {
149	    for x in 0..<w {
150	      pixels.push((dark_data[y * w + x].to_int(), y * w + x))
151	    }
152	  }
153	  // 按暗通道值降序排序（选择排序，取前 top 个）
154	  let count = if top < pixels.length() { top } else { pixels.length() }
155	  let selected_indices : Array[Int] = []
156	  let used : Array[Bool] = Array::make(pixels.length(), false)
157	  for _ in 0..<count {
158	    let mut max_val = -1
159	    let mut max_idx = 0
160	    for i in 0..<pixels.length() {
161	      if !used[i] && pixels[i].0 > max_val {
162	        max_val = pixels[i].0
163	        max_idx = i
164	      }
165	    }
166	    used[max_idx] = true
167	    selected_indices.push(pixels[max_idx].1)
168	  }
169	  // 在这些像素中取每个通道的最大值
170	  let atmospheric : Array[Int] = []
171	  for c in 0..<channels {
172	    let mut max_val = 0
173	    for idx in selected_indices {
174	      let val = img_data[idx * channels + c].to_int()
175	      if val > max_val {
176	        max_val = val
177	      }
178	    }
179	    atmospheric.push(max_val)
180	  }
181	  atmospheric
182	}
   	…

187	pub fn estimate_transmission(
188	  img : @types.Image,
189	  atmospheric : Array[Int],
190	  omega? : Double = 0.95,
191	  patch_size? : Int = 15,
192	) -> @types.Image raise @types.LoadError {
193	  // 输入验证
194	  if img.width <= 0 || img.height <= 0 {
195	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
196	      "estimate_transmission: 图像尺寸必须大于0",
197	    )
198	  }
199	  if img.channels < 1 || img.channels > 4 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "estimate_transmission: 通道数必须在1-4之间",
202	    )
203	  }
204	  if atmospheric.length() == 0 {
205	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
206	      "estimate_transmission: atmospheric数组不能为空",
207	    )
208	  }
209	  if omega < 0.0 || omega > 1.0 {
210	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
211	      "estimate_transmission: omega必须在0-1之间",
212	    )
213	  }
214	  if patch_size <= 0 || patch_size % 2 == 0 {
215	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
216	      "estimate_transmission: patch_size必须是正奇数",
217	    )
218	  }
219	
220	  let om = omega
221	  let ps = patch_size
222	  let w = img.width
223	  let h = img.height
224	  let channels = img.channels
225	  let data = img.data
226	  // 1. 归一化图像（除以大气光）
227	  let normalized : Array[Byte] = []
228	  for i in 0..<(w * h) {
229	    for c in 0..<channels {
230	      let idx = i * channels + c
231	      let val = data[idx].to_double()
232	      let a = if c < atmospheric.length() {
233	        atmospheric[c].to_double()
234	      } else {
235	        255.0	<-- UNCOVERED
236	      }
237	      let norm = if a > 0.0 {
238	        (val / a * 255.0).min(255.0).max(0.0)
239	      } else {
240	        0.0	<-- UNCOVERED
241	      }
242	      normalized.push(norm.to_int().to_byte())
243	    }
244	  }
245	  let norm_img = @types.Image::{
246	    width: w,
247	    height: h,
248	    channels,
249	    data: Bytes::from_array(normalized),
250	  }
251	  // 2. 计算归一化图像的暗通道
252	  let dark = dark_channel(norm_img, patch_size=ps)
253	  // 3. 计算透射率
254	  let dark_data = dark.data
255	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
256	  for i in 0..<(w * h) {
257	    let t = 1.0 - om * dark_data[i].to_double() / 255.0
258	    let t_clamped = t.max(0.0).min(1.0)
259	    out_data[i] = (t_clamped * 255.0).to_int().to_byte()
260	  }
261	  @types.Image::{
262	    width: w,
263	    height: h,
264	    channels: 1,
265	    data: Bytes::from_array(out_data),
266	  }
267	}
   	…

272	pub fn guided_filter_image(
273	  input : @types.Image,
274	  guide : @types.Image,
275	  radius? : Int = 20,
276	  eps? : Double = 0.0001,
277	) -> @types.Image raise @types.LoadError {
278	  // 输入验证
279	  if input.width <= 0 || input.height <= 0 {
280	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
281	      "guided_filter_image: 输入图像尺寸必须大于0",
282	    )
283	  }
284	  if guide.width <= 0 || guide.height <= 0 {
285	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
286	      "guided_filter_image: 引导图像尺寸必须大于0",
287	    )
288	  }
289	  if input.width != guide.width || input.height != guide.height {
290	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
291	      "guided_filter_image: 输入图像和引导图像尺寸必须一致",
292	    )
293	  }
294	  if radius <= 0 {
295	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
296	      "guided_filter_image: radius必须大于0",
297	    )
298	  }
299	  if eps < 0.0 {
300	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
301	      "guided_filter_image: eps必须大于等于0",
302	    )
303	  }
304	
305	  let r = radius
306	  let e = eps
307	  let w = input.width
308	  let h = input.height
309	  let input_data = input.data
310	  let guide_data = guide.data
311	  // 简化版引导滤波：使用均值滤波
312	  // 完整引导滤波需要计算均值、方差、协方差等
313	  // 这里用双边滤波近似
314	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
315	  let half = r / 2
316	  for y in 0..<h {
317	    for x in 0..<w {
318	      let mut sum = 0.0
319	      let mut weight_sum = 0.0
320	      let center_guide = guide_data[y * w + x].to_double()
321	      for dy in -half..<(half + 1) {
322	        for dx in -half..<(half + 1) {
323	          let nx = x + dx
324	          let ny = y + dy
325	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
326	            let spatial_dist = (dx * dx + dy * dy).to_double().sqrt()
327	            let guide_diff = (guide_data[ny * w + nx].to_double() - center_guide).abs()
328	            // 双边权重：空间距离 + 引导图差异
329	            let spatial_weight = (1.0 - spatial_dist / half.to_double()).max(
330	              0.0,
331	            )
332	            let range_weight = (1.0 - guide_diff / 255.0).max(0.0)
333	            let weight = spatial_weight * range_weight + e
334	            sum += input_data[ny * w + nx].to_double() * weight
335	            weight_sum += weight
336	          }
337	        }
338	      }
339	      let result = if weight_sum > 0.0 {
340	        sum / weight_sum
341	      } else {
342	        input_data[y * w + x].to_double()	<-- UNCOVERED
343	      }
344	      out_data[y * w + x] = result.min(255.0).max(0.0).to_int().to_byte()
345	    }
346	  }
347	  @types.Image::{
348	    width: w,
349	    height: h,
350	    channels: 1,
351	    data: Bytes::from_array(out_data),
352	  }
353	}
   	…

358	pub fn dehaze_recover(
359	  img : @types.Image,
360	  transmission : @types.Image,
361	  atmospheric : Array[Int],
362	  t_min? : Double = 0.1,
363	) -> @types.Image raise @types.LoadError {
364	  // 输入验证
365	  if img.width <= 0 || img.height <= 0 {
366	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
367	      "dehaze_recover: 图像尺寸必须大于0",
368	    )
369	  }
370	  if img.channels < 1 || img.channels > 4 {
371	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
372	      "dehaze_recover: 通道数必须在1-4之间",
373	    )
374	  }
375	  if transmission.width != img.width || transmission.height != img.height {
376	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
377	      "dehaze_recover: 透射率图像尺寸必须与原图一致",
378	    )
379	  }
380	  if transmission.channels != 1 {
381	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
382	      "dehaze_recover: 透射率图像必须是单通道",
383	    )
384	  }
385	  if atmospheric.length() == 0 {
386	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
387	      "dehaze_recover: atmospheric数组不能为空",
388	    )
389	  }
390	  if t_min < 0.0 || t_min > 1.0 {
391	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
392	      "dehaze_recover: t_min必须在0-1之间",
393	    )
394	  }
395	
396	  let t0 = t_min
397	  let w = img.width
398	  let h = img.height
399	  let channels = img.channels
400	  let img_data = img.data
401	  let trans_data = transmission.data
402	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
403	  for i in 0..<(w * h) {
404	    let t = trans_data[i].to_double() / 255.0
405	    let t_clamped = t.max(t0)
406	    for c in 0..<channels {
407	      let idx = i * channels + c
408	      let val = img_data[idx].to_double()
409	      let a = if c < atmospheric.length() {
410	        atmospheric[c].to_double()
411	      } else {
412	        255.0	<-- UNCOVERED
413	      }
414	      let recovered = (val - a) / t_clamped + a
415	      out_data[idx] = recovered.min(255.0).max(0.0).to_int().to_byte()
416	    }
417	  }
418	  @types.Image::{
419	    width: w,
420	    height: h,
421	    channels,
422	    data: Bytes::from_array(out_data),
423	  }
424	}
   	…

428	pub fn dehaze_advanced(
429	  img : @types.Image,
430	  config? : DehazeConfig,
431	) -> @types.Image raise @types.LoadError {
432	  // 输入验证
433	  if img.width <= 0 || img.height <= 0 {
434	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
435	      "dehaze_advanced: 图像尺寸必须大于0",
436	    )
437	  }
438	  if img.channels < 1 || img.channels > 4 {
439	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
440	      "dehaze_advanced: 通道数必须在1-4之间",
441	    )
442	  }
443	  let expected_size = img.width * img.height * img.channels
444	  if img.data.length() != expected_size {
445	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
446	      "dehaze_advanced: 数据大小不匹配，期望 " +
447	      expected_size.to_string() +	<-- UNCOVERED
448	      "，实际 " +
449	      img.data.length().to_string(),	<-- UNCOVERED
450	    )
451	  }
452	
453	  let cfg = match config {
454	    Some(c) => c	<-- UNCOVERED
455	    None => dehaze_default_config()
456	  }
457	
458	  // 参数验证
459	  if cfg.patch_size <= 0 || cfg.patch_size % 2 == 0 {
460	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
461	      "dehaze_advanced: patch_size必须是正奇数",
462	    )
463	  }
464	  if cfg.omega < 0.0 || cfg.omega > 1.0 {
465	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
466	      "dehaze_advanced: omega必须在0-1之间",
467	    )
468	  }
469	  if cfg.t_min < 0.0 || cfg.t_min > 1.0 {
470	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
471	      "dehaze_advanced: t_min必须在0-1之间",
472	    )
473	  }
474	  if cfg.guided_radius <= 0 {
475	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
476	      "dehaze_advanced: guided_radius必须大于0",
477	    )
478	  }
479	  if cfg.max_atmospheric <= 0 {
480	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
481	      "dehaze_advanced: max_atmospheric必须大于0",
482	    )
483	  }
484	  // 1. 计算暗通道
485	  let dark = dark_channel(img, patch_size=cfg.patch_size)
486	  // 2. 估计大气光
487	  let atmospheric = estimate_atmospheric_light(
488	    img,
489	    dark,
490	    top_percent=cfg.max_atmospheric,
491	  )
492	  // 3. 估计透射率
493	  let transmission_raw = estimate_transmission(
494	    img,
495	    atmospheric,
496	    omega=cfg.omega,
497	    patch_size=cfg.patch_size,
498	  )
499	  // 4. 引导滤波优化透射率
500	  let transmission = guided_filter_image(
501	    transmission_raw,
502	    img,
503	    radius=cfg.guided_radius,
504	    eps=cfg.guided_eps,
505	  )
506	  // 5. 去雾复原
507	  dehaze_recover(img, transmission, atmospheric, t_min=cfg.t_min)
508	}
   	…

31 uncovered line(s) in src\process\enhance\denoise.mbt:

 22	pub fn denoise_nlm(
 23	  img : @types.Image,
 24	  config? : NlmConfig,
 25	) -> @types.Image raise @types.LoadError {
 26	  // 输入验证
 27	  if img.width <= 0 || img.height <= 0 {
 28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 29	      "denoise_nlm: 图像尺寸必须大于0",
 30	    )
 31	  }
 32	  if img.channels < 1 || img.channels > 4 {
 33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 34	      "denoise_nlm: 通道数必须在1-4之间",
 35	    )
 36	  }
 37	  let expected_size = img.width * img.height * img.channels
 38	  if img.data.length() != expected_size {
 39	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 40	      "denoise_nlm: 数据大小不匹配，期望 " +
 41	      expected_size.to_string() +	<-- UNCOVERED
 42	      "，实际 " +
 43	      img.data.length().to_string(),	<-- UNCOVERED
 44	    )
 45	  }
 46	
 47	  let cfg = match config {
 48	    Some(c) => c	<-- UNCOVERED
 49	    None => nlm_default_config()
 50	  }
 51	
 52	  // 参数验证
 53	  if cfg.search_window <= 0 {
 54	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 55	      "denoise_nlm: search_window必须大于0",
 56	    )
 57	  }
 58	  if cfg.patch_size <= 0 {
 59	    raise @types.LoadError::DecodeFailed("denoise_nlm: patch_size必须大于0")	<-- UNCOVERED
 60	  }
 61	  if cfg.h <= 0.0 {
 62	    raise @types.LoadError::DecodeFailed("denoise_nlm: h必须大于0")	<-- UNCOVERED
 63	  }
 64	
 65	  let w = img.width
 66	  let h = img.height
 67	  let channels = img.channels
 68	  let data = img.data
 69	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
 70	  let half_search = cfg.search_window / 2
 71	  let half_patch = cfg.patch_size / 2
 72	  let h2 = cfg.h * cfg.h
 73	  for y in 0..<h {
 74	    for x in 0..<w {
 75	      for c in 0..<channels {
 76	        let mut weighted_sum = 0.0
 77	        let mut weight_sum = 0.0
 78	        // 在搜索窗口内寻找相似块
 79	        for dy in -half_search..<(half_search + 1) {
 80	          for dx in -half_search..<(half_search + 1) {
 81	            let nx = x + dx
 82	            let ny = y + dy
 83	            if nx >= 0 && nx < w && ny >= 0 && ny < h {
 84	              // 计算两个块的相似度（欧氏距离）
 85	              let mut dist = 0.0
 86	              let mut count = 0
 87	              for py in -half_patch..<(half_patch + 1) {
 88	                for px in -half_patch..<(half_patch + 1) {
 89	                  let p1x = x + px
 90	                  let p1y = y + py
 91	                  let p2x = nx + px
 92	                  let p2y = ny + py
 93	                  if p1x >= 0 &&
 94	                    p1x < w &&
 95	                    p1y >= 0 &&
 96	                    p1y < h &&
 97	                    p2x >= 0 &&
 98	                    p2x < w &&
 99	                    p2y >= 0 &&
100	                    p2y < h {
101	                    let v1 = data[(p1y * w + p1x) * channels + c].to_double()
102	                    let v2 = data[(p2y * w + p2x) * channels + c].to_double()
103	                    let diff = v1 - v2
104	                    dist += diff * diff
105	                    count += 1
106	                  }
107	                }
108	              }
109	              if count > 0 {
110	                dist = dist / count.to_double()
111	                // 高斯权重
112	                let weight = @math.exp(-dist / h2)
113	                weighted_sum += data[(ny * w + nx) * channels + c].to_double() *
114	                  weight
115	                weight_sum += weight
116	              }
117	            }
118	          }
119	        }
120	        if weight_sum > 0.0 {
121	          out_data[(y * w + x) * channels + c] = (weighted_sum / weight_sum)
122	            .min(255.0)
123	            .max(0.0)
124	            .to_int()
125	            .to_byte()
126	        } else {
127	          out_data[(y * w + x) * channels + c] = data[(y * w + x) * channels + c]	<-- UNCOVERED
128	        }
129	      }
130	    }
131	  }
132	  @types.Image::{
133	    width: w,
134	    height: h,
135	    channels,
136	    data: Bytes::from_array(out_data),
137	  }
138	}
   	…

143	pub fn denoise_bilateral(
144	  img : @types.Image,
145	  diameter? : Int = 9,
146	  sigma_color? : Double = 75.0,
147	  sigma_space? : Double = 75.0,
148	) -> @types.Image raise @types.LoadError {
149	  // 输入验证
150	  if img.width <= 0 || img.height <= 0 {
151	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
152	      "denoise_bilateral: 图像尺寸必须大于0",
153	    )
154	  }
155	  if img.channels < 1 || img.channels > 4 {
156	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
157	      "denoise_bilateral: 通道数必须在1-4之间",
158	    )
159	  }
160	  let expected_size = img.width * img.height * img.channels
161	  if img.data.length() != expected_size {
162	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
163	      "denoise_bilateral: 数据大小不匹配，期望 " +
164	      expected_size.to_string() +	<-- UNCOVERED
165	      "，实际 " +
166	      img.data.length().to_string(),	<-- UNCOVERED
167	    )
168	  }
169	  if diameter <= 0 {
170	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
171	      "denoise_bilateral: diameter必须大于0",
172	    )
173	  }
174	  if sigma_color <= 0.0 {
175	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
176	      "denoise_bilateral: sigma_color必须大于0",
177	    )
178	  }
179	  if sigma_space <= 0.0 {
180	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
181	      "denoise_bilateral: sigma_space必须大于0",
182	    )
183	  }
184	
185	  let d = diameter
186	  let sc = sigma_color
187	  let ss = sigma_space
188	  let w = img.width
189	  let h = img.height
190	  let channels = img.channels
191	  let data = img.data
192	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
193	  let half = d / 2
194	  for y in 0..<h {
195	    for x in 0..<w {
196	      for c in 0..<channels {
197	        let center_val = data[(y * w + x) * channels + c].to_double()
198	        let mut weighted_sum = 0.0
199	        let mut weight_sum = 0.0
200	        for dy in -half..<(half + 1) {
201	          for dx in -half..<(half + 1) {
202	            let nx = x + dx
203	            let ny = y + dy
204	            if nx >= 0 && nx < w && ny >= 0 && ny < h {
205	              let neighbor_val = data[(ny * w + nx) * channels + c].to_double()
206	              // 空间距离权重
207	              let spatial_dist = (dx * dx + dy * dy).to_double().sqrt()
208	              let spatial_weight = @math.exp(
209	                -(spatial_dist * spatial_dist) / (2.0 * ss * ss),
210	              )
211	              // 像素值差异权重
212	              let color_diff = (center_val - neighbor_val).abs()
213	              let color_weight = @math.exp(
214	                -(color_diff * color_diff) / (2.0 * sc * sc),
215	              )
216	              // 组合权重
217	              let weight = spatial_weight * color_weight
218	              weighted_sum += neighbor_val * weight
219	              weight_sum += weight
220	            }
221	          }
222	        }
223	        if weight_sum > 0.0 {
224	          out_data[(y * w + x) * channels + c] = (weighted_sum / weight_sum)
225	            .min(255.0)
226	            .max(0.0)
227	            .to_int()
228	            .to_byte()
229	        } else {
230	          out_data[(y * w + x) * channels + c] = data[(y * w + x) * channels + c]	<-- UNCOVERED
231	        }
232	      }
233	    }
234	  }
235	  @types.Image::{
236	    width: w,
237	    height: h,
238	    channels,
239	    data: Bytes::from_array(out_data),
240	  }
241	}
   	…

245	pub fn denoise_median(
246	  img : @types.Image,
247	  kernel_size? : Int = 3,
248	) -> @types.Image raise @types.LoadError {
249	  // 输入验证
250	  if img.width <= 0 || img.height <= 0 {
251	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
252	      "denoise_median: 图像尺寸必须大于0",
253	    )
254	  }
255	  if img.channels < 1 || img.channels > 4 {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "denoise_median: 通道数必须在1-4之间",
258	    )
259	  }
260	  let expected_size = img.width * img.height * img.channels
261	  if img.data.length() != expected_size {
262	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
263	      "denoise_median: 数据大小不匹配，期望 " +
264	      expected_size.to_string() +	<-- UNCOVERED
265	      "，实际 " +
266	      img.data.length().to_string(),	<-- UNCOVERED
267	    )
268	  }
269	  if kernel_size <= 0 || kernel_size % 2 == 0 {
270	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
271	      "denoise_median: kernel_size必须是正奇数",
272	    )
273	  }
274	
275	  let ks = kernel_size
276	  let w = img.width
277	  let h = img.height
278	  let channels = img.channels
279	  let data = img.data
280	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
281	  let half = ks / 2
282	  for y in 0..<h {
283	    for x in 0..<w {
284	      for c in 0..<channels {
285	        // 收集邻域像素
286	        let neighborhood : Array[Int] = []
287	        for dy in -half..<(half + 1) {
288	          for dx in -half..<(half + 1) {
289	            let nx = x + dx
290	            let ny = y + dy
291	            if nx >= 0 && nx < w && ny >= 0 && ny < h {
292	              neighborhood.push(data[(ny * w + nx) * channels + c].to_int())
293	            }
294	          }
295	        }
296	        // 排序取中值（选择排序）
297	        let n = neighborhood.length()
298	        for i in 0..<(n / 2 + 1) {
299	          let mut min_idx = i
300	          for j in (i + 1)..<n {
301	            if neighborhood[j] < neighborhood[min_idx] {
302	              min_idx = j	<-- UNCOVERED
303	            }
304	          }
305	          if min_idx != i {
306	            let tmp = neighborhood[i]	<-- UNCOVERED
307	            neighborhood[i] = neighborhood[min_idx]
308	            neighborhood[min_idx] = tmp
309	          }
310	        }
311	        let median = if n > 0 {
312	          neighborhood[n / 2]
313	        } else {
314	          data[(y * w + x) * channels + c].to_int()	<-- UNCOVERED
315	        }
316	        out_data[(y * w + x) * channels + c] = median.to_byte()
317	      }
318	    }
319	  }
320	  @types.Image::{
321	    width: w,
322	    height: h,
323	    channels,
324	    data: Bytes::from_array(out_data),
325	  }
326	}
   	…

330	pub fn denoise(
331	  img : @types.Image,
332	  method_type : String,
333	) -> @types.Image raise @types.LoadError {
334	  // 输入验证
335	  if img.width <= 0 || img.height <= 0 {
336	    raise @types.LoadError::DecodeFailed("denoise: 图像尺寸必须大于0")	<-- UNCOVERED
337	  }
338	  if img.channels < 1 || img.channels > 4 {
339	    raise @types.LoadError::DecodeFailed("denoise: 通道数必须在1-4之间")	<-- UNCOVERED
340	  }
341	
342	  match method_type {
343	    "nlm" => denoise_nlm(img)
344	    "bilateral" => denoise_bilateral(img)	<-- UNCOVERED
345	    "median" => denoise_median(img)
346	    _ => denoise_bilateral(img)
347	  }
348	}
   	…

37 uncovered line(s) in src\process\enhance\hdr.mbt:

29	pub fn hdr_fusion_average(
30	  images : Array[@types.Image],
31	) -> @types.Image raise @types.LoadError {
32	  // 输入验证
33	  if images.length() == 0 {
34	    raise @types.LoadError::DecodeFailed(
35	      "hdr_fusion_average: 图像数组不能为空",
36	    )
37	  }
38	  let w = images[0].width
39	  let h = images[0].height
40	  let channels = images[0].channels
41	
42	  // 验证第一张图像
43	  if w <= 0 || h <= 0 {
44	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
45	      "hdr_fusion_average: 图像尺寸必须大于0",
46	    )
47	  }
48	  if channels < 1 || channels > 4 {
49	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
50	      "hdr_fusion_average: 通道数必须在1-4之间",
51	    )
52	  }
53	
54	  // 验证所有图像尺寸一致且数据大小正确
55	  let expected_size = w * h * channels
56	  for img_idx in 0..<images.length() {
57	    let img = images[img_idx]
58	    if img.width != w || img.height != h || img.channels != channels {
59	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
60	        "hdr_fusion_average: 第 " +
61	        img_idx.to_string() +	<-- UNCOVERED
62	        " 张图像尺寸/通道不一致",
63	      )
64	    }
65	    if img.data.length() != expected_size {
66	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
67	        "hdr_fusion_average: 第 " +
68	        img_idx.to_string() +	<-- UNCOVERED
69	        " 张图像数据大小不匹配",
70	      )
71	    }
72	  }
73	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
74	  let n = images.length().to_double()
75	  for i in 0..<(w * h) {
76	    for c in 0..<channels {
77	      let mut sum = 0.0
78	      for img in images {
79	        sum += img.data[i * channels + c].to_double()
80	      }
81	      let avg = sum / n
82	      out_data[i * channels + c] = avg.min(255.0).max(0.0).to_int().to_byte()
83	    }
84	  }
85	  @types.Image::{
86	    width: w,
87	    height: h,
88	    channels,
89	    data: Bytes::from_array(out_data),
90	  }
91	}
  	…

 96	pub fn hdr_fusion_weighted(
 97	  images : Array[@types.Image],
 98	) -> @types.Image raise @types.LoadError {
 99	  // 输入验证
100	  if images.length() == 0 {
101	    raise @types.LoadError::DecodeFailed(
102	      "hdr_fusion_weighted: 图像数组不能为空",
103	    )
104	  }
105	  let w = images[0].width
106	  let h = images[0].height
107	  let channels = images[0].channels
108	
109	  // 验证第一张图像
110	  if w <= 0 || h <= 0 {
111	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
112	      "hdr_fusion_weighted: 图像尺寸必须大于0",
113	    )
114	  }
115	  if channels < 1 || channels > 4 {
116	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
117	      "hdr_fusion_weighted: 通道数必须在1-4之间",
118	    )
119	  }
120	
121	  // 验证所有图像尺寸一致且数据大小正确
122	  let expected_size = w * h * channels
123	  for img_idx in 0..<images.length() {
124	    let img = images[img_idx]
125	    if img.width != w || img.height != h || img.channels != channels {
126	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
127	        "hdr_fusion_weighted: 第 " +
128	        img_idx.to_string() +	<-- UNCOVERED
129	        " 张图像尺寸/通道不一致",
130	      )
131	    }
132	    if img.data.length() != expected_size {
133	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
134	        "hdr_fusion_weighted: 第 " +
135	        img_idx.to_string() +	<-- UNCOVERED
136	        " 张图像数据大小不匹配",
137	      )
138	    }
139	  }
140	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
141	  // 计算每张图像的权重（基于亮度，中间调权重高）
142	  let weights : Array[Array[Double]] = []
143	  for img in images {
144	    let img_weight : Array[Double] = []
145	    for i in 0..<(w * h) {
146	      let idx = i * channels
147	      let r = img.data[idx].to_double()
148	      let g = if channels >= 2 { img.data[idx + 1].to_double() } else { r }
149	      let b = if channels >= 3 { img.data[idx + 2].to_double() } else { r }
150	      let luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255.0
151	      // 高斯权重：在 0.5 处最大，向两端递减
152	      let weight = @math.exp(-(luminance - 0.5) * (luminance - 0.5) / 0.08)
153	      img_weight.push(weight)
154	    }
155	    weights.push(img_weight)
156	  }
157	  // 加权融合
158	  for i in 0..<(w * h) {
159	    let mut weight_sum = 0.0
160	    for img_idx in 0..<images.length() {
161	      weight_sum += weights[img_idx][i]
162	    }
163	    for c in 0..<channels {
164	      if weight_sum > 0.0 {
165	        let mut sum = 0.0
166	        for img_idx in 0..<images.length() {
167	          let weight = weights[img_idx][i] / weight_sum
168	          sum += images[img_idx].data[i * channels + c].to_double() * weight
169	        }
170	        out_data[i * channels + c] = sum.min(255.0).max(0.0).to_int().to_byte()
171	      } else {
172	        out_data[i * channels + c] = images[0].data[i * channels + c]	<-- UNCOVERED
173	      }
174	    }
175	  }
176	  @types.Image::{
177	    width: w,
178	    height: h,
179	    channels,
180	    data: Bytes::from_array(out_data),
181	  }
182	}
   	…

187	pub fn tone_mapping_reinhard(
188	  img : @types.Image,
189	  key? : Double = 0.18,
190	) -> @types.Image raise @types.LoadError {
191	  // 输入验证
192	  if img.width <= 0 || img.height <= 0 {
193	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
194	      "tone_mapping_reinhard: 图像尺寸必须大于0",
195	    )
196	  }
197	  if img.channels < 1 || img.channels > 4 {
198	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
199	      "tone_mapping_reinhard: 通道数必须在1-4之间",
200	    )
201	  }
202	  let expected_size = img.width * img.height * img.channels
203	  if img.data.length() != expected_size {
204	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
205	      "tone_mapping_reinhard: 数据大小不匹配，期望 " +
206	      expected_size.to_string() +	<-- UNCOVERED
207	      "，实际 " +
208	      img.data.length().to_string(),	<-- UNCOVERED
209	    )
210	  }
211	  if key <= 0.0 {
212	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
213	      "tone_mapping_reinhard: key必须大于0",
214	    )
215	  }
216	
217	  let k = key
218	  let w = img.width
219	  let h = img.height
220	  let channels = img.channels
221	  let data = img.data
222	  // 计算对数平均亮度
223	  let mut log_sum = 0.0
224	  let delta = 0.0001
225	  for i in 0..<(w * h) {
226	    let idx = i * channels
227	    let r = data[idx].to_double() / 255.0
228	    let g = if channels >= 2 { data[idx + 1].to_double() / 255.0 } else { r }
229	    let b = if channels >= 3 { data[idx + 2].to_double() / 255.0 } else { r }
230	    let luminance = 0.299 * r + 0.587 * g + 0.114 * b
231	    log_sum += @math.ln(luminance + delta)
232	  }
233	  let log_avg = @math.exp(log_sum / (w * h).to_double())
234	  // 应用 Reinhard 色调映射
235	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
236	  for i in 0..<(w * h) {
237	    let idx = i * channels
238	    for c in 0..<channels {
239	      let val = data[idx + c].to_double() / 255.0
240	      // 缩放
241	      let scaled = val * k / (log_avg + delta)
242	      // Reinhard 映射
243	      let mapped = scaled / (1.0 + scaled)
244	      out_data[idx + c] = (mapped * 255.0)
245	        .min(255.0)
246	        .max(0.0)
247	        .to_int()
248	        .to_byte()
249	    }
250	  }
251	  @types.Image::{
252	    width: w,
253	    height: h,
254	    channels,
255	    data: Bytes::from_array(out_data),
256	  }
257	}
   	…

262	pub fn tone_mapping_gamma(
263	  img : @types.Image,
264	  gamma? : Double = 1.0 / 2.2,
265	) -> @types.Image raise @types.LoadError {
266	  // 输入验证
267	  if img.width <= 0 || img.height <= 0 {
268	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
269	      "tone_mapping_gamma: 图像尺寸必须大于0",
270	    )
271	  }
272	  if img.channels < 1 || img.channels > 4 {
273	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
274	      "tone_mapping_gamma: 通道数必须在1-4之间",
275	    )
276	  }
277	  let expected_size = img.width * img.height * img.channels
278	  if img.data.length() != expected_size {
279	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
280	      "tone_mapping_gamma: 数据大小不匹配，期望 " +
281	      expected_size.to_string() +	<-- UNCOVERED
282	      "，实际 " +
283	      img.data.length().to_string(),	<-- UNCOVERED
284	    )
285	  }
286	  if gamma <= 0.0 {
287	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
288	      "tone_mapping_gamma: gamma必须大于0",
289	    )
290	  }
291	
292	  let g = gamma
293	  let w = img.width
294	  let h = img.height
295	  let channels = img.channels
296	  let data = img.data
297	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
298	  for i in 0..<(w * h) {
299	    let idx = i * channels
300	    for c in 0..<channels {
301	      let val = data[idx + c].to_double() / 255.0
302	      let mapped = @math.pow(val, g)
303	      out_data[idx + c] = (mapped * 255.0)
304	        .min(255.0)
305	        .max(0.0)
306	        .to_int()
307	        .to_byte()
308	    }
309	  }
310	  @types.Image::{
311	    width: w,
312	    height: h,
313	    channels,
314	    data: Bytes::from_array(out_data),
315	  }
316	}
   	…

321	pub fn tone_mapping_sigmoid(
322	  img : @types.Image,
323	  strength? : Double = 5.0,
324	) -> @types.Image raise @types.LoadError {
325	  // 输入验证
326	  if img.width <= 0 || img.height <= 0 {
327	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
328	      "tone_mapping_sigmoid: 图像尺寸必须大于0",
329	    )
330	  }
331	  if img.channels < 1 || img.channels > 4 {
332	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
333	      "tone_mapping_sigmoid: 通道数必须在1-4之间",
334	    )
335	  }
336	  let expected_size = img.width * img.height * img.channels
337	  if img.data.length() != expected_size {
338	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
339	      "tone_mapping_sigmoid: 数据大小不匹配，期望 " +
340	      expected_size.to_string() +	<-- UNCOVERED
341	      "，实际 " +
342	      img.data.length().to_string(),	<-- UNCOVERED
343	    )
344	  }
345	  if strength <= 0.0 {
346	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
347	      "tone_mapping_sigmoid: strength必须大于0",
348	    )
349	  }
350	
351	  let s = strength
352	  let w = img.width
353	  let h = img.height
354	  let channels = img.channels
355	  let data = img.data
356	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
357	  for i in 0..<(w * h) {
358	    let idx = i * channels
359	    for c in 0..<channels {
360	      let val = data[idx + c].to_double() / 255.0
361	      let mapped = 1.0 / (1.0 + @math.exp(-s * (val - 0.5)))
362	      out_data[idx + c] = (mapped * 255.0)
363	        .min(255.0)
364	        .max(0.0)
365	        .to_int()
366	        .to_byte()
367	    }
368	  }
369	  @types.Image::{
370	    width: w,
371	    height: h,
372	    channels,
373	    data: Bytes::from_array(out_data),
374	  }
375	}
   	…

379	pub fn hdr_process(
380	  images : Array[@types.Image],
381	  config? : HdrConfig,
382	) -> @types.Image raise @types.LoadError {
383	  // 输入验证
384	  if images.length() == 0 {
385	    raise @types.LoadError::DecodeFailed(
386	      "hdr_process: 图像数组不能为空",
387	    )
388	  }
389	
390	  let cfg = match config {
391	    Some(c) => c	<-- UNCOVERED
392	    None => hdr_default_config()
393	  }
394	
395	  // 配置验证
396	  if cfg.gamma <= 0.0 {
397	    raise @types.LoadError::DecodeFailed("hdr_process: gamma必须大于0")	<-- UNCOVERED
398	  }
399	  if cfg.reinhard_key <= 0.0 {
400	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
401	      "hdr_process: reinhard_key必须大于0",
402	    )
403	  }
404	  if cfg.sigmoid_strength <= 0.0 {
405	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
406	      "hdr_process: sigmoid_strength必须大于0",
407	    )
408	  }
409	  // 1. 多曝光融合
410	  let fused = match cfg.fusion_method {
411	    "average" => hdr_fusion_average(images)	<-- UNCOVERED
412	    _ => hdr_fusion_weighted(images)
413	  }
414	  // 2. 色调映射
415	  let tonemapped = match cfg.tone_mapping {
416	    "gamma" => tone_mapping_gamma(fused, gamma=cfg.gamma)	<-- UNCOVERED
417	    "sigmoid" => tone_mapping_sigmoid(fused, strength=cfg.sigmoid_strength)
418	    _ => tone_mapping_reinhard(fused, key=cfg.reinhard_key)
419	  }
420	  tonemapped
421	}
   	…

37 uncovered line(s) in src\process\enhance\inpaint.mbt:

 21	pub fn inpaint_advanced(
 22	  img : @types.Image,
 23	  mask : @types.Image,
 24	  radius? : Int = 5,
 25	) -> @types.Image raise @types.LoadError {
 26	  // 输入验证
 27	  if img.width <= 0 || img.height <= 0 {
 28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 29	      "inpaint_advanced: 图像尺寸必须大于0",
 30	    )
 31	  }
 32	  if img.channels < 1 || img.channels > 4 {
 33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 34	      "inpaint_advanced: 通道数必须在1-4之间",
 35	    )
 36	  }
 37	  let expected_size = img.width * img.height * img.channels
 38	  if img.data.length() != expected_size {
 39	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 40	      "inpaint_advanced: 图像数据大小不匹配，期望 " +
 41	      expected_size.to_string() +	<-- UNCOVERED
 42	      "，实际 " +
 43	      img.data.length().to_string(),	<-- UNCOVERED
 44	    )
 45	  }
 46	  if mask.width != img.width || mask.height != img.height {
 47	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 48	      "inpaint_advanced: 掩码图像尺寸必须与原图一致",
 49	    )
 50	  }
 51	  if mask.channels < 1 {
 52	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 53	      "inpaint_advanced: 掩码通道数必须大于0",
 54	    )
 55	  }
 56	  if radius <= 0 {
 57	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 58	      "inpaint_advanced: radius必须大于0",
 59	    )
 60	  }
 61	
 62	  let r = radius
 63	  let w = img.width
 64	  let h = img.height
 65	  let channels = img.channels
 66	  let img_data = img.data
 67	  let mask_data = mask.data
 68	  // 复制原图
 69	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
 70	  for i in 0..<(w * h * channels) {
 71	    out_data[i] = img_data[i]
 72	  }
 73	  // 标记需要修复的区域（mask 中非零像素）
 74	  let to_inpaint : Array[Bool] = []
 75	  for i in 0..<(w * h) {
 76	    let mask_val = if mask.channels >= 1 {
 77	      mask_data[i * mask.channels].to_int()
 78	    } else {
 79	      0	<-- UNCOVERED
 80	    }
 81	    to_inpaint.push(mask_val > 128)
 82	  }
 83	  // 快速行进法：迭代修复边界像素
 84	  let mut changed = true
 85	  let mut iteration = 0
 86	  while changed && iteration < 1000 {
 87	    changed = false
 88	    iteration += 1
 89	    // 找到所有待修复区域的边界像素
 90	    let boundary : Array[(Int, Int)] = []
 91	    for y in 0..<h {
 92	      for x in 0..<w {
 93	        if to_inpaint[y * w + x] {
 94	          // 检查是否有已修复的邻域
 95	          let mut has_known_neighbor = false
 96	          let neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
 97	          for _tup0 in neighbors {
 98	            let (dx, dy) = _tup0
 99	            let nx = x + dx
100	            let ny = y + dy
101	            if nx >= 0 &&
102	              nx < w &&
103	              ny >= 0 &&
104	              ny < h &&
105	              !to_inpaint[ny * w + nx] {
106	              has_known_neighbor = true
107	            }
108	          }
109	          if has_known_neighbor {
110	            boundary.push((x, y))
111	          }
112	        }
113	      }
114	    }
115	    // 对边界像素进行修复
116	    for _tup1 in boundary {
117	      let (x, y) = _tup1
118	      // 计算邻域已知像素的加权平均
119	      let mut sum_r = 0.0
120	      let mut sum_g = 0.0
121	      let mut sum_b = 0.0
122	      let mut weight_sum = 0.0
123	      for dy in -r..<(r + 1) {
124	        for dx in -r..<(r + 1) {
125	          let nx = x + dx
126	          let ny = y + dy
127	          if nx >= 0 && nx < w && ny >= 0 && ny < h && !to_inpaint[ny * w + nx] {
128	            // 距离加权（越近权重越大）
129	            let dist = (dx * dx + dy * dy).to_double().sqrt()
130	            let weight = 1.0 / (dist + 1.0)
131	            let idx = (ny * w + nx) * channels
132	            sum_r += out_data[idx].to_double() * weight
133	            if channels >= 2 {
134	              sum_g += out_data[idx + 1].to_double() * weight	<-- UNCOVERED
135	            }
136	            if channels >= 3 {
137	              sum_b += out_data[idx + 2].to_double() * weight	<-- UNCOVERED
138	            }
139	            weight_sum += weight
140	          }
141	        }
142	      }
143	      if weight_sum > 0.0 {
144	        let idx = (y * w + x) * channels
145	        out_data[idx] = (sum_r / weight_sum)
146	          .min(255.0)
147	          .max(0.0)
148	          .to_int()
149	          .to_byte()
150	        if channels >= 2 {
151	          out_data[idx + 1] = (sum_g / weight_sum)	<-- UNCOVERED
152	            .min(255.0)
153	            .max(0.0)
154	            .to_int()
155	            .to_byte()
156	        }
157	        if channels >= 3 {
158	          out_data[idx + 2] = (sum_b / weight_sum)	<-- UNCOVERED
159	            .min(255.0)
160	            .max(0.0)
161	            .to_int()
162	            .to_byte()
163	        }
164	        if channels >= 4 {
165	          out_data[idx + 3] = img_data[idx + 3]	<-- UNCOVERED
166	        }
167	        // 标记为已修复
168	        to_inpaint[y * w + x] = false
169	        changed = true
170	      }
171	    }
172	  }
173	  @types.Image::{
174	    width: w,
175	    height: h,
176	    channels,
177	    data: Bytes::from_array(out_data),
178	  }
179	}
   	…

183	pub fn create_rect_mask(
184	  width : Int,
185	  height : Int,
186	  x : Int,
187	  y : Int,
188	  rect_w : Int,
189	  rect_h : Int,
190	) -> @types.Image raise @types.LoadError {
191	  // 输入验证
192	  if width <= 0 || height <= 0 {
193	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
194	      "create_rect_mask: 图像尺寸必须大于0",
195	    )
196	  }
197	  if x < 0 || y < 0 {
198	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
199	      "create_rect_mask: 矩形坐标不能为负",
200	    )
201	  }
202	  if rect_w <= 0 || rect_h <= 0 {
203	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
204	      "create_rect_mask: 矩形尺寸必须大于0",
205	    )
206	  }
207	  if x + rect_w > width || y + rect_h > height {
208	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
209	      "create_rect_mask: 矩形超出图像边界",
210	    )
211	  }
212	
213	  let mask_data : Array[Byte] = Array::make(width * height, b'\x00')
214	  for py in 0..<height {
215	    for px in 0..<width {
216	      if px >= x && px < x + rect_w && py >= y && py < y + rect_h {
217	        mask_data[py * width + px] = b'\xFF'
218	      }
219	    }
220	  }
221	  @types.Image::{
222	    width,
223	    height,
224	    channels: 1,
225	    data: Bytes::from_array(mask_data),
226	  }
227	}
   	…

231	pub fn remove_small_objects(
232	  img : @types.Image,
233	  max_size? : Int = 100,
234	) -> @types.Image raise @types.LoadError {
235	  // 输入验证
236	  if img.width <= 0 || img.height <= 0 {
237	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
238	      "remove_small_objects: 图像尺寸必须大于0",
239	    )
240	  }
241	  if img.channels < 1 || img.channels > 4 {
242	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
243	      "remove_small_objects: 通道数必须在1-4之间",
244	    )
245	  }
246	  let expected_size = img.width * img.height * img.channels
247	  if img.data.length() != expected_size {
248	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
249	      "remove_small_objects: 数据大小不匹配，期望 " +
250	      expected_size.to_string() +	<-- UNCOVERED
251	      "，实际 " +
252	      img.data.length().to_string(),	<-- UNCOVERED
253	    )
254	  }
255	  if max_size <= 0 {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "remove_small_objects: max_size必须大于0",
258	    )
259	  }
260	
261	  let ms = max_size
262	  let w = img.width
263	  let h = img.height
264	  let channels = img.channels
265	  let data = img.data
266	  // 转换为灰度
267	  let gray : Array[Int] = []
268	  for i in 0..<(w * h) {
269	    let idx = i * channels
270	    let r = data[idx].to_int()
271	    let g = if channels >= 2 { data[idx + 1].to_int() } else { r }	<-- UNCOVERED
272	    let b = if channels >= 3 { data[idx + 2].to_int() } else { r }
273	    gray.push(
274	      (0.299 * r.to_double() + 0.587 * g.to_double() + 0.114 * b.to_double()).to_int(),
275	    )
276	  }
277	  // 连通域分析（洪水填充）
278	  let labels : Array[Int] = Array::make(w * h, 0)
279	  let mut next_label = 1
280	  let sizes : Array[Int] = []
281	  for y in 0..<h {
282	    for x in 0..<w {
283	      if gray[y * w + x] > 128 && labels[y * w + x] == 0 {
284	        // 洪水填充
285	        let stack : Array[(Int, Int)] = []	<-- UNCOVERED
286	        stack.push((x, y))
287	        labels[y * w + x] = next_label
288	        let mut size = 0
289	        while stack.length() > 0 {	<-- UNCOVERED
290	          match stack.pop() {
291	            Some((cx, cy)) => {
292	              size += 1
293	              let neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
294	              for _tup2 in neighbors {
295	                let (dx, dy) = _tup2	<-- UNCOVERED
296	                let nx = cx + dx
297	                let ny = cy + dy
298	                if nx >= 0 &&
299	                  nx < w &&
300	                  ny >= 0 &&
301	                  ny < h &&
302	                  gray[ny * w + nx] > 128 &&
303	                  labels[ny * w + nx] == 0 {
304	                  labels[ny * w + nx] = next_label	<-- UNCOVERED
305	                  stack.push((nx, ny))
306	                }
307	              }
308	            }
309	            None => ignore(())	<-- UNCOVERED
310	          }
311	        }
312	        sizes.push(size)	<-- UNCOVERED
313	        next_label += 1
314	      }
315	    }
316	  }
317	  // 去除小物体（将小连通域的像素设为背景色）
318	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
319	  for i in 0..<(w * h) {
320	    let idx = i * channels
321	    let label = labels[i]
322	    if label > 0 && sizes[label - 1] <= ms {
323	      // 小物体：设为黑色（或周围背景色）
324	      for c in 0..<channels {	<-- UNCOVERED
325	        out_data[idx + c] = b'\x00'
326	      }
327	    } else {
328	      // 保留原像素
329	      for c in 0..<channels {
330	        out_data[idx + c] = data[idx + c]
331	      }
332	    }
333	  }
334	  @types.Image::{
335	    width: w,
336	    height: h,
337	    channels,
338	    data: Bytes::from_array(out_data),
339	  }
340	}
   	…

345	pub fn remove_watermark(
346	  img : @types.Image,
347	  x : Int,
348	  y : Int,
349	  width : Int,
350	  height : Int,
351	) -> @types.Image raise @types.LoadError {
352	  // 输入验证
353	  if img.width <= 0 || img.height <= 0 {
354	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
355	      "remove_watermark: 图像尺寸必须大于0",
356	    )
357	  }
358	  if img.channels < 1 || img.channels > 4 {
359	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
360	      "remove_watermark: 通道数必须在1-4之间",
361	    )
362	  }
363	  if x < 0 || y < 0 {
364	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
365	      "remove_watermark: 区域坐标不能为负",
366	    )
367	  }
368	  if width <= 0 || height <= 0 {
369	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
370	      "remove_watermark: 区域尺寸必须大于0",
371	    )
372	  }
373	  if x + width > img.width || y + height > img.height {
374	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
375	      "remove_watermark: 区域超出图像边界",
376	    )
377	  }
378	
379	  let mask = create_rect_mask(img.width, img.height, x, y, width, height)
380	  inpaint_advanced(img, mask, radius=5)
381	}
   	…

12 uncovered line(s) in src\process\explainable_ai\explainable_ai.mbt:

145	pub fn compute_feature_importance(
146	  image : @types.Image,
147	  config : ExplainableAIConfig,
148	) -> FeatureImportanceResult {
149	  let w = image.width
150	  let h = image.height
151	  let _ = w * h
152	
153	  // 简化的特征重要性：基于区域对比度
154	  let grid_size = 32
155	  let num_regions_x = w / grid_size
156	  let num_regions_y = h / grid_size
157	  let num_regions = num_regions_x * num_regions_y
158	
159	  let feature_names : Array[String] = []
160	  let importance_scores : Array[Double] = []
161	
162	  for ry in 0..<num_regions_y {
163	    for rx in 0..<num_regions_x {
164	      let mut sum = 0.0
165	      let mut sum_sq = 0.0
166	      let count = grid_size * grid_size
167	
168	      for y in 0..<grid_size {
169	        for x in 0..<grid_size {
170	          let idx = (ry * grid_size + y) * w + (rx * grid_size + x)
171	          let v = image.data[idx].to_double()
172	          sum += v
173	          sum_sq += v * v
174	        }
175	      }
176	
177	      let mean = sum / count.to_double()
178	      let variance = sum_sq / count.to_double() - mean * mean
179	      let std_dev = if variance > 0.0 { variance.sqrt() } else { 0.0 }
180	
181	      feature_names.push("region_" + rx.to_string() + "_" + ry.to_string())
182	      importance_scores.push(std_dev)
183	    }
184	  }
185	
186	  // 排序获取Top特征
187	  let top_features : Array[String] = []
188	  let top_scores : Array[Double] = []
189	  let indices : Array[Int] = []
190	  for i in 0..<num_regions {
191	    indices.push(i)
192	  }
193	
194	  // 简单的选择排序
195	  for i in 0..<config.num_features {
196	    if i < num_regions {
197	      let mut max_idx = i
198	      let mut max_val = importance_scores[indices[i]]
199	      for j in (i + 1)..<num_regions {
200	        if importance_scores[indices[j]] > max_val {	<-- UNCOVERED
201	          max_val = importance_scores[indices[j]]
202	          max_idx = j
203	        }
204	      }
205	      let temp = indices[i]
206	      indices[i] = indices[max_idx]
207	      indices[max_idx] = temp
208	      top_features.push(feature_names[indices[i]])
209	      top_scores.push(importance_scores[indices[i]])
210	    }
211	  }
212	
213	  FeatureImportanceResult::{
214	    feature_names,
215	    importance_scores,
216	    num_features: num_regions,
217	    method_type: config.method_type,
218	    top_features,
219	    top_scores,
220	  }
221	}
   	…

225	pub fn generate_saliency_map(
226	  image : @types.Image,
227	  config : ExplainableAIConfig,
228	) -> SaliencyMapResult {
229	  let w = image.width
230	  let h = image.height
231	  let _ = w * h
232	  let out : Array[Byte] = []
233	
234	  // 简化的显著性图：基于梯度近似
235	  for y in 0..<h {
236	    for x in 0..<w {
237	      let idx = y * w + x
238	
239	      // 计算局部梯度
240	      let mut gx = 0.0
241	      let mut gy = 0.0
242	
243	      if x > 0 && x < w - 1 {
244	        gx = image.data[idx + 1].to_double() - image.data[idx - 1].to_double()
245	      }
246	      if y > 0 && y < h - 1 {
247	        gy = image.data[idx + w].to_double() - image.data[idx - w].to_double()
248	      }
249	
250	      let gradient_magnitude = (gx * gx + gy * gy).sqrt()
251	      let normalized = if config.normalize {
252	        gradient_magnitude / 255.0 * 255.0
253	      } else {
254	        gradient_magnitude	<-- UNCOVERED
255	      }
256	
257	      let clamped = if normalized > 255.0 { 255.0 } else { normalized }
258	      out.push(clamped.to_int().to_byte())
259	    }
260	  }
261	
262	  SaliencyMapResult::{
263	    saliency_map: @types.Image::{
264	      width: w,
265	      height: h,
266	      channels: 1,
267	      data: Bytes::from_array(out),
268	    },
269	    method_type: config.method_type,
270	    target_class: "class_" + config.target_class.to_string(),
271	    confidence: 0.7,
272	    num_iterations: config.num_samples,
273	  }
274	}
   	…

278	pub fn generate_confusion_matrix(
279	  predictions : Array[Int],
280	  ground_truth : Array[Int],
281	  num_classes : Int,
282	  class_names : Array[String],
283	) -> ConfusionMatrix {
284	  let matrix : Array[Array[Int]] = []
285	  for _ in 0..<num_classes {
286	    let row : Array[Int] = []
287	    for _ in 0..<num_classes {
288	      row.push(0)
289	    }
290	    matrix.push(row)
291	  }
292	
293	  let mut correct = 0
294	  let total = if predictions.length() < ground_truth.length() {
295	    predictions.length()	<-- UNCOVERED
296	  } else {
297	    ground_truth.length()
298	  }
299	
300	  for i in 0..<total {
301	    let pred = predictions[i]
302	    let gt = ground_truth[i]
303	    if pred >= 0 && pred < num_classes && gt >= 0 && gt < num_classes {
304	      matrix[gt][pred] += 1
305	      if pred == gt {
306	        correct += 1
307	      }
308	    }
309	  }
310	
311	  let accuracy = if total > 0 {
312	    correct.to_double() / total.to_double()
313	  } else {
314	    0.0	<-- UNCOVERED
315	  }
316	
317	  // 计算每个类别的精确率、召回率、F1
318	  let precision : Array[Double] = []
319	  let recall : Array[Double] = []
320	  let f1_scores : Array[Double] = []
321	
322	  for c in 0..<num_classes {
323	    let mut tp = 0
324	    let mut fp = 0
325	    let mut false_neg = 0
326	
327	    for i in 0..<num_classes {
328	      if i == c {
329	        tp = matrix[c][i]
330	      } else {
331	        fp += matrix[i][c]
332	        false_neg += matrix[c][i]
333	      }
334	    }
335	
336	    let p = if tp + fp > 0 {
337	      tp.to_double() / (tp + fp).to_double()
338	    } else {
339	      0.0	<-- UNCOVERED
340	    }
341	    let r = if tp + false_neg > 0 {
342	      tp.to_double() / (tp + false_neg).to_double()
343	    } else {
344	      0.0	<-- UNCOVERED
345	    }
346	    let f1 = if p + r > 0.0 { 2.0 * p * r / (p + r) } else { 0.0 }
347	
348	    precision.push(p)
349	    recall.push(r)
350	    f1_scores.push(f1)
351	  }
352	
353	  ConfusionMatrix::{
354	    matrix,
355	    num_classes,
356	    class_names,
357	    accuracy,
358	    precision,
359	    recall,
360	    f1_scores,
361	  }
362	}
   	…

366	pub fn generate_roc_curve(
367	  scores : Array[Double],
368	  labels : Array[Int],
369	  num_thresholds : Int,
370	) -> ROCCurveData {
371	  let fpr : Array[Double] = []
372	  let tpr : Array[Double] = []
373	  let thresholds : Array[Double] = []
374	
375	  let total = if scores.length() < labels.length() {
376	    scores.length()	<-- UNCOVERED
377	  } else {
378	    labels.length()
379	  }
380	
381	  let mut positives = 0
382	  let mut negatives = 0
383	  for i in 0..<total {
384	    if labels[i] == 1 {
385	      positives += 1
386	    } else {
387	      negatives += 1
388	    }
389	  }
390	
391	  for t_idx in 0..<num_thresholds {
392	    let threshold = t_idx.to_double() / (num_thresholds - 1).to_double()
393	    thresholds.push(threshold)
394	
395	    let mut tp = 0
396	    let mut fp = 0
397	    let mut _tn = 0
398	    let mut _false_neg = 0
399	
400	    for i in 0..<total {
401	      let predicted = if scores[i] >= threshold { 1 } else { 0 }
402	      if predicted == 1 && labels[i] == 1 {
403	        tp += 1
404	      } else if predicted == 1 && labels[i] == 0 {
405	        fp += 1
406	      } else if predicted == 0 && labels[i] == 0 {
407	        _tn += 1
408	      } else {
409	        _false_neg += 1
410	      }
411	    }
412	
413	    let fpr_val = if negatives > 0 {
414	      fp.to_double() / negatives.to_double()
415	    } else {
416	      0.0	<-- UNCOVERED
417	    }
418	    let tpr_val = if positives > 0 {
419	      tp.to_double() / positives.to_double()
420	    } else {
421	      0.0	<-- UNCOVERED
422	    }
423	    fpr.push(fpr_val)
424	    tpr.push(tpr_val)
425	  }
426	
427	  // 简化的AUC计算（梯形法则）
428	  let mut auc = 0.0
429	  for i in 1..<num_thresholds {
430	    auc += (fpr[i] - fpr[i - 1]) * (tpr[i] + tpr[i - 1]) / 2.0
431	  }
432	
433	  ROCCurveData::{ fpr, tpr, thresholds, auc, num_points: num_thresholds }
434	}
   	…

487	pub fn explainable_ai_report_to_string(
488	  importance : FeatureImportanceResult,
489	  saliency : SaliencyMapResult,
490	  metrics : ModelPerformanceMetrics,
491	) -> String {
492	  let mut report = "## 可解释AI与模型可视化报告\n\n"
493	
494	  report = report + "### 特征重要性\n\n"
495	  report = report + "| 指标 | 值 |\n|------|------|\n"
496	  report = report +
497	    "| 方法 | " +
498	    (match importance.method_type {
499	      ExplainabilityMethod::LIME => "LIME"	<-- UNCOVERED
500	      ExplainabilityMethod::SHAP => "SHAP"
501	      ExplainabilityMethod::GradCAM => "Grad-CAM"
502	      ExplainabilityMethod::IntegratedGradients => "Integrated Gradients"	<-- UNCOVERED
503	      ExplainabilityMethod::SaliencyMap => "Saliency Map"
504	      _ => "未知"
505	    }) +
506	    " |\n"
507	  report = report +
508	    "| 特征数量 | " +
509	    importance.num_features.to_string() +
510	    " |\n"
511	  report = report +
512	    "| Top特征数 | " +
513	    importance.top_features.length().to_string() +
514	    " |\n\n"
515	
516	  report = report + "### Top 特征\n\n"
517	  report = report + "| 排名 | 特征 | 重要性 |\n|------|------|------|\n"
518	  for i in 0..<importance.top_features.length() {
519	    report = report +
520	      "| " +
521	      (i + 1).to_string() +
522	      " | " +
523	      importance.top_features[i] +
524	      " | " +
525	      (importance.top_scores[i] * 100.0).to_string() +
526	      "% |\n"
527	  }
528	
529	  report = report + "\n### 显著性图\n\n"
530	  report = report + "| 指标 | 值 |\n|------|------|\n"
531	  report = report +
532	    "| 方法 | " +
533	    (match saliency.method_type {
534	      ExplainabilityMethod::GradCAM => "Grad-CAM"
535	      ExplainabilityMethod::SaliencyMap => "Saliency Map"	<-- UNCOVERED
536	      _ => "未知"
537	    }) +
538	    " |\n"
539	  report = report + "| 目标类别 | " + saliency.target_class + " |\n"
540	  report = report +
541	    "| 置信度 | " +
542	    (saliency.confidence * 100.0).to_string() +
543	    "% |\n"
544	  report = report +
545	    "| 迭代次数 | " +
546	    saliency.num_iterations.to_string() +
547	    " |\n"
548	
549	  report = report + "\n### 模型性能\n\n"
550	  report = report + "| 指标 | 值 |\n|------|------|\n"
551	  report = report +
552	    "| 准确率 | " +
553	    (metrics.accuracy * 100.0).to_string() +
554	    "% |\n"
555	  report = report +
556	    "| 精确率 | " +
557	    (metrics.precision * 100.0).to_string() +
558	    "% |\n"
559	  report = report +
560	    "| 召回率 | " +
561	    (metrics.recall * 100.0).to_string() +
562	    "% |\n"
563	  report = report +
564	    "| F1分数 | " +
565	    (metrics.f1_score * 100.0).to_string() +
566	    "% |\n"
567	  report = report + "| AUC-ROC | " + metrics.auc_roc.to_string() + " |\n"
568	  report = report +
569	    "| 推理时间 | " +
570	    metrics.inference_time_ms.to_string() +
571	    " ms |\n"
572	  report = report +
573	    "| 内存占用 | " +
574	    metrics.memory_mb.to_string() +
575	    " MB |\n"
576	  report = report +
577	    "| 模型大小 | " +
578	    metrics.model_size_mb.to_string() +
579	    " MB |\n"
580	  report = report +
581	    "| 参数数量 | " +
582	    metrics.num_parameters.to_string() +
583	    " |\n"
584	
585	  report
586	}
   	…

17 uncovered line(s) in src\process\face_recognition\face_recognition_system.mbt:

249	pub fn extract_face_feature(
250	  img : @types.Image,
251	  face : FaceDetectionResult,
252	  config : FaceRecognitionConfig,
253	) -> FaceFeature {
254	  let w = img.width
255	  let h = img.height
256	  let dimension = config.feature_dimension
257	  let vector : Array[Double] = []
258	
259	  // 简化版：使用人脸区域的图像统计特征作为特征向量
260	  // 实际实现应该使用深度学习模型（FaceNet/ArcFace 等）
261	
262	  let bx = face.bounding_box[0].to_int()
263	  let by = face.bounding_box[1].to_int()
264	  let bw = face.bounding_box[2].to_int()
265	  let bh = face.bounding_box[3].to_int()
266	
267	  // 计算人脸区域的统计特征
268	  let mut sum = 0.0
269	  let mut sum_sq = 0.0
270	  let mut count = 0
271	  for y in 0..<bh {
272	    for x in 0..<bw {
273	      let px = bx + x
274	      let py = by + y
275	      if px >= 0 && px < w && py >= 0 && py < h {
276	        let pixel = img.data[py * w + px].to_double()
277	        sum += pixel
278	        sum_sq += pixel * pixel
279	        count += 1
280	      }
281	    }
282	  }
283	
284	  let mean = if count > 0 { sum / count.to_double() } else { 0.0 }
285	  let variance = if count > 0 {
286	    sum_sq / count.to_double() - mean * mean
287	  } else {
288	    0.0	<-- UNCOVERED
289	  }
290	  let std_dev = if variance > 0.0 { variance.sqrt() } else { 0.0 }
291	
292	  // 生成特征向量（简化版：用统计特征填充）
293	  for i in 0..<dimension {
294	    let value = mean * 0.01 + std_dev * 0.001 + (i % 10).to_double() * 0.001
295	    vector.push(value)
296	  }
297	
298	  // 计算 L2 范数
299	  let mut norm_sq = 0.0
300	  for v in vector {
301	    norm_sq += v * v
302	  }
303	  let norm = if norm_sq > 0.0 { norm_sq.sqrt() } else { 1.0 }
304	
305	  FaceFeature::{ vector, dimension, norm, is_normalized: false }
306	}
   	…

310	pub fn calculate_cosine_similarity(
311	  feat1 : FaceFeature,
312	  feat2 : FaceFeature,
313	) -> Double {
314	  if feat1.dimension != feat2.dimension {
315	    return 0.0
316	  }
317	
318	  let mut dot = 0.0
319	  for i in 0..<feat1.dimension {
320	    dot += feat1.vector[i] * feat2.vector[i]
321	  }
322	
323	  if feat1.norm > 0.0 && feat2.norm > 0.0 {
324	    dot / (feat1.norm * feat2.norm)
325	  } else {
326	    0.0	<-- UNCOVERED
327	  }
328	}
   	…

353	pub fn recognize_face_in_database(
354	  database : FaceDatabase,
355	  feature : FaceFeature,
356	  bounding_box : Array[Double],
357	) -> FaceRecognitionResult {
358	  let mut best_similarity = 0.0
359	  let mut best_index = -1
360	
361	  for i in 0..<database.num_faces {
362	    let similarity = calculate_cosine_similarity(feature, database.faces[i])	<-- UNCOVERED
363	    if similarity > best_similarity {
364	      best_similarity = similarity	<-- UNCOVERED
365	      best_index = i
366	    }
367	  }
368	
369	  if best_index >= 0 && best_similarity >= database.threshold {
370	    FaceRecognitionResult::{	<-- UNCOVERED
371	      face_id: 0,
372	      identity: database.identities[best_index],
373	      confidence: best_similarity,
374	      similarity: best_similarity,
375	      is_known: true,
376	      matched_face_id: best_index,
377	      bounding_box,
378	    }
379	  } else {
380	    FaceRecognitionResult::{
381	      face_id: 0,
382	      identity: "unknown",
383	      confidence: if best_similarity > 0.0 {
384	        best_similarity	<-- UNCOVERED
385	      } else {
386	        0.0
387	      },
388	      similarity: best_similarity,
389	      is_known: false,
390	      matched_face_id: -1,
391	      bounding_box,
392	    }
393	  }
394	}
   	…

398	pub fn recognize_expression(
399	  img : @types.Image,
400	  face : FaceDetectionResult,
401	) -> ExpressionRecognitionResult {
402	  let w = img.width
403	  let h = img.height
404	
405	  // 简化版：基于人脸区域的亮度和对比度变化判断表情
406	  // 实际实现应该使用深度学习模型
407	
408	  let bx = face.bounding_box[0].to_int()
409	  let by = face.bounding_box[1].to_int()
410	  let bw = face.bounding_box[2].to_int()
411	  let bh = face.bounding_box[3].to_int()
412	
413	  // 计算嘴部区域的亮度变化（简化的表情判断）
414	  let mouth_y = by + bh * 3 / 4
415	  let mut mouth_brightness = 0.0
416	  let mut count = 0
417	  for y in mouth_y..<(mouth_y + bh / 4) {
418	    for x in bx..<(bx + bw) {
419	      if x >= 0 && x < w && y >= 0 && y < h {
420	        mouth_brightness += img.data[y * w + x].to_double()
421	        count += 1
422	      }
423	    }
424	  }
425	  mouth_brightness = if count > 0 {
426	    mouth_brightness / count.to_double()
427	  } else {
428	    0.0	<-- UNCOVERED
429	  }
430	
431	  // 简化的表情分类
432	  let mut expression = ExpressionType::Neutral
433	  let mut confidence = 0.55
434	  let probabilities : Array[Double] = []
435	
436	  if mouth_brightness > 150.0 {
437	    expression = ExpressionType::Happy	<-- UNCOVERED
438	    confidence = 0.6
439	    probabilities.push(0.1) // Neutral	<-- UNCOVERED
440	    probabilities.push(0.6) // Happy
441	    probabilities.push(0.1) // Sad
442	    probabilities.push(0.05) // Angry
443	    probabilities.push(0.05) // Surprise
444	    probabilities.push(0.05) // Fear
445	    probabilities.push(0.03) // Disgust
446	    probabilities.push(0.02) // Contempt
447	  } else if mouth_brightness < 80.0 {
448	    expression = ExpressionType::Sad	<-- UNCOVERED
449	    confidence = 0.5
450	    probabilities.push(0.15)	<-- UNCOVERED
451	    probabilities.push(0.1)
452	    probabilities.push(0.5)
453	    probabilities.push(0.1)
454	    probabilities.push(0.05)
455	    probabilities.push(0.05)
456	    probabilities.push(0.03)
457	    probabilities.push(0.02)
458	  } else {
459	    expression = ExpressionType::Neutral
460	    confidence = 0.55
461	    probabilities.push(0.55)
462	    probabilities.push(0.15)
463	    probabilities.push(0.1)
464	    probabilities.push(0.05)
465	    probabilities.push(0.05)
466	    probabilities.push(0.05)
467	    probabilities.push(0.03)
468	    probabilities.push(0.02)
469	  }
470	
471	  let dominant_expression = if expression == ExpressionType::Neutral {
472	    "neutral"
473	  } else if expression == ExpressionType::Happy {	<-- UNCOVERED
474	    "happy"
475	  } else if expression == ExpressionType::Sad {
476	    "sad"
477	  } else if expression == ExpressionType::Angry {
478	    "angry"
479	  } else if expression == ExpressionType::Surprise {
480	    "surprise"
481	  } else if expression == ExpressionType::Fear {
482	    "fear"
483	  } else if expression == ExpressionType::Disgust {
484	    "disgust"
485	  } else if expression == ExpressionType::Contempt {
486	    "contempt"
487	  } else {
488	    "unknown"	<-- UNCOVERED
489	  }
490	
491	  ExpressionRecognitionResult::{
492	    expression,
493	    confidence,
494	    probabilities,
495	    num_classes: 8,
496	    dominant_expression,
497	  }
498	}
   	…

502	pub fn estimate_age_gender(
503	  img : @types.Image,
504	  face : FaceDetectionResult,
505	) -> AgeGenderEstimationResult {
506	  let w = img.width
507	  let h = img.height
508	
509	  // 简化版：基于人脸区域的纹理和皱纹估计年龄
510	  // 实际实现应该使用深度学习模型
511	
512	  let bx = face.bounding_box[0].to_int()
513	  let by = face.bounding_box[1].to_int()
514	  let bw = face.bounding_box[2].to_int()
515	  let bh = face.bounding_box[3].to_int()
516	
517	  // 计算人脸区域的纹理复杂度（简化的年龄估计）
518	  let mut texture_complexity = 0.0
519	  let mut count = 0
520	  for y in 1..<(bh - 1) {
521	    for x in 1..<(bw - 1) {
522	      let px = bx + x
523	      let py = by + y
524	      if px >= 1 && px < w - 1 && py >= 1 && py < h - 1 {
525	        let idx = py * w + px
526	        let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
527	        let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
528	        texture_complexity += (gx * gx + gy * gy).sqrt()
529	        count += 1
530	      }
531	    }
532	  }
533	  texture_complexity = if count > 0 {
534	    texture_complexity / count.to_double()
535	  } else {
536	    0.0	<-- UNCOVERED
537	  }
538	
539	  // 简化的年龄估计（纹理越复杂，年龄越大）
540	  let age = 20.0 + texture_complexity * 2.0
541	  let age_range = [age - 5.0, age + 5.0]
542	
543	  // 简化的性别判断（基于人脸区域的亮度和形状）
544	  let is_male = texture_complexity > 10.0
545	  let gender = if is_male { "male" } else { "female" }
546	  let gender_confidence = 0.6
547	
548	  AgeGenderEstimationResult::{
549	    age,
550	    age_range,
551	    gender,
552	    gender_confidence,
553	    is_male,
554	  }
555	}
   	…

559	pub fn evaluate_face_quality(
560	  img : @types.Image,
561	  face : FaceDetectionResult,
562	) -> Double {
563	  let w = img.width
564	  let h = img.height
565	
566	  let bx = face.bounding_box[0].to_int()
567	  let by = face.bounding_box[1].to_int()
568	  let bw = face.bounding_box[2].to_int()
569	  let bh = face.bounding_box[3].to_int()
570	
571	  // 计算亮度
572	  let mut brightness = 0.0
573	  let mut sharpness = 0.0
574	  let mut count = 0
575	  for y in 1..<(bh - 1) {
576	    for x in 1..<(bw - 1) {
577	      let px = bx + x
578	      let py = by + y
579	      if px >= 1 && px < w - 1 && py >= 1 && py < h - 1 {
580	        let idx = py * w + px
581	        brightness += img.data[idx].to_double()
582	        let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
583	        let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
584	        sharpness += (gx * gx + gy * gy).sqrt()
585	        count += 1
586	      }
587	    }
588	  }
589	
590	  brightness = if count > 0 { brightness / count.to_double() } else { 0.0 }
591	  sharpness = if count > 0 { sharpness / count.to_double() } else { 0.0 }
592	
593	  // 综合质量评分（亮度 40% + 锐度 60%）
594	  let brightness_score = if brightness > 80.0 && brightness < 180.0 {
595	    1.0
596	  } else {
597	    0.5	<-- UNCOVERED
598	  }
599	  let sharpness_score = if sharpness > 20.0 {
600	    1.0	<-- UNCOVERED
601	  } else if sharpness > 10.0 {
602	    0.7	<-- UNCOVERED
603	  } else {
604	    0.4
605	  }
606	
607	  brightness_score * 0.4 + sharpness_score * 0.6
608	}
   	…

62 uncovered line(s) in src\process\feature\feature_advanced_16f.mbt:

22	fn clamp_int_feat(v : Int, lo : Int, hi : Int) -> Int {
23	  if v < lo {
24	    lo	<-- UNCOVERED
25	  } else if v > hi {
26	    hi	<-- UNCOVERED
27	  } else {
28	    v
29	  }
30	}
  	…

 73	pub fn harris_corners_16(
 74	  img : @types.Image16,
 75	  k? : Float = 0.04F,
 76	  threshold? : Float = 1000.0F,
 77	  min_distance? : Int = 5,
 78	) -> Array[CornerPoint] raise @types.LoadError {
 79	  // 输入验证
 80	  if img.width <= 0 || img.height <= 0 {
 81	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 82	      "harris_corners_16: 图像尺寸必须大于0",
 83	    )
 84	  }
 85	  if img.channels < 1 || img.channels > 4 {
 86	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 87	      "harris_corners_16: 通道数必须在1-4之间",
 88	    )
 89	  }
 90	  let expected_size = img.width * img.height * img.channels * 2
 91	  if img.data.length() != expected_size {
 92	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 93	      "harris_corners_16: 数据大小不匹配，期望 " +
 94	      expected_size.to_string() +	<-- UNCOVERED
 95	      "，实际 " +
 96	      img.data.length().to_string(),	<-- UNCOVERED
 97	    )
 98	  }
 99	  if k <= 0.0F {
100	    raise @types.LoadError::DecodeFailed("harris_corners_16: k必须大于0")	<-- UNCOVERED
101	  }
102	  if threshold <= 0.0F {
103	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
104	      "harris_corners_16: threshold必须大于0",
105	    )
106	  }
107	  if min_distance <= 0 {
108	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
109	      "harris_corners_16: min_distance必须大于0",
110	    )
111	  }
112	
113	  let img8 = img16_to_img8_feat(img)
114	  harris_corners(img8, k~, threshold~, min_distance~)
115	}
   	…

121	pub fn harris_corners_f(
122	  img : @types.ImageF,
123	  k? : Float = 0.04F,
124	  threshold? : Float = 1000.0F,
125	  min_distance? : Int = 5,
126	) -> Array[CornerPoint] raise @types.LoadError {
127	  // 输入验证
128	  if img.width <= 0 || img.height <= 0 {
129	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
130	      "harris_corners_f: 图像尺寸必须大于0",
131	    )
132	  }
133	  if img.channels < 1 || img.channels > 4 {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "harris_corners_f: 通道数必须在1-4之间",
136	    )
137	  }
138	  let expected_size = img.width * img.height * img.channels * 4
139	  if img.data.length() != expected_size {
140	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
141	      "harris_corners_f: 数据大小不匹配，期望 " +
142	      expected_size.to_string() +	<-- UNCOVERED
143	      "，实际 " +
144	      img.data.length().to_string(),	<-- UNCOVERED
145	    )
146	  }
147	  if k <= 0.0F {
148	    raise @types.LoadError::DecodeFailed("harris_corners_f: k必须大于0")	<-- UNCOVERED
149	  }
150	  if threshold <= 0.0F {
151	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
152	      "harris_corners_f: threshold必须大于0",
153	    )
154	  }
155	  if min_distance <= 0 {
156	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
157	      "harris_corners_f: min_distance必须大于0",
158	    )
159	  }
160	
161	  let img8 = imgf_to_img8_feat(img)
162	  harris_corners(img8, k~, threshold~, min_distance~)
163	}
   	…

169	pub fn sift_detect_16(
170	  img : @types.Image16,
171	  max_features? : Int = 500,
172	  contrast_threshold? : Float = 0.04F,
173	) -> Array[SiftDescriptor] raise @types.LoadError {
174	  // 输入验证
175	  if img.width <= 0 || img.height <= 0 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "sift_detect_16: 图像尺寸必须大于0",
178	    )
179	  }
180	  if img.channels < 1 || img.channels > 4 {
181	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
182	      "sift_detect_16: 通道数必须在1-4之间",
183	    )
184	  }
185	  let expected_size = img.width * img.height * img.channels * 2
186	  if img.data.length() != expected_size {
187	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
188	      "sift_detect_16: 数据大小不匹配，期望 " +
189	      expected_size.to_string() +	<-- UNCOVERED
190	      "，实际 " +
191	      img.data.length().to_string(),	<-- UNCOVERED
192	    )
193	  }
194	  if max_features <= 0 {
195	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
196	      "sift_detect_16: max_features必须大于0",
197	    )
198	  }
199	  if contrast_threshold <= 0.0F {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "sift_detect_16: contrast_threshold必须大于0",
202	    )
203	  }
204	
205	  let img8 = img16_to_img8_feat(img)
206	  sift_detect(img8, max_features~, contrast_threshold~)
207	}
   	…

213	pub fn sift_detect_f(
214	  img : @types.ImageF,
215	  max_features? : Int = 500,
216	  contrast_threshold? : Float = 0.04F,
217	) -> Array[SiftDescriptor] raise @types.LoadError {
218	  // 输入验证
219	  if img.width <= 0 || img.height <= 0 {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "sift_detect_f: 图像尺寸必须大于0",
222	    )
223	  }
224	  if img.channels < 1 || img.channels > 4 {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "sift_detect_f: 通道数必须在1-4之间",
227	    )
228	  }
229	  let expected_size = img.width * img.height * img.channels * 4
230	  if img.data.length() != expected_size {
231	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
232	      "sift_detect_f: 数据大小不匹配，期望 " +
233	      expected_size.to_string() +	<-- UNCOVERED
234	      "，实际 " +
235	      img.data.length().to_string(),	<-- UNCOVERED
236	    )
237	  }
238	  if max_features <= 0 {
239	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
240	      "sift_detect_f: max_features必须大于0",
241	    )
242	  }
243	  if contrast_threshold <= 0.0F {
244	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
245	      "sift_detect_f: contrast_threshold必须大于0",
246	    )
247	  }
248	
249	  let img8 = imgf_to_img8_feat(img)
250	  sift_detect(img8, max_features~, contrast_threshold~)
251	}
   	…

257	pub fn orb_detect_16(
258	  img : @types.Image16,
259	  max_features? : Int = 500,
260	  threshold? : Int = 20,
261	) -> Array[OrbDescriptor] raise @types.LoadError {
262	  // 输入验证
263	  if img.width <= 0 || img.height <= 0 {
264	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
265	      "orb_detect_16: 图像尺寸必须大于0",
266	    )
267	  }
268	  if img.channels < 1 || img.channels > 4 {
269	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
270	      "orb_detect_16: 通道数必须在1-4之间",
271	    )
272	  }
273	  let expected_size = img.width * img.height * img.channels * 2
274	  if img.data.length() != expected_size {
275	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
276	      "orb_detect_16: 数据大小不匹配，期望 " +
277	      expected_size.to_string() +	<-- UNCOVERED
278	      "，实际 " +
279	      img.data.length().to_string(),	<-- UNCOVERED
280	    )
281	  }
282	  if max_features <= 0 {
283	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
284	      "orb_detect_16: max_features必须大于0",
285	    )
286	  }
287	  if threshold <= 0 {
288	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
289	      "orb_detect_16: threshold必须大于0",
290	    )
291	  }
292	
293	  let img8 = img16_to_img8_feat(img)
294	  orb_detect(img8, max_features~, threshold~)
295	}
   	…

301	pub fn orb_detect_f(
302	  img : @types.ImageF,
303	  max_features? : Int = 500,
304	  threshold? : Int = 20,
305	) -> Array[OrbDescriptor] raise @types.LoadError {
306	  // 输入验证
307	  if img.width <= 0 || img.height <= 0 {
308	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
309	      "orb_detect_f: 图像尺寸必须大于0",
310	    )
311	  }
312	  if img.channels < 1 || img.channels > 4 {
313	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
314	      "orb_detect_f: 通道数必须在1-4之间",
315	    )
316	  }
317	  let expected_size = img.width * img.height * img.channels * 4
318	  if img.data.length() != expected_size {
319	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
320	      "orb_detect_f: 数据大小不匹配，期望 " +
321	      expected_size.to_string() +	<-- UNCOVERED
322	      "，实际 " +
323	      img.data.length().to_string(),	<-- UNCOVERED
324	    )
325	  }
326	  if max_features <= 0 {
327	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
328	      "orb_detect_f: max_features必须大于0",
329	    )
330	  }
331	  if threshold <= 0 {
332	    raise @types.LoadError::DecodeFailed("orb_detect_f: threshold必须大于0")	<-- UNCOVERED
333	  }
334	
335	  let img8 = imgf_to_img8_feat(img)
336	  orb_detect(img8, max_features~, threshold~)
337	}
   	…

343	pub fn good_features_to_track_16(
344	  img : @types.Image16,
345	  max_corners : Int,
346	  quality_level : Float,
347	  min_distance? : Int = 5,
348	) -> Array[CornerPoint] raise @types.LoadError {
349	  // 输入验证
350	  if img.width <= 0 || img.height <= 0 {
351	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
352	      "good_features_to_track_16: 图像尺寸必须大于0",
353	    )
354	  }
355	  if img.channels < 1 || img.channels > 4 {
356	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
357	      "good_features_to_track_16: 通道数必须在1-4之间",
358	    )
359	  }
360	  let expected_size = img.width * img.height * img.channels * 2
361	  if img.data.length() != expected_size {
362	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
363	      "good_features_to_track_16: 数据大小不匹配，期望 " +
364	      expected_size.to_string() +	<-- UNCOVERED
365	      "，实际 " +
366	      img.data.length().to_string(),	<-- UNCOVERED
367	    )
368	  }
369	  if max_corners <= 0 {
370	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
371	      "good_features_to_track_16: max_corners必须大于0",
372	    )
373	  }
374	  if quality_level <= 0.0F {
375	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
376	      "good_features_to_track_16: quality_level必须大于0",
377	    )
378	  }
379	  if min_distance <= 0 {
380	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
381	      "good_features_to_track_16: min_distance必须大于0",
382	    )
383	  }
384	
385	  let img8 = img16_to_img8_feat(img)
386	  good_features_to_track(img8, max_corners, quality_level, min_distance~)
387	}
   	…

393	pub fn good_features_to_track_f(
394	  img : @types.ImageF,
395	  max_corners : Int,
396	  quality_level : Float,
397	  min_distance? : Int = 5,
398	) -> Array[CornerPoint] raise @types.LoadError {
399	  // 输入验证
400	  if img.width <= 0 || img.height <= 0 {
401	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
402	      "good_features_to_track_f: 图像尺寸必须大于0",
403	    )
404	  }
405	  if img.channels < 1 || img.channels > 4 {
406	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
407	      "good_features_to_track_f: 通道数必须在1-4之间",
408	    )
409	  }
410	  let expected_size = img.width * img.height * img.channels * 4
411	  if img.data.length() != expected_size {
412	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
413	      "good_features_to_track_f: 数据大小不匹配，期望 " +
414	      expected_size.to_string() +	<-- UNCOVERED
415	      "，实际 " +
416	      img.data.length().to_string(),	<-- UNCOVERED
417	    )
418	  }
419	  if max_corners <= 0 {
420	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
421	      "good_features_to_track_f: max_corners必须大于0",
422	    )
423	  }
424	  if quality_level <= 0.0F {
425	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
426	      "good_features_to_track_f: quality_level必须大于0",
427	    )
428	  }
429	  if min_distance <= 0 {
430	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
431	      "good_features_to_track_f: min_distance必须大于0",
432	    )
433	  }
434	
435	  let img8 = imgf_to_img8_feat(img)
436	  good_features_to_track(img8, max_corners, quality_level, min_distance~)
437	}
   	…

19 uncovered line(s) in src\process\feature\gabor.mbt:

 10	pub fn gabor_filter(
 11	  img : @types.Image,
 12	  ksize : Int,
 13	  sigma : Float,
 14	  theta : Float,
 15	  lambda? : Float = 10.0F,
 16	  gamma? : Float = 0.5F,
 17	  phi? : Float = 0.0F,
 18	) -> @types.Image raise @types.LoadError {
 19	  // 输入验证
 20	  if img.width <= 0 || img.height <= 0 {
 21	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 22	      "gabor_filter: 图像尺寸必须大于0",
 23	    )
 24	  }
 25	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 26	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 27	      "gabor_filter: 仅支持 channels=1/3/4",
 28	    )
 29	  }
 30	  let expected_size = img.width * img.height * img.channels
 31	  if img.data.length() != expected_size {
 32	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 33	      "gabor_filter: 数据大小不匹配，期望 " +
 34	      expected_size.to_string() +	<-- UNCOVERED
 35	      "，实际 " +
 36	      img.data.length().to_string(),	<-- UNCOVERED
 37	    )
 38	  }
 39	  if gamma <= 0.0F {
 40	    raise @types.LoadError::DecodeFailed("gabor_filter: gamma必须大于0")	<-- UNCOVERED
 41	  }
 42	  if ksize < 3 || ksize > 31 || ksize % 2 == 0 {
 43	    raise @types.LoadError::DecodeFailed(
 44	      "ksize 必须为 3..31 之间的奇数",
 45	    )
 46	  }
 47	  if sigma <= 0.0F {
 48	    raise @types.LoadError::DecodeFailed("sigma 必须 > 0")
 49	  }
 50	  if lambda <= 0.0F {
 51	    raise @types.LoadError::DecodeFailed("lambda 必须 > 0")
 52	  }
 53	  let ch = img.channels
 54	  let w = img.width
 55	  let h = img.height
 56	  let n = w * h
 57	  if n == 0 {
 58	    return img	<-- UNCOVERED
 59	  }
 60	  // 生成 Gabor 核
 61	  let half = ksize / 2
 62	  let kernel : Array[Float] = Array::make(ksize * ksize, 0.0F)
 63	  let cos_t = @math.cosf(theta)
 64	  let sin_t = @math.sinf(theta)
 65	  for ky in 0..<ksize {
 66	    for kx in 0..<ksize {
 67	      let x = Float::from_int(kx - half)
 68	      let y = Float::from_int(ky - half)
 69	      let x_prime = x * cos_t + y * sin_t
 70	      let y_prime = -x * sin_t + y * cos_t
 71	      let gaussian = @math.expf(
 72	        -(x_prime * x_prime + gamma * gamma * y_prime * y_prime) /
 73	        (2.0F * sigma * sigma),
 74	      )
 75	      let wave = @math.cosf(2.0F * 3.14159265F * x_prime / lambda + phi)
 76	      let val = gaussian * wave
 77	      kernel[ky * ksize + kx] = val
 78	    }
 79	  }
 80	  // 预计算像素 Float 数组，避免内层循环重复类型转换
 81	  let src : Array[Float] = Array::make(n * ch, 0.0F)
 82	  for i in 0..<(n * ch) {
 83	    src[i] = Float::from_int(img.data[i].to_int())
 84	  }
 85	  // 卷积，分离内部/边界区域，ch=1 特化消除内层通道循环
 86	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 87	  if ch == 1 {
 88	    // 内部区域
 89	    for y in half..<(h - half) {
 90	      for x in half..<(w - half) {
 91	        let mut sum = 0.0F
 92	        let base = (y - half) * w + (x - half)
 93	        for ky in 0..<ksize {
 94	          let row = base + ky * w
 95	          let krow = ky * ksize
 96	          for kx in 0..<ksize {
 97	            sum += src[row + kx] * kernel[krow + kx]
 98	          }
 99	        }
100	        let val = sum.to_int()
101	        out[y * w + x] = val.clamp(min=0, max=255).to_byte()
102	      }
103	    }
104	    // 边界区域
105	    for y in 0..<h {
106	      let y_inner = y >= half && y < h - half
107	      for x in 0..<w {
108	        if y_inner && x >= half && x < w - half {
109	          continue
110	        }
111	        let mut sum = 0.0F
112	        for ky in 0..<ksize {
113	          for kx in 0..<ksize {
114	            let px = (x + kx - half).clamp(min=0, max=w - 1)
115	            let py = (y + ky - half).clamp(min=0, max=h - 1)
116	            sum += src[py * w + px] * kernel[ky * ksize + kx]
117	          }
118	        }
119	        let val = sum.to_int()
120	        out[y * w + x] = val.clamp(min=0, max=255).to_byte()
121	      }
122	    }
123	  } else {
124	    for c in 0..<ch {
125	      if ch == 4 && c == 3 {
126	        for i in 0..<n {
127	          out[i * ch + 3] = img.data[i * ch + 3]
128	        }
129	        continue
130	      }
131	      // 内部区域
132	      for y in half..<(h - half) {
133	        for x in half..<(w - half) {
134	          let mut sum = 0.0F
135	          let base = (y - half) * w + (x - half)
136	          for ky in 0..<ksize {
137	            let row = (base + ky * w) * ch + c
138	            let krow = ky * ksize
139	            for kx in 0..<ksize {
140	              sum += src[row + kx * ch] * kernel[krow + kx]
141	            }
142	          }
143	          let val = sum.to_int()
144	          out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
145	        }
146	      }
147	      // 边界区域
148	      for y in 0..<h {
149	        let y_inner = y >= half && y < h - half
150	        for x in 0..<w {
151	          if y_inner && x >= half && x < w - half {
152	            continue
153	          }
154	          let mut sum = 0.0F
155	          for ky in 0..<ksize {
156	            for kx in 0..<ksize {
157	              let px = (x + kx - half).clamp(min=0, max=w - 1)
158	              let py = (y + ky - half).clamp(min=0, max=h - 1)
159	              sum += src[(py * w + px) * ch + c] * kernel[ky * ksize + kx]
160	            }
161	          }
162	          let val = sum.to_int()
163	          out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
164	        }
165	      }
166	    }
167	  }
168	  @types.Image::{
169	    width: w,
170	    height: h,
171	    channels: ch,
172	    data: Bytes::from_array(out),
173	  }
174	}
   	…

181	pub fn gabor_filter_bank(
182	  img : @types.Image,
183	  ksize : Int,
184	  sigma : Float,
185	  num_orientations? : Int = 4,
186	  lambda? : Float = 10.0F,
187	  gamma? : Float = 0.5F,
188	) -> @types.Image raise @types.LoadError {
189	  // 输入验证
190	  if img.width <= 0 || img.height <= 0 {
191	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
192	      "gabor_filter_bank: 图像尺寸必须大于0",
193	    )
194	  }
195	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
196	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
197	      "gabor_filter_bank: 仅支持 channels=1/3/4",
198	    )
199	  }
200	  let expected_size = img.width * img.height * img.channels
201	  if img.data.length() != expected_size {
202	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
203	      "gabor_filter_bank: 数据大小不匹配，期望 " +
204	      expected_size.to_string() +	<-- UNCOVERED
205	      "，实际 " +
206	      img.data.length().to_string(),	<-- UNCOVERED
207	    )
208	  }
209	  if ksize < 3 || ksize > 31 || ksize % 2 == 0 {
210	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
211	      "gabor_filter_bank: ksize必须为3..31之间的奇数",
212	    )
213	  }
214	  if sigma <= 0.0F {
215	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
216	      "gabor_filter_bank: sigma必须大于0",
217	    )
218	  }
219	  if lambda <= 0.0F {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "gabor_filter_bank: lambda必须大于0",
222	    )
223	  }
224	  if gamma <= 0.0F {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "gabor_filter_bank: gamma必须大于0",
227	    )
228	  }
229	  if num_orientations < 1 || num_orientations > 16 {
230	    raise @types.LoadError::DecodeFailed(
231	      "num_orientations 必须在 1..16 之间",
232	    )
233	  }
234	  let ch = img.channels
235	  let w = img.width
236	  let h = img.height
237	  let n = w * h
238	  if n == 0 {
239	    return img	<-- UNCOVERED
240	  }
241	  // 累加各方向响应
242	  let accum : Array[Float] = Array::make(n * ch, 0.0F)
243	  for i in 0..<num_orientations {
244	    let theta = Float::from_int(i) *
245	      3.14159265F /
246	      Float::from_int(num_orientations)
247	    let filtered = gabor_filter(img, ksize, sigma, theta, lambda~, gamma~)
248	    for j in 0..<(n * ch) {
249	      accum[j] += Float::from_int(filtered.data[j].to_int())
250	    }
251	  }
252	  // 归一化
253	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
254	  for j in 0..<(n * ch) {
255	    let val = (accum[j] / Float::from_int(num_orientations)).to_int()
256	    out[j] = val.clamp(min=0, max=255).to_byte()
257	  }
258	  @types.Image::{
259	    width: w,
260	    height: h,
261	    channels: ch,
262	    data: Bytes::from_array(out),
263	  }
264	}
   	…

268	pub fn gabor_kernel(
269	  ksize : Int,
270	  sigma : Float,
271	  theta : Float,
272	  lambda? : Float = 10.0F,
273	  gamma? : Float = 0.5F,
274	  phi? : Float = 0.0F,
275	) -> Array[Array[Float]] raise @types.LoadError {
276	  // 参数验证
277	  if ksize < 3 || ksize > 31 || ksize % 2 == 0 {
278	    raise @types.LoadError::DecodeFailed(
279	      "gabor_kernel: ksize必须为3..31之间的奇数",
280	    )
281	  }
282	  if sigma <= 0.0F {
283	    raise @types.LoadError::DecodeFailed("gabor_kernel: sigma必须大于0")
284	  }
285	  if lambda <= 0.0F {
286	    raise @types.LoadError::DecodeFailed("gabor_kernel: lambda必须大于0")	<-- UNCOVERED
287	  }
288	  if gamma <= 0.0F {
289	    raise @types.LoadError::DecodeFailed("gabor_kernel: gamma必须大于0")	<-- UNCOVERED
290	  }
291	  let half = ksize / 2
292	  let kernel : Array[Array[Float]] = Array::make(ksize, [])
293	  for i in 0..<ksize {
294	    kernel[i] = Array::make(ksize, 0.0F)
295	  }
296	  let cos_t = @math.cosf(theta)
297	  let sin_t = @math.sinf(theta)
298	  for ky in 0..<ksize {
299	    for kx in 0..<ksize {
300	      let x = Float::from_int(kx - half)
301	      let y = Float::from_int(ky - half)
302	      let x_prime = x * cos_t + y * sin_t
303	      let y_prime = -x * sin_t + y * cos_t
304	      let gaussian = @math.expf(
305	        -(x_prime * x_prime + gamma * gamma * y_prime * y_prime) /
306	        (2.0F * sigma * sigma),
307	      )
308	      let wave = @math.cosf(2.0F * 3.14159265F * x_prime / lambda + phi)
309	      kernel[ky][kx] = gaussian * wave
310	    }
311	  }
312	  kernel
313	}
   	…

74 uncovered line(s) in src\process\feature\gabor_template_16f.mbt:

36	fn clamp_int_gabor(v : Int, lo : Int, hi : Int) -> Int {
37	  if v < lo {
38	    lo	<-- UNCOVERED
39	  } else if v > hi {
40	    hi	<-- UNCOVERED
41	  } else {
42	    v
43	  }
44	}
  	…

123	pub fn gabor_filter_16(
124	  img : @types.Image16,
125	  ksize : Int,
126	  sigma : Float,
127	  theta : Float,
128	  lambda? : Float = 10.0F,
129	  gamma? : Float = 0.5F,
130	  phi? : Float = 0.0F,
131	) -> @types.Image16 raise @types.LoadError {
132	  // 输入验证
133	  if img.width <= 0 || img.height <= 0 {
134	    raise @types.LoadError::DecodeFailed(
135	      "gabor_filter_16: 图像尺寸必须大于0",
136	    )
137	  }
138	  if img.channels < 1 || img.channels > 4 {
139	    raise @types.LoadError::DecodeFailed(
140	      "gabor_filter_16: 通道数必须在1-4之间",
141	    )
142	  }
143	  let expected_size = img.width * img.height * img.channels * 2
144	  if img.data.length() != expected_size {
145	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
146	      "gabor_filter_16: 数据大小不匹配，期望 " +
147	      expected_size.to_string() +	<-- UNCOVERED
148	      "，实际 " +
149	      img.data.length().to_string(),	<-- UNCOVERED
150	    )
151	  }
152	  if ksize <= 0 || ksize % 2 == 0 {
153	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
154	      "gabor_filter_16: ksize必须为正奇数",
155	    )
156	  }
157	  if sigma <= 0.0F {
158	    raise @types.LoadError::DecodeFailed("gabor_filter_16: sigma必须大于0")	<-- UNCOVERED
159	  }
160	
161	  let img8 = img16_to_img8_gabor(img)
162	  let result8 = gabor_filter(img8, ksize, sigma, theta, lambda~, gamma~, phi~)
163	  img8_to_img16_gabor(result8)
164	}
   	…

170	pub fn gabor_filter_f(
171	  img : @types.ImageF,
172	  ksize : Int,
173	  sigma : Float,
174	  theta : Float,
175	  lambda? : Float = 10.0F,
176	  gamma? : Float = 0.5F,
177	  phi? : Float = 0.0F,
178	) -> @types.ImageF raise @types.LoadError {
179	  // 输入验证
180	  if img.width <= 0 || img.height <= 0 {
181	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
182	      "gabor_filter_f: 图像尺寸必须大于0",
183	    )
184	  }
185	  if img.channels < 1 || img.channels > 4 {
186	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
187	      "gabor_filter_f: 通道数必须在1-4之间",
188	    )
189	  }
190	  let expected_size = img.width * img.height * img.channels * 4
191	  if img.data.length() != expected_size {
192	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
193	      "gabor_filter_f: 数据大小不匹配，期望 " +
194	      expected_size.to_string() +	<-- UNCOVERED
195	      "，实际 " +
196	      img.data.length().to_string(),	<-- UNCOVERED
197	    )
198	  }
199	  if ksize <= 0 || ksize % 2 == 0 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "gabor_filter_f: ksize必须为正奇数",
202	    )
203	  }
204	  if sigma <= 0.0F {
205	    raise @types.LoadError::DecodeFailed("gabor_filter_f: sigma必须大于0")	<-- UNCOVERED
206	  }
207	
208	  let img8 = imgf_to_img8_gabor(img)
209	  let result8 = gabor_filter(img8, ksize, sigma, theta, lambda~, gamma~, phi~)
210	  img8_to_imgf_gabor(result8)
211	}
   	…

217	pub fn gabor_filter_bank_16(
218	  img : @types.Image16,
219	  ksize : Int,
220	  sigma : Float,
221	  num_orientations? : Int = 4,
222	  lambda? : Float = 10.0F,
223	  gamma? : Float = 0.5F,
224	) -> @types.Image16 raise @types.LoadError {
225	  // 输入验证
226	  if img.width <= 0 || img.height <= 0 {
227	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
228	      "gabor_filter_bank_16: 图像尺寸必须大于0",
229	    )
230	  }
231	  if img.channels < 1 || img.channels > 4 {
232	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
233	      "gabor_filter_bank_16: 通道数必须在1-4之间",
234	    )
235	  }
236	  let expected_size = img.width * img.height * img.channels * 2
237	  if img.data.length() != expected_size {
238	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
239	      "gabor_filter_bank_16: 数据大小不匹配，期望 " +
240	      expected_size.to_string() +	<-- UNCOVERED
241	      "，实际 " +
242	      img.data.length().to_string(),	<-- UNCOVERED
243	    )
244	  }
245	  if ksize <= 0 || ksize % 2 == 0 {
246	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
247	      "gabor_filter_bank_16: ksize必须为正奇数",
248	    )
249	  }
250	  if sigma <= 0.0F {
251	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
252	      "gabor_filter_bank_16: sigma必须大于0",
253	    )
254	  }
255	  if num_orientations <= 0 {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "gabor_filter_bank_16: num_orientations必须大于0",
258	    )
259	  }
260	
261	  let img8 = img16_to_img8_gabor(img)
262	  let result8 = gabor_filter_bank(
263	    img8,
264	    ksize,
265	    sigma,
266	    num_orientations~,
267	    lambda~,
268	    gamma~,
269	  )
270	  img8_to_img16_gabor(result8)
271	}
   	…

277	pub fn gabor_filter_bank_f(
278	  img : @types.ImageF,
279	  ksize : Int,
280	  sigma : Float,
281	  num_orientations? : Int = 4,
282	  lambda? : Float = 10.0F,
283	  gamma? : Float = 0.5F,
284	) -> @types.ImageF raise @types.LoadError {
285	  // 输入验证
286	  if img.width <= 0 || img.height <= 0 {
287	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
288	      "gabor_filter_bank_f: 图像尺寸必须大于0",
289	    )
290	  }
291	  if img.channels < 1 || img.channels > 4 {
292	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
293	      "gabor_filter_bank_f: 通道数必须在1-4之间",
294	    )
295	  }
296	  let expected_size = img.width * img.height * img.channels * 4
297	  if img.data.length() != expected_size {
298	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
299	      "gabor_filter_bank_f: 数据大小不匹配，期望 " +
300	      expected_size.to_string() +	<-- UNCOVERED
301	      "，实际 " +
302	      img.data.length().to_string(),	<-- UNCOVERED
303	    )
304	  }
305	  if ksize <= 0 || ksize % 2 == 0 {
306	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
307	      "gabor_filter_bank_f: ksize必须为正奇数",
308	    )
309	  }
310	  if sigma <= 0.0F {
311	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
312	      "gabor_filter_bank_f: sigma必须大于0",
313	    )
314	  }
315	  if num_orientations <= 0 {
316	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
317	      "gabor_filter_bank_f: num_orientations必须大于0",
318	    )
319	  }
320	
321	  let img8 = imgf_to_img8_gabor(img)
322	  let result8 = gabor_filter_bank(
323	    img8,
324	    ksize,
325	    sigma,
326	    num_orientations~,
327	    lambda~,
328	    gamma~,
329	  )
330	  img8_to_imgf_gabor(result8)
331	}
   	…

337	pub fn template_match_16(
338	  img : @types.Image16,
339	  template : @types.Image16,
340	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
341	) -> Array[TemplateMatchResult] raise @types.LoadError {
342	  // 输入验证
343	  if img.width <= 0 || img.height <= 0 {
344	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
345	      "template_match_16: 图像尺寸必须大于0",
346	    )
347	  }
348	  if img.channels < 1 || img.channels > 4 {
349	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
350	      "template_match_16: 通道数必须在1-4之间",
351	    )
352	  }
353	  let expected_size = img.width * img.height * img.channels * 2
354	  if img.data.length() != expected_size {
355	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
356	      "template_match_16: 数据大小不匹配，期望 " +
357	      expected_size.to_string() +	<-- UNCOVERED
358	      "，实际 " +
359	      img.data.length().to_string(),	<-- UNCOVERED
360	    )
361	  }
362	  if template.width <= 0 || template.height <= 0 {
363	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
364	      "template_match_16: 模板尺寸必须大于0",
365	    )
366	  }
367	  if template.channels < 1 || template.channels > 4 {
368	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
369	      "template_match_16: 模板通道数必须在1-4之间",
370	    )
371	  }
372	  let tpl_expected_size = template.width *
373	    template.height *
374	    template.channels *
375	    2
376	  if template.data.length() != tpl_expected_size {
377	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
378	      "template_match_16: 模板数据大小不匹配，期望 " +
379	      tpl_expected_size.to_string() +	<-- UNCOVERED
380	      "，实际 " +
381	      template.data.length().to_string(),	<-- UNCOVERED
382	    )
383	  }
384	  if template.width > img.width || template.height > img.height {
385	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
386	      "template_match_16: 模板尺寸不能大于图像尺寸",
387	    )
388	  }
389	
390	  let img8 = img16_to_img8_gabor(img)
391	  let tpl8 = img16_to_img8_gabor(template)
392	  template_match(img8, tpl8, match_method~)
393	}
   	…

399	pub fn template_match_f(
400	  img : @types.ImageF,
401	  template : @types.ImageF,
402	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
403	) -> Array[TemplateMatchResult] raise @types.LoadError {
404	  // 输入验证
405	  if img.width <= 0 || img.height <= 0 {
406	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
407	      "template_match_f: 图像尺寸必须大于0",
408	    )
409	  }
410	  if img.channels < 1 || img.channels > 4 {
411	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
412	      "template_match_f: 通道数必须在1-4之间",
413	    )
414	  }
415	  let expected_size = img.width * img.height * img.channels * 4
416	  if img.data.length() != expected_size {
417	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
418	      "template_match_f: 数据大小不匹配，期望 " +
419	      expected_size.to_string() +	<-- UNCOVERED
420	      "，实际 " +
421	      img.data.length().to_string(),	<-- UNCOVERED
422	    )
423	  }
424	  if template.width <= 0 || template.height <= 0 {
425	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
426	      "template_match_f: 模板尺寸必须大于0",
427	    )
428	  }
429	  if template.channels < 1 || template.channels > 4 {
430	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
431	      "template_match_f: 模板通道数必须在1-4之间",
432	    )
433	  }
434	  let tpl_expected_size = template.width *
435	    template.height *
436	    template.channels *
437	    4
438	  if template.data.length() != tpl_expected_size {
439	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
440	      "template_match_f: 模板数据大小不匹配，期望 " +
441	      tpl_expected_size.to_string() +	<-- UNCOVERED
442	      "，实际 " +
443	      template.data.length().to_string(),	<-- UNCOVERED
444	    )
445	  }
446	  if template.width > img.width || template.height > img.height {
447	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
448	      "template_match_f: 模板尺寸不能大于图像尺寸",
449	    )
450	  }
451	
452	  let img8 = imgf_to_img8_gabor(img)
453	  let tpl8 = imgf_to_img8_gabor(template)
454	  template_match(img8, tpl8, match_method~)
455	}
   	…

461	pub fn template_match_best_16(
462	  img : @types.Image16,
463	  template : @types.Image16,
464	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
465	) -> TemplateMatchResult? raise @types.LoadError {
466	  // 输入验证
467	  if img.width <= 0 || img.height <= 0 {
468	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
469	      "template_match_best_16: 图像尺寸必须大于0",
470	    )
471	  }
472	  if img.channels < 1 || img.channels > 4 {
473	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
474	      "template_match_best_16: 通道数必须在1-4之间",
475	    )
476	  }
477	  let expected_size = img.width * img.height * img.channels * 2
478	  if img.data.length() != expected_size {
479	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
480	      "template_match_best_16: 数据大小不匹配，期望 " +
481	      expected_size.to_string() +	<-- UNCOVERED
482	      "，实际 " +
483	      img.data.length().to_string(),	<-- UNCOVERED
484	    )
485	  }
486	  if template.width <= 0 || template.height <= 0 {
487	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
488	      "template_match_best_16: 模板尺寸必须大于0",
489	    )
490	  }
491	  if template.channels < 1 || template.channels > 4 {
492	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
493	      "template_match_best_16: 模板通道数必须在1-4之间",
494	    )
495	  }
496	  let tpl_expected_size = template.width *
497	    template.height *
498	    template.channels *
499	    2
500	  if template.data.length() != tpl_expected_size {
501	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
502	      "template_match_best_16: 模板数据大小不匹配，期望 " +
503	      tpl_expected_size.to_string() +	<-- UNCOVERED
504	      "，实际 " +
505	      template.data.length().to_string(),	<-- UNCOVERED
506	    )
507	  }
508	  if template.width > img.width || template.height > img.height {
509	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
510	      "template_match_best_16: 模板尺寸不能大于图像尺寸",
511	    )
512	  }
513	
514	  let img8 = img16_to_img8_gabor(img)
515	  let tpl8 = img16_to_img8_gabor(template)
516	  template_match_best(img8, tpl8, match_method~)
517	}
   	…

523	pub fn template_match_best_f(
524	  img : @types.ImageF,
525	  template : @types.ImageF,
526	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
527	) -> TemplateMatchResult? raise @types.LoadError {
528	  // 输入验证
529	  if img.width <= 0 || img.height <= 0 {
530	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
531	      "template_match_best_f: 图像尺寸必须大于0",
532	    )
533	  }
534	  if img.channels < 1 || img.channels > 4 {
535	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
536	      "template_match_best_f: 通道数必须在1-4之间",
537	    )
538	  }
539	  let expected_size = img.width * img.height * img.channels * 4
540	  if img.data.length() != expected_size {
541	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
542	      "template_match_best_f: 数据大小不匹配，期望 " +
543	      expected_size.to_string() +	<-- UNCOVERED
544	      "，实际 " +
545	      img.data.length().to_string(),	<-- UNCOVERED
546	    )
547	  }
548	  if template.width <= 0 || template.height <= 0 {
549	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
550	      "template_match_best_f: 模板尺寸必须大于0",
551	    )
552	  }
553	  if template.channels < 1 || template.channels > 4 {
554	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
555	      "template_match_best_f: 模板通道数必须在1-4之间",
556	    )
557	  }
558	  let tpl_expected_size = template.width *
559	    template.height *
560	    template.channels *
561	    4
562	  if template.data.length() != tpl_expected_size {
563	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
564	      "template_match_best_f: 模板数据大小不匹配，期望 " +
565	      tpl_expected_size.to_string() +	<-- UNCOVERED
566	      "，实际 " +
567	      template.data.length().to_string(),	<-- UNCOVERED
568	    )
569	  }
570	  if template.width > img.width || template.height > img.height {
571	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
572	      "template_match_best_f: 模板尺寸不能大于图像尺寸",
573	    )
574	  }
575	
576	  let img8 = imgf_to_img8_gabor(img)
577	  let tpl8 = imgf_to_img8_gabor(template)
578	  template_match_best(img8, tpl8, match_method~)
579	}
   	…

16 uncovered line(s) in src\process\feature\glcm.mbt:

20	pub fn compute_glcm(
21	  img : @types.Image,
22	  dx : Int,
23	  dy : Int,
24	  levels? : Int = 256,
25	) -> Array[Array[Int]] raise @types.LoadError {
26	  // 输入验证
27	  if img.width <= 0 || img.height <= 0 {
28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
29	      "compute_glcm: 图像尺寸必须大于0",
30	    )
31	  }
32	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
34	      "compute_glcm: 仅支持 channels=1/3/4",
35	    )
36	  }
37	  let expected_size = img.width * img.height * img.channels
38	  if img.data.length() != expected_size {
39	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
40	      "compute_glcm: 数据大小不匹配，期望 " +
41	      expected_size.to_string() +	<-- UNCOVERED
42	      "，实际 " +
43	      img.data.length().to_string(),	<-- UNCOVERED
44	    )
45	  }
46	  if dx == 0 && dy == 0 {
47	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
48	      "compute_glcm: dx和dy不能同时为0",
49	    )
50	  }
51	  if levels < 2 || levels > 256 {
52	    raise @types.LoadError::DecodeFailed("levels 必须在 2..256 之间")
53	  }
54	  let w = img.width
55	  let h = img.height
56	  let n = w * h
57	  if n == 0 {
58	    return []	<-- UNCOVERED
59	  }
60	  // 转灰度并量化
61	  let gray : Array[Int] = Array::make(n, 0)
62	  let ch = img.channels
63	  let data_int : Array[Int] = Array::make(n * ch, 0)
64	  for i in 0..<(n * ch) {
65	    data_int[i] = img.data[i].to_int()
66	  }
67	  let scale = if levels < 256 { 256 / levels } else { 1 }
68	  for i in 0..<n {
69	    let v = if ch == 1 {
70	      data_int[i]
71	    } else {
72	      let r = data_int[i * ch]	<-- UNCOVERED
73	      let g = data_int[i * ch + 1]
74	      let b = data_int[i * ch + 2]
75	      (r * 299 + g * 587 + b * 114) / 1000
76	    }
77	    gray[i] = if scale > 1 { v / scale } else { v }
78	    if gray[i] >= levels {
79	      gray[i] = levels - 1	<-- UNCOVERED
80	    }
81	  }
82	  // 构建 GLCM
83	  let glcm : Array[Array[Int]] = Array::make(levels, [])
84	  for i in 0..<levels {
85	    glcm[i] = Array::make(levels, 0)
86	  }
87	  for y in 0..<h {
88	    for x in 0..<w {
89	      let nx = x + dx
90	      let ny = y + dy
91	      if nx >= 0 && nx < w && ny >= 0 && ny < h {
92	        let v1 = gray[y * w + x]
93	        let v2 = gray[ny * w + nx]
94	        glcm[v1][v2] += 1
95	      }
96	    }
97	  }
98	  glcm
99	}
  	…

103	pub fn glcm_features(glcm : Array[Array[Int]]) -> GlcmFeatures {
104	  let levels = glcm.length()
105	  if levels == 0 {
106	    return GlcmFeatures::{
107	      contrast: 0.0F,
108	      correlation: 0.0F,
109	      energy: 0.0F,
110	      homogeneity: 0.0F,
111	      entropy: 0.0F,
112	      asm_value: 0.0F,
113	      dissimilarity: 0.0F,
114	    }
115	  }
116	  // 归一化
117	  let mut total = 0.0F
118	  for i in 0..<levels {
119	    for j in 0..<levels {
120	      total += Float::from_int(glcm[i][j])
121	    }
122	  }
123	  if total == 0.0F {
124	    return GlcmFeatures::{	<-- UNCOVERED
125	      contrast: 0.0F,
126	      correlation: 0.0F,
127	      energy: 0.0F,
128	      homogeneity: 0.0F,
129	      entropy: 0.0F,
130	      asm_value: 0.0F,
131	      dissimilarity: 0.0F,
132	    }
133	  }
134	  // 计算均值和标准差
135	  let mut mean_i = 0.0F
136	  let mut mean_j = 0.0F
137	  for i in 0..<levels {
138	    for j in 0..<levels {
139	      let p = Float::from_int(glcm[i][j]) / total
140	      mean_i += Float::from_int(i) * p
141	      mean_j += Float::from_int(j) * p
142	    }
143	  }
144	  let mut var_i = 0.0F
145	  let mut var_j = 0.0F
146	  for i in 0..<levels {
147	    for j in 0..<levels {
148	      let p = Float::from_int(glcm[i][j]) / total
149	      var_i += p * (Float::from_int(i) - mean_i) * (Float::from_int(i) - mean_i)
150	      var_j += p * (Float::from_int(j) - mean_j) * (Float::from_int(j) - mean_j)
151	    }
152	  }
153	  let std_i = var_i.sqrt()
154	  let std_j = var_j.sqrt()
155	  // 计算特征
156	  let mut contrast = 0.0F
157	  let mut correlation = 0.0F
158	  let mut asm_val = 0.0F
159	  let mut homogeneity = 0.0F
160	  let mut entropy = 0.0F
161	  let mut dissimilarity = 0.0F
162	  for i in 0..<levels {
163	    for j in 0..<levels {
164	      let p = Float::from_int(glcm[i][j]) / total
165	      if p > 0.0F {
166	        let diff = Float::from_int(i - j)
167	        let abs_diff = diff.abs()
168	        contrast += p * diff * diff
169	        dissimilarity += p * abs_diff
170	        asm_val += p * p
171	        homogeneity += p / (1.0F + diff * diff)
172	        entropy += -p * Float::from_double(@math.log2(p.to_double()))
173	        if std_i > 0.0F && std_j > 0.0F {
174	          correlation += p *
175	            (Float::from_int(i) - mean_i) *
176	            (Float::from_int(j) - mean_j) /
177	            (std_i * std_j)
178	        }
179	      }
180	    }
181	  }
182	  GlcmFeatures::{
183	    contrast,
184	    correlation,
185	    energy: asm_val.sqrt(),
186	    homogeneity,
187	    entropy,
188	    asm_value: asm_val,
189	    dissimilarity,
190	  }
191	}
   	…

196	pub fn glcm_features_multi_direction(
197	  img : @types.Image,
198	  levels? : Int = 256,
199	) -> Array[GlcmFeatures] raise @types.LoadError {
200	  // 输入验证
201	  if img.width <= 0 || img.height <= 0 {
202	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
203	      "glcm_features_multi_direction: 图像尺寸必须大于0",
204	    )
205	  }
206	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
207	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
208	      "glcm_features_multi_direction: 仅支持 channels=1/3/4",
209	    )
210	  }
211	  let expected_size = img.width * img.height * img.channels
212	  if img.data.length() != expected_size {
213	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
214	      "glcm_features_multi_direction: 数据大小不匹配，期望 " +
215	      expected_size.to_string() +	<-- UNCOVERED
216	      "，实际 " +
217	      img.data.length().to_string(),	<-- UNCOVERED
218	    )
219	  }
220	  if levels < 2 || levels > 256 {
221	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
222	      "glcm_features_multi_direction: levels必须在2..256之间",
223	    )
224	  }
225	
226	  let directions = [(1, 0), (1, 1), (0, 1), (-1, 1)]
227	  let result : Array[GlcmFeatures] = []
228	  for dir in directions {
229	    let (dx, dy) = dir
230	    let glcm = compute_glcm(img, dx, dy, levels~)
231	    result.push(glcm_features(glcm))
232	  }
233	  result
234	}
   	…

18 uncovered line(s) in src\process\feature\harris.mbt:

 15	pub fn harris_corners(
 16	  img : @types.Image,
 17	  k? : Float = 0.04F,
 18	  threshold? : Float = 1000.0F,
 19	  min_distance? : Int = 5,
 20	) -> Array[CornerPoint] raise @types.LoadError {
 21	  // 输入验证
 22	  if img.width <= 0 || img.height <= 0 {
 23	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 24	      "harris_corners: 图像尺寸必须大于0",
 25	    )
 26	  }
 27	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 29	      "harris_corners: 仅支持 channels=1/3/4",
 30	    )
 31	  }
 32	  let expected_size = img.width * img.height * img.channels
 33	  if img.data.length() != expected_size {
 34	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 35	      "harris_corners: 数据大小不匹配，期望 " +
 36	      expected_size.to_string() +	<-- UNCOVERED
 37	      "，实际 " +
 38	      img.data.length().to_string(),	<-- UNCOVERED
 39	    )
 40	  }
 41	  if k <= 0.0F {
 42	    raise @types.LoadError::DecodeFailed("harris_corners: k必须大于0")
 43	  }
 44	  if threshold < 0.0F {
 45	    raise @types.LoadError::DecodeFailed("harris_corners: threshold必须 >= 0")	<-- UNCOVERED
 46	  }
 47	  if min_distance < 0 {
 48	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 49	      "harris_corners: min_distance必须 >= 0",
 50	    )
 51	  }
 52	  let w = img.width
 53	  let h = img.height
 54	  let n = w * h
 55	  if n == 0 {
 56	    return []	<-- UNCOVERED
 57	  }
 58	  // 转灰度
 59	  let gray : Array[Float] = Array::make(n, 0.0F)
 60	  let ch = img.channels
 61	  for i in 0..<n {
 62	    let v = if ch == 1 {
 63	      img.data[i].to_int()
 64	    } else {
 65	      let r = img.data[i * ch].to_int()
 66	      let g = img.data[i * ch + 1].to_int()
 67	      let b = img.data[i * ch + 2].to_int()
 68	      (r * 299 + g * 587 + b * 114) / 1000
 69	    }
 70	    gray[i] = Float::from_int(v)
 71	  }
 72	  // Sobel 梯度
 73	  let ix : Array[Float] = Array::make(n, 0.0F)
 74	  let iy : Array[Float] = Array::make(n, 0.0F)
 75	  for y in 1..<(h - 1) {
 76	    for x in 1..<(w - 1) {
 77	      let idx = y * w + x
 78	      ix[idx] = (
 79	          gray[(y - 1) * w + (x + 1)] +
 80	          2.0F * gray[y * w + (x + 1)] +
 81	          gray[(y + 1) * w + (x + 1)] -
 82	          gray[(y - 1) * w + (x - 1)] -
 83	          2.0F * gray[y * w + (x - 1)] -
 84	          gray[(y + 1) * w + (x - 1)]
 85	        ) /
 86	        8.0F
 87	      iy[idx] = (
 88	          gray[(y + 1) * w + (x - 1)] +
 89	          2.0F * gray[(y + 1) * w + x] +
 90	          gray[(y + 1) * w + (x + 1)] -
 91	          gray[(y - 1) * w + (x - 1)] -
 92	          2.0F * gray[(y - 1) * w + x] -
 93	          gray[(y - 1) * w + (x + 1)]
 94	        ) /
 95	        8.0F
 96	    }
 97	  }
 98	  // 计算 Ix², Iy², IxIy
 99	  let ixx : Array[Float] = Array::make(n, 0.0F)
100	  let iyy : Array[Float] = Array::make(n, 0.0F)
101	  let ixy : Array[Float] = Array::make(n, 0.0F)
102	  for i in 0..<n {
103	    ixx[i] = ix[i] * ix[i]
104	    iyy[i] = iy[i] * iy[i]
105	    ixy[i] = ix[i] * iy[i]
106	  }
107	  // 高斯加权（sigma=1, 3x3 核）
108	  let sxx = harris_gaussian_blur(ixx, w, h)
109	  let syy = harris_gaussian_blur(iyy, w, h)
110	  let sxy = harris_gaussian_blur(ixy, w, h)
111	  // Harris 响应: R = det(M) - k * trace(M)²
112	  let response : Array[Float] = Array::make(n, 0.0F)
113	  for i in 0..<n {
114	    let det = sxx[i] * syy[i] - sxy[i] * sxy[i]
115	    let trace = sxx[i] + syy[i]
116	    response[i] = det - k * trace * trace
117	  }
118	  // 非极大值抑制 + 阈值
119	  let candidates : Array[CornerPoint] = []
120	  for y in 1..<(h - 1) {
121	    for x in 1..<(w - 1) {
122	      let idx = y * w + x
123	      let r = response[idx]
124	      if r < threshold {
125	        continue
126	      }
127	      // 检查是否为局部最大值
128	      let mut is_max = true
129	      for dy in -1..<=1 {
130	        for dx in -1..<=1 {
131	          if dx == 0 && dy == 0 {
132	            continue
133	          }
134	          if response[(y + dy) * w + (x + dx)] > r {
135	            is_max = false
136	            break
137	          }
138	        }
139	        if !is_max {
140	          break
141	        }
142	      }
143	      if is_max {
144	        candidates.push(CornerPoint::{ x, y, response: r })
145	      }
146	    }
147	  }
148	  // 按响应值排序（降序）
149	  candidates.sort_by(fn(a, b) { b.response.compare(a.response) })	<-- UNCOVERED
150	  // 距离过滤
151	  let result : Array[CornerPoint] = []
152	  for c in candidates {
153	    let mut ok = true
154	    for r in result {
155	      let dx = c.x - r.x	<-- UNCOVERED
156	      let dy = c.y - r.y
157	      if dx * dx + dy * dy < min_distance * min_distance {
158	        ok = false	<-- UNCOVERED
159	        break
160	      }
161	    }
162	    if ok {
163	      result.push(c)
164	    }
165	  }
166	  result
167	}
   	…

215	pub fn draw_corners(
216	  img : @types.Image,
217	  corners : Array[CornerPoint],
218	  color? : Array[Byte] = [b'\xFF', b'\x00', b'\x00'],
219	  radius? : Int = 2,
220	) -> @types.Image raise @types.LoadError {
221	  // 输入验证
222	  if img.width <= 0 || img.height <= 0 {
223	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
224	      "draw_corners: 图像尺寸必须大于0",
225	    )
226	  }
227	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
228	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
229	      "draw_corners: 仅支持 channels=1/3/4",
230	    )
231	  }
232	  let expected_size = img.width * img.height * img.channels
233	  if img.data.length() != expected_size {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "draw_corners: 数据大小不匹配，期望 " +
236	      expected_size.to_string() +	<-- UNCOVERED
237	      "，实际 " +
238	      img.data.length().to_string(),	<-- UNCOVERED
239	    )
240	  }
241	  if color.length() == 0 {
242	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
243	      "draw_corners: color数组不能为空",
244	    )
245	  }
246	  if radius < 0 {
247	    raise @types.LoadError::DecodeFailed("draw_corners: radius必须 >= 0")	<-- UNCOVERED
248	  }
249	  let ch = img.channels
250	  let w = img.width
251	  let h = img.height
252	  let out : Array[Byte] = Array::make(w * h * ch, b'\x00')
253	  for i in 0..<(w * h * ch) {
254	    out[i] = img.data[i]
255	  }
256	  let draw_ch = if ch >= 3 { 3 } else { 1 }
257	  for corner in corners {
258	    for dy in -radius..<=radius {
259	      for dx in -radius..<=radius {
260	        let nx = corner.x + dx
261	        let ny = corner.y + dy
262	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
263	          for c in 0..<draw_ch {
264	            if c < color.length() {
265	              out[(ny * w + nx) * ch + c] = color[c]
266	            }
267	          }
268	        }
269	      }
270	    }
271	  }
272	  @types.Image::{
273	    width: w,
274	    height: h,
275	    channels: ch,
276	    data: Bytes::from_array(out),
277	  }
278	}
   	…

113 uncovered line(s) in src\process\feature\hash_quality_integral_16f.mbt:

21	fn clamp_int_iq(v : Int, lo : Int, hi : Int) -> Int {
22	  if v < lo {
23	    lo	<-- UNCOVERED
24	  } else if v > hi {
25	    hi	<-- UNCOVERED
26	  } else {
27	    v
28	  }
29	}
  	…

 98	pub fn dhash_16(img : @types.Image16) -> Array[Int] raise @types.LoadError {
 99	  // 输入验证
100	  if img.width <= 0 || img.height <= 0 {
101	    raise @types.LoadError::DecodeFailed("dhash_16: 图像尺寸必须大于0")
102	  }
103	  if img.channels < 1 || img.channels > 4 {
104	    raise @types.LoadError::DecodeFailed(
105	      "dhash_16: 通道数必须在1-4之间",
106	    )
107	  }
108	  let expected_size = img.width * img.height * img.channels * 2
109	  if img.data.length() != expected_size {
110	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
111	      "dhash_16: 数据大小不匹配，期望 " +
112	      expected_size.to_string() +	<-- UNCOVERED
113	      "，实际 " +
114	      img.data.length().to_string(),	<-- UNCOVERED
115	    )
116	  }
117	
118	  let img8 = img16_to_img8_iq(img)
119	  dhash(img8)
120	}
   	…

124	pub fn phash_16(img : @types.Image16) -> Array[Int] raise @types.LoadError {
125	  // 输入验证
126	  if img.width <= 0 || img.height <= 0 {
127	    raise @types.LoadError::DecodeFailed("phash_16: 图像尺寸必须大于0")
128	  }
129	  if img.channels < 1 || img.channels > 4 {
130	    raise @types.LoadError::DecodeFailed(
131	      "phash_16: 通道数必须在1-4之间",
132	    )
133	  }
134	  let expected_size = img.width * img.height * img.channels * 2
135	  if img.data.length() != expected_size {
136	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
137	      "phash_16: 数据大小不匹配，期望 " +
138	      expected_size.to_string() +	<-- UNCOVERED
139	      "，实际 " +
140	      img.data.length().to_string(),	<-- UNCOVERED
141	    )
142	  }
143	
144	  let img8 = img16_to_img8_iq(img)
145	  phash(img8)
146	}
   	…

152	pub fn ahash_f(img : @types.ImageF) -> Array[Int] raise @types.LoadError {
153	  // 输入验证
154	  if img.width <= 0 || img.height <= 0 {
155	    raise @types.LoadError::DecodeFailed("ahash_f: 图像尺寸必须大于0")	<-- UNCOVERED
156	  }
157	  if img.channels < 1 || img.channels > 4 {
158	    raise @types.LoadError::DecodeFailed("ahash_f: 通道数必须在1-4之间")	<-- UNCOVERED
159	  }
160	  let expected_size = img.width * img.height * img.channels * 4
161	  if img.data.length() != expected_size {
162	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
163	      "ahash_f: 数据大小不匹配，期望 " +
164	      expected_size.to_string() +	<-- UNCOVERED
165	      "，实际 " +
166	      img.data.length().to_string(),	<-- UNCOVERED
167	    )
168	  }
169	
170	  let img8 = imgf_to_img8_iq(img)
171	  ahash(img8)
172	}
   	…

176	pub fn dhash_f(img : @types.ImageF) -> Array[Int] raise @types.LoadError {
177	  // 输入验证
178	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
179	    raise @types.LoadError::DecodeFailed("dhash_f: 图像尺寸必须大于0")
180	  }
181	  if img.channels < 1 || img.channels > 4 {
182	    raise @types.LoadError::DecodeFailed("dhash_f: 通道数必须在1-4之间")	<-- UNCOVERED
183	  }
184	  let expected_size = img.width * img.height * img.channels * 4
185	  if img.data.length() != expected_size {	<-- UNCOVERED
186	    raise @types.LoadError::DecodeFailed(
187	      "dhash_f: 数据大小不匹配，期望 " +
188	      expected_size.to_string() +	<-- UNCOVERED
189	      "，实际 " +
190	      img.data.length().to_string(),	<-- UNCOVERED
191	    )
192	  }
193	
194	  let img8 = imgf_to_img8_iq(img)	<-- UNCOVERED
195	  dhash(img8)
196	}
   	…

200	pub fn phash_f(img : @types.ImageF) -> Array[Int] raise @types.LoadError {
201	  // 输入验证
202	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
203	    raise @types.LoadError::DecodeFailed("phash_f: 图像尺寸必须大于0")
204	  }
205	  if img.channels < 1 || img.channels > 4 {
206	    raise @types.LoadError::DecodeFailed("phash_f: 通道数必须在1-4之间")	<-- UNCOVERED
207	  }
208	  let expected_size = img.width * img.height * img.channels * 4
209	  if img.data.length() != expected_size {	<-- UNCOVERED
210	    raise @types.LoadError::DecodeFailed(
211	      "phash_f: 数据大小不匹配，期望 " +
212	      expected_size.to_string() +	<-- UNCOVERED
213	      "，实际 " +
214	      img.data.length().to_string(),	<-- UNCOVERED
215	    )
216	  }
217	
218	  let img8 = imgf_to_img8_iq(img)	<-- UNCOVERED
219	  phash(img8)
220	}
   	…

226	pub fn mse_16(
227	  img1 : @types.Image16,
228	  img2 : @types.Image16,
229	) -> Double raise @types.LoadError {
230	  // 输入验证
231	  if img1.width <= 0 || img1.height <= 0 {
232	    raise @types.LoadError::DecodeFailed("mse_16: 图像1尺寸必须大于0")	<-- UNCOVERED
233	  }
234	  if img1.channels < 1 || img1.channels > 4 {
235	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
236	      "mse_16: 图像1通道数必须在1-4之间",
237	    )
238	  }
239	  let img1_expected_size = img1.width * img1.height * img1.channels * 2
240	  if img1.data.length() != img1_expected_size {
241	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
242	      "mse_16: 图像1数据大小不匹配，期望 " +
243	      img1_expected_size.to_string() +	<-- UNCOVERED
244	      "，实际 " +
245	      img1.data.length().to_string(),	<-- UNCOVERED
246	    )
247	  }
248	  if img2.width <= 0 || img2.height <= 0 {
249	    raise @types.LoadError::DecodeFailed("mse_16: 图像2尺寸必须大于0")	<-- UNCOVERED
250	  }
251	  if img2.channels < 1 || img2.channels > 4 {
252	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
253	      "mse_16: 图像2通道数必须在1-4之间",
254	    )
255	  }
256	  let img2_expected_size = img2.width * img2.height * img2.channels * 2
257	  if img2.data.length() != img2_expected_size {
258	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
259	      "mse_16: 图像2数据大小不匹配，期望 " +
260	      img2_expected_size.to_string() +	<-- UNCOVERED
261	      "，实际 " +
262	      img2.data.length().to_string(),	<-- UNCOVERED
263	    )
264	  }
265	  if img1.width != img2.width || img1.height != img2.height {
266	    raise @types.LoadError::DecodeFailed("mse_16: 两图像尺寸必须一致")	<-- UNCOVERED
267	  }
268	
269	  let img1_8 = img16_to_img8_iq(img1)
270	  let img2_8 = img16_to_img8_iq(img2)
271	  mse(img1_8, img2_8)
272	}
   	…

276	pub fn psnr_16(
277	  img1 : @types.Image16,
278	  img2 : @types.Image16,
279	) -> Double raise @types.LoadError {
280	  // 输入验证
281	  if img1.width <= 0 || img1.height <= 0 {
282	    raise @types.LoadError::DecodeFailed("psnr_16: 图像1尺寸必须大于0")	<-- UNCOVERED
283	  }
284	  if img1.channels < 1 || img1.channels > 4 {
285	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
286	      "psnr_16: 图像1通道数必须在1-4之间",
287	    )
288	  }
289	  let img1_expected_size = img1.width * img1.height * img1.channels * 2
290	  if img1.data.length() != img1_expected_size {
291	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
292	      "psnr_16: 图像1数据大小不匹配，期望 " +
293	      img1_expected_size.to_string() +	<-- UNCOVERED
294	      "，实际 " +
295	      img1.data.length().to_string(),	<-- UNCOVERED
296	    )
297	  }
298	  if img2.width <= 0 || img2.height <= 0 {
299	    raise @types.LoadError::DecodeFailed("psnr_16: 图像2尺寸必须大于0")	<-- UNCOVERED
300	  }
301	  if img2.channels < 1 || img2.channels > 4 {
302	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
303	      "psnr_16: 图像2通道数必须在1-4之间",
304	    )
305	  }
306	  let img2_expected_size = img2.width * img2.height * img2.channels * 2
307	  if img2.data.length() != img2_expected_size {
308	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
309	      "psnr_16: 图像2数据大小不匹配，期望 " +
310	      img2_expected_size.to_string() +	<-- UNCOVERED
311	      "，实际 " +
312	      img2.data.length().to_string(),	<-- UNCOVERED
313	    )
314	  }
315	  if img1.width != img2.width || img1.height != img2.height {
316	    raise @types.LoadError::DecodeFailed("psnr_16: 两图像尺寸必须一致")	<-- UNCOVERED
317	  }
318	
319	  let img1_8 = img16_to_img8_iq(img1)
320	  let img2_8 = img16_to_img8_iq(img2)
321	  psnr(img1_8, img2_8)
322	}
   	…

326	pub fn ssim_16(
327	  img1 : @types.Image16,
328	  img2 : @types.Image16,
329	) -> Double raise @types.LoadError {
330	  // 输入验证
331	  if img1.width <= 0 || img1.height <= 0 {
332	    raise @types.LoadError::DecodeFailed("ssim_16: 图像1尺寸必须大于0")	<-- UNCOVERED
333	  }
334	  if img1.channels < 1 || img1.channels > 4 {
335	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
336	      "ssim_16: 图像1通道数必须在1-4之间",
337	    )
338	  }
339	  let img1_expected_size = img1.width * img1.height * img1.channels * 2
340	  if img1.data.length() != img1_expected_size {
341	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
342	      "ssim_16: 图像1数据大小不匹配，期望 " +
343	      img1_expected_size.to_string() +	<-- UNCOVERED
344	      "，实际 " +
345	      img1.data.length().to_string(),	<-- UNCOVERED
346	    )
347	  }
348	  if img2.width <= 0 || img2.height <= 0 {
349	    raise @types.LoadError::DecodeFailed("ssim_16: 图像2尺寸必须大于0")	<-- UNCOVERED
350	  }
351	  if img2.channels < 1 || img2.channels > 4 {
352	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
353	      "ssim_16: 图像2通道数必须在1-4之间",
354	    )
355	  }
356	  let img2_expected_size = img2.width * img2.height * img2.channels * 2
357	  if img2.data.length() != img2_expected_size {
358	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
359	      "ssim_16: 图像2数据大小不匹配，期望 " +
360	      img2_expected_size.to_string() +	<-- UNCOVERED
361	      "，实际 " +
362	      img2.data.length().to_string(),	<-- UNCOVERED
363	    )
364	  }
365	  if img1.width != img2.width || img1.height != img2.height {
366	    raise @types.LoadError::DecodeFailed("ssim_16: 两图像尺寸必须一致")	<-- UNCOVERED
367	  }
368	
369	  let img1_8 = img16_to_img8_iq(img1)
370	  let img2_8 = img16_to_img8_iq(img2)
371	  ssim(img1_8, img2_8)
372	}
   	…

378	pub fn mse_f(
379	  img1 : @types.ImageF,
380	  img2 : @types.ImageF,
381	) -> Double raise @types.LoadError {
382	  // 输入验证
383	  if img1.width <= 0 || img1.height <= 0 {
384	    raise @types.LoadError::DecodeFailed("mse_f: 图像1尺寸必须大于0")	<-- UNCOVERED
385	  }
386	  if img1.channels < 1 || img1.channels > 4 {
387	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
388	      "mse_f: 图像1通道数必须在1-4之间",
389	    )
390	  }
391	  let img1_expected_size = img1.width * img1.height * img1.channels * 4
392	  if img1.data.length() != img1_expected_size {
393	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
394	      "mse_f: 图像1数据大小不匹配，期望 " +
395	      img1_expected_size.to_string() +	<-- UNCOVERED
396	      "，实际 " +
397	      img1.data.length().to_string(),	<-- UNCOVERED
398	    )
399	  }
400	  if img2.width <= 0 || img2.height <= 0 {
401	    raise @types.LoadError::DecodeFailed("mse_f: 图像2尺寸必须大于0")	<-- UNCOVERED
402	  }
403	  if img2.channels < 1 || img2.channels > 4 {
404	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
405	      "mse_f: 图像2通道数必须在1-4之间",
406	    )
407	  }
408	  let img2_expected_size = img2.width * img2.height * img2.channels * 4
409	  if img2.data.length() != img2_expected_size {
410	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
411	      "mse_f: 图像2数据大小不匹配，期望 " +
412	      img2_expected_size.to_string() +	<-- UNCOVERED
413	      "，实际 " +
414	      img2.data.length().to_string(),	<-- UNCOVERED
415	    )
416	  }
417	  if img1.width != img2.width || img1.height != img2.height {
418	    raise @types.LoadError::DecodeFailed("mse_f: 两图像尺寸必须一致")	<-- UNCOVERED
419	  }
420	
421	  let img1_8 = imgf_to_img8_iq(img1)
422	  let img2_8 = imgf_to_img8_iq(img2)
423	  mse(img1_8, img2_8)
424	}
   	…

428	pub fn psnr_f(
429	  img1 : @types.ImageF,
430	  img2 : @types.ImageF,
431	) -> Double raise @types.LoadError {
432	  // 输入验证
433	  if img1.width <= 0 || img1.height <= 0 {	<-- UNCOVERED
434	    raise @types.LoadError::DecodeFailed("psnr_f: 图像1尺寸必须大于0")
435	  }
436	  if img1.channels < 1 || img1.channels > 4 {
437	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
438	      "psnr_f: 图像1通道数必须在1-4之间",
439	    )
440	  }
441	  let img1_expected_size = img1.width * img1.height * img1.channels * 4
442	  if img1.data.length() != img1_expected_size {	<-- UNCOVERED
443	    raise @types.LoadError::DecodeFailed(
444	      "psnr_f: 图像1数据大小不匹配，期望 " +
445	      img1_expected_size.to_string() +	<-- UNCOVERED
446	      "，实际 " +
447	      img1.data.length().to_string(),	<-- UNCOVERED
448	    )
449	  }
450	  if img2.width <= 0 || img2.height <= 0 {
451	    raise @types.LoadError::DecodeFailed("psnr_f: 图像2尺寸必须大于0")	<-- UNCOVERED
452	  }
453	  if img2.channels < 1 || img2.channels > 4 {
454	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
455	      "psnr_f: 图像2通道数必须在1-4之间",
456	    )
457	  }
458	  let img2_expected_size = img2.width * img2.height * img2.channels * 4
459	  if img2.data.length() != img2_expected_size {	<-- UNCOVERED
460	    raise @types.LoadError::DecodeFailed(
461	      "psnr_f: 图像2数据大小不匹配，期望 " +
462	      img2_expected_size.to_string() +	<-- UNCOVERED
463	      "，实际 " +
464	      img2.data.length().to_string(),	<-- UNCOVERED
465	    )
466	  }
467	  if img1.width != img2.width || img1.height != img2.height {
468	    raise @types.LoadError::DecodeFailed("psnr_f: 两图像尺寸必须一致")	<-- UNCOVERED
469	  }
470	
471	  let img1_8 = imgf_to_img8_iq(img1)	<-- UNCOVERED
472	  let img2_8 = imgf_to_img8_iq(img2)
473	  psnr(img1_8, img2_8)
474	}
   	…

478	pub fn ssim_f(
479	  img1 : @types.ImageF,
480	  img2 : @types.ImageF,
481	) -> Double raise @types.LoadError {
482	  // 输入验证
483	  if img1.width <= 0 || img1.height <= 0 {	<-- UNCOVERED
484	    raise @types.LoadError::DecodeFailed("ssim_f: 图像1尺寸必须大于0")
485	  }
486	  if img1.channels < 1 || img1.channels > 4 {
487	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
488	      "ssim_f: 图像1通道数必须在1-4之间",
489	    )
490	  }
491	  let img1_expected_size = img1.width * img1.height * img1.channels * 4
492	  if img1.data.length() != img1_expected_size {	<-- UNCOVERED
493	    raise @types.LoadError::DecodeFailed(
494	      "ssim_f: 图像1数据大小不匹配，期望 " +
495	      img1_expected_size.to_string() +	<-- UNCOVERED
496	      "，实际 " +
497	      img1.data.length().to_string(),	<-- UNCOVERED
498	    )
499	  }
500	  if img2.width <= 0 || img2.height <= 0 {
501	    raise @types.LoadError::DecodeFailed("ssim_f: 图像2尺寸必须大于0")	<-- UNCOVERED
502	  }
503	  if img2.channels < 1 || img2.channels > 4 {
504	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
505	      "ssim_f: 图像2通道数必须在1-4之间",
506	    )
507	  }
508	  let img2_expected_size = img2.width * img2.height * img2.channels * 4
509	  if img2.data.length() != img2_expected_size {	<-- UNCOVERED
510	    raise @types.LoadError::DecodeFailed(
511	      "ssim_f: 图像2数据大小不匹配，期望 " +
512	      img2_expected_size.to_string() +	<-- UNCOVERED
513	      "，实际 " +
514	      img2.data.length().to_string(),	<-- UNCOVERED
515	    )
516	  }
517	  if img1.width != img2.width || img1.height != img2.height {
518	    raise @types.LoadError::DecodeFailed("ssim_f: 两图像尺寸必须一致")	<-- UNCOVERED
519	  }
520	
521	  let img1_8 = imgf_to_img8_iq(img1)	<-- UNCOVERED
522	  let img2_8 = imgf_to_img8_iq(img2)
523	  ssim(img1_8, img2_8)
524	}
   	…

530	pub fn integral_image_16(
531	  img : @types.Image16,
532	) -> IntegralImage raise @types.LoadError {
533	  // 输入验证
534	  if img.width <= 0 || img.height <= 0 {
535	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
536	      "integral_image_16: 图像尺寸必须大于0",
537	    )
538	  }
539	  if img.channels < 1 || img.channels > 4 {
540	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
541	      "integral_image_16: 通道数必须在1-4之间",
542	    )
543	  }
544	  let expected_size = img.width * img.height * img.channels * 2
545	  if img.data.length() != expected_size {
546	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
547	      "integral_image_16: 数据大小不匹配，期望 " +
548	      expected_size.to_string() +	<-- UNCOVERED
549	      "，实际 " +
550	      img.data.length().to_string(),	<-- UNCOVERED
551	    )
552	  }
553	
554	  let img8 = img16_to_img8_iq(img)
555	  integral_image(img8)
556	}
   	…

560	pub fn integral_image_sq_16(
561	  img : @types.Image16,
562	) -> IntegralImageSq raise @types.LoadError {
563	  // 输入验证
564	  if img.width <= 0 || img.height <= 0 {
565	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
566	      "integral_image_sq_16: 图像尺寸必须大于0",
567	    )
568	  }
569	  if img.channels < 1 || img.channels > 4 {
570	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
571	      "integral_image_sq_16: 通道数必须在1-4之间",
572	    )
573	  }
574	  let expected_size = img.width * img.height * img.channels * 2
575	  if img.data.length() != expected_size {
576	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
577	      "integral_image_sq_16: 数据大小不匹配，期望 " +
578	      expected_size.to_string() +	<-- UNCOVERED
579	      "，实际 " +
580	      img.data.length().to_string(),	<-- UNCOVERED
581	    )
582	  }
583	
584	  let img8 = img16_to_img8_iq(img)
585	  integral_image_sq(img8)
586	}
   	…

592	pub fn integral_image_f(
593	  img : @types.ImageF,
594	) -> IntegralImage raise @types.LoadError {
595	  // 输入验证
596	  if img.width <= 0 || img.height <= 0 {
597	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
598	      "integral_image_f: 图像尺寸必须大于0",
599	    )
600	  }
601	  if img.channels < 1 || img.channels > 4 {
602	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
603	      "integral_image_f: 通道数必须在1-4之间",
604	    )
605	  }
606	  let expected_size = img.width * img.height * img.channels * 4
607	  if img.data.length() != expected_size {
608	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
609	      "integral_image_f: 数据大小不匹配，期望 " +
610	      expected_size.to_string() +	<-- UNCOVERED
611	      "，实际 " +
612	      img.data.length().to_string(),	<-- UNCOVERED
613	    )
614	  }
615	
616	  let img8 = imgf_to_img8_iq(img)
617	  integral_image(img8)
618	}
   	…

622	pub fn integral_image_sq_f(
623	  img : @types.ImageF,
624	) -> IntegralImageSq raise @types.LoadError {
625	  // 输入验证
626	  if img.width <= 0 || img.height <= 0 {
627	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
628	      "integral_image_sq_f: 图像尺寸必须大于0",
629	    )
630	  }
631	  if img.channels < 1 || img.channels > 4 {
632	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
633	      "integral_image_sq_f: 通道数必须在1-4之间",
634	    )
635	  }
636	  let expected_size = img.width * img.height * img.channels * 4
637	  if img.data.length() != expected_size {
638	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
639	      "integral_image_sq_f: 数据大小不匹配，期望 " +
640	      expected_size.to_string() +	<-- UNCOVERED
641	      "，实际 " +
642	      img.data.length().to_string(),	<-- UNCOVERED
643	    )
644	  }
645	
646	  let img8 = imgf_to_img8_iq(img)
647	  integral_image_sq(img8)
648	}
   	…

33 uncovered line(s) in src\process\feature\histogram.mbt:

 7	pub fn histogram(img : @types.Image) -> Array[Int] raise @types.LoadError {
 8	  // 输入验证
 9	  if img.width <= 0 || img.height <= 0 {
10	    raise @types.LoadError::DecodeFailed("histogram: 图像尺寸必须大于0")	<-- UNCOVERED
11	  }
12	  if img.channels < 1 || img.channels > 4 {
13	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
14	      "histogram: 通道数必须在1-4之间",
15	    )
16	  }
17	  let expected_size = img.width * img.height * img.channels
18	  if img.data.length() != expected_size {
19	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
20	      "histogram: 数据大小不匹配，期望 " +
21	      expected_size.to_string() +	<-- UNCOVERED
22	      "，实际 " +
23	      img.data.length().to_string(),	<-- UNCOVERED
24	    )
25	  }
26	
27	  let hist : Array[Int] = Array::make(256, 0)
28	  let n = img.width * img.height
29	  let ch = img.channels
30	  let data = img.data
31	  for i in 0..<n {
32	    let base = i * ch
33	    let v = if ch == 1 {
34	      data[base].to_int()
35	    } else {
36	      let r = data[base].to_int()
37	      let g = data[base + 1].to_int()
38	      let b = data[base + 2].to_int()
39	      (r * 299 + g * 587 + b * 114) / 1000
40	    }
41	    hist[v] += 1
42	  }
43	  hist
44	}
  	…

 49	pub fn histogram_equalize(
 50	  img : @types.Image,
 51	) -> @types.Image raise @types.LoadError {
 52	  // 输入验证
 53	  if img.width <= 0 || img.height <= 0 {
 54	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 55	      "histogram_equalize: 图像尺寸必须大于0",
 56	    )
 57	  }
 58	  if img.channels < 1 || img.channels > 4 {
 59	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 60	      "histogram_equalize: 通道数必须在1-4之间",
 61	    )
 62	  }
 63	  let expected_size = img.width * img.height * img.channels
 64	  if img.data.length() != expected_size {
 65	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 66	      "histogram_equalize: 数据大小不匹配，期望 " +
 67	      expected_size.to_string() +	<-- UNCOVERED
 68	      "，实际 " +
 69	      img.data.length().to_string(),	<-- UNCOVERED
 70	    )
 71	  }
 72	
 73	  let hist = histogram(img)
 74	  let n = img.width * img.height
 75	  // 计算 CDF
 76	  let cdf : Array[Int] = Array::make(256, 0)
 77	  let mut cum = 0
 78	  for i in 0..<256 {
 79	    cum += hist[i]
 80	    cdf[i] = cum
 81	  }
 82	  // 找到 CDF_min（第一个非零值）
 83	  let mut cdf_min = 0
 84	  for i in 0..<256 {
 85	    if cdf[i] > 0 {
 86	      cdf_min = cdf[i]
 87	      break
 88	    }
 89	  }
 90	  // 计算映射 LUT
 91	  let lut : Array[Byte] = Array::make(256, b'\x00')
 92	  let denom = n - cdf_min
 93	  if denom <= 0 {
 94	    // 所有像素相同，返回原图
 95	    return img
 96	  }
 97	  for i in 0..<256 {
 98	    let mapped = (cdf[i] - cdf_min) * 255 / denom
 99	    lut[i] = mapped.clamp(min=0, max=255).to_byte()
100	  }
101	  // 应用 LUT
102	  let ch = img.channels
103	  let color_ch = if ch >= 3 { 3 } else { ch }
104	  let data_int : Array[Int] = Array::make(n * ch, 0)
105	  for i in 0..<(n * ch) {
106	    data_int[i] = img.data[i].to_int()
107	  }
108	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
109	  for i in 0..<n {
110	    for c in 0..<color_ch {
111	      out[i * ch + c] = lut[data_int[i * ch + c]]
112	    }
113	    if ch == 4 {
114	      out[i * ch + 3] = img.data[i * ch + 3]	<-- UNCOVERED
115	    }
116	  }
117	  @types.Image::{
118	    width: img.width,
119	    height: img.height,
120	    channels: ch,
121	    data: Bytes::from_array(out),
122	  }
123	}
   	…

128	pub fn histogram_normalize(
129	  img : @types.Image,
130	) -> @types.Image raise @types.LoadError {
131	  // 输入验证
132	  if img.width <= 0 || img.height <= 0 {
133	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
134	      "histogram_normalize: 图像尺寸必须大于0",
135	    )
136	  }
137	  if img.channels < 1 || img.channels > 4 {
138	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
139	      "histogram_normalize: 通道数必须在1-4之间",
140	    )
141	  }
142	  let expected_size = img.width * img.height * img.channels
143	  if img.data.length() != expected_size {
144	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
145	      "histogram_normalize: 数据大小不匹配，期望 " +
146	      expected_size.to_string() +	<-- UNCOVERED
147	      "，实际 " +
148	      img.data.length().to_string(),	<-- UNCOVERED
149	    )
150	  }
151	
152	  let ch = img.channels
153	  let color_ch = if ch >= 3 { 3 } else { ch }
154	  let n = img.width * img.height
155	  let data_int : Array[Int] = Array::make(n * ch, 0)
156	  for i in 0..<(n * ch) {
157	    data_int[i] = img.data[i].to_int()
158	  }
159	  // 找到颜色通道的最小值和最大值
160	  let mut min_val = 255
161	  let mut max_val = 0
162	  for i in 0..<n {
163	    for c in 0..<color_ch {
164	      let v = data_int[i * ch + c]
165	      if v < min_val {
166	        min_val = v
167	      }
168	      if v > max_val {
169	        max_val = v
170	      }
171	    }
172	  }
173	  let range = max_val - min_val
174	  if range == 0 {
175	    return img
176	  }
177	  // 应用线性拉伸
178	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
179	  for i in 0..<n {
180	    for c in 0..<color_ch {
181	      let v = data_int[i * ch + c]
182	      let scaled = (v - min_val) * 255 / range
183	      out[i * ch + c] = scaled.clamp(min=0, max=255).to_byte()
184	    }
185	    if ch == 4 {
186	      out[i * ch + 3] = img.data[i * ch + 3]	<-- UNCOVERED
187	    }
188	  }
189	  @types.Image::{
190	    width: img.width,
191	    height: img.height,
192	    channels: ch,
193	    data: Bytes::from_array(out),
194	  }
195	}
   	…

208	pub fn compare_hist(
209	  hist1 : Array[Int],
210	  hist2 : Array[Int],
211	  compare_method : HistCompareMethod,
212	) -> Float raise @types.LoadError {
213	  // 参数验证
214	  if hist1.length() == 0 {
215	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
216	      "compare_hist: hist1数组不能为空",
217	    )
218	  }
219	  if hist2.length() == 0 {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "compare_hist: hist2数组不能为空",
222	    )
223	  }
224	  if hist1.length() != hist2.length() {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "compare_hist: hist1和hist2长度必须相同",
227	    )
228	  }
229	
230	  let n = hist1.length()
231	  match compare_method {
232	    Correlation => {
233	      let mut sum1 = 0.0F
234	      let mut sum2 = 0.0F
235	      let mut sum12 = 0.0F
236	      let mut sum1_sq = 0.0F
237	      let mut sum2_sq = 0.0F
238	      for i in 0..<n {
239	        let h1 = Float::from_int(hist1[i])
240	        let h2 = Float::from_int(hist2[i])
241	        sum1 += h1
242	        sum2 += h2
243	        sum12 += h1 * h2
244	        sum1_sq += h1 * h1
245	        sum2_sq += h2 * h2
246	      }
247	      let nf = Float::from_int(n)
248	      let num = nf * sum12 - sum1 * sum2
249	      let den = ((nf * sum1_sq - sum1 * sum1) * (nf * sum2_sq - sum2 * sum2)).sqrt()
250	      if den == 0.0F {
251	        0.0F	<-- UNCOVERED
252	      } else {
253	        num / den
254	      }
255	    }
256	    ChiSquare => {
257	      let mut chi = 0.0F
258	      for i in 0..<n {
259	        let h1 = Float::from_int(hist1[i])
260	        let h2 = Float::from_int(hist2[i])
261	        let diff = h1 - h2
262	        let sum = h1 + h2
263	        if sum > 0.0F {
264	          chi += diff * diff / sum
265	        }
266	      }
267	      chi
268	    }
269	    Bhattacharyya => {
270	      let mut bc = 0.0F
271	      let mut s1 = 0.0F
272	      let mut s2 = 0.0F
273	      for i in 0..<n {
274	        let h1 = Float::from_int(hist1[i])
275	        let h2 = Float::from_int(hist2[i])
276	        bc += (h1 * h2).sqrt()
277	        s1 += h1
278	        s2 += h2
279	      }
280	      if s1 > 0.0F && s2 > 0.0F {
281	        bc / (s1 * s2).sqrt()
282	      } else {
283	        0.0F	<-- UNCOVERED
284	      }
285	    }
286	  }
287	}
   	…

292	pub fn histogram_matching(
293	  img : @types.Image,
294	  reference : @types.Image,
295	) -> @types.Image raise @types.LoadError {
296	  // 输入验证
297	  if img.width <= 0 || img.height <= 0 {
298	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
299	      "histogram_matching: 图像尺寸必须大于0",
300	    )
301	  }
302	  if img.channels < 1 || img.channels > 4 {
303	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
304	      "histogram_matching: 通道数必须在1-4之间",
305	    )
306	  }
307	  let expected_size = img.width * img.height * img.channels
308	  if img.data.length() != expected_size {
309	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
310	      "histogram_matching: 数据大小不匹配，期望 " +
311	      expected_size.to_string() +	<-- UNCOVERED
312	      "，实际 " +
313	      img.data.length().to_string(),	<-- UNCOVERED
314	    )
315	  }
316	  if reference.width <= 0 || reference.height <= 0 {
317	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
318	      "histogram_matching: 参考图像尺寸必须大于0",
319	    )
320	  }
321	  if reference.channels < 1 || reference.channels > 4 {
322	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
323	      "histogram_matching: 参考图像通道数必须在1-4之间",
324	    )
325	  }
326	  let ref_expected_size = reference.width *
327	    reference.height *
328	    reference.channels
329	  if reference.data.length() != ref_expected_size {
330	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
331	      "histogram_matching: 参考图像数据大小不匹配，期望 " +
332	      ref_expected_size.to_string() +	<-- UNCOVERED
333	      "，实际 " +
334	      reference.data.length().to_string(),	<-- UNCOVERED
335	    )
336	  }
337	
338	  let ch = img.channels
339	  let color_ch = if ch >= 3 { 3 } else { ch }
340	  let n = img.width * img.height
341	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
342	  for c in 0..<color_ch {
343	    let hist_in : Array[Int] = Array::make(256, 0)
344	    let hist_ref : Array[Int] = Array::make(256, 0)
345	    for i in 0..<n {
346	      hist_in[img.data[i * ch + c].to_int()] += 1
347	    }
348	    let n_ref = reference.width * reference.height
349	    let ref_ch = reference.channels
350	    let ref_color_ch = if ref_ch >= 3 { 3 } else { ref_ch }
351	    if c < ref_color_ch {
352	      for i in 0..<n_ref {
353	        hist_ref[reference.data[i * ref_ch + c].to_int()] += 1
354	      }
355	    }
356	    let cdf_in : Array[Float] = Array::make(256, 0.0F)
357	    let cdf_ref : Array[Float] = Array::make(256, 0.0F)
358	    let mut cum_in = 0
359	    let mut cum_ref = 0
360	    for i in 0..<256 {
361	      cum_in += hist_in[i]
362	      cdf_in[i] = Float::from_int(cum_in) / Float::from_int(n)
363	      cum_ref += hist_ref[i]
364	      cdf_ref[i] = Float::from_int(cum_ref) / Float::from_int(n_ref)
365	    }
366	    let lut : Array[Byte] = Array::make(256, b'\x00')
367	    for i in 0..<256 {
368	      let target = cdf_in[i]
369	      let mut best_j = 0
370	      let mut best_diff = 1.0F
371	      for j in 0..<256 {
372	        let diff = (cdf_ref[j] - target).abs()
373	        if diff < best_diff {
374	          best_diff = diff
375	          best_j = j
376	        }
377	      }
378	      lut[i] = best_j.to_byte()
379	    }
380	    for i in 0..<n {
381	      out[i * ch + c] = lut[img.data[i * ch + c].to_int()]
382	    }
383	  }
384	  if ch == 4 {
385	    for i in 0..<n {	<-- UNCOVERED
386	      out[i * ch + 3] = img.data[i * ch + 3]
387	    }
388	  }
389	  @types.Image::{
390	    width: img.width,
391	    height: img.height,
392	    channels: ch,
393	    data: Bytes::from_array(out),
394	  }
395	}
   	…

81 uncovered line(s) in src\process\feature\histogram_16f.mbt:

37	fn clamp_int_h(v : Int, lo : Int, hi : Int) -> Int {
38	  if v < lo {
39	    lo	<-- UNCOVERED
40	  } else if v > hi {
41	    hi	<-- UNCOVERED
42	  } else {
43	    v
44	  }
45	}
  	…

48	fn clamp_float_h(v : Float, lo : Float, hi : Float) -> Float {
49	  if v < lo {
50	    lo	<-- UNCOVERED
51	  } else if v > hi {
52	    hi	<-- UNCOVERED
53	  } else {
54	    v
55	  }
56	}
  	…

62	pub fn histogram_16(img : @types.Image16) -> Array[Int] raise @types.LoadError {
63	  // 输入验证
64	  if img.width <= 0 || img.height <= 0 {
65	    raise @types.LoadError::DecodeFailed(
66	      "histogram_16: 图像尺寸必须大于0",
67	    )
68	  }
69	  if img.channels < 1 || img.channels > 4 {
70	    raise @types.LoadError::DecodeFailed(
71	      "histogram_16: 通道数必须在1-4之间",
72	    )
73	  }
74	  let expected_size = img.width * img.height * img.channels * 2
75	  if img.data.length() != expected_size {
76	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
77	      "histogram_16: 数据大小不匹配，期望 " +
78	      expected_size.to_string() +	<-- UNCOVERED
79	      "，实际 " +
80	      img.data.length().to_string(),	<-- UNCOVERED
81	    )
82	  }
83	
84	  let hist : Array[Int] = Array::make(65536, 0)
85	  let n = img.width * img.height
86	  let ch = img.channels
87	  for i in 0..<n {
88	    let v = if ch == 1 {
89	      read_u16_le_h(img.data, i * 2)
90	    } else {
91	      let r = read_u16_le_h(img.data, i * ch * 2)
92	      let g = read_u16_le_h(img.data, i * ch * 2 + 2)
93	      let b = read_u16_le_h(img.data, i * ch * 2 + 4)
94	      (r * 299 + g * 587 + b * 114) / 1000
95	    }
96	    hist[v] += 1
97	  }
98	  hist
99	}
  	…

103	pub fn histogram_f(img : @types.ImageF) -> Array[Int] raise @types.LoadError {
104	  // 输入验证
105	  if img.width <= 0 || img.height <= 0 {
106	    raise @types.LoadError::DecodeFailed(
107	      "histogram_f: 图像尺寸必须大于0",
108	    )
109	  }
110	  if img.channels < 1 || img.channels > 4 {
111	    raise @types.LoadError::DecodeFailed(
112	      "histogram_f: 通道数必须在1-4之间",
113	    )
114	  }
115	  let expected_size = img.width * img.height * img.channels * 4
116	  if img.data.length() != expected_size {
117	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
118	      "histogram_f: 数据大小不匹配，期望 " +
119	      expected_size.to_string() +	<-- UNCOVERED
120	      "，实际 " +
121	      img.data.length().to_string(),	<-- UNCOVERED
122	    )
123	  }
124	
125	  let hist : Array[Int] = Array::make(256, 0)
126	  let n = img.width * img.height
127	  let ch = img.channels
128	  for i in 0..<n {
129	    let v = if ch == 1 {
130	      read_float_le_h(img.data, i * 4)
131	    } else {
132	      let r = read_float_le_h(img.data, i * ch * 4)	<-- UNCOVERED
133	      let g = read_float_le_h(img.data, i * ch * 4 + 4)
134	      let b = read_float_le_h(img.data, i * ch * 4 + 8)
135	      r * 0.299F + g * 0.587F + b * 0.114F
136	    }
137	    let bin = if v < 0.0F {
138	      0	<-- UNCOVERED
139	    } else if v >= 1.0F {
140	      255	<-- UNCOVERED
141	    } else {
142	      (v * 255.0F + 0.5F).to_int()
143	    }
144	    hist[bin] += 1
145	  }
146	  hist
147	}
   	…

153	pub fn histogram_equalize_16(
154	  img : @types.Image16,
155	) -> @types.Image16 raise @types.LoadError {
156	  let hist = histogram_16(img)
157	  let n = img.width * img.height
158	  let ch = img.channels
159	  let bpp = ch * 2
160	  // 计算 CDF
161	  let cdf : Array[Int] = Array::make(65536, 0)
162	  let mut cum = 0
163	  for i in 0..<65536 {
164	    cum += hist[i]
165	    cdf[i] = cum
166	  }
167	  let mut cdf_min = 0
168	  for i in 0..<65536 {
169	    if cdf[i] > 0 {
170	      cdf_min = cdf[i]
171	      break
172	    }
173	  }
174	  let denom = n - cdf_min
175	  if denom <= 0 {
176	    return img
177	  }
178	  // 计算映射 LUT
179	  let lut : Array[Int] = Array::make(65536, 0)	<-- UNCOVERED
180	  for i in 0..<65536 {
181	    let mapped = (cdf[i] - cdf_min) * 65535 / denom	<-- UNCOVERED
182	    lut[i] = clamp_int_h(mapped, 0, 65535)
183	  }
184	  // 应用 LUT
185	  let color_ch = if ch >= 3 { 3 } else { ch }	<-- UNCOVERED
186	  let n_total = n * ch
187	  let src : Array[Int] = Array::make(n_total, 0)	<-- UNCOVERED
188	  for i in 0..<n_total {
189	    src[i] = read_u16_le_h(img.data, i * 2)	<-- UNCOVERED
190	  }
191	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
192	  for i in 0..<n {
193	    for c in 0..<color_ch {	<-- UNCOVERED
194	      write_u16_le_h(out, i * bpp + c * 2, lut[src[i * ch + c]])
195	    }
196	    if ch == 4 {
197	      let alpha_off = i * bpp + 6	<-- UNCOVERED
198	      out[alpha_off] = img.data[alpha_off]
199	      out[alpha_off + 1] = img.data[alpha_off + 1]
200	    }
201	  }
202	  @types.Image16::{
203	    width: img.width,
204	    height: img.height,
205	    channels: ch,
206	    data: Bytes::from_array(out),	<-- UNCOVERED
207	  }
208	}
   	…

212	pub fn histogram_equalize_f(
213	  img : @types.ImageF,
214	) -> @types.ImageF raise @types.LoadError {
215	  let hist = histogram_f(img)
216	  let n = img.width * img.height
217	  let ch = img.channels
218	  let bpp = ch * 4
219	  let cdf : Array[Int] = Array::make(256, 0)
220	  let mut cum = 0
221	  for i in 0..<256 {
222	    cum += hist[i]
223	    cdf[i] = cum
224	  }
225	  let mut cdf_min = 0
226	  for i in 0..<256 {
227	    if cdf[i] > 0 {
228	      cdf_min = cdf[i]
229	      break
230	    }
231	  }
232	  let denom = n - cdf_min
233	  if denom <= 0 {
234	    return img
235	  }
236	  let lut : Array[Float] = Array::make(256, 0.0F)	<-- UNCOVERED
237	  for i in 0..<256 {
238	    let mapped = Float::from_int(cdf[i] - cdf_min) / Float::from_int(denom)	<-- UNCOVERED
239	    lut[i] = clamp_float_h(mapped, 0.0F, 1.0F)
240	  }
241	  let color_ch = if ch >= 3 { 3 } else { ch }	<-- UNCOVERED
242	  let n_total = n * ch
243	  let src : Array[Float] = Array::make(n_total, 0.0F)	<-- UNCOVERED
244	  for i in 0..<n_total {
245	    src[i] = read_float_le_h(img.data, i * 4)	<-- UNCOVERED
246	  }
247	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
248	  for i in 0..<n {
249	    for c in 0..<color_ch {	<-- UNCOVERED
250	      let v = src[i * ch + c]
251	      let bin = if v < 0.0F {
252	        0	<-- UNCOVERED
253	      } else if v >= 1.0F {
254	        255
255	      } else {
256	        (v * 255.0F + 0.5F).to_int()	<-- UNCOVERED
257	      }
258	      write_f32_le_h(out, i * bpp + c * 4, lut[bin])	<-- UNCOVERED
259	    }
260	    if ch == 4 {
261	      let alpha_off = i * bpp + 12	<-- UNCOVERED
262	      for b in 0..<4 {
263	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
264	      }
265	    }
266	  }
267	  @types.ImageF::{
268	    width: img.width,
269	    height: img.height,
270	    channels: ch,
271	    data: Bytes::from_array(out),	<-- UNCOVERED
272	  }
273	}
   	…

279	pub fn histogram_normalize_16(
280	  img : @types.Image16,
281	) -> @types.Image16 raise @types.LoadError {
282	  // 输入验证
283	  if img.width <= 0 || img.height <= 0 {
284	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
285	      "histogram_normalize_16: 图像尺寸必须大于0",
286	    )
287	  }
288	  if img.channels < 1 || img.channels > 4 {
289	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
290	      "histogram_normalize_16: 通道数必须在1-4之间",
291	    )
292	  }
293	  let expected_size = img.width * img.height * img.channels * 2
294	  if img.data.length() != expected_size {
295	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
296	      "histogram_normalize_16: 数据大小不匹配，期望 " +
297	      expected_size.to_string() +	<-- UNCOVERED
298	      "，实际 " +
299	      img.data.length().to_string(),	<-- UNCOVERED
300	    )
301	  }
302	
303	  let ch = img.channels
304	  let color_ch = if ch >= 3 { 3 } else { ch }
305	  let n = img.width * img.height
306	  let bpp = ch * 2
307	  let n_total = n * ch
308	  let src : Array[Int] = Array::make(n_total, 0)
309	  for i in 0..<n_total {
310	    src[i] = read_u16_le_h(img.data, i * 2)
311	  }
312	  let mut min_val = 65535
313	  let mut max_val = 0
314	  for i in 0..<n {
315	    for c in 0..<color_ch {
316	      let v = src[i * ch + c]
317	      if v < min_val {
318	        min_val = v
319	      }
320	      if v > max_val {
321	        max_val = v
322	      }
323	    }
324	  }
325	  let range = max_val - min_val
326	  if range == 0 {
327	    return img
328	  }
329	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
330	  for i in 0..<n {
331	    for c in 0..<color_ch {
332	      let v = src[i * ch + c]
333	      let scaled = (v - min_val) * 65535 / range
334	      write_u16_le_h(out, i * bpp + c * 2, clamp_int_h(scaled, 0, 65535))
335	    }
336	    if ch == 4 {
337	      let alpha_off = i * bpp + 6	<-- UNCOVERED
338	      out[alpha_off] = img.data[alpha_off]
339	      out[alpha_off + 1] = img.data[alpha_off + 1]
340	    }
341	  }
342	  @types.Image16::{
343	    width: img.width,
344	    height: img.height,
345	    channels: ch,
346	    data: Bytes::from_array(out),
347	  }
348	}
   	…

352	pub fn histogram_normalize_f(
353	  img : @types.ImageF,
354	) -> @types.ImageF raise @types.LoadError {
355	  // 输入验证
356	  if img.width <= 0 || img.height <= 0 {
357	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
358	      "histogram_normalize_f: 图像尺寸必须大于0",
359	    )
360	  }
361	  if img.channels < 1 || img.channels > 4 {
362	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
363	      "histogram_normalize_f: 通道数必须在1-4之间",
364	    )
365	  }
366	  let expected_size = img.width * img.height * img.channels * 4
367	  if img.data.length() != expected_size {
368	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
369	      "histogram_normalize_f: 数据大小不匹配，期望 " +
370	      expected_size.to_string() +	<-- UNCOVERED
371	      "，实际 " +
372	      img.data.length().to_string(),	<-- UNCOVERED
373	    )
374	  }
375	
376	  let ch = img.channels
377	  let color_ch = if ch >= 3 { 3 } else { ch }
378	  let n = img.width * img.height
379	  let bpp = ch * 4
380	  let n_total = n * ch
381	  let src : Array[Float] = Array::make(n_total, 0.0F)
382	  for i in 0..<n_total {
383	    src[i] = read_float_le_h(img.data, i * 4)
384	  }
385	  let mut min_val = 1.0F
386	  let mut max_val = 0.0F
387	  for i in 0..<n {
388	    for c in 0..<color_ch {
389	      let v = src[i * ch + c]
390	      if v < min_val {
391	        min_val = v
392	      }
393	      if v > max_val {
394	        max_val = v
395	      }
396	    }
397	  }
398	  let range = max_val - min_val
399	  if range <= 0.000001F {
400	    return img
401	  }
402	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
403	  for i in 0..<n {
404	    for c in 0..<color_ch {	<-- UNCOVERED
405	      let v = src[i * ch + c]
406	      let scaled = (v - min_val) / range
407	      write_f32_le_h(out, i * bpp + c * 4, clamp_float_h(scaled, 0.0F, 1.0F))	<-- UNCOVERED
408	    }
409	    if ch == 4 {
410	      let alpha_off = i * bpp + 12	<-- UNCOVERED
411	      for b in 0..<4 {
412	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
413	      }
414	    }
415	  }
416	  @types.ImageF::{
417	    width: img.width,
418	    height: img.height,
419	    channels: ch,
420	    data: Bytes::from_array(out),	<-- UNCOVERED
421	  }
422	}
   	…

428	pub fn histogram_matching_16(
429	  img : @types.Image16,
430	  reference : @types.Image16,
431	) -> @types.Image16 raise @types.LoadError {
432	  // 输入验证
433	  if img.width <= 0 || img.height <= 0 {
434	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
435	      "histogram_matching_16: 图像尺寸必须大于0",
436	    )
437	  }
438	  if img.channels < 1 || img.channels > 4 {
439	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
440	      "histogram_matching_16: 通道数必须在1-4之间",
441	    )
442	  }
443	  let expected_size = img.width * img.height * img.channels * 2
444	  if img.data.length() != expected_size {
445	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
446	      "histogram_matching_16: 数据大小不匹配，期望 " +
447	      expected_size.to_string() +	<-- UNCOVERED
448	      "，实际 " +
449	      img.data.length().to_string(),	<-- UNCOVERED
450	    )
451	  }
452	  if reference.width <= 0 || reference.height <= 0 {
453	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
454	      "histogram_matching_16: 参考图像尺寸必须大于0",
455	    )
456	  }
457	  if reference.channels < 1 || reference.channels > 4 {
458	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
459	      "histogram_matching_16: 参考图像通道数必须在1-4之间",
460	    )
461	  }
462	  let ref_expected_size = reference.width *
463	    reference.height *
464	    reference.channels *
465	    2
466	  if reference.data.length() != ref_expected_size {
467	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
468	      "histogram_matching_16: 参考图像数据大小不匹配，期望 " +
469	      ref_expected_size.to_string() +	<-- UNCOVERED
470	      "，实际 " +
471	      reference.data.length().to_string(),	<-- UNCOVERED
472	    )
473	  }
474	
475	  let ch = img.channels
476	  let color_ch = if ch >= 3 { 3 } else { ch }
477	  let n = img.width * img.height
478	  let bpp = ch * 2
479	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
480	  let n_ref = reference.width * reference.height
481	  let ref_ch = reference.channels
482	  let ref_color_ch = if ref_ch >= 3 { 3 } else { ref_ch }
483	  for c in 0..<color_ch {
484	    let hist_in : Array[Int] = Array::make(65536, 0)
485	    let hist_ref : Array[Int] = Array::make(65536, 0)
486	    for i in 0..<n {
487	      hist_in[read_u16_le_h(img.data, i * bpp + c * 2)] += 1
488	    }
489	    if c < ref_color_ch {
490	      for i in 0..<n_ref {
491	        hist_ref[read_u16_le_h(reference.data, i * ref_ch * 2 + c * 2)] += 1
492	      }
493	    }
494	    let cdf_in : Array[Float] = Array::make(65536, 0.0F)
495	    let cdf_ref : Array[Float] = Array::make(65536, 0.0F)
496	    let mut cum_in = 0
497	    let mut cum_ref = 0
498	    for i in 0..<65536 {
499	      cum_in += hist_in[i]
500	      cdf_in[i] = Float::from_int(cum_in) / Float::from_int(n)
501	      cum_ref += hist_ref[i]
502	      cdf_ref[i] = Float::from_int(cum_ref) / Float::from_int(n_ref)
503	    }
504	    let lut : Array[Int] = Array::make(65536, 0)
505	    for i in 0..<65536 {
506	      let target = cdf_in[i]
507	      let mut best_j = 0
508	      let mut best_diff = 1.0F
509	      for j in 0..<65536 {
510	        let diff = (cdf_ref[j] - target).abs()	<-- UNCOVERED
511	        if diff < best_diff {
512	          best_diff = diff
513	          best_j = j
514	        }
515	      }
516	      lut[i] = best_j
517	    }
518	    for i in 0..<n {
519	      let v = read_u16_le_h(img.data, i * bpp + c * 2)
520	      write_u16_le_h(out, i * bpp + c * 2, lut[v])
521	    }
522	  }
523	  if ch == 4 {
524	    for i in 0..<n {
525	      let alpha_off = i * bpp + 6
526	      out[alpha_off] = img.data[alpha_off]
527	      out[alpha_off + 1] = img.data[alpha_off + 1]
528	    }
529	  }
530	  @types.Image16::{
531	    width: img.width,
532	    height: img.height,
533	    channels: ch,
534	    data: Bytes::from_array(out),
535	  }
536	}
   	…

540	pub fn histogram_matching_f(
541	  img : @types.ImageF,
542	  reference : @types.ImageF,
543	) -> @types.ImageF raise @types.LoadError {
544	  // 输入验证
545	  if img.width <= 0 || img.height <= 0 {
546	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
547	      "histogram_matching_f: 图像尺寸必须大于0",
548	    )
549	  }
550	  if img.channels < 1 || img.channels > 4 {
551	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
552	      "histogram_matching_f: 通道数必须在1-4之间",
553	    )
554	  }
555	  let expected_size = img.width * img.height * img.channels * 4
556	  if img.data.length() != expected_size {
557	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
558	      "histogram_matching_f: 数据大小不匹配，期望 " +
559	      expected_size.to_string() +	<-- UNCOVERED
560	      "，实际 " +
561	      img.data.length().to_string(),	<-- UNCOVERED
562	    )
563	  }
564	  if reference.width <= 0 || reference.height <= 0 {
565	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
566	      "histogram_matching_f: 参考图像尺寸必须大于0",
567	    )
568	  }
569	  if reference.channels < 1 || reference.channels > 4 {
570	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
571	      "histogram_matching_f: 参考图像通道数必须在1-4之间",
572	    )
573	  }
574	  let ref_expected_size = reference.width *
575	    reference.height *
576	    reference.channels *
577	    4
578	  if reference.data.length() != ref_expected_size {
579	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
580	      "histogram_matching_f: 参考图像数据大小不匹配，期望 " +
581	      ref_expected_size.to_string() +	<-- UNCOVERED
582	      "，实际 " +
583	      reference.data.length().to_string(),	<-- UNCOVERED
584	    )
585	  }
586	
587	  let ch = img.channels
588	  let color_ch = if ch >= 3 { 3 } else { ch }
589	  let n = img.width * img.height
590	  let bpp = ch * 4
591	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
592	  let n_ref = reference.width * reference.height
593	  let ref_ch = reference.channels
594	  let ref_color_ch = if ref_ch >= 3 { 3 } else { ref_ch }
595	  for c in 0..<color_ch {
596	    let hist_in : Array[Int] = Array::make(256, 0)
597	    let hist_ref : Array[Int] = Array::make(256, 0)
598	    for i in 0..<n {
599	      let v = read_float_le_h(img.data, i * bpp + c * 4)
600	      let bin = if v < 0.0F {
601	        0	<-- UNCOVERED
602	      } else if v >= 1.0F {
603	        255	<-- UNCOVERED
604	      } else {
605	        (v * 255.0F + 0.5F).to_int()
606	      }
607	      hist_in[bin] += 1
608	    }
609	    if c < ref_color_ch {
610	      for i in 0..<n_ref {
611	        let v = read_float_le_h(reference.data, i * ref_ch * 4 + c * 4)
612	        let bin = if v < 0.0F {
613	          0	<-- UNCOVERED
614	        } else if v >= 1.0F {
615	          255	<-- UNCOVERED
616	        } else {
617	          (v * 255.0F + 0.5F).to_int()
618	        }
619	        hist_ref[bin] += 1
620	      }
621	    }
622	    let cdf_in : Array[Float] = Array::make(256, 0.0F)
623	    let cdf_ref : Array[Float] = Array::make(256, 0.0F)
624	    let mut cum_in = 0
625	    let mut cum_ref = 0
626	    for i in 0..<256 {
627	      cum_in += hist_in[i]
628	      cdf_in[i] = Float::from_int(cum_in) / Float::from_int(n)
629	      cum_ref += hist_ref[i]
630	      cdf_ref[i] = Float::from_int(cum_ref) / Float::from_int(n_ref)
631	    }
632	    let lut : Array[Int] = Array::make(256, 0)
633	    for i in 0..<256 {
634	      let target = cdf_in[i]
635	      let mut best_j = 0
636	      let mut best_diff = 1.0F
637	      for j in 0..<256 {
638	        let diff = (cdf_ref[j] - target).abs()
639	        if diff < best_diff {
640	          best_diff = diff
641	          best_j = j
642	        }
643	      }
644	      lut[i] = best_j
645	    }
646	    for i in 0..<n {
647	      let v = read_float_le_h(img.data, i * bpp + c * 4)
648	      let bin = if v < 0.0F {
649	        0	<-- UNCOVERED
650	      } else if v >= 1.0F {
651	        255	<-- UNCOVERED
652	      } else {
653	        (v * 255.0F + 0.5F).to_int()
654	      }
655	      let mapped = Float::from_int(lut[bin]) / 255.0F
656	      write_f32_le_h(out, i * bpp + c * 4, clamp_float_h(mapped, 0.0F, 1.0F))
657	    }
658	  }
659	  if ch == 4 {
660	    for i in 0..<n {	<-- UNCOVERED
661	      let alpha_off = i * bpp + 12
662	      for b in 0..<4 {
663	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
664	      }
665	    }
666	  }
667	  @types.ImageF::{
668	    width: img.width,
669	    height: img.height,
670	    channels: ch,
671	    data: Bytes::from_array(out),
672	  }
673	}
   	…

21 uncovered line(s) in src\process\feature\image_hash.mbt:

 5	pub fn ahash(img : @types.Image) -> Array[Int] raise @types.LoadError {
 6	  // 输入验证
 7	  if img.width <= 0 || img.height <= 0 {
 8	    raise @types.LoadError::DecodeFailed("ahash: 图像尺寸必须大于0")	<-- UNCOVERED
 9	  }
10	  if img.channels < 1 || img.channels > 4 {
11	    raise @types.LoadError::DecodeFailed("ahash: 通道数必须在1-4之间")	<-- UNCOVERED
12	  }
13	  let expected_size = img.width * img.height * img.channels
14	  if img.data.length() != expected_size {
15	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
16	      "ahash: 数据大小不匹配，期望 " +
17	      expected_size.to_string() +	<-- UNCOVERED
18	      "，实际 " +
19	      img.data.length().to_string(),	<-- UNCOVERED
20	    )
21	  }
22	
23	  // 缩放到 8x8 灰度
24	  let small = resize_to_gray_8x8(img)
25	  let mut sum = 0
26	  for i in 0..<64 {
27	    sum += small[i]
28	  }
29	  let mean = sum / 64
30	  let hash : Array[Int] = Array::make(64, 0)
31	  for i in 0..<64 {
32	    hash[i] = if small[i] > mean { 1 } else { 0 }
33	  }
34	  hash
35	}
  	…

39	pub fn dhash(img : @types.Image) -> Array[Int] raise @types.LoadError {
40	  // 输入验证
41	  if img.width <= 0 || img.height <= 0 {
42	    raise @types.LoadError::DecodeFailed("dhash: 图像尺寸必须大于0")	<-- UNCOVERED
43	  }
44	  if img.channels < 1 || img.channels > 4 {
45	    raise @types.LoadError::DecodeFailed("dhash: 通道数必须在1-4之间")	<-- UNCOVERED
46	  }
47	  let expected_size = img.width * img.height * img.channels
48	  if img.data.length() != expected_size {
49	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
50	      "dhash: 数据大小不匹配，期望 " +
51	      expected_size.to_string() +	<-- UNCOVERED
52	      "，实际 " +
53	      img.data.length().to_string(),	<-- UNCOVERED
54	    )
55	  }
56	
57	  let small = resize_to_gray_9x8(img)
58	  let hash : Array[Int] = Array::make(64, 0)
59	  for row in 0..<8 {
60	    for col in 0..<8 {
61	      hash[row * 8 + col] = if small[row * 9 + col] > small[row * 9 + col + 1] {
62	        1	<-- UNCOVERED
63	      } else {
64	        0
65	      }
66	    }
67	  }
68	  hash
69	}
  	…

 74	pub fn phash(img : @types.Image) -> Array[Int] raise @types.LoadError {
 75	  // 输入验证
 76	  if img.width <= 0 || img.height <= 0 {
 77	    raise @types.LoadError::DecodeFailed("phash: 图像尺寸必须大于0")	<-- UNCOVERED
 78	  }
 79	  if img.channels < 1 || img.channels > 4 {
 80	    raise @types.LoadError::DecodeFailed("phash: 通道数必须在1-4之间")	<-- UNCOVERED
 81	  }
 82	  let expected_size = img.width * img.height * img.channels
 83	  if img.data.length() != expected_size {
 84	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 85	      "phash: 数据大小不匹配，期望 " +
 86	      expected_size.to_string() +	<-- UNCOVERED
 87	      "，实际 " +
 88	      img.data.length().to_string(),	<-- UNCOVERED
 89	    )
 90	  }
 91	
 92	  let small = resize_to_gray_8x8(img)
 93	  // 简化 DCT：计算行均值和列均值
 94	  let row_means : Array[Int] = Array::make(8, 0)
 95	  let col_means : Array[Int] = Array::make(8, 0)
 96	  for r in 0..<8 {
 97	    let mut s = 0
 98	    for c in 0..<8 {
 99	      s += small[r * 8 + c]
100	    }
101	    row_means[r] = s / 8
102	  }
103	  for c in 0..<8 {
104	    let mut s = 0
105	    for r in 0..<8 {
106	      s += small[r * 8 + c]
107	    }
108	    col_means[c] = s / 8
109	  }
110	  // 全局均值
111	  let mut total = 0
112	  for i in 0..<8 {
113	    total += row_means[i]
114	  }
115	  let global_mean = total / 8
116	  // 生成哈希：行均值和列均值与全局均值比较
117	  let hash : Array[Int] = Array::make(64, 0)
118	  for i in 0..<8 {
119	    hash[i] = if row_means[i] > global_mean { 1 } else { 0 }
120	  }
121	  for i in 0..<8 {
122	    hash[8 + i] = if col_means[i] > global_mean { 1 } else { 0 }
123	  }
124	  // 填充剩余 48 位用像素级比较
125	  for i in 0..<48 {
126	    hash[16 + i] = if small[i + 16] > global_mean { 1 } else { 0 }
127	  }
128	  hash
129	}
   	…

133	pub fn hamming_distance(
134	  h1 : Array[Int],
135	  h2 : Array[Int],
136	) -> Int raise @types.LoadError {
137	  // 参数验证
138	  if h1.length() == 0 {
139	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
140	      "hamming_distance: h1数组不能为空",
141	    )
142	  }
143	  if h2.length() == 0 {
144	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
145	      "hamming_distance: h2数组不能为空",
146	    )
147	  }
148	  if h1.length() != h2.length() {
149	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
150	      "hamming_distance: h1和h2长度必须相同",
151	    )
152	  }
153	
154	  let n = h1.length()
155	  let mut dist = 0
156	  for i in 0..<n {
157	    if h1[i] != h2[i] {
158	      dist += 1
159	    }
160	  }
161	  dist
162	}
   	…

165	fn resize_to_gray_8x8(img : @types.Image) -> Array[Int] {
166	  let ch = img.channels
167	  let w = img.width
168	  let h = img.height
169	  let out : Array[Int] = Array::make(64, 0)
170	  for y in 0..<8 {
171	    for x in 0..<8 {
172	      // 最近邻采样
173	      let sx = x * w / 8
174	      let sy = y * h / 8
175	      let v = if ch == 1 {
176	        img.data[sy * w + sx].to_int()
177	      } else {
178	        let r = img.data[(sy * w + sx) * ch].to_int()	<-- UNCOVERED
179	        let g = img.data[(sy * w + sx) * ch + 1].to_int()
180	        let b = img.data[(sy * w + sx) * ch + 2].to_int()
181	        (r * 299 + g * 587 + b * 114) / 1000
182	      }
183	      out[y * 8 + x] = v
184	    }
185	  }
186	  out
187	}
   	…

190	fn resize_to_gray_9x8(img : @types.Image) -> Array[Int] {
191	  let ch = img.channels
192	  let w = img.width
193	  let h = img.height
194	  let out : Array[Int] = Array::make(72, 0)
195	  for y in 0..<8 {
196	    for x in 0..<9 {
197	      let sx = x * w / 9
198	      let sy = y * h / 8
199	      let v = if ch == 1 {
200	        img.data[sy * w + sx].to_int()
201	      } else {
202	        let r = img.data[(sy * w + sx) * ch].to_int()	<-- UNCOVERED
203	        let g = img.data[(sy * w + sx) * ch + 1].to_int()
204	        let b = img.data[(sy * w + sx) * ch + 2].to_int()
205	        (r * 299 + g * 587 + b * 114) / 1000
206	      }
207	      out[y * 9 + x] = v
208	    }
209	  }
210	  out
211	}
   	…

36 uncovered line(s) in src\process\feature\image_quality.mbt:

 7	pub fn mse(
 8	  img1 : @types.Image,
 9	  img2 : @types.Image,
10	) -> Double raise @types.LoadError {
11	  // 输入验证
12	  if img1.width <= 0 || img1.height <= 0 {
13	    raise @types.LoadError::DecodeFailed("mse: 图像1尺寸必须大于0")	<-- UNCOVERED
14	  }
15	  if img2.width <= 0 || img2.height <= 0 {
16	    raise @types.LoadError::DecodeFailed("mse: 图像2尺寸必须大于0")	<-- UNCOVERED
17	  }
18	  if img1.channels < 1 || img1.channels > 4 {
19	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
20	      "mse: 图像1通道数必须在1-4之间",
21	    )
22	  }
23	  if img2.channels < 1 || img2.channels > 4 {
24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
25	      "mse: 图像2通道数必须在1-4之间",
26	    )
27	  }
28	  if img1.width != img2.width || img1.height != img2.height {
29	    raise @types.LoadError::DecodeFailed("mse: 两幅图像尺寸必须相同")	<-- UNCOVERED
30	  }
31	  if img1.channels != img2.channels {
32	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
33	      "mse: 两幅图像通道数必须相同",
34	    )
35	  }
36	  let expected_size = img1.width * img1.height * img1.channels
37	  if img1.data.length() != expected_size {
38	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
39	      "mse: 图像1数据大小不匹配，期望 " +
40	      expected_size.to_string() +	<-- UNCOVERED
41	      "，实际 " +
42	      img1.data.length().to_string(),	<-- UNCOVERED
43	    )
44	  }
45	  if img2.data.length() != expected_size {
46	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
47	      "mse: 图像2数据大小不匹配，期望 " +
48	      expected_size.to_string() +	<-- UNCOVERED
49	      "，实际 " +
50	      img2.data.length().to_string(),	<-- UNCOVERED
51	    )
52	  }
53	
54	  let n = img1.data.length()
55	  let mut sum : Int = 0
56	  for i in 0..<n {
57	    let d = img1.data[i].to_int() - img2.data[i].to_int()
58	    sum += d * d
59	  }
60	  sum.to_double() / n.to_double()
61	}
  	…

 67	pub fn psnr(
 68	  img1 : @types.Image,
 69	  img2 : @types.Image,
 70	) -> Double raise @types.LoadError {
 71	  // 输入验证
 72	  if img1.width <= 0 || img1.height <= 0 {
 73	    raise @types.LoadError::DecodeFailed("psnr: 图像1尺寸必须大于0")	<-- UNCOVERED
 74	  }
 75	  if img2.width <= 0 || img2.height <= 0 {
 76	    raise @types.LoadError::DecodeFailed("psnr: 图像2尺寸必须大于0")	<-- UNCOVERED
 77	  }
 78	  if img1.channels < 1 || img1.channels > 4 {
 79	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 80	      "psnr: 图像1通道数必须在1-4之间",
 81	    )
 82	  }
 83	  if img2.channels < 1 || img2.channels > 4 {
 84	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 85	      "psnr: 图像2通道数必须在1-4之间",
 86	    )
 87	  }
 88	  if img1.width != img2.width || img1.height != img2.height {
 89	    raise @types.LoadError::DecodeFailed("psnr: 两幅图像尺寸必须相同")	<-- UNCOVERED
 90	  }
 91	  if img1.channels != img2.channels {
 92	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 93	      "psnr: 两幅图像通道数必须相同",
 94	    )
 95	  }
 96	  let expected_size = img1.width * img1.height * img1.channels
 97	  if img1.data.length() != expected_size {
 98	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 99	      "psnr: 图像1数据大小不匹配，期望 " +
100	      expected_size.to_string() +	<-- UNCOVERED
101	      "，实际 " +
102	      img1.data.length().to_string(),	<-- UNCOVERED
103	    )
104	  }
105	  if img2.data.length() != expected_size {
106	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
107	      "psnr: 图像2数据大小不匹配，期望 " +
108	      expected_size.to_string() +	<-- UNCOVERED
109	      "，实际 " +
110	      img2.data.length().to_string(),	<-- UNCOVERED
111	    )
112	  }
113	
114	  let m = mse(img1, img2)
115	  if m == 0.0 {
116	    return 1.0 / 0.0
117	  }
118	  10.0 * @math.log10(255.0 * 255.0 / m)
119	}
   	…

125	pub fn ssim(
126	  img1 : @types.Image,
127	  img2 : @types.Image,
128	) -> Double raise @types.LoadError {
129	  // 输入验证
130	  if img1.width <= 0 || img1.height <= 0 {
131	    raise @types.LoadError::DecodeFailed("ssim: 图像1尺寸必须大于0")	<-- UNCOVERED
132	  }
133	  if img2.width <= 0 || img2.height <= 0 {
134	    raise @types.LoadError::DecodeFailed("ssim: 图像2尺寸必须大于0")	<-- UNCOVERED
135	  }
136	  if img1.channels < 1 || img1.channels > 4 {
137	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
138	      "ssim: 图像1通道数必须在1-4之间",
139	    )
140	  }
141	  if img2.channels < 1 || img2.channels > 4 {
142	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
143	      "ssim: 图像2通道数必须在1-4之间",
144	    )
145	  }
146	  if img1.width != img2.width || img1.height != img2.height {
147	    raise @types.LoadError::DecodeFailed("ssim: 两幅图像尺寸必须相同")	<-- UNCOVERED
148	  }
149	  if img1.channels != img2.channels {
150	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
151	      "ssim: 两幅图像通道数必须相同",
152	    )
153	  }
154	  let expected_size = img1.width * img1.height * img1.channels
155	  if img1.data.length() != expected_size {
156	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
157	      "ssim: 图像1数据大小不匹配，期望 " +
158	      expected_size.to_string() +	<-- UNCOVERED
159	      "，实际 " +
160	      img1.data.length().to_string(),	<-- UNCOVERED
161	    )
162	  }
163	  if img2.data.length() != expected_size {
164	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
165	      "ssim: 图像2数据大小不匹配，期望 " +
166	      expected_size.to_string() +	<-- UNCOVERED
167	      "，实际 " +
168	      img2.data.length().to_string(),	<-- UNCOVERED
169	    )
170	  }
171	
172	  let w = img1.width
173	  let h = img1.height
174	  let ch = img1.channels
175	  let n = w * h
176	  let c1 = 0.01 * 255.0 * (0.01 * 255.0)
177	  let c2 = 0.03 * 255.0 * (0.03 * 255.0)
178	  let win = 8
179	  let half = win / 2
180	  // 预计算通道求和/平方和/乘积和图像，用于构建积分图
181	  let sum1 : Array[Double] = Array::make(n, 0.0)
182	  let sum2 : Array[Double] = Array::make(n, 0.0)
183	  let sq1 : Array[Double] = Array::make(n, 0.0)
184	  let sq2 : Array[Double] = Array::make(n, 0.0)
185	  let s12 : Array[Double] = Array::make(n, 0.0)
186	  for i in 0..<n {
187	    let mut a = 0.0
188	    let mut b = 0.0
189	    let mut aa = 0.0
190	    let mut bb = 0.0
191	    let mut ab = 0.0
192	    for c in 0..<ch {
193	      let v1 = img1.data[i * ch + c].to_int().to_double()
194	      let v2 = img2.data[i * ch + c].to_int().to_double()
195	      a += v1
196	      b += v2
197	      aa += v1 * v1
198	      bb += v2 * v2
199	      ab += v1 * v2
200	    }
201	    sum1[i] = a
202	    sum2[i] = b
203	    sq1[i] = aa
204	    sq2[i] = bb
205	    s12[i] = ab
206	  }
207	  // 构建积分图 (w+1)*(h+1)，integral[y][x] = sum of img[0..y, 0..x]
208	  let iw = w + 1
209	  let ih = h + 1
210	  let i_sum1 : Array[Double] = Array::make(iw * ih, 0.0)
211	  let i_sum2 : Array[Double] = Array::make(iw * ih, 0.0)
212	  let i_sq1 : Array[Double] = Array::make(iw * ih, 0.0)
213	  let i_sq2 : Array[Double] = Array::make(iw * ih, 0.0)
214	  let i_s12 : Array[Double] = Array::make(iw * ih, 0.0)
215	  for y in 1..<=h {
216	    let mut rs1 = 0.0
217	    let mut rs2 = 0.0
218	    let mut rq1 = 0.0
219	    let mut rq2 = 0.0
220	    let mut r12 = 0.0
221	    for x in 1..<=w {
222	      let idx = (y - 1) * w + (x - 1)
223	      rs1 += sum1[idx]
224	      rs2 += sum2[idx]
225	      rq1 += sq1[idx]
226	      rq2 += sq2[idx]
227	      r12 += s12[idx]
228	      let above = (y - 1) * iw + x
229	      i_sum1[y * iw + x] = i_sum1[above] + rs1
230	      i_sum2[y * iw + x] = i_sum2[above] + rs2
231	      i_sq1[y * iw + x] = i_sq1[above] + rq1
232	      i_sq2[y * iw + x] = i_sq2[above] + rq2
233	      i_s12[y * iw + x] = i_s12[above] + r12
234	    }
235	  }
236	  let mut total : Double = 0.0
237	  let mut count = 0
238	  let inner_n = win * win * ch
239	  let inner_n_d = inner_n.to_double()
240	  // 内部区域：用积分图 O(1) 计算窗口统计量
241	  for y in half..<(h - half) {
242	    for x in half..<(w - half) {
243	      let x0 = x - half
244	      let y0 = y - half
245	      let x1 = x0 + win
246	      let y1 = y0 + win
247	      let s1 = i_sum1[y1 * iw + x1] -
248	        i_sum1[y0 * iw + x1] -
249	        i_sum1[y1 * iw + x0] +
250	        i_sum1[y0 * iw + x0]
251	      let s2 = i_sum2[y1 * iw + x1] -
252	        i_sum2[y0 * iw + x1] -
253	        i_sum2[y1 * iw + x0] +
254	        i_sum2[y0 * iw + x0]
255	      let sq1v = i_sq1[y1 * iw + x1] -
256	        i_sq1[y0 * iw + x1] -
257	        i_sq1[y1 * iw + x0] +
258	        i_sq1[y0 * iw + x0]
259	      let sq2v = i_sq2[y1 * iw + x1] -
260	        i_sq2[y0 * iw + x1] -
261	        i_sq2[y1 * iw + x0] +
262	        i_sq2[y0 * iw + x0]
263	      let s12v = i_s12[y1 * iw + x1] -
264	        i_s12[y0 * iw + x1] -
265	        i_s12[y1 * iw + x0] +
266	        i_s12[y0 * iw + x0]
267	      let mu1 = s1 / inner_n_d
268	      let mu2 = s2 / inner_n_d
269	      let var1 = sq1v / inner_n_d - mu1 * mu1
270	      let var2 = sq2v / inner_n_d - mu2 * mu2
271	      let cov = s12v / inner_n_d - mu1 * mu2
272	      let s = (2.0 * mu1 * mu2 + c1) *
273	        (2.0 * cov + c2) /
274	        ((mu1 * mu1 + mu2 * mu2 + c1) * (var1 + var2 + c2))
275	      total += s
276	      count += 1
277	    }
278	  }
279	  // 边界区域：预计算 Double 数组，用 O(win²) 方法
280	  let src1 : Array[Double] = Array::make(n * ch, 0.0)
281	  let src2 : Array[Double] = Array::make(n * ch, 0.0)
282	  for i in 0..<(n * ch) {
283	    src1[i] = img1.data[i].to_int().to_double()
284	    src2[i] = img2.data[i].to_int().to_double()
285	  }
286	  for y in 0..<h {
287	    let y_inner = y >= half && y < h - half
288	    for x in 0..<w {
289	      if y_inner && x >= half && x < w - half {
290	        continue
291	      }
292	      let mut mu1 : Double = 0.0
293	      let mut mu2 : Double = 0.0
294	      let mut nn = 0
295	      for dy in 0..<win {
296	        for dx in 0..<win {
297	          let sx = clamp_i(x + dx - half, 0, w - 1)
298	          let sy = clamp_i(y + dy - half, 0, h - 1)
299	          for c in 0..<ch {
300	            mu1 += src1[(sy * w + sx) * ch + c]
301	            mu2 += src2[(sy * w + sx) * ch + c]
302	            nn += 1
303	          }
304	        }
305	      }
306	      mu1 /= nn.to_double()
307	      mu2 /= nn.to_double()
308	      let mut var1 : Double = 0.0
309	      let mut var2 : Double = 0.0
310	      let mut cov : Double = 0.0
311	      for dy in 0..<win {
312	        for dx in 0..<win {
313	          let sx = clamp_i(x + dx - half, 0, w - 1)
314	          let sy = clamp_i(y + dy - half, 0, h - 1)
315	          for c in 0..<ch {
316	            let v1 = src1[(sy * w + sx) * ch + c] - mu1
317	            let v2 = src2[(sy * w + sx) * ch + c] - mu2
318	            var1 += v1 * v1
319	            var2 += v2 * v2
320	            cov += v1 * v2
321	          }
322	        }
323	      }
324	      var1 /= nn.to_double()
325	      var2 /= nn.to_double()
326	      cov /= nn.to_double()
327	      let s = (2.0 * mu1 * mu2 + c1) *
328	        (2.0 * cov + c2) /
329	        ((mu1 * mu1 + mu2 * mu2 + c1) * (var1 + var2 + c2))
330	      total += s
331	      count += 1
332	    }
333	  }
334	  total / count.to_double()
335	}
   	…

27 uncovered line(s) in src\process\feature\integral_image.mbt:

14	pub fn integral_image(
15	  img : @types.Image,
16	) -> IntegralImage raise @types.LoadError {
17	  // 输入验证
18	  if img.width <= 0 || img.height <= 0 {
19	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
20	      "integral_image: 图像尺寸必须大于0",
21	    )
22	  }
23	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
24	    raise @types.LoadError::DecodeFailed(
25	      "integral_image: 仅支持 channels=1/3/4",
26	    )
27	  }
28	  let expected_size = img.width * img.height * img.channels
29	  if img.data.length() != expected_size {
30	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
31	      "integral_image: 数据大小不匹配，期望 " +
32	      expected_size.to_string() +	<-- UNCOVERED
33	      "，实际 " +
34	      img.data.length().to_string(),	<-- UNCOVERED
35	    )
36	  }
37	  let w = img.width
38	  let h = img.height
39	  let ch = img.channels
40	  let iw = w + 1
41	  let ih = h + 1
42	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
43	  for i in 0..<(w * h * ch) {
44	    data_int[i] = img.data[i].to_int()
45	  }
46	  let data : Array[Int64] = Array::make(iw * ih, 0L)
47	  for y in 0..<h {
48	    let mut row_sum = 0L
49	    for x in 0..<w {
50	      row_sum += Int64::from_int(data_int[(y * w + x) * ch])
51	      data[(y + 1) * iw + x + 1] = data[y * iw + x + 1] + row_sum
52	    }
53	  }
54	  IntegralImage::{ width: w, height: h, data }
55	}
  	…

59	pub fn integral_sum(
60	  integral : IntegralImage,
61	  x : Int,
62	  y : Int,
63	  w : Int,
64	  h : Int,
65	) -> Int64 raise @types.LoadError {
66	  // 参数验证
67	  if integral.width <= 0 || integral.height <= 0 {
68	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
69	      "integral_sum: 积分图像尺寸必须大于0",
70	    )
71	  }
72	  if x < 0 || y < 0 {
73	    raise @types.LoadError::DecodeFailed("integral_sum: x和y必须 >= 0")	<-- UNCOVERED
74	  }
75	  if w <= 0 || h <= 0 {
76	    raise @types.LoadError::DecodeFailed("integral_sum: w和h必须大于0")	<-- UNCOVERED
77	  }
78	  if x + w > integral.width || y + h > integral.height {
79	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
80	      "integral_sum: 矩形区域超出图像边界",
81	    )
82	  }
83	
84	  let iw = integral.width + 1
85	  let x2 = x + w
86	  let y2 = y + h
87	  let p00 = integral.data[y * iw + x]
88	  let p10 = integral.data[y * iw + x2]
89	  let p01 = integral.data[y2 * iw + x]
90	  let p11 = integral.data[y2 * iw + x2]
91	  p11 - p10 - p01 + p00
92	}
  	…

 96	pub fn integral_mean(
 97	  integral : IntegralImage,
 98	  x : Int,
 99	  y : Int,
100	  w : Int,
101	  h : Int,
102	) -> Float raise @types.LoadError {
103	  // 参数验证
104	  if integral.width <= 0 || integral.height <= 0 {
105	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
106	      "integral_mean: 积分图像尺寸必须大于0",
107	    )
108	  }
109	  if x < 0 || y < 0 {
110	    raise @types.LoadError::DecodeFailed("integral_mean: x和y必须 >= 0")	<-- UNCOVERED
111	  }
112	  if w < 0 || h < 0 {
113	    raise @types.LoadError::DecodeFailed("integral_mean: w和h必须 >= 0")	<-- UNCOVERED
114	  }
115	  if w == 0 || h == 0 {
116	    return 0.0F
117	  }
118	  if x + w > integral.width || y + h > integral.height {
119	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
120	      "integral_mean: 矩形区域超出图像边界",
121	    )
122	  }
123	
124	  let sum = integral_sum(integral, x, y, w, h)
125	  Float::from_int(sum.to_int()) / Float::from_int(w * h)
126	}
   	…

138	pub fn integral_image_sq(
139	  img : @types.Image,
140	) -> IntegralImageSq raise @types.LoadError {
141	  // 输入验证
142	  if img.width <= 0 || img.height <= 0 {
143	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
144	      "integral_image_sq: 图像尺寸必须大于0",
145	    )
146	  }
147	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
148	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
149	      "integral_image_sq: 仅支持 channels=1/3/4",
150	    )
151	  }
152	  let expected_size = img.width * img.height * img.channels
153	  if img.data.length() != expected_size {
154	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
155	      "integral_image_sq: 数据大小不匹配，期望 " +
156	      expected_size.to_string() +	<-- UNCOVERED
157	      "，实际 " +
158	      img.data.length().to_string(),	<-- UNCOVERED
159	    )
160	  }
161	  let w = img.width
162	  let h = img.height
163	  let ch = img.channels
164	  let iw = w + 1
165	  let ih = h + 1
166	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
167	  for i in 0..<(w * h * ch) {
168	    data_int[i] = img.data[i].to_int()
169	  }
170	  let data : Array[Int64] = Array::make(iw * ih, 0L)
171	  for y in 0..<h {
172	    let mut row_sum = 0L
173	    for x in 0..<w {
174	      let v = Int64::from_int(data_int[(y * w + x) * ch])
175	      row_sum += v * v
176	      data[(y + 1) * iw + x + 1] = data[y * iw + x + 1] + row_sum
177	    }
178	  }
179	  IntegralImageSq::{ width: w, height: h, data }
180	}
   	…

185	pub fn integral_variance(
186	  integral : IntegralImage,
187	  integral_sq : IntegralImageSq,
188	  x : Int,
189	  y : Int,
190	  w : Int,
191	  h : Int,
192	) -> Float raise @types.LoadError {
193	  // 参数验证
194	  if integral.width <= 0 || integral.height <= 0 {
195	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
196	      "integral_variance: 积分图像尺寸必须大于0",
197	    )
198	  }
199	  if integral_sq.width <= 0 || integral_sq.height <= 0 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "integral_variance: 平方积分图像尺寸必须大于0",
202	    )
203	  }
204	  if integral.width != integral_sq.width ||
205	    integral.height != integral_sq.height {
206	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
207	      "integral_variance: 积分图像和平方积分图像尺寸必须相同",
208	    )
209	  }
210	  if x < 0 || y < 0 {
211	    raise @types.LoadError::DecodeFailed("integral_variance: x和y必须 >= 0")	<-- UNCOVERED
212	  }
213	  if w <= 0 || h <= 0 {
214	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
215	      "integral_variance: w和h必须大于0",
216	    )
217	  }
218	  if x + w > integral.width || y + h > integral.height {
219	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
220	      "integral_variance: 矩形区域超出图像边界",
221	    )
222	  }
223	
224	  let n = Float::from_int(w * h)
225	  let sum = Float::from_int(integral_sum(integral, x, y, w, h).to_int())
226	  let sum_sq = Float::from_int(
227	    integral_sum_sq(integral_sq, x, y, w, h).to_int(),
228	  )
229	  let mean = sum / n
230	  sum_sq / n - mean * mean
231	}
   	…

235	pub fn integral_sum_sq(
236	  integral_sq : IntegralImageSq,
237	  x : Int,
238	  y : Int,
239	  w : Int,
240	  h : Int,
241	) -> Int64 raise @types.LoadError {
242	  // 参数验证
243	  if integral_sq.width <= 0 || integral_sq.height <= 0 {
244	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
245	      "integral_sum_sq: 平方积分图像尺寸必须大于0",
246	    )
247	  }
248	  if x < 0 || y < 0 {
249	    raise @types.LoadError::DecodeFailed("integral_sum_sq: x和y必须 >= 0")	<-- UNCOVERED
250	  }
251	  if w <= 0 || h <= 0 {
252	    raise @types.LoadError::DecodeFailed("integral_sum_sq: w和h必须大于0")	<-- UNCOVERED
253	  }
254	  if x + w > integral_sq.width || y + h > integral_sq.height {
255	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
256	      "integral_sum_sq: 矩形区域超出图像边界",
257	    )
258	  }
259	
260	  let iw = integral_sq.width + 1
261	  let x2 = x + w
262	  let y2 = y + h
263	  let p00 = integral_sq.data[y * iw + x]
264	  let p10 = integral_sq.data[y * iw + x2]
265	  let p01 = integral_sq.data[y2 * iw + x]
266	  let p11 = integral_sq.data[y2 * iw + x2]
267	  p11 - p10 - p01 + p00
268	}
   	…

11 uncovered line(s) in src\process\feature\lbp.mbt:

  7	pub fn lbp(img : @types.Image) -> @types.Image raise @types.LoadError {
  8	  // 输入验证
  9	  if img.width <= 0 || img.height <= 0 {
 10	    raise @types.LoadError::DecodeFailed("lbp: 图像尺寸必须大于0")	<-- UNCOVERED
 11	  }
 12	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 13	    raise @types.LoadError::DecodeFailed("lbp: 仅支持 channels=1/3/4")
 14	  }
 15	  let expected_size = img.width * img.height * img.channels
 16	  if img.data.length() != expected_size {
 17	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 18	      "lbp: 数据大小不匹配，期望 " +
 19	      expected_size.to_string() +	<-- UNCOVERED
 20	      "，实际 " +
 21	      img.data.length().to_string(),	<-- UNCOVERED
 22	    )
 23	  }
 24	  let ch = img.channels
 25	  let color_ch = if ch >= 3 { 3 } else { 1 }
 26	  let w = img.width
 27	  let h = img.height
 28	  let n = w * h
 29	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 30	  // 预计算像素 Int 数组
 31	  let src : Array[Int] = Array::make(n * ch, 0)
 32	  for i in 0..<(n * ch) {
 33	    src[i] = img.data[i].to_int()
 34	  }
 35	  for c in 0..<color_ch {
 36	    // 内部区域：x in [1, w-1), y in [1, h-1)，直接访问 8 邻域
 37	    for y in 1..<(h - 1) {
 38	      for x in 1..<(w - 1) {
 39	        let center = src[(y * w + x) * ch + c]
 40	        let mut lbp_val = 0
 41	        // 左上、上、右上、右、右下、下、左下、左
 42	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
 43	          lbp_val = lbp_val | 1
 44	        }
 45	        if src[((y - 1) * w + x) * ch + c] >= center {
 46	          lbp_val = lbp_val | 2
 47	        }
 48	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
 49	          lbp_val = lbp_val | 4
 50	        }
 51	        if src[(y * w + (x + 1)) * ch + c] >= center {
 52	          lbp_val = lbp_val | 8
 53	        }
 54	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
 55	          lbp_val = lbp_val | 16
 56	        }
 57	        if src[((y + 1) * w + x) * ch + c] >= center {
 58	          lbp_val = lbp_val | 32
 59	        }
 60	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
 61	          lbp_val = lbp_val | 64
 62	        }
 63	        if src[(y * w + (x - 1)) * ch + c] >= center {
 64	          lbp_val = lbp_val | 128
 65	        }
 66	        out[(y * w + x) * ch + c] = lbp_val.to_byte()
 67	      }
 68	    }
 69	    // 边界区域：使用 get_pixel_safe
 70	    for y in 0..<h {
 71	      let y_inner = y >= 1 && y < h - 1
 72	      for x in 0..<w {
 73	        if y_inner && x >= 1 && x < w - 1 {
 74	          continue
 75	        }
 76	        let center = src[(y * w + x) * ch + c]
 77	        let mut lbp_val = 0
 78	        if get_pixel_safe(src, x - 1, y - 1, c, ch, w, h) >= center {
 79	          lbp_val = lbp_val | 1
 80	        }
 81	        if get_pixel_safe(src, x, y - 1, c, ch, w, h) >= center {
 82	          lbp_val = lbp_val | 2
 83	        }
 84	        if get_pixel_safe(src, x + 1, y - 1, c, ch, w, h) >= center {
 85	          lbp_val = lbp_val | 4
 86	        }
 87	        if get_pixel_safe(src, x + 1, y, c, ch, w, h) >= center {
 88	          lbp_val = lbp_val | 8
 89	        }
 90	        if get_pixel_safe(src, x + 1, y + 1, c, ch, w, h) >= center {
 91	          lbp_val = lbp_val | 16
 92	        }
 93	        if get_pixel_safe(src, x, y + 1, c, ch, w, h) >= center {
 94	          lbp_val = lbp_val | 32
 95	        }
 96	        if get_pixel_safe(src, x - 1, y + 1, c, ch, w, h) >= center {
 97	          lbp_val = lbp_val | 64
 98	        }
 99	        if get_pixel_safe(src, x - 1, y, c, ch, w, h) >= center {
100	          lbp_val = lbp_val | 128
101	        }
102	        out[(y * w + x) * ch + c] = lbp_val.to_byte()
103	      }
104	    }
105	  }
106	  if ch == 4 {
107	    for i in 0..<n {	<-- UNCOVERED
108	      out[i * 4 + 3] = img.data[i * 4 + 3]
109	    }
110	  }
111	  @types.Image::{
112	    width: w,
113	    height: h,
114	    channels: ch,
115	    data: Bytes::from_array(out),
116	  }
117	}
   	…

123	pub fn lbp_uniform(img : @types.Image) -> @types.Image raise @types.LoadError {
124	  // 输入验证
125	  if img.width <= 0 || img.height <= 0 {
126	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
127	      "lbp_uniform: 图像尺寸必须大于0",
128	    )
129	  }
130	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
131	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
132	      "lbp_uniform: 仅支持 channels=1/3/4",
133	    )
134	  }
135	  let expected_size = img.width * img.height * img.channels
136	  if img.data.length() != expected_size {
137	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
138	      "lbp_uniform: 数据大小不匹配，期望 " +
139	      expected_size.to_string() +	<-- UNCOVERED
140	      "，实际 " +
141	      img.data.length().to_string(),	<-- UNCOVERED
142	    )
143	  }
144	  let ch = img.channels
145	  let color_ch = if ch >= 3 { 3 } else { 1 }
146	  let w = img.width
147	  let h = img.height
148	  let n = w * h
149	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
150	  // 预计算均匀模式映射表
151	  let uniform_map = build_uniform_map()
152	  // 预计算像素 Int 数组
153	  let src : Array[Int] = Array::make(n * ch, 0)
154	  for i in 0..<(n * ch) {
155	    src[i] = img.data[i].to_int()
156	  }
157	  for c in 0..<color_ch {
158	    // 内部区域
159	    for y in 1..<(h - 1) {
160	      for x in 1..<(w - 1) {
161	        let center = src[(y * w + x) * ch + c]
162	        let mut lbp_val = 0
163	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
164	          lbp_val = lbp_val | 1
165	        }
166	        if src[((y - 1) * w + x) * ch + c] >= center {
167	          lbp_val = lbp_val | 2
168	        }
169	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
170	          lbp_val = lbp_val | 4
171	        }
172	        if src[(y * w + (x + 1)) * ch + c] >= center {
173	          lbp_val = lbp_val | 8
174	        }
175	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
176	          lbp_val = lbp_val | 16
177	        }
178	        if src[((y + 1) * w + x) * ch + c] >= center {
179	          lbp_val = lbp_val | 32
180	        }
181	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
182	          lbp_val = lbp_val | 64
183	        }
184	        if src[(y * w + (x - 1)) * ch + c] >= center {
185	          lbp_val = lbp_val | 128
186	        }
187	        out[(y * w + x) * ch + c] = uniform_map[lbp_val].to_byte()
188	      }
189	    }
190	    // 边界区域
191	    for y in 0..<h {
192	      let y_inner = y >= 1 && y < h - 1
193	      for x in 0..<w {
194	        if y_inner && x >= 1 && x < w - 1 {
195	          continue
196	        }
197	        let center = src[(y * w + x) * ch + c]
198	        let mut lbp_val = 0
199	        if get_pixel_safe(src, x - 1, y - 1, c, ch, w, h) >= center {
200	          lbp_val = lbp_val | 1
201	        }
202	        if get_pixel_safe(src, x, y - 1, c, ch, w, h) >= center {
203	          lbp_val = lbp_val | 2
204	        }
205	        if get_pixel_safe(src, x + 1, y - 1, c, ch, w, h) >= center {
206	          lbp_val = lbp_val | 4
207	        }
208	        if get_pixel_safe(src, x + 1, y, c, ch, w, h) >= center {
209	          lbp_val = lbp_val | 8
210	        }
211	        if get_pixel_safe(src, x + 1, y + 1, c, ch, w, h) >= center {
212	          lbp_val = lbp_val | 16
213	        }
214	        if get_pixel_safe(src, x, y + 1, c, ch, w, h) >= center {
215	          lbp_val = lbp_val | 32
216	        }
217	        if get_pixel_safe(src, x - 1, y + 1, c, ch, w, h) >= center {
218	          lbp_val = lbp_val | 64
219	        }
220	        if get_pixel_safe(src, x - 1, y, c, ch, w, h) >= center {
221	          lbp_val = lbp_val | 128
222	        }
223	        out[(y * w + x) * ch + c] = uniform_map[lbp_val].to_byte()
224	      }
225	    }
226	  }
227	  if ch == 4 {
228	    for i in 0..<n {	<-- UNCOVERED
229	      out[i * 4 + 3] = img.data[i * 4 + 3]
230	    }
231	  }
232	  @types.Image::{
233	    width: w,
234	    height: h,
235	    channels: ch,
236	    data: Bytes::from_array(out),
237	  }
238	}
   	…

26 uncovered line(s) in src\process\feature\lbp_16f.mbt:

 80	fn uniform_lbp_map(lbp_val : Int) -> Int {
 81	  // 计算 0->1 和 1->0 跳变次数
 82	  let mut transitions = 0
 83	  let mut prev = (lbp_val >> 7) & 1
 84	  for i in 0..<8 {
 85	    let curr = (lbp_val >> i) & 1
 86	    if curr != prev {
 87	      transitions += 1	<-- UNCOVERED
 88	    }
 89	    prev = curr
 90	  }
 91	  if transitions <= 2 {
 92	    // 均匀模式：计算 1 的个数作为标签（0-8）
 93	    let mut count = 0
 94	    for i in 0..<8 {
 95	      if ((lbp_val >> i) & 1) == 1 {
 96	        count += 1
 97	      }
 98	    }
 99	    count
100	  } else {
101	    // 非均匀模式
102	    9	<-- UNCOVERED
103	  }
104	}
   	…

110	pub fn lbp_16(img : @types.Image16) -> @types.Image16 raise @types.LoadError {
111	  // 输入验证
112	  if img.width <= 0 || img.height <= 0 {
113	    raise @types.LoadError::DecodeFailed("lbp_16: 图像尺寸必须大于0")	<-- UNCOVERED
114	  }
115	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
116	    raise @types.LoadError::DecodeFailed("lbp_16: 仅支持 channels=1/3/4")
117	  }
118	  let expected_size = img.width * img.height * img.channels * 2
119	  if img.data.length() != expected_size {
120	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
121	      "lbp_16: 数据大小不匹配，期望 " +
122	      expected_size.to_string() +	<-- UNCOVERED
123	      "，实际 " +
124	      img.data.length().to_string(),	<-- UNCOVERED
125	    )
126	  }
127	  let ch = img.channels
128	  let color_ch = if ch >= 3 { 3 } else { 1 }
129	  let w = img.width
130	  let h = img.height
131	  let n = w * h
132	  let bpp = ch * 2
133	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
134	  let src : Array[Int] = Array::make(n * ch, 0)
135	  for i in 0..<(n * ch) {
136	    src[i] = read_u16_le_l(img.data, i * 2)
137	  }
138	  for c in 0..<color_ch {
139	    for y in 1..<(h - 1) {
140	      for x in 1..<(w - 1) {
141	        let center = src[(y * w + x) * ch + c]
142	        let mut lbp_val = 0
143	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
144	          lbp_val = lbp_val | 1
145	        }
146	        if src[((y - 1) * w + x) * ch + c] >= center {
147	          lbp_val = lbp_val | 2
148	        }
149	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
150	          lbp_val = lbp_val | 4
151	        }
152	        if src[(y * w + (x + 1)) * ch + c] >= center {
153	          lbp_val = lbp_val | 8
154	        }
155	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
156	          lbp_val = lbp_val | 16
157	        }
158	        if src[((y + 1) * w + x) * ch + c] >= center {
159	          lbp_val = lbp_val | 32
160	        }
161	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
162	          lbp_val = lbp_val | 64
163	        }
164	        if src[(y * w + (x - 1)) * ch + c] >= center {
165	          lbp_val = lbp_val | 128
166	        }
167	        write_u16_le_l(out, (y * w + x) * bpp + c * 2, lbp_val)
168	      }
169	    }
170	    for y in 0..<h {
171	      let y_inner = y >= 1 && y < h - 1
172	      for x in 0..<w {
173	        if y_inner && x >= 1 && x < w - 1 {
174	          continue
175	        }
176	        let center = src[(y * w + x) * ch + c]
177	        let mut lbp_val = 0
178	        if get_pixel_safe_16(src, x - 1, y - 1, c, ch, w, h) >= center {
179	          lbp_val = lbp_val | 1
180	        }
181	        if get_pixel_safe_16(src, x, y - 1, c, ch, w, h) >= center {
182	          lbp_val = lbp_val | 2
183	        }
184	        if get_pixel_safe_16(src, x + 1, y - 1, c, ch, w, h) >= center {
185	          lbp_val = lbp_val | 4
186	        }
187	        if get_pixel_safe_16(src, x + 1, y, c, ch, w, h) >= center {
188	          lbp_val = lbp_val | 8
189	        }
190	        if get_pixel_safe_16(src, x + 1, y + 1, c, ch, w, h) >= center {
191	          lbp_val = lbp_val | 16
192	        }
193	        if get_pixel_safe_16(src, x, y + 1, c, ch, w, h) >= center {
194	          lbp_val = lbp_val | 32
195	        }
196	        if get_pixel_safe_16(src, x - 1, y + 1, c, ch, w, h) >= center {
197	          lbp_val = lbp_val | 64
198	        }
199	        if get_pixel_safe_16(src, x - 1, y, c, ch, w, h) >= center {
200	          lbp_val = lbp_val | 128
201	        }
202	        write_u16_le_l(out, (y * w + x) * bpp + c * 2, lbp_val)
203	      }
204	    }
205	  }
206	  if ch == 4 {
207	    for i in 0..<n {
208	      let alpha_off = i * bpp + 6
209	      out[alpha_off] = img.data[alpha_off]
210	      out[alpha_off + 1] = img.data[alpha_off + 1]
211	    }
212	  }
213	  @types.Image16::{
214	    width: w,
215	    height: h,
216	    channels: ch,
217	    data: Bytes::from_array(out),
218	  }
219	}
   	…

223	pub fn lbp_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
224	  // 输入验证
225	  if img.width <= 0 || img.height <= 0 {
226	    raise @types.LoadError::DecodeFailed("lbp_f: 图像尺寸必须大于0")	<-- UNCOVERED
227	  }
228	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
229	    raise @types.LoadError::DecodeFailed("lbp_f: 仅支持 channels=1/3/4")	<-- UNCOVERED
230	  }
231	  let expected_size = img.width * img.height * img.channels * 4
232	  if img.data.length() != expected_size {
233	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
234	      "lbp_f: 数据大小不匹配，期望 " +
235	      expected_size.to_string() +	<-- UNCOVERED
236	      "，实际 " +
237	      img.data.length().to_string(),	<-- UNCOVERED
238	    )
239	  }
240	  let ch = img.channels
241	  let color_ch = if ch >= 3 { 3 } else { 1 }
242	  let w = img.width
243	  let h = img.height
244	  let n = w * h
245	  let bpp = ch * 4
246	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
247	  let src : Array[Float] = Array::make(n * ch, 0.0F)
248	  for i in 0..<(n * ch) {
249	    src[i] = read_float_le_l(img.data, i * 4)
250	  }
251	  for c in 0..<color_ch {
252	    for y in 1..<(h - 1) {
253	      for x in 1..<(w - 1) {
254	        let center = src[(y * w + x) * ch + c]
255	        let mut lbp_val = 0
256	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
257	          lbp_val = lbp_val | 1
258	        }
259	        if src[((y - 1) * w + x) * ch + c] >= center {
260	          lbp_val = lbp_val | 2
261	        }
262	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
263	          lbp_val = lbp_val | 4
264	        }
265	        if src[(y * w + (x + 1)) * ch + c] >= center {
266	          lbp_val = lbp_val | 8
267	        }
268	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
269	          lbp_val = lbp_val | 16
270	        }
271	        if src[((y + 1) * w + x) * ch + c] >= center {
272	          lbp_val = lbp_val | 32
273	        }
274	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
275	          lbp_val = lbp_val | 64
276	        }
277	        if src[(y * w + (x - 1)) * ch + c] >= center {
278	          lbp_val = lbp_val | 128
279	        }
280	        write_f32_le_l(
281	          out,
282	          (y * w + x) * bpp + c * 4,
283	          Float::from_int(lbp_val) / 255.0F,
284	        )
285	      }
286	    }
287	    for y in 0..<h {
288	      let y_inner = y >= 1 && y < h - 1
289	      for x in 0..<w {
290	        if y_inner && x >= 1 && x < w - 1 {
291	          continue
292	        }
293	        let center = src[(y * w + x) * ch + c]
294	        let mut lbp_val = 0
295	        if get_pixel_safe_f(src, x - 1, y - 1, c, ch, w, h) >= center {
296	          lbp_val = lbp_val | 1
297	        }
298	        if get_pixel_safe_f(src, x, y - 1, c, ch, w, h) >= center {
299	          lbp_val = lbp_val | 2
300	        }
301	        if get_pixel_safe_f(src, x + 1, y - 1, c, ch, w, h) >= center {
302	          lbp_val = lbp_val | 4
303	        }
304	        if get_pixel_safe_f(src, x + 1, y, c, ch, w, h) >= center {
305	          lbp_val = lbp_val | 8
306	        }
307	        if get_pixel_safe_f(src, x + 1, y + 1, c, ch, w, h) >= center {
308	          lbp_val = lbp_val | 16
309	        }
310	        if get_pixel_safe_f(src, x, y + 1, c, ch, w, h) >= center {
311	          lbp_val = lbp_val | 32
312	        }
313	        if get_pixel_safe_f(src, x - 1, y + 1, c, ch, w, h) >= center {
314	          lbp_val = lbp_val | 64
315	        }
316	        if get_pixel_safe_f(src, x - 1, y, c, ch, w, h) >= center {
317	          lbp_val = lbp_val | 128
318	        }
319	        write_f32_le_l(
320	          out,
321	          (y * w + x) * bpp + c * 4,
322	          Float::from_int(lbp_val) / 255.0F,
323	        )
324	      }
325	    }
326	  }
327	  if ch == 4 {
328	    for i in 0..<n {	<-- UNCOVERED
329	      let alpha_off = i * bpp + 12
330	      for b in 0..<4 {
331	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
332	      }
333	    }
334	  }
335	  @types.ImageF::{
336	    width: w,
337	    height: h,
338	    channels: ch,
339	    data: Bytes::from_array(out),
340	  }
341	}
   	…

347	pub fn lbp_uniform_16(
348	  img : @types.Image16,
349	) -> @types.Image16 raise @types.LoadError {
350	  // 输入验证
351	  if img.width <= 0 || img.height <= 0 {
352	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
353	      "lbp_uniform_16: 图像尺寸必须大于0",
354	    )
355	  }
356	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
357	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
358	      "lbp_uniform_16: 仅支持 channels=1/3/4",
359	    )
360	  }
361	  let expected_size = img.width * img.height * img.channels * 2
362	  if img.data.length() != expected_size {
363	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
364	      "lbp_uniform_16: 数据大小不匹配，期望 " +
365	      expected_size.to_string() +	<-- UNCOVERED
366	      "，实际 " +
367	      img.data.length().to_string(),	<-- UNCOVERED
368	    )
369	  }
370	  let ch = img.channels
371	  let color_ch = if ch >= 3 { 3 } else { 1 }
372	  let w = img.width
373	  let h = img.height
374	  let n = w * h
375	  let bpp = ch * 2
376	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
377	  let src : Array[Int] = Array::make(n * ch, 0)
378	  for i in 0..<(n * ch) {
379	    src[i] = read_u16_le_l(img.data, i * 2)
380	  }
381	  for c in 0..<color_ch {
382	    for y in 1..<(h - 1) {
383	      for x in 1..<(w - 1) {
384	        let center = src[(y * w + x) * ch + c]
385	        let mut lbp_val = 0
386	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
387	          lbp_val = lbp_val | 1
388	        }
389	        if src[((y - 1) * w + x) * ch + c] >= center {
390	          lbp_val = lbp_val | 2
391	        }
392	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
393	          lbp_val = lbp_val | 4
394	        }
395	        if src[(y * w + (x + 1)) * ch + c] >= center {
396	          lbp_val = lbp_val | 8
397	        }
398	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
399	          lbp_val = lbp_val | 16
400	        }
401	        if src[((y + 1) * w + x) * ch + c] >= center {
402	          lbp_val = lbp_val | 32
403	        }
404	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
405	          lbp_val = lbp_val | 64
406	        }
407	        if src[(y * w + (x - 1)) * ch + c] >= center {
408	          lbp_val = lbp_val | 128
409	        }
410	        write_u16_le_l(out, (y * w + x) * bpp + c * 2, uniform_lbp_map(lbp_val))
411	      }
412	    }
413	    for y in 0..<h {
414	      let y_inner = y >= 1 && y < h - 1
415	      for x in 0..<w {
416	        if y_inner && x >= 1 && x < w - 1 {
417	          continue
418	        }
419	        let center = src[(y * w + x) * ch + c]
420	        let mut lbp_val = 0
421	        if get_pixel_safe_16(src, x - 1, y - 1, c, ch, w, h) >= center {
422	          lbp_val = lbp_val | 1
423	        }
424	        if get_pixel_safe_16(src, x, y - 1, c, ch, w, h) >= center {
425	          lbp_val = lbp_val | 2
426	        }
427	        if get_pixel_safe_16(src, x + 1, y - 1, c, ch, w, h) >= center {
428	          lbp_val = lbp_val | 4
429	        }
430	        if get_pixel_safe_16(src, x + 1, y, c, ch, w, h) >= center {
431	          lbp_val = lbp_val | 8
432	        }
433	        if get_pixel_safe_16(src, x + 1, y + 1, c, ch, w, h) >= center {
434	          lbp_val = lbp_val | 16
435	        }
436	        if get_pixel_safe_16(src, x, y + 1, c, ch, w, h) >= center {
437	          lbp_val = lbp_val | 32
438	        }
439	        if get_pixel_safe_16(src, x - 1, y + 1, c, ch, w, h) >= center {
440	          lbp_val = lbp_val | 64
441	        }
442	        if get_pixel_safe_16(src, x - 1, y, c, ch, w, h) >= center {
443	          lbp_val = lbp_val | 128
444	        }
445	        write_u16_le_l(out, (y * w + x) * bpp + c * 2, uniform_lbp_map(lbp_val))
446	      }
447	    }
448	  }
449	  if ch == 4 {
450	    for i in 0..<n {	<-- UNCOVERED
451	      let alpha_off = i * bpp + 6
452	      out[alpha_off] = img.data[alpha_off]
453	      out[alpha_off + 1] = img.data[alpha_off + 1]
454	    }
455	  }
456	  @types.Image16::{
457	    width: w,
458	    height: h,
459	    channels: ch,
460	    data: Bytes::from_array(out),
461	  }
462	}
   	…

466	pub fn lbp_uniform_f(
467	  img : @types.ImageF,
468	) -> @types.ImageF raise @types.LoadError {
469	  // 输入验证
470	  if img.width <= 0 || img.height <= 0 {
471	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
472	      "lbp_uniform_f: 图像尺寸必须大于0",
473	    )
474	  }
475	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
476	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
477	      "lbp_uniform_f: 仅支持 channels=1/3/4",
478	    )
479	  }
480	  let expected_size = img.width * img.height * img.channels * 4
481	  if img.data.length() != expected_size {
482	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
483	      "lbp_uniform_f: 数据大小不匹配，期望 " +
484	      expected_size.to_string() +	<-- UNCOVERED
485	      "，实际 " +
486	      img.data.length().to_string(),	<-- UNCOVERED
487	    )
488	  }
489	  let ch = img.channels
490	  let color_ch = if ch >= 3 { 3 } else { 1 }
491	  let w = img.width
492	  let h = img.height
493	  let n = w * h
494	  let bpp = ch * 4
495	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
496	  let src : Array[Float] = Array::make(n * ch, 0.0F)
497	  for i in 0..<(n * ch) {
498	    src[i] = read_float_le_l(img.data, i * 4)
499	  }
500	  for c in 0..<color_ch {
501	    for y in 1..<(h - 1) {
502	      for x in 1..<(w - 1) {
503	        let center = src[(y * w + x) * ch + c]
504	        let mut lbp_val = 0
505	        if src[((y - 1) * w + (x - 1)) * ch + c] >= center {
506	          lbp_val = lbp_val | 1
507	        }
508	        if src[((y - 1) * w + x) * ch + c] >= center {
509	          lbp_val = lbp_val | 2
510	        }
511	        if src[((y - 1) * w + (x + 1)) * ch + c] >= center {
512	          lbp_val = lbp_val | 4
513	        }
514	        if src[(y * w + (x + 1)) * ch + c] >= center {
515	          lbp_val = lbp_val | 8
516	        }
517	        if src[((y + 1) * w + (x + 1)) * ch + c] >= center {
518	          lbp_val = lbp_val | 16
519	        }
520	        if src[((y + 1) * w + x) * ch + c] >= center {
521	          lbp_val = lbp_val | 32
522	        }
523	        if src[((y + 1) * w + (x - 1)) * ch + c] >= center {
524	          lbp_val = lbp_val | 64
525	        }
526	        if src[(y * w + (x - 1)) * ch + c] >= center {
527	          lbp_val = lbp_val | 128
528	        }
529	        let label = uniform_lbp_map(lbp_val)
530	        write_f32_le_l(
531	          out,
532	          (y * w + x) * bpp + c * 4,
533	          Float::from_int(label) / 9.0F,
534	        )
535	      }
536	    }
537	    for y in 0..<h {
538	      let y_inner = y >= 1 && y < h - 1
539	      for x in 0..<w {
540	        if y_inner && x >= 1 && x < w - 1 {
541	          continue
542	        }
543	        let center = src[(y * w + x) * ch + c]
544	        let mut lbp_val = 0
545	        if get_pixel_safe_f(src, x - 1, y - 1, c, ch, w, h) >= center {
546	          lbp_val = lbp_val | 1
547	        }
548	        if get_pixel_safe_f(src, x, y - 1, c, ch, w, h) >= center {
549	          lbp_val = lbp_val | 2
550	        }
551	        if get_pixel_safe_f(src, x + 1, y - 1, c, ch, w, h) >= center {
552	          lbp_val = lbp_val | 4
553	        }
554	        if get_pixel_safe_f(src, x + 1, y, c, ch, w, h) >= center {
555	          lbp_val = lbp_val | 8
556	        }
557	        if get_pixel_safe_f(src, x + 1, y + 1, c, ch, w, h) >= center {
558	          lbp_val = lbp_val | 16
559	        }
560	        if get_pixel_safe_f(src, x, y + 1, c, ch, w, h) >= center {
561	          lbp_val = lbp_val | 32
562	        }
563	        if get_pixel_safe_f(src, x - 1, y + 1, c, ch, w, h) >= center {
564	          lbp_val = lbp_val | 64
565	        }
566	        if get_pixel_safe_f(src, x - 1, y, c, ch, w, h) >= center {
567	          lbp_val = lbp_val | 128
568	        }
569	        let label = uniform_lbp_map(lbp_val)
570	        write_f32_le_l(
571	          out,
572	          (y * w + x) * bpp + c * 4,
573	          Float::from_int(label) / 9.0F,
574	        )
575	      }
576	    }
577	  }
578	  if ch == 4 {
579	    for i in 0..<n {	<-- UNCOVERED
580	      let alpha_off = i * bpp + 12
581	      for b in 0..<4 {
582	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
583	      }
584	    }
585	  }
586	  @types.ImageF::{
587	    width: w,
588	    height: h,
589	    channels: ch,
590	    data: Bytes::from_array(out),
591	  }
592	}
   	…

10 uncovered line(s) in src\process\feature\match.mbt:

15	pub fn sift_match(
16	  query : Array[SiftDescriptor],
17	  train : Array[SiftDescriptor],
18	  max_distance? : Float = 300.0F,
19	  ratio_test? : Float = 0.75F,
20	) -> Array[SiftMatch] raise @types.LoadError {
21	  // 参数验证
22	  if max_distance <= 0.0F {
23	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
24	      "sift_match: max_distance必须大于0",
25	    )
26	  }
27	  if ratio_test <= 0.0F || ratio_test >= 1.0F {
28	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
29	      "sift_match: ratio_test必须在0-1之间",
30	    )
31	  }
32	  // 空数组直接返回空结果
33	  if query.length() == 0 || train.length() == 0 {
34	    return []
35	  }
36	
37	  let matches : Array[SiftMatch] = []
38	  for qi in 0..<query.length() {
39	    let qd = query[qi].data
40	    // 找最近和次近
41	    let mut best1_dist = 10000000000.0F
42	    let mut best1_idx = -1
43	    let mut best2_dist = 10000000000.0F
44	    for ti in 0..<train.length() {
45	      let td = train[ti].data
46	      let d = l2_distance(qd, td)
47	      if d < best1_dist {
48	        best2_dist = best1_dist
49	        best1_dist = d
50	        best1_idx = ti
51	      } else if d < best2_dist {
52	        best2_dist = d
53	      }
54	    }
55	    if best1_idx >= 0 && best1_dist < max_distance {
56	      // Lowe 比率测试
57	      if best2_dist > 0.0F && best1_dist / best2_dist < ratio_test {
58	        matches.push(SiftMatch::{
59	          query_idx: qi,
60	          train_idx: best1_idx,
61	          distance: best1_dist,
62	        })
63	      }
64	    }
65	  }
66	  matches
67	}
  	…

 73	pub fn ransac_homography(
 74	  matches : Array[((Float, Float), (Float, Float))],
 75	  iterations? : Int = 1000,
 76	  threshold? : Float = 3.0F,
 77	) -> Array[Float]? raise @types.LoadError {
 78	  // 参数验证
 79	  if iterations <= 0 {
 80	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 81	      "ransac_homography: iterations必须大于0",
 82	    )
 83	  }
 84	  if threshold <= 0.0F {
 85	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 86	      "ransac_homography: threshold必须大于0",
 87	    )
 88	  }
 89	
 90	  let n = matches.length()
 91	  if n < 4 {
 92	    return None
 93	  }
 94	  let mut best_h : Array[Float]? = None
 95	  let mut best_inliers = 0
 96	  for iter in 0..<iterations {
 97	    // 确定性选 4 对点（基于迭代号散列）
 98	    let indices = deterministic_indices(n, 4, iter)
 99	    let pts : Array[((Float, Float), (Float, Float))] = []
100	    for idx in indices {
101	      pts.push(matches[idx])
102	    }
103	    // 计算单应性矩阵
104	    let h = compute_homography_dlt(pts)
105	    if h.is_empty() {
106	      continue	<-- UNCOVERED
107	    }
108	    // 统计内点
109	    let mut inliers = 0
110	    for i in 0..<n {
111	      let ((qx, qy), (tx, ty)) = matches[i]
112	      let err = reprojection_error(h, qx, qy, tx, ty)
113	      if err < threshold {
114	        inliers += 1
115	      }
116	    }
117	    if inliers > best_inliers {
118	      best_inliers = inliers
119	      best_h = Some(h)
120	    }
121	  }
122	  best_h
123	}
   	…

128	fn l2_distance(a : Array[Float], b : Array[Float]) -> Float {
129	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
130	  let mut sum = 0.0F
131	  for i in 0..<n {
132	    let d = a[i] - b[i]
133	    sum += d * d
134	  }
135	  sum.sqrt()
136	}
   	…

139	fn deterministic_indices(n : Int, k : Int, seed : Int) -> Array[Int] {
140	  let result : Array[Int] = []
141	  let mut s = seed
142	  let mut count = 0
143	  let mut attempts = 0
144	  let max_attempts = n * 4
145	  while count < k && count < n && attempts < max_attempts {
146	    attempts += 1
147	    s = (s * 31 + 17) % n
148	    if s < 0 {
149	      s = -s	<-- UNCOVERED
150	    }
151	    let idx = s % n
152	    let mut dup = false
153	    for r in result {
154	      if r == idx {
155	        dup = true
156	        break
157	      }
158	    }
159	    if !dup {
160	      result.push(idx)
161	      count += 1
162	    }
163	    s += 1
164	  }
165	  // 如果不够，补充前面的
166	  for i in 0..<n {
167	    if result.length() >= k {
168	      break
169	    }
170	    let mut dup = false
171	    for r in result {
172	      if r == i {
173	        dup = true
174	        break
175	      }
176	    }
177	    if !dup {
178	      result.push(i)
179	    }
180	  }
181	  result
182	}
   	…

185	fn compute_homography_dlt(
186	  pts : Array[((Float, Float), (Float, Float))],
187	) -> Array[Float] {
188	  if pts.length() < 4 {
189	    return []	<-- UNCOVERED
190	  }
191	  // 构建 8x8 线性方程组 A*h = b
192	  // h = [h11, h12, h13, h21, h22, h23, h31, h32]
193	  // 对于每对点 (x,y) -> (u,v):
194	  // u = (h11*x + h12*y + h13) / (h31*x + h32*y + 1)
195	  // v = (h21*x + h22*y + h23) / (h31*x + h32*y + 1)
196	  // 线性化：
197	  // x*h11 + y*h12 + h13 - x*u*h31 - y*u*h32 = u
198	  // x*h21 + y*h22 + h23 - x*v*h31 - y*v*h32 = v
199	  let a : Array[Array[Float]] = []
200	  let b : Array[Float] = []
201	  for i in 0..<4 {
202	    let ((x, y), (u, v)) = pts[i]
203	    // 第一行
204	    let row1 : Array[Float] = Array::make(8, 0.0F)
205	    row1[0] = x
206	    row1[1] = y
207	    row1[2] = 1.0F
208	    row1[6] = -x * u
209	    row1[7] = -y * u
210	    a.push(row1)
211	    b.push(u)
212	    // 第二行
213	    let row2 : Array[Float] = Array::make(8, 0.0F)
214	    row2[3] = x
215	    row2[4] = y
216	    row2[5] = 1.0F
217	    row2[6] = -x * v
218	    row2[7] = -y * v
219	    a.push(row2)
220	    b.push(v)
221	  }
222	  // 高斯消元解 8x8 方程组
223	  gauss_elimination(a, b, 8)
224	}
   	…

227	fn gauss_elimination(
228	  a : Array[Array[Float]],
229	  b : Array[Float],
230	  n : Int,
231	) -> Array[Float] {
232	  // 前向消元
233	  for col in 0..<n {
234	    // 找主元
235	    let mut max_row = col
236	    let mut max_val = a[col][col].abs()
237	    for row in (col + 1)..<n {
238	      if a[row][col].abs() > max_val {
239	        max_val = a[row][col].abs()
240	        max_row = row
241	      }
242	    }
243	    if max_val < 0.0000000001F {
244	      return []	<-- UNCOVERED
245	    }
246	    // 交换行
247	    if max_row != col {
248	      let tmp = a[col]
249	      a[col] = a[max_row]
250	      a[max_row] = tmp
251	      let tmpb = b[col]
252	      b[col] = b[max_row]
253	      b[max_row] = tmpb
254	    }
255	    // 消元
256	    for row in (col + 1)..<n {
257	      let factor = a[row][col] / a[col][col]
258	      for j in col..<n {
259	        a[row][j] -= factor * a[col][j]
260	      }
261	      b[row] -= factor * b[col]
262	    }
263	  }
264	  // 回代
265	  let x : Array[Float] = Array::make(n, 0.0F)
266	  for ii in 0..<n {
267	    let i = n - 1 - ii
268	    let mut sum = b[i]
269	    for j in (i + 1)..<n {
270	      sum -= a[i][j] * x[j]
271	    }
272	    x[i] = sum / a[i][i]
273	  }
274	  x
275	}
   	…

278	fn reprojection_error(
279	  h : Array[Float],
280	  qx : Float,
281	  qy : Float,
282	  tx : Float,
283	  ty : Float,
284	) -> Float {
285	  // h = [h11, h12, h13, h21, h22, h23, h31, h32]
286	  let denom = h[6] * qx + h[7] * qy + 1.0F
287	  if denom.abs() < 0.0000000001F {
288	    return 10000000000.0F	<-- UNCOVERED
289	  }
290	  let pu = (h[0] * qx + h[1] * qy + h[2]) / denom
291	  let pv = (h[3] * qx + h[4] * qy + h[5]) / denom
292	  let du = pu - tx
293	  let dv = pv - ty
294	  (du * du + dv * dv).sqrt()
295	}
   	…

25 uncovered line(s) in src\process\feature\optical_flow.mbt:

 21	pub fn lucas_kanade(
 22	  prev : @types.Image,
 23	  next : @types.Image,
 24	  points : Array[(Int, Int)],
 25	  window_size? : Int = 15,
 26	  max_iter? : Int = 20,
 27	) -> Array[FlowResult] raise @types.LoadError {
 28	  // 输入验证
 29	  if prev.width <= 0 || prev.height <= 0 {
 30	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 31	      "lucas_kanade: prev图像尺寸必须大于0",
 32	    )
 33	  }
 34	  if next.width <= 0 || next.height <= 0 {
 35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 36	      "lucas_kanade: next图像尺寸必须大于0",
 37	    )
 38	  }
 39	  if prev.channels < 1 || prev.channels > 4 {
 40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 41	      "lucas_kanade: prev通道数必须在1-4之间",
 42	    )
 43	  }
 44	  if next.channels < 1 || next.channels > 4 {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "lucas_kanade: next通道数必须在1-4之间",
 47	    )
 48	  }
 49	  let prev_expected_size = prev.width * prev.height * prev.channels
 50	  if prev.data.length() != prev_expected_size {
 51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 52	      "lucas_kanade: prev数据大小不匹配，期望 " +
 53	      prev_expected_size.to_string() +	<-- UNCOVERED
 54	      "，实际 " +
 55	      prev.data.length().to_string(),	<-- UNCOVERED
 56	    )
 57	  }
 58	  let next_expected_size = next.width * next.height * next.channels
 59	  if next.data.length() != next_expected_size {
 60	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 61	      "lucas_kanade: next数据大小不匹配，期望 " +
 62	      next_expected_size.to_string() +	<-- UNCOVERED
 63	      "，实际 " +
 64	      next.data.length().to_string(),	<-- UNCOVERED
 65	    )
 66	  }
 67	  if prev.width != next.width || prev.height != next.height {
 68	    raise @types.LoadError::DecodeFailed(
 69	      "lucas_kanade: 两帧图像尺寸必须一致",
 70	    )
 71	  }
 72	  if points.length() == 0 {
 73	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 74	      "lucas_kanade: points数组不能为空",
 75	    )
 76	  }
 77	  if window_size < 3 || window_size > 31 || window_size % 2 == 0 {
 78	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 79	      "lucas_kanade: window_size必须为3..31之间的奇数",
 80	    )
 81	  }
 82	  if max_iter <= 0 {
 83	    raise @types.LoadError::DecodeFailed("lucas_kanade: max_iter必须大于0")	<-- UNCOVERED
 84	  }
 85	  let w = prev.width
 86	  let h = prev.height
 87	  // 转灰度
 88	  let (gray_prev, _) = to_gray_lk(prev)
 89	  let (gray_next, _) = to_gray_lk(next)
 90	  let half_win = window_size / 2
 91	  let results : Array[FlowResult] = []
 92	  for i in 0..<points.length() {
 93	    let (px, py) = points[i]
 94	    // 初始化位移
 95	    let mut u = 0.0F
 96	    let mut v = 0.0F
 97	    let mut found = false
 98	    // 迭代求解
 99	    for _ in 0..<max_iter {
100	      // 构建 Lucas-Kanade 方程：A^T A * d = A^T b
101	      let mut ixx = 0.0F
102	      let mut ixy = 0.0F
103	      let mut iyy = 0.0F
104	      let mut ix_it = 0.0F
105	      let mut iy_it = 0.0F
106	      for dy in -half_win..<=half_win {
107	        for dx in -half_win..<=half_win {
108	          let x = px + dx
109	          let y = py + dy
110	          if x < 1 || x >= w - 1 || y < 1 || y >= h - 1 {
111	            continue
112	          }
113	          // 空间梯度（中心差分）
114	          let ix = Float::from_int(
115	              gray_prev[y * w + x + 1] - gray_prev[y * w + x - 1],
116	            ) /
117	            2.0F
118	          let iy = Float::from_int(
119	              gray_prev[(y + 1) * w + x] - gray_prev[(y - 1) * w + x],
120	            ) /
121	            2.0F
122	          // 时间差分：I_t = I_next(x+u, y+v) - I_prev(x, y)
123	          let nx = Float::from_int(x) + u
124	          let ny = Float::from_int(y) + v
125	          let next_val = sample_bilinear_lk(gray_next, w, h, nx, ny)
126	          let prev_val = Float::from_int(gray_prev[y * w + x])
127	          let it = next_val - prev_val
128	          // 累加 A^T A 和 A^T b
129	          ixx += ix * ix
130	          ixy += ix * iy
131	          iyy += iy * iy
132	          ix_it += ix * it
133	          iy_it += iy * it
134	        }
135	      }
136	      // 求解 2x2 线性方程
137	      let det = ixx * iyy - ixy * ixy
138	      if det.abs() < 0.01F {
139	        break
140	      }
141	      let du = -(iyy * ix_it - ixy * iy_it) / det
142	      let dv = -(ixx * iy_it - ixy * ix_it) / det
143	      u += du
144	      v += dv
145	      // 收敛检测
146	      if du.abs() < 0.01F && dv.abs() < 0.01F {
147	        found = true
148	        break
149	      }
150	      found = true
151	    }
152	    results.push(FlowResult::{
153	      x: Float::from_int(px),
154	      y: Float::from_int(py),
155	      u,
156	      v,
157	      found,
158	    })
159	  }
160	  results
161	}
   	…

168	pub fn horn_schunck(
169	  prev : @types.Image,
170	  next : @types.Image,
171	  alpha? : Float = 1.0F,
172	  iterations? : Int = 100,
173	) -> (Array[Float], Array[Float]) raise @types.LoadError {
174	  // 输入验证
175	  if prev.width <= 0 || prev.height <= 0 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "horn_schunck: prev图像尺寸必须大于0",
178	    )
179	  }
180	  if next.width <= 0 || next.height <= 0 {
181	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
182	      "horn_schunck: next图像尺寸必须大于0",
183	    )
184	  }
185	  if prev.channels < 1 || prev.channels > 4 {
186	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
187	      "horn_schunck: prev通道数必须在1-4之间",
188	    )
189	  }
190	  if next.channels < 1 || next.channels > 4 {
191	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
192	      "horn_schunck: next通道数必须在1-4之间",
193	    )
194	  }
195	  let prev_expected_size = prev.width * prev.height * prev.channels
196	  if prev.data.length() != prev_expected_size {
197	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
198	      "horn_schunck: prev数据大小不匹配，期望 " +
199	      prev_expected_size.to_string() +	<-- UNCOVERED
200	      "，实际 " +
201	      prev.data.length().to_string(),	<-- UNCOVERED
202	    )
203	  }
204	  let next_expected_size = next.width * next.height * next.channels
205	  if next.data.length() != next_expected_size {
206	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
207	      "horn_schunck: next数据大小不匹配，期望 " +
208	      next_expected_size.to_string() +	<-- UNCOVERED
209	      "，实际 " +
210	      next.data.length().to_string(),	<-- UNCOVERED
211	    )
212	  }
213	  if prev.width != next.width || prev.height != next.height {
214	    raise @types.LoadError::DecodeFailed(
215	      "horn_schunck: 两帧图像尺寸必须一致",
216	    )
217	  }
218	  if alpha <= 0.0F {
219	    raise @types.LoadError::DecodeFailed("horn_schunck: alpha必须大于0")	<-- UNCOVERED
220	  }
221	  if iterations <= 0 {
222	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
223	      "horn_schunck: iterations必须大于0",
224	    )
225	  }
226	  let w = prev.width
227	  let h = prev.height
228	  let n = w * h
229	  let (gray_prev, _) = to_gray_lk(prev)
230	  let (gray_next, _) = to_gray_lk(next)
231	  // 计算空间梯度和时间梯度
232	  let ex : Array[Float] = Array::make(n, 0.0F)
233	  let ey : Array[Float] = Array::make(n, 0.0F)
234	  let et : Array[Float] = Array::make(n, 0.0F)
235	  for y in 0..<h {
236	    for x in 0..<w {
237	      let idx = y * w + x
238	      let xl = if x > 0 { x - 1 } else { 0 }
239	      let xr = if x < w - 1 { x + 1 } else { w - 1 }
240	      let yu = if y > 0 { y - 1 } else { 0 }
241	      let yd = if y < h - 1 { y + 1 } else { h - 1 }
242	      // Ex = (I(x+1,y) - I(x-1,y) + J(x+1,y) - J(x-1,y)) / 4
243	      ex[idx] = Float::from_int(
244	          gray_prev[y * w + xr] -
245	          gray_prev[y * w + xl] +
246	          gray_next[y * w + xr] -
247	          gray_next[y * w + xl],
248	        ) /
249	        4.0F
250	      // Ey = (I(x,y+1) - I(x,y-1) + J(x,y+1) - J(x,y-1)) / 4
251	      ey[idx] = Float::from_int(
252	          gray_prev[yd * w + x] -
253	          gray_prev[yu * w + x] +
254	          gray_next[yd * w + x] -
255	          gray_next[yu * w + x],
256	        ) /
257	        4.0F
258	      // Et = J(x,y) - I(x,y)
259	      et[idx] = Float::from_int(gray_next[idx] - gray_prev[idx])
260	    }
261	  }
262	  // 初始化光流场
263	  let u : Array[Float] = Array::make(n, 0.0F)
264	  let v : Array[Float] = Array::make(n, 0.0F)
265	  let alpha_sq = alpha * alpha
266	  // 迭代求解
267	  for _ in 0..<iterations {
268	    let u_prev = u.copy()
269	    let v_prev = v.copy()
270	    for y in 0..<h {
271	      for x in 0..<w {
272	        let idx = y * w + x
273	        // 计算 u, v 的邻域平均（4 邻域）
274	        let xl = if x > 0 { idx - 1 } else { idx }
275	        let xr = if x < w - 1 { idx + 1 } else { idx }
276	        let yu = if y > 0 { idx - w } else { idx }
277	        let yd = if y < h - 1 { idx + w } else { idx }
278	        let u_avg = (u_prev[xl] + u_prev[xr] + u_prev[yu] + u_prev[yd]) / 4.0F
279	        let v_avg = (v_prev[xl] + v_prev[xr] + v_prev[yu] + v_prev[yd]) / 4.0F
280	        // Horn-Schunck 更新
281	        let denom = alpha_sq + ex[idx] * ex[idx] + ey[idx] * ey[idx]
282	        if denom > 0.0F {
283	          let common = ex[idx] * u_avg + ey[idx] * v_avg + et[idx]
284	          u[idx] = u_avg - ex[idx] * common / denom
285	          v[idx] = v_avg - ey[idx] * common / denom
286	        }
287	      }
288	    }
289	  }
290	  (u, v)
291	}
   	…

78 uncovered line(s) in src\process\feature\optical_glcm_16f.mbt:

21	fn clamp_int_of(v : Int, lo : Int, hi : Int) -> Int {
22	  if v < lo {
23	    lo	<-- UNCOVERED
24	  } else if v > hi {
25	    hi	<-- UNCOVERED
26	  } else {
27	    v
28	  }
29	}
  	…

 72	pub fn lucas_kanade_16(
 73	  prev : @types.Image16,
 74	  next : @types.Image16,
 75	  points : Array[(Int, Int)],
 76	  window_size? : Int = 15,
 77	  max_iter? : Int = 20,
 78	) -> Array[FlowResult] raise @types.LoadError {
 79	  // 输入验证
 80	  if prev.width <= 0 || prev.height <= 0 {
 81	    raise @types.LoadError::DecodeFailed(
 82	      "lucas_kanade_16: 前一帧图像尺寸必须大于0",
 83	    )
 84	  }
 85	  if prev.channels < 1 || prev.channels > 4 {
 86	    raise @types.LoadError::DecodeFailed(
 87	      "lucas_kanade_16: 前一帧通道数必须在1-4之间",
 88	    )
 89	  }
 90	  let prev_expected_size = prev.width * prev.height * prev.channels * 2
 91	  if prev.data.length() != prev_expected_size {
 92	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 93	      "lucas_kanade_16: 前一帧数据大小不匹配，期望 " +
 94	      prev_expected_size.to_string() +	<-- UNCOVERED
 95	      "，实际 " +
 96	      prev.data.length().to_string(),	<-- UNCOVERED
 97	    )
 98	  }
 99	  if next.width <= 0 || next.height <= 0 {
100	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
101	      "lucas_kanade_16: 后一帧图像尺寸必须大于0",
102	    )
103	  }
104	  if next.channels < 1 || next.channels > 4 {
105	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
106	      "lucas_kanade_16: 后一帧通道数必须在1-4之间",
107	    )
108	  }
109	  let next_expected_size = next.width * next.height * next.channels * 2
110	  if next.data.length() != next_expected_size {
111	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
112	      "lucas_kanade_16: 后一帧数据大小不匹配，期望 " +
113	      next_expected_size.to_string() +	<-- UNCOVERED
114	      "，实际 " +
115	      next.data.length().to_string(),	<-- UNCOVERED
116	    )
117	  }
118	  if prev.width != next.width || prev.height != next.height {
119	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
120	      "lucas_kanade_16: 两帧图像尺寸必须一致",
121	    )
122	  }
123	  if points.length() == 0 {
124	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
125	      "lucas_kanade_16: 点列表不能为空",
126	    )
127	  }
128	  if window_size <= 0 || window_size % 2 == 0 {
129	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
130	      "lucas_kanade_16: window_size必须为正奇数",
131	    )
132	  }
133	  if max_iter <= 0 {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "lucas_kanade_16: max_iter必须大于0",
136	    )
137	  }
138	
139	  let prev8 = img16_to_img8_of(prev)
140	  let next8 = img16_to_img8_of(next)
141	  lucas_kanade(prev8, next8, points, window_size~, max_iter~)
142	}
   	…

148	pub fn lucas_kanade_f(
149	  prev : @types.ImageF,
150	  next : @types.ImageF,
151	  points : Array[(Int, Int)],
152	  window_size? : Int = 15,
153	  max_iter? : Int = 20,
154	) -> Array[FlowResult] raise @types.LoadError {
155	  // 输入验证
156	  if prev.width <= 0 || prev.height <= 0 {
157	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
158	      "lucas_kanade_f: 前一帧图像尺寸必须大于0",
159	    )
160	  }
161	  if prev.channels < 1 || prev.channels > 4 {
162	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
163	      "lucas_kanade_f: 前一帧通道数必须在1-4之间",
164	    )
165	  }
166	  let prev_expected_size = prev.width * prev.height * prev.channels * 4
167	  if prev.data.length() != prev_expected_size {
168	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
169	      "lucas_kanade_f: 前一帧数据大小不匹配，期望 " +
170	      prev_expected_size.to_string() +	<-- UNCOVERED
171	      "，实际 " +
172	      prev.data.length().to_string(),	<-- UNCOVERED
173	    )
174	  }
175	  if next.width <= 0 || next.height <= 0 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "lucas_kanade_f: 后一帧图像尺寸必须大于0",
178	    )
179	  }
180	  if next.channels < 1 || next.channels > 4 {
181	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
182	      "lucas_kanade_f: 后一帧通道数必须在1-4之间",
183	    )
184	  }
185	  let next_expected_size = next.width * next.height * next.channels * 4
186	  if next.data.length() != next_expected_size {
187	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
188	      "lucas_kanade_f: 后一帧数据大小不匹配，期望 " +
189	      next_expected_size.to_string() +	<-- UNCOVERED
190	      "，实际 " +
191	      next.data.length().to_string(),	<-- UNCOVERED
192	    )
193	  }
194	  if prev.width != next.width || prev.height != next.height {
195	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
196	      "lucas_kanade_f: 两帧图像尺寸必须一致",
197	    )
198	  }
199	  if points.length() == 0 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "lucas_kanade_f: 点列表不能为空",
202	    )
203	  }
204	  if window_size <= 0 || window_size % 2 == 0 {
205	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
206	      "lucas_kanade_f: window_size必须为正奇数",
207	    )
208	  }
209	  if max_iter <= 0 {
210	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
211	      "lucas_kanade_f: max_iter必须大于0",
212	    )
213	  }
214	
215	  let prev8 = imgf_to_img8_of(prev)
216	  let next8 = imgf_to_img8_of(next)
217	  lucas_kanade(prev8, next8, points, window_size~, max_iter~)
218	}
   	…

224	pub fn horn_schunck_16(
225	  prev : @types.Image16,
226	  next : @types.Image16,
227	  alpha? : Float = 1.0F,
228	  iterations? : Int = 100,
229	) -> (Array[Float], Array[Float]) raise @types.LoadError {
230	  // 输入验证
231	  if prev.width <= 0 || prev.height <= 0 {
232	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
233	      "horn_schunck_16: 前一帧图像尺寸必须大于0",
234	    )
235	  }
236	  if prev.channels < 1 || prev.channels > 4 {
237	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
238	      "horn_schunck_16: 前一帧通道数必须在1-4之间",
239	    )
240	  }
241	  let prev_expected_size = prev.width * prev.height * prev.channels * 2
242	  if prev.data.length() != prev_expected_size {
243	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
244	      "horn_schunck_16: 前一帧数据大小不匹配，期望 " +
245	      prev_expected_size.to_string() +	<-- UNCOVERED
246	      "，实际 " +
247	      prev.data.length().to_string(),	<-- UNCOVERED
248	    )
249	  }
250	  if next.width <= 0 || next.height <= 0 {
251	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
252	      "horn_schunck_16: 后一帧图像尺寸必须大于0",
253	    )
254	  }
255	  if next.channels < 1 || next.channels > 4 {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "horn_schunck_16: 后一帧通道数必须在1-4之间",
258	    )
259	  }
260	  let next_expected_size = next.width * next.height * next.channels * 2
261	  if next.data.length() != next_expected_size {
262	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
263	      "horn_schunck_16: 后一帧数据大小不匹配，期望 " +
264	      next_expected_size.to_string() +	<-- UNCOVERED
265	      "，实际 " +
266	      next.data.length().to_string(),	<-- UNCOVERED
267	    )
268	  }
269	  if prev.width != next.width || prev.height != next.height {
270	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
271	      "horn_schunck_16: 两帧图像尺寸必须一致",
272	    )
273	  }
274	  if alpha <= 0.0F {
275	    raise @types.LoadError::DecodeFailed("horn_schunck_16: alpha必须大于0")	<-- UNCOVERED
276	  }
277	  if iterations <= 0 {
278	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
279	      "horn_schunck_16: iterations必须大于0",
280	    )
281	  }
282	
283	  let prev8 = img16_to_img8_of(prev)
284	  let next8 = img16_to_img8_of(next)
285	  horn_schunck(prev8, next8, alpha~, iterations~)
286	}
   	…

292	pub fn horn_schunck_f(
293	  prev : @types.ImageF,
294	  next : @types.ImageF,
295	  alpha? : Float = 1.0F,
296	  iterations? : Int = 100,
297	) -> (Array[Float], Array[Float]) raise @types.LoadError {
298	  // 输入验证
299	  if prev.width <= 0 || prev.height <= 0 {
300	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
301	      "horn_schunck_f: 前一帧图像尺寸必须大于0",
302	    )
303	  }
304	  if prev.channels < 1 || prev.channels > 4 {
305	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
306	      "horn_schunck_f: 前一帧通道数必须在1-4之间",
307	    )
308	  }
309	  let prev_expected_size = prev.width * prev.height * prev.channels * 4
310	  if prev.data.length() != prev_expected_size {
311	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
312	      "horn_schunck_f: 前一帧数据大小不匹配，期望 " +
313	      prev_expected_size.to_string() +	<-- UNCOVERED
314	      "，实际 " +
315	      prev.data.length().to_string(),	<-- UNCOVERED
316	    )
317	  }
318	  if next.width <= 0 || next.height <= 0 {
319	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
320	      "horn_schunck_f: 后一帧图像尺寸必须大于0",
321	    )
322	  }
323	  if next.channels < 1 || next.channels > 4 {
324	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
325	      "horn_schunck_f: 后一帧通道数必须在1-4之间",
326	    )
327	  }
328	  let next_expected_size = next.width * next.height * next.channels * 4
329	  if next.data.length() != next_expected_size {
330	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
331	      "horn_schunck_f: 后一帧数据大小不匹配，期望 " +
332	      next_expected_size.to_string() +	<-- UNCOVERED
333	      "，实际 " +
334	      next.data.length().to_string(),	<-- UNCOVERED
335	    )
336	  }
337	  if prev.width != next.width || prev.height != next.height {
338	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
339	      "horn_schunck_f: 两帧图像尺寸必须一致",
340	    )
341	  }
342	  if alpha <= 0.0F {
343	    raise @types.LoadError::DecodeFailed("horn_schunck_f: alpha必须大于0")	<-- UNCOVERED
344	  }
345	  if iterations <= 0 {
346	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
347	      "horn_schunck_f: iterations必须大于0",
348	    )
349	  }
350	
351	  let prev8 = imgf_to_img8_of(prev)
352	  let next8 = imgf_to_img8_of(next)
353	  horn_schunck(prev8, next8, alpha~, iterations~)
354	}
   	…

360	pub fn compute_glcm_16(
361	  img : @types.Image16,
362	  dx : Int,
363	  dy : Int,
364	  levels? : Int = 256,
365	) -> Array[Array[Int]] raise @types.LoadError {
366	  // 输入验证
367	  if img.width <= 0 || img.height <= 0 {
368	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
369	      "compute_glcm_16: 图像尺寸必须大于0",
370	    )
371	  }
372	  if img.channels < 1 || img.channels > 4 {
373	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
374	      "compute_glcm_16: 通道数必须在1-4之间",
375	    )
376	  }
377	  let expected_size = img.width * img.height * img.channels * 2
378	  if img.data.length() != expected_size {
379	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
380	      "compute_glcm_16: 数据大小不匹配，期望 " +
381	      expected_size.to_string() +	<-- UNCOVERED
382	      "，实际 " +
383	      img.data.length().to_string(),	<-- UNCOVERED
384	    )
385	  }
386	  if levels <= 0 {
387	    raise @types.LoadError::DecodeFailed("compute_glcm_16: levels必须大于0")	<-- UNCOVERED
388	  }
389	
390	  let img8 = img16_to_img8_of(img)
391	  compute_glcm(img8, dx, dy, levels~)
392	}
   	…

398	pub fn compute_glcm_f(
399	  img : @types.ImageF,
400	  dx : Int,
401	  dy : Int,
402	  levels? : Int = 256,
403	) -> Array[Array[Int]] raise @types.LoadError {
404	  // 输入验证
405	  if img.width <= 0 || img.height <= 0 {
406	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
407	      "compute_glcm_f: 图像尺寸必须大于0",
408	    )
409	  }
410	  if img.channels < 1 || img.channels > 4 {
411	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
412	      "compute_glcm_f: 通道数必须在1-4之间",
413	    )
414	  }
415	  let expected_size = img.width * img.height * img.channels * 4
416	  if img.data.length() != expected_size {
417	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
418	      "compute_glcm_f: 数据大小不匹配，期望 " +
419	      expected_size.to_string() +	<-- UNCOVERED
420	      "，实际 " +
421	      img.data.length().to_string(),	<-- UNCOVERED
422	    )
423	  }
424	  if levels <= 0 {
425	    raise @types.LoadError::DecodeFailed("compute_glcm_f: levels必须大于0")	<-- UNCOVERED
426	  }
427	
428	  let img8 = imgf_to_img8_of(img)
429	  compute_glcm(img8, dx, dy, levels~)
430	}
   	…

436	pub fn glcm_features_multi_direction_16(
437	  img : @types.Image16,
438	  levels? : Int = 256,
439	) -> Array[GlcmFeatures] raise @types.LoadError {
440	  // 输入验证
441	  if img.width <= 0 || img.height <= 0 {
442	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
443	      "glcm_features_multi_direction_16: 图像尺寸必须大于0",
444	    )
445	  }
446	  if img.channels < 1 || img.channels > 4 {
447	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
448	      "glcm_features_multi_direction_16: 通道数必须在1-4之间",
449	    )
450	  }
451	  let expected_size = img.width * img.height * img.channels * 2
452	  if img.data.length() != expected_size {
453	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
454	      "glcm_features_multi_direction_16: 数据大小不匹配，期望 " +
455	      expected_size.to_string() +	<-- UNCOVERED
456	      "，实际 " +
457	      img.data.length().to_string(),	<-- UNCOVERED
458	    )
459	  }
460	  if levels <= 0 {
461	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
462	      "glcm_features_multi_direction_16: levels必须大于0",
463	    )
464	  }
465	
466	  let img8 = img16_to_img8_of(img)
467	  glcm_features_multi_direction(img8, levels~)
468	}
   	…

474	pub fn glcm_features_multi_direction_f(
475	  img : @types.ImageF,
476	  levels? : Int = 256,
477	) -> Array[GlcmFeatures] raise @types.LoadError {
478	  // 输入验证
479	  if img.width <= 0 || img.height <= 0 {
480	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
481	      "glcm_features_multi_direction_f: 图像尺寸必须大于0",
482	    )
483	  }
484	  if img.channels < 1 || img.channels > 4 {
485	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
486	      "glcm_features_multi_direction_f: 通道数必须在1-4之间",
487	    )
488	  }
489	  let expected_size = img.width * img.height * img.channels * 4
490	  if img.data.length() != expected_size {
491	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
492	      "glcm_features_multi_direction_f: 数据大小不匹配，期望 " +
493	      expected_size.to_string() +	<-- UNCOVERED
494	      "，实际 " +
495	      img.data.length().to_string(),	<-- UNCOVERED
496	    )
497	  }
498	  if levels <= 0 {
499	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
500	      "glcm_features_multi_direction_f: levels必须大于0",
501	    )
502	  }
503	
504	  let img8 = imgf_to_img8_of(img)
505	  glcm_features_multi_direction(img8, levels~)
506	}
   	…

10 uncovered line(s) in src\process\feature\orb.mbt:

 86	fn check_fast9(vals : Array[Int], high : Int, low : Int) -> Bool {
 87	  // 将 16 个点复制两遍以处理环绕
 88	  let brighter : Array[Bool] = Array::make(32, false)
 89	  let darker : Array[Bool] = Array::make(32, false)
 90	  for i in 0..<16 {
 91	    brighter[i] = vals[i] > high
 92	    darker[i] = vals[i] < low
 93	    brighter[i + 16] = brighter[i]
 94	    darker[i + 16] = darker[i]
 95	  }
 96	  // 检查 9 连续
 97	  let mut b_run = 0
 98	  let mut d_run = 0
 99	  for i in 0..<32 {
100	    if brighter[i] {
101	      b_run += 1
102	      if b_run >= 9 {
103	        return true	<-- UNCOVERED
104	      }
105	    } else {
106	      b_run = 0
107	    }
108	    if darker[i] {
109	      d_run += 1
110	      if d_run >= 9 {
111	        return true
112	      }
113	    } else {
114	      d_run = 0
115	    }
116	  }
117	  false
118	}
   	…

270	pub fn orb_detect(
271	  img : @types.Image,
272	  max_features? : Int = 500,
273	  threshold? : Int = 20,
274	) -> Array[OrbDescriptor] raise @types.LoadError {
275	  // 输入验证
276	  if img.width <= 0 || img.height <= 0 {
277	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
278	      "orb_detect: 图像尺寸必须大于0",
279	    )
280	  }
281	  if img.width < 7 || img.height < 7 {
282	    raise @types.LoadError::DecodeFailed(
283	      "orb_detect: ORB 需要至少 7x7 图像",
284	    )
285	  }
286	  if img.channels < 1 || img.channels > 4 {
287	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
288	      "orb_detect: 通道数必须在1-4之间",
289	    )
290	  }
291	  let expected_size = img.width * img.height * img.channels
292	  if img.data.length() != expected_size {
293	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
294	      "orb_detect: 数据大小不匹配，期望 " +
295	      expected_size.to_string() +	<-- UNCOVERED
296	      "，实际 " +
297	      img.data.length().to_string(),	<-- UNCOVERED
298	    )
299	  }
300	  if max_features <= 0 {
301	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
302	      "orb_detect: max_features必须大于0",
303	    )
304	  }
305	  if threshold <= 0 || threshold > 255 {
306	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
307	      "orb_detect: threshold必须在1-255范围内",
308	    )
309	  }
310	  let (gray, w, h) = to_gray_orb(img)
311	  // FAST 检测
312	  let keypoints = fast_detect(gray, w, h, threshold)
313	  // 限制特征数：取前 max_features 个
314	  let kps = if keypoints.length() > max_features {
315	    keypoints[0:max_features]
316	  } else {
317	    keypoints
318	  }
319	  // 计算 BRIEF 描述子
320	  let pattern = brief_pattern()
321	  let descriptors : Array[OrbDescriptor] = []
322	  for kp in kps {
323	    let desc = compute_brief(gray, w, h, kp, pattern)
324	    descriptors.push(OrbDescriptor::{ kp, data: desc })
325	  }
326	  descriptors
327	}
   	…

351	pub fn orb_match(
352	  query : Array[OrbDescriptor],
353	  train : Array[OrbDescriptor],
354	  max_distance? : Int = 64,
355	) -> Array[DescriptorMatch] raise @types.LoadError {
356	  // 参数验证
357	  if query.length() == 0 {
358	    raise @types.LoadError::DecodeFailed("orb_match: query数组不能为空")
359	  }
360	  if train.length() == 0 {
361	    raise @types.LoadError::DecodeFailed("orb_match: train数组不能为空")	<-- UNCOVERED
362	  }
363	  if max_distance < 0 {
364	    raise @types.LoadError::DecodeFailed("orb_match: max_distance必须 >= 0")	<-- UNCOVERED
365	  }
366	
367	  let matches : Array[DescriptorMatch] = []
368	  for i in 0..<query.length() {
369	    let mut best_j = -1
370	    let mut best_dist = max_distance + 1
371	    for j in 0..<train.length() {
372	      let dist = orb_hamming(query[i], train[j])
373	      if dist < best_dist {
374	        best_dist = dist
375	        best_j = j
376	      }
377	    }
378	    if best_j >= 0 && best_dist <= max_distance {
379	      matches.push(DescriptorMatch::{
380	        query_idx: i,
381	        train_idx: best_j,
382	        distance: best_dist,
383	      })
384	    }
385	  }
386	  matches
387	}
   	…

10 uncovered line(s) in src\process\feature\shi_tomasi.mbt:

  8	pub fn good_features_to_track(
  9	  img : @types.Image,
 10	  max_corners : Int,
 11	  quality_level : Float,
 12	  min_distance? : Int = 5,
 13	) -> Array[CornerPoint] raise @types.LoadError {
 14	  // 输入验证
 15	  if img.width <= 0 || img.height <= 0 {
 16	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 17	      "good_features_to_track: 图像尺寸必须大于0",
 18	    )
 19	  }
 20	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 21	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 22	      "good_features_to_track: 仅支持 channels=1/3/4",
 23	    )
 24	  }
 25	  let expected_size = img.width * img.height * img.channels
 26	  if img.data.length() != expected_size {
 27	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 28	      "good_features_to_track: 数据大小不匹配，期望 " +
 29	      expected_size.to_string() +	<-- UNCOVERED
 30	      "，实际 " +
 31	      img.data.length().to_string(),	<-- UNCOVERED
 32	    )
 33	  }
 34	  if max_corners <= 0 {
 35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 36	      "good_features_to_track: max_corners必须大于0",
 37	    )
 38	  }
 39	  if quality_level <= 0.0F || quality_level > 1.0F {
 40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 41	      "good_features_to_track: quality_level必须在(0, 1]范围内",
 42	    )
 43	  }
 44	  if min_distance < 0 {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "good_features_to_track: min_distance必须 >= 0",
 47	    )
 48	  }
 49	  let w = img.width
 50	  let h = img.height
 51	  let n = w * h
 52	  if n == 0 {
 53	    return []	<-- UNCOVERED
 54	  }
 55	  // 转灰度
 56	  let gray : Array[Float] = Array::make(n, 0.0F)
 57	  let ch = img.channels
 58	  for i in 0..<n {
 59	    let v = if ch == 1 {
 60	      img.data[i].to_int()
 61	    } else {
 62	      let r = img.data[i * ch].to_int()
 63	      let g = img.data[i * ch + 1].to_int()
 64	      let b = img.data[i * ch + 2].to_int()
 65	      (r * 299 + g * 587 + b * 114) / 1000
 66	    }
 67	    gray[i] = Float::from_int(v)
 68	  }
 69	  // Sobel 梯度
 70	  let ix : Array[Float] = Array::make(n, 0.0F)
 71	  let iy : Array[Float] = Array::make(n, 0.0F)
 72	  for y in 1..<(h - 1) {
 73	    for x in 1..<(w - 1) {
 74	      let idx = y * w + x
 75	      ix[idx] = (
 76	          gray[(y - 1) * w + (x + 1)] +
 77	          2.0F * gray[y * w + (x + 1)] +
 78	          gray[(y + 1) * w + (x + 1)] -
 79	          gray[(y - 1) * w + (x - 1)] -
 80	          2.0F * gray[y * w + (x - 1)] -
 81	          gray[(y + 1) * w + (x - 1)]
 82	        ) /
 83	        8.0F
 84	      iy[idx] = (
 85	          gray[(y + 1) * w + (x - 1)] +
 86	          2.0F * gray[(y + 1) * w + x] +
 87	          gray[(y + 1) * w + (x + 1)] -
 88	          gray[(y - 1) * w + (x - 1)] -
 89	          2.0F * gray[(y - 1) * w + x] -
 90	          gray[(y - 1) * w + (x + 1)]
 91	        ) /
 92	        8.0F
 93	    }
 94	  }
 95	  // 结构张量分量
 96	  let ixx : Array[Float] = Array::make(n, 0.0F)
 97	  let iyy : Array[Float] = Array::make(n, 0.0F)
 98	  let ixy : Array[Float] = Array::make(n, 0.0F)
 99	  for i in 0..<n {
100	    ixx[i] = ix[i] * ix[i]
101	    iyy[i] = iy[i] * iy[i]
102	    ixy[i] = ix[i] * iy[i]
103	  }
104	  // 高斯加权
105	  let sxx = shi_tomasi_gaussian_blur(ixx, w, h)
106	  let syy = shi_tomasi_gaussian_blur(iyy, w, h)
107	  let sxy = shi_tomasi_gaussian_blur(ixy, w, h)
108	  // Shi-Tomasi 响应: R = min(λ1, λ2) = (trace - sqrt(trace² - 4*det)) / 2
109	  let response : Array[Float] = Array::make(n, 0.0F)
110	  for i in 0..<n {
111	    let det = sxx[i] * syy[i] - sxy[i] * sxy[i]
112	    let trace = sxx[i] + syy[i]
113	    let disc = trace * trace - 4.0F * det
114	    if disc < 0.0F {
115	      response[i] = 0.0F	<-- UNCOVERED
116	    } else {
117	      response[i] = (trace - disc.sqrt()) / 2.0F
118	    }
119	  }
120	  // 找最大响应值用于 quality threshold
121	  let mut max_response = 0.0F
122	  for i in 0..<n {
123	    if response[i] > max_response {
124	      max_response = response[i]
125	    }
126	  }
127	  let threshold = max_response * quality_level
128	  // 非极大值抑制 + 阈值
129	  let candidates : Array[CornerPoint] = []
130	  for y in 1..<(h - 1) {
131	    for x in 1..<(w - 1) {
132	      let idx = y * w + x
133	      let r = response[idx]
134	      if r <= threshold {
135	        continue
136	      }
137	      let mut is_max = true
138	      for dy in -1..<=1 {
139	        for dx in -1..<=1 {
140	          if dx == 0 && dy == 0 {
141	            continue
142	          }
143	          if response[(y + dy) * w + (x + dx)] > r {
144	            is_max = false
145	            break
146	          }
147	        }
148	        if !is_max {
149	          break
150	        }
151	      }
152	      if is_max {
153	        candidates.push(CornerPoint::{ x, y, response: r })
154	      }
155	    }
156	  }
157	  // 按响应值排序（降序）
158	  candidates.sort_by(fn(a, b) { b.response.compare(a.response) })
159	  // 距离过滤
160	  let result : Array[CornerPoint] = []
161	  for c in candidates {
162	    if result.length() >= max_corners {
163	      break
164	    }
165	    let mut ok = true
166	    for r in result {
167	      let dx = c.x - r.x
168	      let dy = c.y - r.y
169	      if dx * dx + dy * dy < min_distance * min_distance {
170	        ok = false
171	        break
172	      }
173	    }
174	    if ok {
175	      result.push(c)
176	    }
177	  }
178	  result
179	}
   	…

13 uncovered line(s) in src\process\feature\sift.mbt:

 28	pub fn sift_detect(
 29	  img : @types.Image,
 30	  max_features? : Int = 500,
 31	  contrast_threshold? : Float = 0.04F,
 32	) -> Array[SiftDescriptor] raise @types.LoadError {
 33	  // 输入验证
 34	  if img.width <= 0 || img.height <= 0 {
 35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 36	      "sift_detect: 图像尺寸必须大于0",
 37	    )
 38	  }
 39	  if img.width < 16 || img.height < 16 {
 40	    raise @types.LoadError::DecodeFailed(
 41	      "sift_detect: SIFT 需要至少 16x16 图像",
 42	    )
 43	  }
 44	  if img.channels < 1 || img.channels > 4 {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "sift_detect: 通道数必须在1-4之间",
 47	    )
 48	  }
 49	  let expected_size = img.width * img.height * img.channels
 50	  if img.data.length() != expected_size {
 51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 52	      "sift_detect: 数据大小不匹配，期望 " +
 53	      expected_size.to_string() +	<-- UNCOVERED
 54	      "，实际 " +
 55	      img.data.length().to_string(),	<-- UNCOVERED
 56	    )
 57	  }
 58	  if max_features <= 0 {
 59	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 60	      "sift_detect: max_features必须大于0",
 61	    )
 62	  }
 63	  if contrast_threshold <= 0.0F {
 64	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 65	      "sift_detect: contrast_threshold必须大于0",
 66	    )
 67	  }
 68	  let (gray, w, h) = to_gray_sift(img)
 69	  let n_layers = 3
 70	  let sigma0 = 1.6F
 71	  let edge_thresh = 10.0F
 72	  // 构建高斯金字塔: 每个元素为 (levels, w, h)
 73	  let gauss_pyramid = build_gaussian_pyramid_sift(gray, w, h, n_layers, sigma0)
 74	  // 构建 DoG 金字塔
 75	  let dog_pyramid = build_dog_pyramid(gauss_pyramid)
 76	  // 检测关键点
 77	  let keypoints : Array[SiftKeyPoint] = []
 78	  for oct in 0..<gauss_pyramid.length() {
 79	    let (gauss_levels, oct_w, oct_h) = gauss_pyramid[oct]
 80	    let (dog_levels, _, _) = dog_pyramid[oct]
 81	    // dog_levels 有 n_levels-1 层，在 1..<(n_levels-2) 检测
 82	    for layer in 1..<(dog_levels.length() - 1) {
 83	      let prev_dog = dog_levels[layer - 1]
 84	      let curr_dog = dog_levels[layer]
 85	      let next_dog = dog_levels[layer + 1]
 86	      let sigma = sigma0 *
 87	        Float::from_int(1 << oct) *
 88	        Float::from_double(
 89	          @math.pow(
 90	            2.0,
 91	            Float::from_int(layer).to_double() /
 92	            Float::from_int(n_layers).to_double(),
 93	          ),
 94	        )
 95	      for y in 1..<(oct_h - 1) {
 96	        for x in 1..<(oct_w - 1) {
 97	          let val = curr_dog[y * oct_w + x]
 98	          // 检查是否为 3x3x3 局部极值
 99	          if !is_extremum(prev_dog, curr_dog, next_dog, x, y, oct_w) {
100	            continue
101	          }
102	          // 对比度过滤
103	          if val.abs() < contrast_threshold * 0.5F {
104	            continue	<-- UNCOVERED
105	          }
106	          // 边缘响应过滤
107	          let ratio = edge_response(curr_dog, x, y, oct_w)
108	          if ratio > edge_thresh {
109	            continue
110	          }
111	          let fx = Float::from_int(x)
112	          let fy = Float::from_int(y)
113	          // 方向分配
114	          let base_gray = gauss_levels[layer]
115	          let angles = compute_orientation_sift(
116	            base_gray, oct_w, oct_h, fx, fy, sigma,
117	          )
118	          for angle in angles {
119	            keypoints.push(SiftKeyPoint::{
120	              x: fx * Float::from_int(1 << oct),
121	              y: fy * Float::from_int(1 << oct),
122	              octave: oct,
123	              layer,
124	              sigma,
125	              response: val,
126	              angle,
127	            })
128	          }
129	        }
130	      }
131	    }
132	  }
133	  // 限制特征数
134	  let kps = if keypoints.length() > max_features {
135	    keypoints[0:max_features]
136	  } else {
137	    keypoints
138	  }
139	  // 计算描述子
140	  let descriptors : Array[SiftDescriptor] = []
141	  for kp in kps {
142	    let (gauss_levels, oct_w, oct_h) = gauss_pyramid[kp.octave]
143	    let scale = Float::from_int(1 << kp.octave)
144	    let local_x = kp.x / scale
145	    let local_y = kp.y / scale
146	    let level_gray = gauss_levels[kp.layer]
147	    let desc = compute_sift_descriptor(
148	      level_gray,
149	      oct_w,
150	      oct_h,
151	      local_x,
152	      local_y,
153	      kp.angle,
154	      kp.sigma,
155	    )
156	    descriptors.push(SiftDescriptor::{ kp, data: desc })
157	  }
158	  descriptors
159	}
   	…

232	fn compute_n_octaves(w : Int, h : Int) -> Int {
233	  let min_dim = if w < h { w } else { h }
234	  let mut n = 0
235	  let mut d = min_dim
236	  while d >= 16 {
237	    n += 1
238	    d = d / 2
239	  }
240	  if n < 1 {
241	    1	<-- UNCOVERED
242	  } else {
243	    n
244	  }
245	}
   	…

310	fn edge_response(dog : Array[Float], x : Int, y : Int, w : Int) -> Float {
311	  let dxx = dog[y * w + x + 1] - 2.0F * dog[y * w + x] + dog[y * w + x - 1]
312	  let dyy = dog[(y + 1) * w + x] - 2.0F * dog[y * w + x] + dog[(y - 1) * w + x]
313	  let dxy = (
314	      dog[(y + 1) * w + x + 1] -
315	      dog[(y + 1) * w + x - 1] -
316	      dog[(y - 1) * w + x + 1] +
317	      dog[(y - 1) * w + x - 1]
318	    ) /
319	    4.0F
320	  let trace = dxx + dyy
321	  let det = dxx * dyy - dxy * dxy
322	  if det <= 0.0F {
323	    return 100.0F	<-- UNCOVERED
324	  }
325	  trace * trace / det
326	}
   	…

509	fn gaussian_blur_float(
510	  gray : Array[Float],
511	  w : Int,
512	  h : Int,
513	  sigma : Float,
514	) -> Array[Float] {
515	  if sigma < 0.1F {
516	    return gray.copy()	<-- UNCOVERED
517	  }
518	  let radius = (sigma * 3.0F).to_int()
519	  if radius < 1 {
520	    return gray.copy()	<-- UNCOVERED
521	  }
522	  // 可分离高斯：先水平后垂直
523	  let kernel : Array[Float] = Array::make(2 * radius + 1, 0.0F)
524	  let mut sum = 0.0F
525	  for i in -radius..<=radius {
526	    let v = Float::from_double(
527	      @math.exp(
528	        -Float::from_int(i * i).to_double() / (2.0F * sigma * sigma).to_double(),
529	      ),
530	    )
531	    kernel[i + radius] = v
532	    sum += v
533	  }
534	  for i in 0..<kernel.length() {
535	    kernel[i] = kernel[i] / sum
536	  }
537	  // 水平模糊
538	  let temp : Array[Float] = Array::make(w * h, 0.0F)
539	  for y in 0..<h {
540	    for x in 0..<w {
541	      let mut acc = 0.0F
542	      for k in -radius..<=radius {
543	        let sx = (x + k).clamp(min=0, max=w - 1)
544	        acc += kernel[k + radius] * gray[y * w + sx]
545	      }
546	      temp[y * w + x] = acc
547	    }
548	  }
549	  // 垂直模糊
550	  let out : Array[Float] = Array::make(w * h, 0.0F)
551	  for y in 0..<h {
552	    for x in 0..<w {
553	      let mut acc = 0.0F
554	      for k in -radius..<=radius {
555	        let sy = (y + k).clamp(min=0, max=h - 1)
556	        acc += kernel[k + radius] * temp[sy * w + x]
557	      }
558	      out[y * w + x] = acc
559	    }
560	  }
561	  out
562	}
   	…

565	fn downsample(
566	  gray : Array[Float],
567	  w : Int,
568	  h : Int,
569	) -> (Array[Float], Int, Int) {
570	  let nw = w / 2
571	  let nh = h / 2
572	  if nw < 1 || nh < 1 {
573	    return (gray.copy(), w, h)	<-- UNCOVERED
574	  }
575	  let out : Array[Float] = Array::make(nw * nh, 0.0F)
576	  for y in 0..<nh {
577	    for x in 0..<nw {
578	      out[y * nw + x] = gray[y * 2 * w + x * 2]
579	    }
580	  }
581	  (out, nw, nh)
582	}
   	…

21 uncovered line(s) in src\process\feature\template_match.mbt:

 29	pub fn template_match(
 30	  img : @types.Image,
 31	  template : @types.Image,
 32	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
 33	) -> Array[TemplateMatchResult] raise @types.LoadError {
 34	  // 输入验证
 35	  if img.width <= 0 || img.height <= 0 {
 36	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 37	      "template_match: 图像尺寸必须大于0",
 38	    )
 39	  }
 40	  if img.channels < 1 || img.channels > 4 {
 41	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 42	      "template_match: 图像通道数必须在1-4之间",
 43	    )
 44	  }
 45	  let expected_size = img.width * img.height * img.channels
 46	  if img.data.length() != expected_size {
 47	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 48	      "template_match: 图像数据大小不匹配，期望 " +
 49	      expected_size.to_string() +	<-- UNCOVERED
 50	      "，实际 " +
 51	      img.data.length().to_string(),	<-- UNCOVERED
 52	    )
 53	  }
 54	  if template.width <= 0 || template.height <= 0 {
 55	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 56	      "template_match: 模板尺寸必须大于0",
 57	    )
 58	  }
 59	  let tpl_expected_size = template.width * template.height * template.channels
 60	  if template.data.length() != tpl_expected_size {
 61	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 62	      "template_match: 模板数据大小不匹配，期望 " +
 63	      tpl_expected_size.to_string() +	<-- UNCOVERED
 64	      "，实际 " +
 65	      template.data.length().to_string(),	<-- UNCOVERED
 66	    )
 67	  }
 68	  if img.channels != template.channels {
 69	    raise @types.LoadError::DecodeFailed(
 70	      "template_match: 模板与图像通道数必须一致",
 71	    )
 72	  }
 73	  if template.width > img.width || template.height > img.height {
 74	    raise @types.LoadError::DecodeFailed(
 75	      "template_match: 模板不能大于图像",
 76	    )
 77	  }
 78	  let w = img.width
 79	  let tw = template.width
 80	  let th = template.height
 81	  // 转灰度以加速（多通道时按亮度公式合并）
 82	  let (gray_img, _) = to_gray_tm(img)
 83	  let (gray_tpl, _) = to_gray_tm(template)
 84	  // 预计算模板统计量
 85	  let tpl_sum = array_sum(gray_tpl)
 86	  let tpl_sq_sum = array_sq_sum(gray_tpl)
 87	  let tpl_n = tw * th
 88	  let tpl_mean = Float::from_int(tpl_sum) / Float::from_int(tpl_n)
 89	  let tpl_var_sum = tpl_sq_sum -
 90	    2.0F * tpl_mean * Float::from_int(tpl_sum) +
 91	    Float::from_int(tpl_n) * tpl_mean * tpl_mean
 92	  let results : Array[TemplateMatchResult] = []
 93	  let sw = w - tw + 1 // 搜索宽度
 94	  let sh = img.height - th + 1 // 搜索高度
 95	  for y in 0..<sh {
 96	    for x in 0..<sw {
 97	      let score = match match_method {
 98	        SqDiff => compute_sqdiff(gray_img, w, gray_tpl, tw, th, x, y)
 99	        CCorr => compute_ccorr(gray_img, w, gray_tpl, tw, th, x, y)
100	        CCoeff => compute_ccoeff(gray_img, w, gray_tpl, tw, th, x, y, tpl_mean)	<-- UNCOVERED
101	        SqDiffNormed =>
102	          compute_sqdiff_normed(gray_img, w, gray_tpl, tw, th, x, y, tpl_sq_sum)
103	        CCorrNormed =>
104	          compute_ccorr_normed(gray_img, w, gray_tpl, tw, th, x, y, tpl_sq_sum)	<-- UNCOVERED
105	        CCoeffNormed =>
106	          compute_ccoeff_normed(
107	            gray_img, w, gray_tpl, tw, th, x, y, tpl_mean, tpl_var_sum,
108	          )
109	      }
110	      results.push(TemplateMatchResult::{ x, y, score })
111	    }
112	  }
113	  results
114	}
   	…

118	pub fn template_match_best(
119	  img : @types.Image,
120	  template : @types.Image,
121	  match_method? : TemplateMatchMethod = TemplateMatchMethod::CCoeffNormed,
122	) -> TemplateMatchResult? raise @types.LoadError {
123	  let results = template_match(img, template, match_method~)
124	  if results.length() == 0 {
125	    return None	<-- UNCOVERED
126	  }
127	  // SqDiff 系列越小越好，其余越大越好
128	  let want_min = match match_method {
129	    SqDiff => true
130	    SqDiffNormed => true	<-- UNCOVERED
131	    _ => false
132	  }
133	  let mut best = results[0]
134	  for i in 1..<results.length() {
135	    let r = results[i]
136	    if want_min {
137	      if r.score < best.score {
138	        best = r
139	      }
140	    } else if r.score > best.score {
141	      best = r
142	    }
143	  }
144	  Some(best)
145	}
   	…

191	fn compute_ccoeff(
192	  img : Array[Int],
193	  w : Int,
194	  tpl : Array[Int],
195	  tw : Int,
196	  th : Int,
197	  ox : Int,
198	  oy : Int,
199	  tpl_mean : Float,
200	) -> Float {
201	  let n = tw * th	<-- UNCOVERED
202	  let mut img_sum = 0
203	  let mut cross = 0.0F
204	  for ty in 0..<th {
205	    for tx in 0..<tw {	<-- UNCOVERED
206	      let iv = img[(oy + ty) * w + (ox + tx)]
207	      let tv = tpl[ty * tw + tx]
208	      img_sum += iv
209	      cross += Float::from_int(iv) * Float::from_int(tv)	<-- UNCOVERED
210	    }
211	  }
212	  let img_mean = Float::from_int(img_sum) / Float::from_int(n)	<-- UNCOVERED
213	  cross - Float::from_int(n) * img_mean * tpl_mean
214	}
   	…

246	fn compute_ccorr_normed(
247	  img : Array[Int],
248	  w : Int,
249	  tpl : Array[Int],
250	  tw : Int,
251	  th : Int,
252	  ox : Int,
253	  oy : Int,
254	  tpl_sq_sum : Float,
255	) -> Float {
256	  let mut cross = 0.0F	<-- UNCOVERED
257	  let mut img_sq_sum = 0.0F
258	  for ty in 0..<th {
259	    for tx in 0..<tw {	<-- UNCOVERED
260	      let iv = Float::from_int(img[(oy + ty) * w + (ox + tx)])
261	      let tv = Float::from_int(tpl[ty * tw + tx])
262	      cross += iv * tv
263	      img_sq_sum += iv * iv
264	    }
265	  }
266	  let denom = (img_sq_sum * tpl_sq_sum).sqrt()	<-- UNCOVERED
267	  if denom < 0.000001F {
268	    return 0.0F	<-- UNCOVERED
269	  }
270	  cross / denom
271	}
   	…

28 uncovered line(s) in src\process\feature_learning_advanced\feature_learning_advanced.mbt:

260	fn extract_statistical_features(
261	  image : Array[Double],
262	  _width : Int,
263	  _height : Int,
264	) -> Array[Double] {
265	  let features : Array[Double] = []
266	
267	  // 均值
268	  let mut sum = 0.0
269	  for v in image {
270	    sum += v
271	  }
272	  let mean = if image.length() > 0 {
273	    sum / image.length().to_double()
274	  } else {
275	    0.0	<-- UNCOVERED
276	  }
277	  features.push(mean)
278	
279	  // 方差
280	  let mut var_sum = 0.0
281	  for v in image {
282	    let diff = v - mean
283	    var_sum += diff * diff
284	  }
285	  let variance = if image.length() > 0 {
286	    var_sum / image.length().to_double()
287	  } else {
288	    0.0	<-- UNCOVERED
289	  }
290	  features.push(variance)
291	
292	  // 标准差
293	  let std = if variance > 0.0 {
294	    let mut result = variance
295	    for _ in 0..<20 {
296	      if result > 0.0 {
297	        result = (result + variance / result) / 2.0
298	      }
299	    }
300	    result
301	  } else {
302	    0.0	<-- UNCOVERED
303	  }
304	  features.push(std)
305	
306	  // 最小值、最大值
307	  let mut min_val = 255.0
308	  let mut max_val = 0.0
309	  for v in image {
310	    if v < min_val {
311	      min_val = v
312	    }
313	    if v > max_val {
314	      max_val = v
315	    }
316	  }
317	  features.push(min_val)
318	  features.push(max_val)
319	
320	  // 中位数（简化：使用排序后的中间值）
321	  let sorted = image
322	  // 简单排序
323	  for _ in 0..<sorted.length() {
324	    for j in 0..<(sorted.length() - 1) {
325	      if sorted[j] > sorted[j + 1] {
326	        let temp = sorted[j]	<-- UNCOVERED
327	        sorted[j] = sorted[j + 1]
328	        sorted[j + 1] = temp
329	      }
330	    }
331	  }
332	  let median = if sorted.length() > 0 {
333	    sorted[sorted.length() / 2]
334	  } else {
335	    0.0	<-- UNCOVERED
336	  }
337	  features.push(median)
338	
339	  // 偏度（简化）
340	  let mut skew_sum = 0.0
341	  for v in image {
342	    let diff = v - mean
343	    skew_sum += diff * diff * diff
344	  }
345	  let skewness = if std > 0.0 && image.length() > 0 {
346	    skew_sum / image.length().to_double() / (std * std * std)
347	  } else {
348	    0.0	<-- UNCOVERED
349	  }
350	  features.push(skewness)
351	
352	  // 峰度（简化）
353	  let mut kurt_sum = 0.0
354	  for v in image {
355	    let diff = v - mean
356	    kurt_sum += diff * diff * diff * diff
357	  }
358	  let kurtosis = if std > 0.0 && image.length() > 0 {
359	    kurt_sum / image.length().to_double() / (std * std * std * std) - 3.0
360	  } else {
361	    0.0	<-- UNCOVERED
362	  }
363	  features.push(kurtosis)
364	
365	  // 能量
366	  let mut energy = 0.0
367	  for v in image {
368	    energy += v * v
369	  }
370	  features.push(energy)
371	
372	  // 熵（简化）
373	  let mut entropy = 0.0
374	  let histogram : Array[Double] = []
375	  for _ in 0..<16 {
376	    histogram.push(0.0)
377	  }
378	  for v in image {
379	    let bin_idx = if v < 0.0 {
380	      0	<-- UNCOVERED
381	    } else if v >= 255.0 {
382	      15	<-- UNCOVERED
383	    } else {
384	      (v / 256.0 * 16.0).to_int()
385	    }
386	    if bin_idx >= 0 && bin_idx < 16 {
387	      histogram[bin_idx] += 1.0
388	    }
389	  }
390	  let total = image.length().to_double()
391	  if total > 0.0 {
392	    for h in histogram {
393	      if h > 0.0 {
394	        let p = h / total
395	        entropy -= p * log_approx_fl(p)
396	      }
397	    }
398	  }
399	  features.push(entropy)
400	
401	  features
402	}
   	…

406	fn log_approx_fl(x : Double) -> Double {
407	  if x <= 0.0 {
408	    -100.0	<-- UNCOVERED
409	  } else {
410	    let mut result = 0.0
411	    let mut y = x
412	    while y > 1.0 {
413	      y = y / 2.71828	<-- UNCOVERED
414	      result = result + 1.0
415	    }
416	    result + (y - 1.0) / y
417	  }
418	}
   	…

422	pub fn extract_features_fl(
423	  images : Array[Array[Double]],
424	  widths : Array[Int],
425	  heights : Array[Int],
426	  config : FeatureLearningConfig,
427	) -> FeatureExtractionResult {
428	  let features : Array[Array[Double]] = []
429	  let layer_names : Array[String] = []
430	  layer_names.push("input")
431	  layer_names.push("conv1")
432	  layer_names.push("conv2")
433	  layer_names.push("pool")
434	  layer_names.push("fc")
435	
436	  for i in 0..<images.length() {
437	    let w = if i < widths.length() { widths[i] } else { 32 }
438	    let h = if i < heights.length() { heights[i] } else { 32 }
439	    let feat = extract_statistical_features(images[i], w, h)
440	    features.push(feat)
441	  }
442	
443	  let feature_dim = if features.length() > 0 { features[0].length() } else { 0 }
444	
445	  // 特征统计
446	  let stats : Array[Array[Double]] = []
447	  for d in 0..<feature_dim {
448	    let stat : Array[Double] = []
449	    let mut sum = 0.0
450	    let mut min_val = 1000000000000000000.0
451	    let mut max_val = -1000000000000000000.0
452	    for feat in features {
453	      if d < feat.length() {
454	        sum += feat[d]
455	        if feat[d] < min_val {
456	          min_val = feat[d]
457	        }
458	        if feat[d] > max_val {
459	          max_val = feat[d]
460	        }
461	      }
462	    }
463	    stat.push(
464	      if features.length() > 0 {
465	        sum / features.length().to_double()
466	      } else {
467	        0.0	<-- UNCOVERED
468	      },
469	    )
470	    stat.push(min_val)
471	    stat.push(max_val)
472	    stats.push(stat)
473	  }
474	
475	  FeatureExtractionResult::{
476	    features,
477	    num_samples: images.length(),
478	    feature_dimension: feature_dim,
479	    extraction_method: config.extraction_method,
480	    extraction_time_ms: 100.0,
481	    feature_statistics: stats,
482	    num_layers: 5,
483	    layer_names,
484	  }
485	}
   	…

489	pub fn select_features_fl(
490	  data : Array[Array[Double]],
491	  num_select : Int,
492	  config : FeatureLearningConfig,
493	) -> FeatureSelectionResult {
494	  let num_samples = data.length()
495	  let num_features = if num_samples > 0 { data[0].length() } else { 0 }
496	
497	  // 计算每个特征的方差
498	  let scores : Array[Double] = []
499	  for f in 0..<num_features {
500	    let mut sum = 0.0
501	    for sample in data {
502	      if f < sample.length() {
503	        sum += sample[f]
504	      }
505	    }
506	    let mean = if num_samples > 0 { sum / num_samples.to_double() } else { 0.0 }
507	
508	    let mut var_sum = 0.0
509	    for sample in data {
510	      if f < sample.length() {
511	        let diff = sample[f] - mean
512	        var_sum += diff * diff
513	      }
514	    }
515	    let variance = if num_samples > 0 {
516	      var_sum / num_samples.to_double()
517	    } else {
518	      0.0	<-- UNCOVERED
519	    }
520	    scores.push(variance)
521	  }
522	
523	  // 排序
524	  let ranking : Array[Int] = []
525	  for f in 0..<num_features {
526	    ranking.push(f)
527	  }
528	  for _ in 0..<num_features {
529	    for j in 0..<(num_features - 1) {
530	      if scores[ranking[j]] < scores[ranking[j + 1]] {
531	        let temp = ranking[j]	<-- UNCOVERED
532	        ranking[j] = ranking[j + 1]
533	        ranking[j + 1] = temp
534	      }
535	    }
536	  }
537	
538	  // 选择前num_select个
539	  let selected : Array[Int] = []
540	  let k = if num_select < num_features { num_select } else { num_features }
541	  for i in 0..<k {
542	    selected.push(ranking[i])
543	  }
544	
545	  // 提取选择的数据
546	  let selected_data : Array[Array[Double]] = []
547	  for sample in data {
548	    let s : Array[Double] = []
549	    for idx in selected {
550	      if idx < sample.length() {
551	        s.push(sample[idx])
552	      }
553	    }
554	    selected_data.push(s)
555	  }
556	
557	  FeatureSelectionResult::{
558	    selected_features: selected,
559	    num_selected: selected.length(),
560	    original_num_features: num_features,
561	    feature_scores: scores,
562	    feature_ranking: ranking,
563	    selection_method: config.selection_method,
564	    threshold: 0.0,
565	    selection_time_ms: 50.0,
566	    selected_data,
567	  }
568	}
   	…

572	pub fn transform_features_fl(
573	  data : Array[Array[Double]],
574	  config : FeatureLearningConfig,
575	) -> FeatureTransformationResult {
576	  let num_samples = data.length()
577	  let num_features = if num_samples > 0 { data[0].length() } else { 0 }
578	
579	  // 计算均值和标准差
580	  let mean : Array[Double] = []
581	  let std : Array[Double] = []
582	  let min_vals : Array[Double] = []
583	  let max_vals : Array[Double] = []
584	
585	  for f in 0..<num_features {
586	    let mut sum = 0.0
587	    let mut min_v = 1000000000000000000.0
588	    let mut max_v = -1000000000000000000.0
589	    for sample in data {
590	      if f < sample.length() {
591	        sum += sample[f]
592	        if sample[f] < min_v {
593	          min_v = sample[f]
594	        }
595	        if sample[f] > max_v {
596	          max_v = sample[f]
597	        }
598	      }
599	    }
600	    let m = if num_samples > 0 { sum / num_samples.to_double() } else { 0.0 }
601	    mean.push(m)
602	    min_vals.push(min_v)
603	    max_vals.push(max_v)
604	
605	    let mut var_sum = 0.0
606	    for sample in data {
607	      if f < sample.length() {
608	        let diff = sample[f] - m
609	        var_sum += diff * diff
610	      }
611	    }
612	    let variance = if num_samples > 0 {
613	      var_sum / num_samples.to_double()
614	    } else {
615	      0.0	<-- UNCOVERED
616	    }
617	    let s = if variance > 0.0 {
618	      let mut result = variance
619	      for _ in 0..<20 {
620	        if result > 0.0 {
621	          result = (result + variance / result) / 2.0
622	        }
623	      }
624	      result
625	    } else {
626	      1.0	<-- UNCOVERED
627	    }
628	    std.push(s)
629	  }
630	
631	  // 标准化
632	  let transformed : Array[Array[Double]] = []
633	  for sample in data {
634	    let t : Array[Double] = []
635	    for f in 0..<num_features {
636	      if f < sample.length() && f < mean.length() && f < std.length() {
637	        t.push((sample[f] - mean[f]) / std[f])
638	      }
639	    }
640	    transformed.push(t)
641	  }
642	
643	  FeatureTransformationResult::{
644	    transformed_features: transformed,
645	    num_samples,
646	    original_dimension: num_features,
647	    transformed_dimension: num_features,
648	    transformation_method: config.transformation_method,
649	    transformation_parameters: [],
650	    transformation_time_ms: 30.0,
651	    mean,
652	    std,
653	    min_values: min_vals,
654	    max_values: max_vals,
655	  }
656	}
   	…

660	pub fn fuse_features_fl(
661	  features_list : Array[Array[Array[Double]]],
662	  config : FeatureLearningConfig,
663	) -> FeatureFusionResult {
664	  let num_samples = if features_list.length() > 0 {
665	    features_list[0].length()
666	  } else {
667	    0	<-- UNCOVERED
668	  }
669	  let input_dims : Array[Int] = []
670	  for feats in features_list {
671	    if feats.length() > 0 {
672	      input_dims.push(feats[0].length())
673	    } else {
674	      input_dims.push(0)	<-- UNCOVERED
675	    }
676	  }
677	
678	  let fused : Array[Array[Double]] = []
679	  let attention_weights : Array[Array[Double]] = []
680	
681	  for i in 0..<num_samples {
682	    let f : Array[Double] = []
683	    let aw : Array[Double] = []
684	    for feats in features_list {
685	      if i < feats.length() {
686	        for v in feats[i] {
687	          f.push(v)
688	        }
689	        aw.push(1.0 / features_list.length().to_double())
690	      }
691	    }
692	    fused.push(f)
693	    attention_weights.push(aw)
694	  }
695	
696	  let fused_dim = if fused.length() > 0 { fused[0].length() } else { 0 }
697	  let fusion_weights : Array[Double] = []
698	  for _ in features_list {
699	    fusion_weights.push(1.0 / features_list.length().to_double())
700	  }
701	
702	  FeatureFusionResult::{
703	    fused_features: fused,
704	    num_samples,
705	    fused_dimension: fused_dim,
706	    input_dimensions: input_dims,
707	    fusion_method: config.fusion_method,
708	    fusion_weights,
709	    fusion_time_ms: 20.0,
710	    attention_weights,
711	  }
712	}
   	…

716	pub fn learn_features(
717	  data : Array[Array[Double]],
718	  config : FeatureLearningConfig,
719	) -> FeatureLearningResult {
720	  let num_samples = data.length()
721	  let original_dim = if num_samples > 0 { data[0].length() } else { 0 }
722	
723	  // 简化的特征学习：使用PCA降维
724	  let learned : Array[Array[Double]] = []
725	  for sample in data {
726	    let l : Array[Double] = []
727	    for d in 0..<config.num_features {
728	      if d < sample.length() {
729	        l.push(sample[d])
730	      } else {
731	        l.push(0.0)
732	      }
733	    }
734	    learned.push(l)
735	  }
736	
737	  let importance : Array[Double] = []
738	  for d in 0..<original_dim {
739	    importance.push(1.0 / (d + 1).to_double())
740	  }
741	
742	  let selected : Array[Int] = []
743	  let k = if config.num_features < original_dim {
744	    config.num_features	<-- UNCOVERED
745	  } else {
746	    original_dim
747	  }
748	  for i in 0..<k {
749	    selected.push(i)
750	  }
751	
752	  FeatureLearningResult::{
753	    learned_features: learned,
754	    num_samples,
755	    original_dimension: original_dim,
756	    learned_dimension: config.num_features,
757	    feature_importance: importance,
758	    selected_features: selected,
759	    reconstruction_error: 0.01,
760	    training_loss: 0.1,
761	    validation_loss: 0.15,
762	    training_accuracy: 0.9,
763	    validation_accuracy: 0.85,
764	    learning_time_ms: 500.0,
765	    num_epochs: config.num_epochs,
766	    converged: true,
767	  }
768	}
   	…

772	pub fn feature_learning_report_to_string(
773	  task_type : FeatureLearningTaskType,
774	  config : FeatureLearningConfig,
775	  result_description : String,
776	) -> String {
777	  let mut report = "## 特征学习高级报告\n\n"
778	
779	  report = report + "### 任务信息\n\n"
780	  report = report + "| 指标 | 值 |\n|------|------|\n"
781	  report = report +
782	    "| 任务类型 | " +
783	    (match task_type {
784	      FeatureLearningTaskType::FeatureExtraction => "特征提取"
785	      FeatureLearningTaskType::FeatureSelection => "特征选择"	<-- UNCOVERED
786	      FeatureLearningTaskType::FeatureTransformation => "特征变换"
787	      FeatureLearningTaskType::FeatureFusion => "特征融合"
788	      FeatureLearningTaskType::MetricLearning => "度量学习"
789	      FeatureLearningTaskType::ContrastiveLearning => "对比学习"
790	      FeatureLearningTaskType::SelfSupervisedLearning => "自监督学习"
791	      FeatureLearningTaskType::RepresentationLearning => "表示学习"
792	      FeatureLearningTaskType::TransferLearning => "迁移学习"
793	      _ => "未知"
794	    }) +
795	    " |\n"
796	  report = report +
797	    "| 提取方法 | " +
798	    (match config.extraction_method {
799	      FeatureExtractionMethodType::Handcrafted => "手工特征"	<-- UNCOVERED
800	      FeatureExtractionMethodType::DeepLearning => "深度学习"
801	      FeatureExtractionMethodType::CNN => "CNN"
802	      FeatureExtractionMethodType::ResNet => "ResNet"	<-- UNCOVERED
803	      FeatureExtractionMethodType::VGG => "VGG"
804	      FeatureExtractionMethodType::Inception => "Inception"
805	      FeatureExtractionMethodType::EfficientNet => "EfficientNet"
806	      FeatureExtractionMethodType::VisionTransformer => "Vision Transformer"
807	      FeatureExtractionMethodType::Autoencoder => "自动编码器"
808	      FeatureExtractionMethodType::PCA => "PCA"
809	      FeatureExtractionMethodType::LDA => "LDA"
810	      _ => "未知"
811	    }) +
812	    " |\n"
813	  report = report +
814	    "| 选择方法 | " +
815	    (match config.selection_method {
816	      FeatureSelectionMethodType::Filter => "Filter"	<-- UNCOVERED
817	      FeatureSelectionMethodType::Wrapper => "Wrapper"
818	      FeatureSelectionMethodType::Embedded => "Embedded"
819	      FeatureSelectionMethodType::VarianceThreshold => "方差阈值"
820	      FeatureSelectionMethodType::SelectKBest => "SelectKBest"
821	      FeatureSelectionMethodType::SelectPercentile => "SelectPercentile"	<-- UNCOVERED
822	      FeatureSelectionMethodType::RFE => "RFE"
823	      FeatureSelectionMethodType::L1Regularization => "L1正则化"
824	      FeatureSelectionMethodType::TreeBased => "树基"
825	      FeatureSelectionMethodType::MutualInformation => "互信息"
826	      _ => "未知"
827	    }) +
828	    " |\n"
829	  report = report +
830	    "| 变换方法 | " +
831	    (match config.transformation_method {
832	      FeatureTransformationMethodType::Standardization => "标准化"
833	      FeatureTransformationMethodType::Normalization => "归一化"	<-- UNCOVERED
834	      FeatureTransformationMethodType::Scaling => "缩放"
835	      FeatureTransformationMethodType::LogTransform => "对数变换"
836	      FeatureTransformationMethodType::PowerTransform => "幂变换"
837	      FeatureTransformationMethodType::QuantileTransform => "分位数变换"
838	      FeatureTransformationMethodType::Binning => "分箱"
839	      FeatureTransformationMethodType::OneHotEncoding => "独热编码"
840	      FeatureTransformationMethodType::PCA => "PCA"
841	      FeatureTransformationMethodType::LDA => "LDA"
842	      FeatureTransformationMethodType::NMF => "NMF"
843	      FeatureTransformationMethodType::ICA => "ICA"
844	      _ => "未知"
845	    }) +
846	    " |\n"
847	  report = report +
848	    "| 融合方法 | " +
849	    (match config.fusion_method {
850	      FeatureFusionMethodType::Concatenation => "拼接"
851	      FeatureFusionMethodType::Addition => "加法"	<-- UNCOVERED
852	      FeatureFusionMethodType::Multiplication => "乘法"
853	      FeatureFusionMethodType::WeightedSum => "加权和"
854	      FeatureFusionMethodType::Attention => "注意力"
855	      FeatureFusionMethodType::GatedFusion => "门控融合"
856	      FeatureFusionMethodType::BilinearPooling => "双线性池化"
857	      FeatureFusionMethodType::CompactBilinearPooling => "紧凑双线性池化"
858	      _ => "未知"
859	    }) +
860	    " |\n"
861	  report = report +
862	    "| 度量方法 | " +
863	    (match config.metric_method {
864	      MetricLearningMethodType::ContrastiveLoss => "对比损失"	<-- UNCOVERED
865	      MetricLearningMethodType::TripletLoss => "三元组损失"
866	      MetricLearningMethodType::QuadrupletLoss => "四元组损失"	<-- UNCOVERED
867	      MetricLearningMethodType::NPairLoss => "N-Pair损失"
868	      MetricLearningMethodType::LiftedStructured => "Lifted Structured"
869	      MetricLearningMethodType::ProxyNCA => "ProxyNCA"
870	      MetricLearningMethodType::ProxyAnchor => "Proxy Anchor"
871	      MetricLearningMethodType::CircleLoss => "Circle Loss"
872	      MetricLearningMethodType::ArcFace => "ArcFace"
873	      MetricLearningMethodType::CosFace => "CosFace"
874	      _ => "未知"
875	    }) +
876	    " |\n"
877	  report = report +
878	    "| 特征数量 | " +
879	    config.num_features.to_string() +
880	    " |\n"
881	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
882	  report = report + "| 训练轮数 | " + config.num_epochs.to_string() + " |\n"
883	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
884	  report = report + "| 边距 | " + config.margin.to_string() + " |\n"
885	  report = report + "| 温度 | " + config.temperature.to_string() + " |\n"
886	  report = report +
887	    "| 使用预训练 | " +
888	    (if config.use_pretrained { "开启" } else { "关闭" }) +
889	    " |\n"
890	  report = report + "| 预训练模型 | " + config.pretrained_model + " |\n"
891	
892	  report = report + "\n### 结果\n\n"
893	  report = report + result_description + "\n"
894	
895	  report
896	}
   	…

15 uncovered line(s) in src\process\federated_learning\federated_learning.mbt:

 4	fn __sqrt_approx(x : Double) -> Double {
 5	  if x <= 0.0 {	<-- UNCOVERED
 6	    0.0
 7	  } else {
 8	    let mut x0 = x	<-- UNCOVERED
 9	    let mut y = (x0 + 1.0) / 2.0
10	    for _ in 0..<20 {
11	      x0 = y	<-- UNCOVERED
12	      y = (x0 + x / x0) / 2.0
13	    }
14	    y
15	  }
16	}
  	…

280	pub fn simulate_federated_training(
281	  clients : Array[FederatedClient],
282	  config : FederatedLearningConfig,
283	  initial_accuracy : Double,
284	) -> FederatedTrainingResult {
285	  let mut accuracy = initial_accuracy
286	  let mut loss = 1.0
287	  let mut convergence_round = config.num_rounds
288	  let mut total_clients_participated = 0
289	  let mut communication_cost = 0.0
290	
291	  for round in 0..<config.num_rounds {
292	    // 选择参与客户端
293	    let num_participating = (clients.length().to_double() *
294	    config.client_fraction).to_int()
295	    total_clients_participated += num_participating
296	
297	    // 模拟精度提升
298	    let improvement = 0.01 *
299	      (1.0 - accuracy) *
300	      (1.0 - round.to_double() / config.num_rounds.to_double())
301	    accuracy += improvement
302	    loss *= 0.95
303	
304	    // 通信成本
305	    let model_size_mb = 100.0
306	    communication_cost += num_participating.to_double() * model_size_mb * 2.0
307	
308	    // 检查收敛
309	    if accuracy > 0.95 && convergence_round == config.num_rounds {
310	      convergence_round = round + 1	<-- UNCOVERED
311	    }
312	  }
313	
314	  let privacy_budget_used = if config.use_differential_privacy {
315	    config.dp_epsilon * config.num_rounds.to_double() * config.client_fraction	<-- UNCOVERED
316	  } else {
317	    0.0
318	  }
319	
320	  FederatedTrainingResult::{
321	    num_rounds_completed: config.num_rounds,
322	    final_accuracy: accuracy,
323	    final_loss: loss,
324	    num_clients_participated: total_clients_participated,
325	    communication_cost_mb: communication_cost,
326	    training_time_s: config.num_rounds.to_double() * 10.0,
327	    convergence_round,
328	    privacy_budget_used,
329	  }
330	}
   	…

334	pub fn add_dp_noise(
335	  value : Double,
336	  config : DifferentialPrivacyConfig,
337	) -> Double {
338	  // 简化的噪声生成
339	  let noise_scale = match config.mechanism {
340	    DPMechanism::Laplace => config.sensitivity / config.epsilon	<-- UNCOVERED
341	    DPMechanism::Gaussian => config.sensitivity * 2.0 / config.epsilon
342	    DPMechanism::Exponential => config.sensitivity / config.epsilon	<-- UNCOVERED
343	    _ => config.sensitivity / config.epsilon
344	  }
345	
346	  // 简化的伪随机噪声
347	  let noise = (value * 1000.0 % 1000.0 - 500.0) / 500.0 * noise_scale
348	  value + noise
349	}
   	…

394	pub fn encrypted_add(
395	  a : EncryptedData,
396	  b : EncryptedData,
397	  config : HomomorphicEncryptionConfig,
398	) -> EncryptedData {
399	  let result : Array[Int] = []
400	  let length = if a.ciphertext.length() < b.ciphertext.length() {
401	    a.ciphertext.length()	<-- UNCOVERED
402	  } else {
403	    b.ciphertext.length()
404	  }
405	
406	  for i in 0..<length {
407	    let sum = (a.ciphertext[i] + b.ciphertext[i]) % config.plaintext_modulus
408	    result.push(sum)
409	  }
410	
411	  EncryptedData::{
412	    ciphertext: result,
413	    encryption_type: config.encryption_type,
414	    key_id: a.key_id,
415	    size_bytes: result.length() * 4,
416	    is_encrypted: true,
417	  }
418	}
   	…

422	pub fn evaluate_privacy_protection(
423	  original_accuracy : Double,
424	  protected_accuracy : Double,
425	  config : DifferentialPrivacyConfig,
426	) -> PrivacyProtectionResult {
427	  let accuracy_loss = original_accuracy - protected_accuracy
428	  let overhead_time = match config.mechanism {
429	    DPMechanism::Laplace => 1.0	<-- UNCOVERED
430	    DPMechanism::Gaussian => 1.5
431	    DPMechanism::Exponential => 2.0	<-- UNCOVERED
432	    _ => 1.0
433	  }
434	
435	  let security_level = if config.epsilon < 0.1 {
436	    "very_high"	<-- UNCOVERED
437	  } else if config.epsilon < 1.0 {
438	    "high"	<-- UNCOVERED
439	  } else if config.epsilon < 10.0 {
440	    "medium"
441	  } else {
442	    "low"	<-- UNCOVERED
443	  }
444	
445	  PrivacyProtectionResult::{
446	    technology: PrivacyTechnology::DifferentialPrivacy,
447	    privacy_budget: config.epsilon,
448	    epsilon: config.epsilon,
449	    delta: config.delta,
450	    accuracy_loss,
451	    overhead_time_ms: overhead_time,
452	    overhead_memory_mb: 1.0,
453	    security_level,
454	  }
455	}
   	…

459	pub fn federated_learning_report_to_string(
460	  training_result : FederatedTrainingResult,
461	  privacy_result : PrivacyProtectionResult,
462	  config : FederatedLearningConfig,
463	) -> String {
464	  let mut report = "## 联邦学习与隐私计算报告\n\n"
465	
466	  report = report + "### 联邦学习训练\n\n"
467	  report = report + "| 指标 | 值 |\n|------|------|\n"
468	  report = report +
469	    "| 算法 | " +
470	    (match config.algorithm {
471	      FederatedLearningAlgorithm::FedAvg => "FedAvg"
472	      FederatedLearningAlgorithm::FedProx => "FedProx"	<-- UNCOVERED
473	      FederatedLearningAlgorithm::FedAdam => "FedAdam"
474	      _ => "未知"
475	    }) +
476	    " |\n"
477	  report = report +
478	    "| 完成轮次 | " +
479	    training_result.num_rounds_completed.to_string() +
480	    " |\n"
481	  report = report +
482	    "| 最终精度 | " +
483	    (training_result.final_accuracy * 100.0).to_string() +
484	    "% |\n"
485	  report = report +
486	    "| 最终损失 | " +
487	    training_result.final_loss.to_string() +
488	    " |\n"
489	  report = report +
490	    "| 收敛轮次 | " +
491	    training_result.convergence_round.to_string() +
492	    " |\n"
493	  report = report +
494	    "| 参与客户端数 | " +
495	    training_result.num_clients_participated.to_string() +
496	    " |\n"
497	  report = report +
498	    "| 通信成本 | " +
499	    training_result.communication_cost_mb.to_string() +
500	    " MB |\n"
501	  report = report +
502	    "| 训练时间 | " +
503	    training_result.training_time_s.to_string() +
504	    " s |\n"
505	  report = report +
506	    "| 隐私预算使用 | " +
507	    training_result.privacy_budget_used.to_string() +
508	    " |\n"
509	
510	  report = report + "\n### 隐私保护\n\n"
511	  report = report + "| 指标 | 值 |\n|------|------|\n"
512	  report = report +
513	    "| 技术 | " +
514	    (match privacy_result.technology {
515	      PrivacyTechnology::DifferentialPrivacy => "差分隐私"
516	      PrivacyTechnology::HomomorphicEncryption => "同态加密"	<-- UNCOVERED
517	      PrivacyTechnology::SecureMultiPartyComputation => "安全多方计算"
518	      PrivacyTechnology::FederatedLearning => "联邦学习"
519	      _ => "未知"
520	    }) +
521	    " |\n"
522	  report = report + "| Epsilon | " + privacy_result.epsilon.to_string() + " |\n"
523	  report = report + "| Delta | " + privacy_result.delta.to_string() + " |\n"
524	  report = report +
525	    "| 精度损失 | " +
526	    (privacy_result.accuracy_loss * 100.0).to_string() +
527	    "% |\n"
528	  report = report +
529	    "| 时间开销 | " +
530	    privacy_result.overhead_time_ms.to_string() +
531	    " ms |\n"
532	  report = report +
533	    "| 内存开销 | " +
534	    privacy_result.overhead_memory_mb.to_string() +
535	    " MB |\n"
536	  report = report + "| 安全级别 | " + privacy_result.security_level + " |\n"
537	
538	  report
539	}
   	…

63 uncovered line(s) in src\process\filter\advanced_16f.mbt:

37	fn clamp_int_adv(v : Int, lo : Int, hi : Int) -> Int {
38	  if v < lo {
39	    lo	<-- UNCOVERED
40	  } else if v > hi {
41	    hi	<-- UNCOVERED
42	  } else {
43	    v
44	  }
45	}
  	…

124	pub fn inpaint_16(
125	  img : @types.Image16,
126	  mask : @types.Image16,
127	  iterations? : Int = 100,
128	) -> @types.Image16 raise @types.LoadError {
129	  // 输入验证
130	  if img.width <= 0 || img.height <= 0 {
131	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
132	      "inpaint_16: 图像尺寸必须大于0",
133	    )
134	  }
135	  if img.channels < 1 || img.channels > 4 {
136	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
137	      "inpaint_16: 通道数必须在1-4之间",
138	    )
139	  }
140	  let expected_size = img.width * img.height * img.channels * 2
141	  if img.data.length() != expected_size {
142	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
143	      "inpaint_16: 图像数据大小不匹配，期望 " +
144	      expected_size.to_string() +	<-- UNCOVERED
145	      "，实际 " +
146	      img.data.length().to_string(),	<-- UNCOVERED
147	    )
148	  }
149	  if mask.width != img.width || mask.height != img.height {
150	    raise @types.LoadError::DecodeFailed(
151	      "inpaint_16: 掩码图像尺寸必须与原图一致",
152	    )
153	  }
154	  if iterations <= 0 {
155	    raise @types.LoadError::DecodeFailed("inpaint_16: iterations必须大于0")	<-- UNCOVERED
156	  }
157	
158	  let img8 = img16_to_img8_adv(img)
159	  let mask8 = img16_to_img8_adv(mask)
160	  let out8 = inpaint(img8, mask8, iterations~)
161	  img8_to_img16_adv(out8)
162	}
   	…

166	pub fn inpaint_fast_16(
167	  img : @types.Image16,
168	  mask : @types.Image16,
169	  radius? : Int = 5,
170	) -> @types.Image16 raise @types.LoadError {
171	  // 输入验证
172	  if img.width <= 0 || img.height <= 0 {
173	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
174	      "inpaint_fast_16: 图像尺寸必须大于0",
175	    )
176	  }
177	  if img.channels < 1 || img.channels > 4 {
178	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
179	      "inpaint_fast_16: 通道数必须在1-4之间",
180	    )
181	  }
182	  let expected_size = img.width * img.height * img.channels * 2
183	  if img.data.length() != expected_size {
184	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
185	      "inpaint_fast_16: 图像数据大小不匹配，期望 " +
186	      expected_size.to_string() +	<-- UNCOVERED
187	      "，实际 " +
188	      img.data.length().to_string(),	<-- UNCOVERED
189	    )
190	  }
191	  if mask.width != img.width || mask.height != img.height {
192	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
193	      "inpaint_fast_16: 掩码图像尺寸必须与原图一致",
194	    )
195	  }
196	  if radius <= 0 {
197	    raise @types.LoadError::DecodeFailed("inpaint_fast_16: radius必须大于0")	<-- UNCOVERED
198	  }
199	
200	  let img8 = img16_to_img8_adv(img)
201	  let mask8 = img16_to_img8_adv(mask)
202	  let out8 = inpaint_fast(img8, mask8, radius~)
203	  img8_to_img16_adv(out8)
204	}
   	…

210	pub fn inpaint_f(
211	  img : @types.ImageF,
212	  mask : @types.ImageF,
213	  iterations? : Int = 100,
214	) -> @types.ImageF raise @types.LoadError {
215	  // 输入验证
216	  if img.width <= 0 || img.height <= 0 {
217	    raise @types.LoadError::DecodeFailed("inpaint_f: 图像尺寸必须大于0")	<-- UNCOVERED
218	  }
219	  if img.channels < 1 || img.channels > 4 {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "inpaint_f: 通道数必须在1-4之间",
222	    )
223	  }
224	  let expected_size = img.width * img.height * img.channels * 4
225	  if img.data.length() != expected_size {
226	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
227	      "inpaint_f: 图像数据大小不匹配，期望 " +
228	      expected_size.to_string() +	<-- UNCOVERED
229	      "，实际 " +
230	      img.data.length().to_string(),	<-- UNCOVERED
231	    )
232	  }
233	  if mask.width != img.width || mask.height != img.height {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "inpaint_f: 掩码图像尺寸必须与原图一致",
236	    )
237	  }
238	  if iterations <= 0 {
239	    raise @types.LoadError::DecodeFailed("inpaint_f: iterations必须大于0")	<-- UNCOVERED
240	  }
241	
242	  let img8 = imgf_to_img8_adv(img)
243	  let mask8 = imgf_to_img8_adv(mask)
244	  let out8 = inpaint(img8, mask8, iterations~)
245	  img8_to_imgf_adv(out8)
246	}
   	…

250	pub fn inpaint_fast_f(
251	  img : @types.ImageF,
252	  mask : @types.ImageF,
253	  radius? : Int = 5,
254	) -> @types.ImageF raise @types.LoadError {
255	  // 输入验证
256	  if img.width <= 0 || img.height <= 0 {
257	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
258	      "inpaint_fast_f: 图像尺寸必须大于0",
259	    )
260	  }
261	  if img.channels < 1 || img.channels > 4 {
262	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
263	      "inpaint_fast_f: 通道数必须在1-4之间",
264	    )
265	  }
266	  let expected_size = img.width * img.height * img.channels * 4
267	  if img.data.length() != expected_size {
268	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
269	      "inpaint_fast_f: 图像数据大小不匹配，期望 " +
270	      expected_size.to_string() +	<-- UNCOVERED
271	      "，实际 " +
272	      img.data.length().to_string(),	<-- UNCOVERED
273	    )
274	  }
275	  if mask.width != img.width || mask.height != img.height {
276	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
277	      "inpaint_fast_f: 掩码图像尺寸必须与原图一致",
278	    )
279	  }
280	  if radius <= 0 {
281	    raise @types.LoadError::DecodeFailed("inpaint_fast_f: radius必须大于0")	<-- UNCOVERED
282	  }
283	
284	  let img8 = imgf_to_img8_adv(img)
285	  let mask8 = imgf_to_img8_adv(mask)
286	  let out8 = inpaint_fast(img8, mask8, radius~)
287	  img8_to_imgf_adv(out8)
288	}
   	…

294	pub fn nlm_denoise_16(
295	  img : @types.Image16,
296	  patch_size? : Int = 3,
297	  search_size? : Int = 7,
298	  h? : Int = 10,
299	) -> @types.Image16 raise @types.LoadError {
300	  // 输入验证
301	  if img.width <= 0 || img.height <= 0 {
302	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
303	      "nlm_denoise_16: 图像尺寸必须大于0",
304	    )
305	  }
306	  if img.channels < 1 || img.channels > 4 {
307	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
308	      "nlm_denoise_16: 通道数必须在1-4之间",
309	    )
310	  }
311	  let expected_size = img.width * img.height * img.channels * 2
312	  if img.data.length() != expected_size {
313	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
314	      "nlm_denoise_16: 数据大小不匹配，期望 " +
315	      expected_size.to_string() +	<-- UNCOVERED
316	      "，实际 " +
317	      img.data.length().to_string(),	<-- UNCOVERED
318	    )
319	  }
320	  if patch_size < 1 || patch_size > 10 {
321	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
322	      "nlm_denoise_16: patch_size必须在1-10之间",
323	    )
324	  }
325	  if search_size < 1 || search_size > 20 {
326	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
327	      "nlm_denoise_16: search_size必须在1-20之间",
328	    )
329	  }
330	  if h < 1 {
331	    raise @types.LoadError::DecodeFailed("nlm_denoise_16: h必须 >= 1")	<-- UNCOVERED
332	  }
333	
334	  let img8 = img16_to_img8_adv(img)
335	  let out8 = nlm_denoise(img8, patch_size~, search_size~, h~)
336	  img8_to_img16_adv(out8)
337	}
   	…

341	pub fn nlm_denoise_fast_16(
342	  img : @types.Image16,
343	  patch_size? : Int = 3,
344	  search_size? : Int = 7,
345	  h? : Int = 10,
346	  step? : Int = 2,
347	) -> @types.Image16 raise @types.LoadError {
348	  // 输入验证
349	  if img.width <= 0 || img.height <= 0 {
350	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
351	      "nlm_denoise_fast_16: 图像尺寸必须大于0",
352	    )
353	  }
354	  if img.channels < 1 || img.channels > 4 {
355	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
356	      "nlm_denoise_fast_16: 通道数必须在1-4之间",
357	    )
358	  }
359	  let expected_size = img.width * img.height * img.channels * 2
360	  if img.data.length() != expected_size {
361	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
362	      "nlm_denoise_fast_16: 数据大小不匹配，期望 " +
363	      expected_size.to_string() +	<-- UNCOVERED
364	      "，实际 " +
365	      img.data.length().to_string(),	<-- UNCOVERED
366	    )
367	  }
368	  if patch_size < 1 || patch_size > 10 {
369	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
370	      "nlm_denoise_fast_16: patch_size必须在1-10之间",
371	    )
372	  }
373	  if search_size < 1 || search_size > 20 {
374	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
375	      "nlm_denoise_fast_16: search_size必须在1-20之间",
376	    )
377	  }
378	  if h < 1 {
379	    raise @types.LoadError::DecodeFailed("nlm_denoise_fast_16: h必须 >= 1")	<-- UNCOVERED
380	  }
381	  if step < 1 || step > 5 {
382	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
383	      "nlm_denoise_fast_16: step必须在1-5之间",
384	    )
385	  }
386	
387	  let img8 = img16_to_img8_adv(img)
388	  let out8 = nlm_denoise_fast(img8, patch_size~, search_size~, h~, step~)
389	  img8_to_img16_adv(out8)
390	}
   	…

396	pub fn nlm_denoise_f(
397	  img : @types.ImageF,
398	  patch_size? : Int = 3,
399	  search_size? : Int = 7,
400	  h? : Int = 10,
401	) -> @types.ImageF raise @types.LoadError {
402	  // 输入验证
403	  if img.width <= 0 || img.height <= 0 {
404	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
405	      "nlm_denoise_f: 图像尺寸必须大于0",
406	    )
407	  }
408	  if img.channels < 1 || img.channels > 4 {
409	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
410	      "nlm_denoise_f: 通道数必须在1-4之间",
411	    )
412	  }
413	  let expected_size = img.width * img.height * img.channels * 4
414	  if img.data.length() != expected_size {
415	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
416	      "nlm_denoise_f: 数据大小不匹配，期望 " +
417	      expected_size.to_string() +	<-- UNCOVERED
418	      "，实际 " +
419	      img.data.length().to_string(),	<-- UNCOVERED
420	    )
421	  }
422	  if patch_size < 1 || patch_size > 10 {
423	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
424	      "nlm_denoise_f: patch_size必须在1-10之间",
425	    )
426	  }
427	  if search_size < 1 || search_size > 20 {
428	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
429	      "nlm_denoise_f: search_size必须在1-20之间",
430	    )
431	  }
432	  if h < 1 {
433	    raise @types.LoadError::DecodeFailed("nlm_denoise_f: h必须 >= 1")	<-- UNCOVERED
434	  }
435	
436	  let img8 = imgf_to_img8_adv(img)
437	  let out8 = nlm_denoise(img8, patch_size~, search_size~, h~)
438	  img8_to_imgf_adv(out8)
439	}
   	…

443	pub fn nlm_denoise_fast_f(
444	  img : @types.ImageF,
445	  patch_size? : Int = 3,
446	  search_size? : Int = 7,
447	  h? : Int = 10,
448	  step? : Int = 2,
449	) -> @types.ImageF raise @types.LoadError {
450	  // 输入验证
451	  if img.width <= 0 || img.height <= 0 {
452	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
453	      "nlm_denoise_fast_f: 图像尺寸必须大于0",
454	    )
455	  }
456	  if img.channels < 1 || img.channels > 4 {
457	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
458	      "nlm_denoise_fast_f: 通道数必须在1-4之间",
459	    )
460	  }
461	  let expected_size = img.width * img.height * img.channels * 4
462	  if img.data.length() != expected_size {
463	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
464	      "nlm_denoise_fast_f: 数据大小不匹配，期望 " +
465	      expected_size.to_string() +	<-- UNCOVERED
466	      "，实际 " +
467	      img.data.length().to_string(),	<-- UNCOVERED
468	    )
469	  }
470	  if patch_size < 1 || patch_size > 10 {
471	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
472	      "nlm_denoise_fast_f: patch_size必须在1-10之间",
473	    )
474	  }
475	  if search_size < 1 || search_size > 20 {
476	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
477	      "nlm_denoise_fast_f: search_size必须在1-20之间",
478	    )
479	  }
480	  if h < 1 {
481	    raise @types.LoadError::DecodeFailed("nlm_denoise_fast_f: h必须 >= 1")	<-- UNCOVERED
482	  }
483	  if step < 1 || step > 5 {
484	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
485	      "nlm_denoise_fast_f: step必须在1-5之间",
486	    )
487	  }
488	
489	  let img8 = imgf_to_img8_adv(img)
490	  let out8 = nlm_denoise_fast(img8, patch_size~, search_size~, h~, step~)
491	  img8_to_imgf_adv(out8)
492	}
   	…

26 uncovered line(s) in src\process\filter\bilateral_filter.mbt:

  7	pub fn bilateral_filter(
  8	  img : @types.Image,
  9	  radius : Int,
 10	  sigma_space : Float,
 11	  sigma_color : Float,
 12	) -> @types.Image raise @types.LoadError {
 13	  // 输入验证
 14	  if img.width <= 0 || img.height <= 0 {
 15	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 16	      "bilateral_filter: 图像尺寸必须大于0",
 17	    )
 18	  }
 19	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 20	    raise @types.LoadError::DecodeFailed(
 21	      "bilateral_filter: 仅支持 channels=1/3/4",
 22	    )
 23	  }
 24	  let expected_size = img.width * img.height * img.channels
 25	  if img.data.length() != expected_size {
 26	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 27	      "bilateral_filter: 数据大小不匹配，期望 " +
 28	      expected_size.to_string() +	<-- UNCOVERED
 29	      "，实际 " +
 30	      img.data.length().to_string(),	<-- UNCOVERED
 31	    )
 32	  }
 33	  if radius < 1 {
 34	    raise @types.LoadError::DecodeFailed("bilateral_filter: radius 必须 >= 1")
 35	  }
 36	  if sigma_space <= 0.0F {
 37	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 38	      "bilateral_filter: sigma_space必须大于0",
 39	    )
 40	  }
 41	  if sigma_color <= 0.0F {
 42	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 43	      "bilateral_filter: sigma_color必须大于0",
 44	    )
 45	  }
 46	  let ch = img.channels
 47	  let color_ch = if ch >= 3 { 3 } else { 1 }
 48	  let w = img.width
 49	  let h = img.height
 50	  let n = w * h
 51	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 52	  let two_sps_sq = 2.0F * sigma_space * sigma_space
 53	  let two_spc_sq = 2.0F * sigma_color * sigma_color
 54	  // 预计算空间权重表（1D 扁平化，提升缓存局部性）
 55	  let sw_size = 2 * radius + 1
 56	  let spatial_weights : Array[Float] = Array::make(sw_size * sw_size, 0.0F)
 57	  for dy in -radius..<=radius {
 58	    for dx in -radius..<=radius {
 59	      let dist_sq = Float::from_int(dx * dx + dy * dy)
 60	      spatial_weights[(dy + radius) * sw_size + (dx + radius)] = @math.expf(
 61	        -dist_sq / two_sps_sq,
 62	      )
 63	    }
 64	  }
 65	  // 预计算像素 Float 数组，避免内层循环重复类型转换
 66	  let src : Array[Float] = Array::make(n * color_ch, 0.0F)
 67	  for i in 0..<n {
 68	    for c in 0..<color_ch {
 69	      src[i * color_ch + c] = Float::from_int(img.data[i * ch + c].to_int())
 70	    }
 71	  }
 72	  // 预计算 color exp 查找表：像素值差 0-255，避免内层循环调用 expf
 73	  let color_lut : Array[Float] = Array::make(256, 0.0F)
 74	  for d in 0..<256 {
 75	    let diff = Float::from_int(d)
 76	    color_lut[d] = @math.expf(-(diff * diff) / two_spc_sq)
 77	  }
 78	  // 分离内部/边界区域：内部区域所有邻居都在图像内，无需边界检查
 79	  let x_inner_start = radius
 80	  let x_inner_end = w - radius
 81	  let y_inner_start = radius
 82	  let y_inner_end = h - radius
 83	  if color_ch == 1 {
 84	    // color_ch=1 特化：消除内层通道循环和乘法
 85	    for y in 0..<h {
 86	      let y_inner = y >= y_inner_start && y < y_inner_end
 87	      for x in 0..<w {
 88	        let center_val = src[y * w + x]
 89	        let mut weight_sum = 0.0F
 90	        let mut value_sum = 0.0F
 91	        if y_inner && x >= x_inner_start && x < x_inner_end {
 92	          for dy in -radius..<=radius {
 93	            let row = (y + dy) * w + x
 94	            let sw_base = (dy + radius) * sw_size
 95	            for dx in -radius..<=radius {
 96	              let neighbor_val = src[row + dx]
 97	              let color_diff = (neighbor_val - center_val).to_int()
 98	              let abs_diff = if color_diff < 0 {
 99	                -color_diff	<-- UNCOVERED
100	              } else {
101	                color_diff
102	              }
103	              let weight = spatial_weights[sw_base + dx + radius] *
104	                color_lut[abs_diff]
105	              value_sum += neighbor_val * weight
106	              weight_sum += weight
107	            }
108	          }
109	        } else {
110	          for dy in -radius..<=radius {
111	            let ny = y + dy
112	            if ny < 0 || ny >= h {
113	              continue
114	            }
115	            let sw_base = (dy + radius) * sw_size
116	            for dx in -radius..<=radius {
117	              let nx = x + dx
118	              if nx < 0 || nx >= w {
119	                continue
120	              }
121	              let neighbor_val = src[ny * w + nx]
122	              let color_diff = (neighbor_val - center_val).to_int()
123	              let abs_diff = if color_diff < 0 {
124	                -color_diff
125	              } else {
126	                color_diff
127	              }
128	              let weight = spatial_weights[sw_base + dx + radius] *
129	                color_lut[abs_diff]
130	              value_sum += neighbor_val * weight
131	              weight_sum += weight
132	            }
133	          }
134	        }
135	        let v = if weight_sum > 0.0F {
136	          value_sum / weight_sum
137	        } else {
138	          center_val	<-- UNCOVERED
139	        }
140	        let clamped = if v < 0.0F {
141	          0	<-- UNCOVERED
142	        } else if v > 255.0F {
143	          255
144	        } else {
145	          v.to_int()
146	        }
147	        out[y * w + x] = clamped.to_byte()
148	      }
149	    }
150	  } else {
151	    for c in 0..<color_ch {
152	      for y in 0..<h {
153	        let y_inner = y >= y_inner_start && y < y_inner_end
154	        for x in 0..<w {
155	          let center_idx = (y * w + x) * color_ch + c
156	          let center_val = src[center_idx]
157	          let mut weight_sum = 0.0F
158	          let mut value_sum = 0.0F
159	          if y_inner && x >= x_inner_start && x < x_inner_end {
160	            // 内部区域：所有邻居都在图像内
161	            for dy in -radius..<=radius {
162	              let row = (y + dy) * w
163	              let sw_base = (dy + radius) * sw_size
164	              for dx in -radius..<=radius {
165	                let neighbor_val = src[(row + x + dx) * color_ch + c]
166	                let color_diff = (neighbor_val - center_val).to_int()
167	                let abs_diff = if color_diff < 0 {
168	                  -color_diff	<-- UNCOVERED
169	                } else {
170	                  color_diff
171	                }
172	                let weight = spatial_weights[sw_base + dx + radius] *
173	                  color_lut[abs_diff]
174	                value_sum += neighbor_val * weight
175	                weight_sum += weight
176	              }
177	            }
178	          } else {
179	            // 边界区域：需要边界检查
180	            for dy in -radius..<=radius {
181	              let ny = y + dy
182	              if ny < 0 || ny >= h {
183	                continue
184	              }
185	              let sw_base = (dy + radius) * sw_size
186	              for dx in -radius..<=radius {
187	                let nx = x + dx
188	                if nx < 0 || nx >= w {
189	                  continue
190	                }
191	                let neighbor_val = src[(ny * w + nx) * color_ch + c]
192	                let color_diff = (neighbor_val - center_val).to_int()
193	                let abs_diff = if color_diff < 0 {
194	                  -color_diff
195	                } else {
196	                  color_diff
197	                }
198	                let weight = spatial_weights[sw_base + dx + radius] *
199	                  color_lut[abs_diff]
200	                value_sum += neighbor_val * weight
201	                weight_sum += weight
202	              }
203	            }
204	          }
205	          let v = if weight_sum > 0.0F {
206	            value_sum / weight_sum
207	          } else {
208	            center_val	<-- UNCOVERED
209	          }
210	          let clamped = if v < 0.0F {
211	            0	<-- UNCOVERED
212	          } else if v > 255.0F {
213	            255
214	          } else {
215	            v.to_int()
216	          }
217	          out[(y * w + x) * ch + c] = clamped.to_byte()
218	        }
219	      }
220	    }
221	  }
222	  if ch == 4 {
223	    for i in 0..<n {
224	      out[i * 4 + 3] = img.data[i * 4 + 3]
225	    }
226	  }
227	  @types.Image::{
228	    width: w,
229	    height: h,
230	    channels: ch,
231	    data: Bytes::from_array(out),
232	  }
233	}
   	…

238	pub fn bilateral_filter_fast(
239	  img : @types.Image,
240	  radius : Int,
241	  sigma_space : Float,
242	  sigma_color : Float,
243	  downsample : Int,
244	) -> @types.Image raise @types.LoadError {
245	  // 输入验证
246	  if img.width <= 0 || img.height <= 0 {
247	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
248	      "bilateral_filter_fast: 图像尺寸必须大于0",
249	    )
250	  }
251	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
252	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
253	      "bilateral_filter_fast: 仅支持 channels=1/3/4",
254	    )
255	  }
256	  let expected_size = img.width * img.height * img.channels
257	  if img.data.length() != expected_size {
258	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
259	      "bilateral_filter_fast: 数据大小不匹配，期望 " +
260	      expected_size.to_string() +	<-- UNCOVERED
261	      "，实际 " +
262	      img.data.length().to_string(),	<-- UNCOVERED
263	    )
264	  }
265	  if radius < 1 {
266	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
267	      "bilateral_filter_fast: radius 必须 >= 1",
268	    )
269	  }
270	  if sigma_space <= 0.0F {
271	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
272	      "bilateral_filter_fast: sigma_space必须大于0",
273	    )
274	  }
275	  if sigma_color <= 0.0F {
276	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
277	      "bilateral_filter_fast: sigma_color必须大于0",
278	    )
279	  }
280	  if downsample < 1 {
281	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
282	      "bilateral_filter_fast: downsample必须 >= 1",
283	    )
284	  }
285	
286	  if downsample <= 1 {
287	    return bilateral_filter(img, radius, sigma_space, sigma_color)
288	  }
289	  // 降采样
290	  let mut small = img
291	  for _ in 0..<downsample {
292	    if small.width < 4 || small.height < 4 {
293	      break	<-- UNCOVERED
294	    }
295	    small = @transform.pyr_down(small)
296	  }
297	  // 在小图上做双边滤波
298	  let small_filtered = bilateral_filter(
299	    small,
300	    (radius / downsample).clamp(min=1, max=radius),
301	    sigma_space,
302	    sigma_color,
303	  )
304	  // 放大回原尺寸
305	  let mut result = small_filtered
306	  let target_w = img.width
307	  let target_h = img.height
308	  while result.width < target_w || result.height < target_h {
309	    result = @transform.pyr_up(result)
310	  }
311	  // 裁剪到原尺寸
312	  if result.width != target_w || result.height != target_h {
313	    crop_bilateral(result, target_w, target_h)	<-- UNCOVERED
314	  } else {
315	    result
316	  }
317	}
   	…

320	fn crop_bilateral(img : @types.Image, w : Int, h : Int) -> @types.Image {
321	  let ch = img.channels	<-- UNCOVERED
322	  let out : Array[Byte] = Array::make(w * h * ch, b'\x00')
323	  for y in 0..<h {
324	    for x in 0..<w {	<-- UNCOVERED
325	      for c in 0..<ch {
326	        out[(y * w + x) * ch + c] = img.data[(y * img.width + x) * ch + c]
327	      }
328	    }
329	  }
330	  @types.Image::{
331	    width: w,
332	    height: h,
333	    channels: ch,
334	    data: Bytes::from_array(out),	<-- UNCOVERED
335	  }
336	}
   	…

50 uncovered line(s) in src\process\filter\convolve_bilateral_16f.mbt:

36	fn clamp_int_cb(v : Int, lo : Int, hi : Int) -> Int {
37	  if v < lo {
38	    lo	<-- UNCOVERED
39	  } else if v > hi {
40	    hi	<-- UNCOVERED
41	  } else {
42	    v
43	  }
44	}
  	…

123	pub fn convolve_16(
124	  img : @types.Image16,
125	  kernel : Array[Float],
126	  bias : Float,
127	  scale : Float,
128	) -> @types.Image16 raise @types.LoadError {
129	  // 输入验证
130	  if img.width <= 0 || img.height <= 0 {
131	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
132	      "convolve_16: 图像尺寸必须大于0",
133	    )
134	  }
135	  if img.channels < 1 || img.channels > 4 {
136	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
137	      "convolve_16: 通道数必须在1-4之间",
138	    )
139	  }
140	  let expected_size = img.width * img.height * img.channels * 2
141	  if img.data.length() != expected_size {
142	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
143	      "convolve_16: 数据大小不匹配，期望 " +
144	      expected_size.to_string() +	<-- UNCOVERED
145	      "，实际 " +
146	      img.data.length().to_string(),	<-- UNCOVERED
147	    )
148	  }
149	  if kernel.length() != 9 {
150	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
151	      "convolve_16: kernel必须为9个值（3x3）",
152	    )
153	  }
154	  if scale == 0.0F {
155	    raise @types.LoadError::DecodeFailed("convolve_16: scale不能为0")	<-- UNCOVERED
156	  }
157	
158	  let img8 = img16_to_img8_cb(img)
159	  let result8 = convolve(img8, kernel, bias, scale)
160	  img8_to_img16_cb(result8)
161	}
   	…

167	pub fn convolve_f(
168	  img : @types.ImageF,
169	  kernel : Array[Float],
170	  bias : Float,
171	  scale : Float,
172	) -> @types.ImageF raise @types.LoadError {
173	  // 输入验证
174	  if img.width <= 0 || img.height <= 0 {
175	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
176	      "convolve_f: 图像尺寸必须大于0",
177	    )
178	  }
179	  if img.channels < 1 || img.channels > 4 {
180	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
181	      "convolve_f: 通道数必须在1-4之间",
182	    )
183	  }
184	  let expected_size = img.width * img.height * img.channels * 4
185	  if img.data.length() != expected_size {
186	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
187	      "convolve_f: 数据大小不匹配，期望 " +
188	      expected_size.to_string() +	<-- UNCOVERED
189	      "，实际 " +
190	      img.data.length().to_string(),	<-- UNCOVERED
191	    )
192	  }
193	  if kernel.length() != 9 {
194	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
195	      "convolve_f: kernel必须为9个值（3x3）",
196	    )
197	  }
198	  if scale == 0.0F {
199	    raise @types.LoadError::DecodeFailed("convolve_f: scale不能为0")	<-- UNCOVERED
200	  }
201	
202	  let img8 = imgf_to_img8_cb(img)
203	  let result8 = convolve(img8, kernel, bias, scale)
204	  img8_to_imgf_cb(result8)
205	}
   	…

211	pub fn bilateral_filter_16(
212	  img : @types.Image16,
213	  radius : Int,
214	  sigma_space : Float,
215	  sigma_color : Float,
216	) -> @types.Image16 raise @types.LoadError {
217	  // 输入验证
218	  if img.width <= 0 || img.height <= 0 {
219	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
220	      "bilateral_filter_16: 图像尺寸必须大于0",
221	    )
222	  }
223	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
224	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
225	      "bilateral_filter_16: 仅支持 channels=1/3/4",
226	    )
227	  }
228	  let expected_size = img.width * img.height * img.channels * 2
229	  if img.data.length() != expected_size {
230	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
231	      "bilateral_filter_16: 数据大小不匹配，期望 " +
232	      expected_size.to_string() +	<-- UNCOVERED
233	      "，实际 " +
234	      img.data.length().to_string(),	<-- UNCOVERED
235	    )
236	  }
237	  if radius < 1 {
238	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
239	      "bilateral_filter_16: radius必须 >= 1",
240	    )
241	  }
242	  if sigma_space <= 0.0F {
243	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
244	      "bilateral_filter_16: sigma_space必须大于0",
245	    )
246	  }
247	  if sigma_color <= 0.0F {
248	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
249	      "bilateral_filter_16: sigma_color必须大于0",
250	    )
251	  }
252	
253	  let img8 = img16_to_img8_cb(img)
254	  let result8 = bilateral_filter(img8, radius, sigma_space, sigma_color)
255	  img8_to_img16_cb(result8)
256	}
   	…

262	pub fn bilateral_filter_f(
263	  img : @types.ImageF,
264	  radius : Int,
265	  sigma_space : Float,
266	  sigma_color : Float,
267	) -> @types.ImageF raise @types.LoadError {
268	  // 输入验证
269	  if img.width <= 0 || img.height <= 0 {
270	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
271	      "bilateral_filter_f: 图像尺寸必须大于0",
272	    )
273	  }
274	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
275	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
276	      "bilateral_filter_f: 仅支持 channels=1/3/4",
277	    )
278	  }
279	  let expected_size = img.width * img.height * img.channels * 4
280	  if img.data.length() != expected_size {
281	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
282	      "bilateral_filter_f: 数据大小不匹配，期望 " +
283	      expected_size.to_string() +	<-- UNCOVERED
284	      "，实际 " +
285	      img.data.length().to_string(),	<-- UNCOVERED
286	    )
287	  }
288	  if radius < 1 {
289	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
290	      "bilateral_filter_f: radius必须 >= 1",
291	    )
292	  }
293	  if sigma_space <= 0.0F {
294	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
295	      "bilateral_filter_f: sigma_space必须大于0",
296	    )
297	  }
298	  if sigma_color <= 0.0F {
299	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
300	      "bilateral_filter_f: sigma_color必须大于0",
301	    )
302	  }
303	
304	  let img8 = imgf_to_img8_cb(img)
305	  let result8 = bilateral_filter(img8, radius, sigma_space, sigma_color)
306	  img8_to_imgf_cb(result8)
307	}
   	…

313	pub fn bilateral_filter_fast_16(
314	  img : @types.Image16,
315	  radius : Int,
316	  sigma_space : Float,
317	  sigma_color : Float,
318	  downsample : Int,
319	) -> @types.Image16 raise @types.LoadError {
320	  // 输入验证
321	  if img.width <= 0 || img.height <= 0 {
322	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
323	      "bilateral_filter_fast_16: 图像尺寸必须大于0",
324	    )
325	  }
326	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
327	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
328	      "bilateral_filter_fast_16: 仅支持 channels=1/3/4",
329	    )
330	  }
331	  let expected_size = img.width * img.height * img.channels * 2
332	  if img.data.length() != expected_size {
333	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
334	      "bilateral_filter_fast_16: 数据大小不匹配，期望 " +
335	      expected_size.to_string() +	<-- UNCOVERED
336	      "，实际 " +
337	      img.data.length().to_string(),	<-- UNCOVERED
338	    )
339	  }
340	  if radius < 1 {
341	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
342	      "bilateral_filter_fast_16: radius必须 >= 1",
343	    )
344	  }
345	  if sigma_space <= 0.0F {
346	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
347	      "bilateral_filter_fast_16: sigma_space必须大于0",
348	    )
349	  }
350	  if sigma_color <= 0.0F {
351	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
352	      "bilateral_filter_fast_16: sigma_color必须大于0",
353	    )
354	  }
355	  if downsample < 1 {
356	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
357	      "bilateral_filter_fast_16: downsample必须 >= 1",
358	    )
359	  }
360	
361	  let img8 = img16_to_img8_cb(img)
362	  let result8 = bilateral_filter_fast(
363	    img8, radius, sigma_space, sigma_color, downsample,
364	  )
365	  img8_to_img16_cb(result8)
366	}
   	…

372	pub fn bilateral_filter_fast_f(
373	  img : @types.ImageF,
374	  radius : Int,
375	  sigma_space : Float,
376	  sigma_color : Float,
377	  downsample : Int,
378	) -> @types.ImageF raise @types.LoadError {
379	  // 输入验证
380	  if img.width <= 0 || img.height <= 0 {
381	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
382	      "bilateral_filter_fast_f: 图像尺寸必须大于0",
383	    )
384	  }
385	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
386	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
387	      "bilateral_filter_fast_f: 仅支持 channels=1/3/4",
388	    )
389	  }
390	  let expected_size = img.width * img.height * img.channels * 4
391	  if img.data.length() != expected_size {
392	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
393	      "bilateral_filter_fast_f: 数据大小不匹配，期望 " +
394	      expected_size.to_string() +	<-- UNCOVERED
395	      "，实际 " +
396	      img.data.length().to_string(),	<-- UNCOVERED
397	    )
398	  }
399	  if radius < 1 {
400	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
401	      "bilateral_filter_fast_f: radius必须 >= 1",
402	    )
403	  }
404	  if sigma_space <= 0.0F {
405	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
406	      "bilateral_filter_fast_f: sigma_space必须大于0",
407	    )
408	  }
409	  if sigma_color <= 0.0F {
410	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
411	      "bilateral_filter_fast_f: sigma_color必须大于0",
412	    )
413	  }
414	  if downsample < 1 {
415	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
416	      "bilateral_filter_fast_f: downsample必须 >= 1",
417	    )
418	  }
419	
420	  let img8 = imgf_to_img8_cb(img)
421	  let result8 = bilateral_filter_fast(
422	    img8, radius, sigma_space, sigma_color, downsample,
423	  )
424	  img8_to_imgf_cb(result8)
425	}
   	…

37 uncovered line(s) in src\process\filter\filter.mbt:

16	fn clamp_b(v : Int) -> Byte {
17	  if v < 0 {
18	    b'\x00'	<-- UNCOVERED
19	  } else if v > 255 {
20	    b'\xFF'	<-- UNCOVERED
21	  } else {
22	    v.to_byte()
23	  }
24	}
  	…

 40	pub fn box_blur(
 41	  img : @types.Image,
 42	  radius : Int,
 43	) -> @types.Image raise @types.LoadError {
 44	  // 输入验证
 45	  if img.width <= 0 || img.height <= 0 {
 46	    raise @types.LoadError::DecodeFailed("box_blur: 图像尺寸必须大于0")	<-- UNCOVERED
 47	  }
 48	  if img.channels < 1 || img.channels > 4 {
 49	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 50	      "box_blur: 通道数必须在1-4之间",
 51	    )
 52	  }
 53	  let expected_size = img.width * img.height * img.channels
 54	  if img.data.length() != expected_size {
 55	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 56	      "box_blur: 数据大小不匹配，期望 " +
 57	      expected_size.to_string() +	<-- UNCOVERED
 58	      "，实际 " +
 59	      img.data.length().to_string(),	<-- UNCOVERED
 60	    )
 61	  }
 62	  if radius < 0 {
 63	    raise @types.LoadError::DecodeFailed("box_blur: radius不能为负")	<-- UNCOVERED
 64	  }
 65	  if radius <= 0 {
 66	    return img
 67	  }
 68	  let w = img.width
 69	  let h = img.height
 70	  let ch = img.channels
 71	  let color_ch = if ch >= 3 { 3 } else { ch }
 72	  let n = w * h
 73	  let win_size = 2 * radius + 1
 74	  // 水平模糊：img -> temp
 75	  let temp : Array[Int] = Array::make(n * color_ch, 0)
 76	  for y in 0..<h {
 77	    for c in 0..<color_ch {
 78	      let mut sum = 0
 79	      for dx in 0..<win_size {
 80	        let x = clamp_coord(dx - radius, w)
 81	        sum += img.data[(y * w + x) * ch + c].to_int()
 82	      }
 83	      for x in 0..<w {
 84	        temp[(y * w + x) * color_ch + c] = sum / win_size
 85	        let x_out = clamp_coord(x - radius, w)
 86	        let x_in = clamp_coord(x + radius + 1, w)
 87	        sum = sum -
 88	          img.data[(y * w + x_out) * ch + c].to_int() +
 89	          img.data[(y * w + x_in) * ch + c].to_int()
 90	      }
 91	    }
 92	  }
 93	  // 垂直模糊：temp -> out
 94	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 95	  for x in 0..<w {
 96	    for c in 0..<color_ch {
 97	      let mut sum = 0
 98	      for dy in 0..<win_size {
 99	        let y = clamp_coord(dy - radius, h)
100	        sum += temp[(y * w + x) * color_ch + c]
101	      }
102	      for y in 0..<h {
103	        out[(y * w + x) * ch + c] = (sum / win_size).to_byte()
104	        let y_out = clamp_coord(y - radius, h)
105	        let y_in = clamp_coord(y + radius + 1, h)
106	        sum = sum -
107	          temp[(y_out * w + x) * color_ch + c] +
108	          temp[(y_in * w + x) * color_ch + c]
109	      }
110	    }
111	  }
112	  // 复制 alpha
113	  if ch == 4 {
114	    for i in 0..<n {
115	      out[i * ch + 3] = img.data[i * ch + 3]
116	    }
117	  }
118	  @types.Image::{
119	    width: w,
120	    height: h,
121	    channels: ch,
122	    data: Bytes::from_array(out),
123	  }
124	}
   	…

129	pub fn gaussian_blur(
130	  img : @types.Image,
131	  radius : Int,
132	  sigma : Float,
133	) -> @types.Image raise @types.LoadError {
134	  // 输入验证
135	  if img.width <= 0 || img.height <= 0 {
136	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
137	      "gaussian_blur: 图像尺寸必须大于0",
138	    )
139	  }
140	  if img.channels < 1 || img.channels > 4 {
141	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
142	      "gaussian_blur: 通道数必须在1-4之间",
143	    )
144	  }
145	  let expected_size = img.width * img.height * img.channels
146	  if img.data.length() != expected_size {
147	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
148	      "gaussian_blur: 数据大小不匹配，期望 " +
149	      expected_size.to_string() +	<-- UNCOVERED
150	      "，实际 " +
151	      img.data.length().to_string(),	<-- UNCOVERED
152	    )
153	  }
154	  if radius < 0 {
155	    raise @types.LoadError::DecodeFailed("gaussian_blur: radius不能为负")	<-- UNCOVERED
156	  }
157	  if sigma <= 0.0F {
158	    raise @types.LoadError::DecodeFailed("gaussian_blur: sigma必须大于0")	<-- UNCOVERED
159	  }
160	  if radius <= 0 {
161	    return img
162	  }
163	  let w = img.width
164	  let h = img.height
165	  let ch = img.channels
166	  let color_ch = if ch >= 3 { 3 } else { ch }
167	  let n = w * h
168	  let win_size = 2 * radius + 1
169	  // 生成 1D 高斯核
170	  let kernel : Array[Float] = Array::make(win_size, 0.0F)
171	  let two_sigma_sq = 2.0F * sigma * sigma
172	  let mut ksum = 0.0F
173	  for i in 0..<win_size {
174	    let x = Float::from_int(i - radius)
175	    let val = @math.expf(-(x * x) / two_sigma_sq)
176	    kernel[i] = val
177	    ksum += val
178	  }
179	  // 归一化
180	  for i in 0..<win_size {
181	    kernel[i] = kernel[i] / ksum
182	  }
183	  // 预计算像素 Float 数组，避免内层循环重复类型转换
184	  let src : Array[Float] = Array::make(n * color_ch, 0.0F)
185	  for i in 0..<n {
186	    for c in 0..<color_ch {
187	      src[i * color_ch + c] = Float::from_int(img.data[i * ch + c].to_int())
188	    }
189	  }
190	  // 水平模糊：src -> temp (Float)，分离内部/边界区域
191	  let temp : Array[Float] = Array::make(n * color_ch, 0.0F)
192	  let x_inner_start = if radius >= w { w } else { radius }
193	  let x_inner_end = if radius >= w { w } else { w - radius }
194	  for y in 0..<h {
195	    let row_base = y * w
196	    for c in 0..<color_ch {
197	      // 内部区域：x >= radius && x < w - radius，无需 clamp_coord
198	      for x in x_inner_start..<x_inner_end {
199	        let mut acc = 0.0F
200	        let base = (row_base + x - radius) * color_ch + c
201	        for k in 0..<win_size {
202	          acc += kernel[k] * src[base + k * color_ch]
203	        }
204	        temp[(row_base + x) * color_ch + c] = acc
205	      }
206	      // 左边界：x < radius
207	      for x in 0..<x_inner_start {
208	        let mut acc = 0.0F
209	        for k in 0..<win_size {
210	          let sx = clamp_coord(x + k - radius, w)
211	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
212	        }
213	        temp[(row_base + x) * color_ch + c] = acc
214	      }
215	      // 右边界：x >= w - radius
216	      for x in x_inner_end..<w {
217	        let mut acc = 0.0F
218	        for k in 0..<win_size {
219	          let sx = clamp_coord(x + k - radius, w)
220	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
221	        }
222	        temp[(row_base + x) * color_ch + c] = acc
223	      }
224	    }
225	  }
226	  // 垂直模糊：temp -> out，分离内部/边界区域
227	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
228	  let y_inner_start = if radius >= h { h } else { radius }
229	  let y_inner_end = if radius >= h { h } else { h - radius }
230	  for x in 0..<w {
231	    for c in 0..<color_ch {
232	      // 内部区域：y >= radius && y < h - radius
233	      for y in y_inner_start..<y_inner_end {
234	        let mut acc = 0.0F
235	        let base = ((y - radius) * w + x) * color_ch + c
236	        for k in 0..<win_size {
237	          acc += kernel[k] * temp[base + k * w * color_ch]
238	        }
239	        out[(y * w + x) * ch + c] = (acc + 0.5F)
240	          .to_int()
241	          .clamp(min=0, max=255)
242	          .to_byte()
243	      }
244	      // 上边界：y < radius
245	      for y in 0..<y_inner_start {
246	        let mut acc = 0.0F
247	        for k in 0..<win_size {
248	          let sy = clamp_coord(y + k - radius, h)
249	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
250	        }
251	        out[(y * w + x) * ch + c] = (acc + 0.5F)
252	          .to_int()
253	          .clamp(min=0, max=255)
254	          .to_byte()
255	      }
256	      // 下边界：y >= h - radius
257	      for y in y_inner_end..<h {
258	        let mut acc = 0.0F
259	        for k in 0..<win_size {
260	          let sy = clamp_coord(y + k - radius, h)
261	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
262	        }
263	        out[(y * w + x) * ch + c] = (acc + 0.5F)
264	          .to_int()
265	          .clamp(min=0, max=255)
266	          .to_byte()
267	      }
268	    }
269	  }
270	  // 复制 alpha
271	  if ch == 4 {
272	    for i in 0..<n {	<-- UNCOVERED
273	      out[i * ch + 3] = img.data[i * ch + 3]
274	    }
275	  }
276	  @types.Image::{
277	    width: w,
278	    height: h,
279	    channels: ch,
280	    data: Bytes::from_array(out),
281	  }
282	}
   	…

287	pub fn sharpen(
288	  img : @types.Image,
289	  amount : Float,
290	) -> @types.Image raise @types.LoadError {
291	  // 输入验证
292	  if img.width <= 0 || img.height <= 0 {
293	    raise @types.LoadError::DecodeFailed("sharpen: 图像尺寸必须大于0")	<-- UNCOVERED
294	  }
295	  if img.channels < 1 || img.channels > 4 {
296	    raise @types.LoadError::DecodeFailed("sharpen: 通道数必须在1-4之间")	<-- UNCOVERED
297	  }
298	  let expected_size = img.width * img.height * img.channels
299	  if img.data.length() != expected_size {
300	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
301	      "sharpen: 数据大小不匹配，期望 " +
302	      expected_size.to_string() +	<-- UNCOVERED
303	      "，实际 " +
304	      img.data.length().to_string(),	<-- UNCOVERED
305	    )
306	  }
307	  if amount < 0.0F {
308	    raise @types.LoadError::DecodeFailed("sharpen: amount不能为负")	<-- UNCOVERED
309	  }
310	
311	  let w = img.width
312	  let h = img.height
313	  let ch = img.channels
314	  let color_ch = if ch >= 3 { 3 } else { ch }
315	  let n = w * h
316	  let center = 1.0F + 4.0F * amount
317	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
318	  // 预计算像素 Int 数组
319	  let px : Array[Int] = Array::make(n * ch, 0)
320	  for i in 0..<(n * ch) {
321	    px[i] = img.data[i].to_int()
322	  }
323	  // 内部区域：1 <= x < w-1 && 1 <= y < h-1，无需 clamp_coord
324	  for y in 1..<(h - 1) {
325	    for x in 1..<(w - 1) {
326	      let base = (y * w + x) * ch
327	      let base_l = base - ch
328	      let base_r = base + ch
329	      let base_t = base - w * ch
330	      let base_b = base + w * ch
331	      for c in 0..<color_ch {
332	        let cx = px[base + c]
333	        let left = px[base_l + c]
334	        let right = px[base_r + c]
335	        let top = px[base_t + c]
336	        let bottom = px[base_b + c]
337	        let val = Float::from_int(cx) * center -
338	          amount * Float::from_int(left + right + top + bottom)
339	        out[base + c] = (val + 0.5F).to_int().clamp(min=0, max=255).to_byte()
340	      }
341	    }
342	  }
343	  // 边界区域：x==0 || x==w-1 || y==0 || y==h-1
344	  for y in 0..<h {
345	    for x in 0..<w {
346	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
347	        continue
348	      }
349	      let base = (y * w + x) * ch
350	      for c in 0..<color_ch {
351	        let cx = px[base + c]
352	        let x_l = clamp_coord(x - 1, w)
353	        let x_r = clamp_coord(x + 1, w)
354	        let y_t = clamp_coord(y - 1, h)
355	        let y_b = clamp_coord(y + 1, h)
356	        let left = px[(y * w + x_l) * ch + c]
357	        let right = px[(y * w + x_r) * ch + c]
358	        let top = px[(y_t * w + x) * ch + c]
359	        let bottom = px[(y_b * w + x) * ch + c]
360	        let val = Float::from_int(cx) * center -
361	          amount * Float::from_int(left + right + top + bottom)
362	        out[base + c] = (val + 0.5F).to_int().clamp(min=0, max=255).to_byte()
363	      }
364	    }
365	  }
366	  // 复制 alpha
367	  if ch == 4 {
368	    for i in 0..<n {
369	      out[i * ch + 3] = img.data[i * ch + 3]
370	    }
371	  }
372	  @types.Image::{
373	    width: w,
374	    height: h,
375	    channels: ch,
376	    data: Bytes::from_array(out),
377	  }
378	}
   	…

383	pub fn median_blur(
384	  img : @types.Image,
385	  ksize : Int,
386	) -> @types.Image raise @types.LoadError {
387	  // 输入验证
388	  if img.width <= 0 || img.height <= 0 {
389	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
390	      "median_blur: 图像尺寸必须大于0",
391	    )
392	  }
393	  if img.channels < 1 || img.channels > 4 {
394	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
395	      "median_blur: 通道数必须在1-4之间",
396	    )
397	  }
398	  let expected_size = img.width * img.height * img.channels
399	  if img.data.length() != expected_size {
400	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
401	      "median_blur: 数据大小不匹配，期望 " +
402	      expected_size.to_string() +	<-- UNCOVERED
403	      "，实际 " +
404	      img.data.length().to_string(),	<-- UNCOVERED
405	    )
406	  }
407	  if ksize <= 0 {
408	    raise @types.LoadError::DecodeFailed("median_blur: ksize必须大于0")	<-- UNCOVERED
409	  }
410	  if ksize % 2 == 0 {
411	    raise @types.LoadError::DecodeFailed("median_blur: ksize必须为奇数")	<-- UNCOVERED
412	  }
413	  if ksize <= 1 {
414	    return img
415	  }
416	  let radius = ksize / 2
417	  let w = img.width
418	  let h = img.height
419	  let ch = img.channels
420	  let color_ch = if ch >= 3 { 3 } else { ch }
421	  let n = w * h
422	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
423	  let buf : Array[Int] = Array::make(ksize * ksize, 0)
424	  for y in 0..<h {
425	    for x in 0..<w {
426	      for c in 0..<color_ch {
427	        let mut idx = 0
428	        for dy in -radius..<=radius {
429	          for dx in -radius..<=radius {
430	            let sx = clamp_coord(x + dx, w)
431	            let sy = clamp_coord(y + dy, h)
432	            buf[idx] = img.data[(sy * w + sx) * ch + c].to_int()
433	            idx += 1
434	          }
435	        }
436	        // 部分排序找中值（不需要完整排序）
437	        let mid = ksize * ksize / 2
438	        let mut lo = 0
439	        let mut hi = ksize * ksize - 1
440	        while lo < hi {
441	          let pivot = buf[(lo + hi) / 2]
442	          let mut i = lo
443	          let mut j = hi
444	          while i <= j {
445	            while buf[i] < pivot {
446	              i += 1
447	            }
448	            while buf[j] > pivot {
449	              j -= 1
450	            }
451	            if i <= j {
452	              let tmp = buf[i]
453	              buf[i] = buf[j]
454	              buf[j] = tmp
455	              i += 1
456	              j -= 1
457	            }
458	          }
459	          if mid <= j {
460	            hi = j
461	          } else if mid >= i {
462	            lo = i
463	          } else {
464	            lo = mid
465	            hi = mid
466	          }
467	        }
468	        out[(y * w + x) * ch + c] = buf[mid].to_byte()
469	      }
470	    }
471	  }
472	  // 复制 alpha
473	  if ch == 4 {
474	    for i in 0..<n {
475	      out[i * ch + 3] = img.data[i * ch + 3]
476	    }
477	  }
478	  @types.Image::{
479	    width: w,
480	    height: h,
481	    channels: ch,
482	    data: Bytes::from_array(out),
483	  }
484	}
   	…

489	pub fn convolve(
490	  img : @types.Image,
491	  kernel : Array[Float],
492	  bias : Float,
493	  scale : Float,
494	) -> @types.Image raise @types.LoadError {
495	  // 输入验证
496	  if img.width <= 0 || img.height <= 0 {
497	    raise @types.LoadError::DecodeFailed("convolve: 图像尺寸必须大于0")	<-- UNCOVERED
498	  }
499	  if img.channels < 1 || img.channels > 4 {
500	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
501	      "convolve: 通道数必须在1-4之间",
502	    )
503	  }
504	  let expected_size = img.width * img.height * img.channels
505	  if img.data.length() != expected_size {
506	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
507	      "convolve: 数据大小不匹配，期望 " +
508	      expected_size.to_string() +	<-- UNCOVERED
509	      "，实际 " +
510	      img.data.length().to_string(),	<-- UNCOVERED
511	    )
512	  }
513	  if kernel.length() != 9 {
514	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
515	      "convolve: kernel 必须为 9 个值（3x3）",
516	    )
517	  }
518	  if scale == 0.0F {
519	    raise @types.LoadError::DecodeFailed("convolve: scale不能为0")	<-- UNCOVERED
520	  }
521	  let ch = img.channels
522	  let w = img.width
523	  let h = img.height
524	  let color_ch = if ch >= 3 { 3 } else { ch }
525	  let out : Array[Byte] = Array::make(w * h * ch, b'\x00')
526	  for y in 0..<h {
527	    for x in 0..<w {
528	      for c in 0..<color_ch {
529	        let mut acc = 0.0F
530	        for ky in 0..<3 {
531	          for kx in 0..<3 {
532	            let sx = clamp_int(x + kx - 1, 0, w - 1)
533	            let sy = clamp_int(y + ky - 1, 0, h - 1)
534	            let v = Float::from_int(img.data[(sy * w + sx) * ch + c].to_int())
535	            acc += kernel[ky * 3 + kx] * v
536	          }
537	        }
538	        let result = (acc * scale + bias).to_int()
539	        out[(y * w + x) * ch + c] = clamp_b(result)
540	      }
541	      if ch == 4 {
542	        out[(y * w + x) * ch + 3] = img.data[(y * w + x) * ch + 3]	<-- UNCOVERED
543	      }
544	    }
545	  }
546	  @types.Image::{
547	    width: w,
548	    height: h,
549	    channels: ch,
550	    data: Bytes::from_array(out),
551	  }
552	}
   	…

63 uncovered line(s) in src\process\filter\filter_16f.mbt:

 48	pub fn box_blur_16(
 49	  img : @types.Image16,
 50	  radius : Int,
 51	) -> @types.Image16 raise @types.LoadError {
 52	  // 输入验证
 53	  if img.width <= 0 || img.height <= 0 {
 54	    raise @types.LoadError::DecodeFailed(
 55	      "box_blur_16: 图像尺寸必须大于0",
 56	    )
 57	  }
 58	  if img.channels < 1 || img.channels > 4 {
 59	    raise @types.LoadError::DecodeFailed(
 60	      "box_blur_16: 通道数必须在1-4之间",
 61	    )
 62	  }
 63	  let expected_size = img.width * img.height * img.channels * 2
 64	  if img.data.length() != expected_size {
 65	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 66	      "box_blur_16: 数据大小不匹配，期望 " +
 67	      expected_size.to_string() +	<-- UNCOVERED
 68	      "，实际 " +
 69	      img.data.length().to_string(),	<-- UNCOVERED
 70	    )
 71	  }
 72	  if radius < 0 {
 73	    raise @types.LoadError::DecodeFailed("box_blur_16: radius不能为负")	<-- UNCOVERED
 74	  }
 75	  if radius <= 0 {
 76	    return img
 77	  }
 78	  let w = img.width
 79	  let h = img.height
 80	  let ch = img.channels
 81	  let color_ch = if ch >= 3 { 3 } else { ch }
 82	  let n = w * h
 83	  let win_size = 2 * radius + 1
 84	  let bpp = ch * 2
 85	  // 水平模糊：img -> temp
 86	  let temp : Array[Int] = Array::make(n * color_ch, 0)
 87	  for y in 0..<h {
 88	    for c in 0..<color_ch {
 89	      let mut sum = 0
 90	      for dx in 0..<win_size {
 91	        let x = clamp_coord_f(dx - radius, w)
 92	        sum += read_u16_le_f(img.data, (y * w + x) * bpp + c * 2)
 93	      }
 94	      for x in 0..<w {
 95	        temp[(y * w + x) * color_ch + c] = sum / win_size
 96	        let x_out = clamp_coord_f(x - radius, w)
 97	        let x_in = clamp_coord_f(x + radius + 1, w)
 98	        sum = sum -
 99	          read_u16_le_f(img.data, (y * w + x_out) * bpp + c * 2) +
100	          read_u16_le_f(img.data, (y * w + x_in) * bpp + c * 2)
101	      }
102	    }
103	  }
104	  // 垂直模糊：temp -> out
105	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
106	  for x in 0..<w {
107	    for c in 0..<color_ch {
108	      let mut sum = 0
109	      for dy in 0..<win_size {
110	        let y = clamp_coord_f(dy - radius, h)
111	        sum += temp[(y * w + x) * color_ch + c]
112	      }
113	      for y in 0..<h {
114	        write_u16_le_f(out, (y * w + x) * bpp + c * 2, sum / win_size)
115	        let y_out = clamp_coord_f(y - radius, h)
116	        let y_in = clamp_coord_f(y + radius + 1, h)
117	        sum = sum -
118	          temp[(y_out * w + x) * color_ch + c] +
119	          temp[(y_in * w + x) * color_ch + c]
120	      }
121	    }
122	  }
123	  // 复制 alpha
124	  if ch == 4 {
125	    for i in 0..<n {	<-- UNCOVERED
126	      let alpha_off = i * bpp + 6
127	      out[alpha_off] = img.data[alpha_off]
128	      out[alpha_off + 1] = img.data[alpha_off + 1]
129	    }
130	  }
131	  @types.Image16::{
132	    width: w,
133	    height: h,
134	    channels: ch,
135	    data: Bytes::from_array(out),
136	  }
137	}
   	…

142	pub fn box_blur_f(
143	  img : @types.ImageF,
144	  radius : Int,
145	) -> @types.ImageF raise @types.LoadError {
146	  // 输入验证
147	  if img.width <= 0 || img.height <= 0 {
148	    raise @types.LoadError::DecodeFailed(
149	      "box_blur_f: 图像尺寸必须大于0",
150	    )
151	  }
152	  if img.channels < 1 || img.channels > 4 {
153	    raise @types.LoadError::DecodeFailed(
154	      "box_blur_f: 通道数必须在1-4之间",
155	    )
156	  }
157	  let expected_size = img.width * img.height * img.channels * 4
158	  if img.data.length() != expected_size {
159	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
160	      "box_blur_f: 数据大小不匹配，期望 " +
161	      expected_size.to_string() +	<-- UNCOVERED
162	      "，实际 " +
163	      img.data.length().to_string(),	<-- UNCOVERED
164	    )
165	  }
166	  if radius < 0 {
167	    raise @types.LoadError::DecodeFailed("box_blur_f: radius不能为负")	<-- UNCOVERED
168	  }
169	  if radius <= 0 {
170	    return img
171	  }
172	  let w = img.width
173	  let h = img.height
174	  let ch = img.channels
175	  let color_ch = if ch >= 3 { 3 } else { ch }
176	  let n = w * h
177	  let win_size = 2 * radius + 1
178	  let bpp = ch * 4
179	  let win_size_f = Float::from_int(win_size)
180	  // 水平模糊：img -> temp
181	  let temp : Array[Float] = Array::make(n * color_ch, 0.0F)
182	  for y in 0..<h {
183	    for c in 0..<color_ch {
184	      let mut sum = 0.0F
185	      for dx in 0..<win_size {
186	        let x = clamp_coord_f(dx - radius, w)
187	        sum += read_f32_le_f(img.data, (y * w + x) * bpp + c * 4)
188	      }
189	      for x in 0..<w {
190	        temp[(y * w + x) * color_ch + c] = sum / win_size_f
191	        let x_out = clamp_coord_f(x - radius, w)
192	        let x_in = clamp_coord_f(x + radius + 1, w)
193	        sum = sum -
194	          read_f32_le_f(img.data, (y * w + x_out) * bpp + c * 4) +
195	          read_f32_le_f(img.data, (y * w + x_in) * bpp + c * 4)
196	      }
197	    }
198	  }
199	  // 垂直模糊：temp -> out
200	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
201	  for x in 0..<w {
202	    for c in 0..<color_ch {
203	      let mut sum = 0.0F
204	      for dy in 0..<win_size {
205	        let y = clamp_coord_f(dy - radius, h)
206	        sum += temp[(y * w + x) * color_ch + c]
207	      }
208	      for y in 0..<h {
209	        write_f32_le_f(out, (y * w + x) * bpp + c * 4, sum / win_size_f)
210	        let y_out = clamp_coord_f(y - radius, h)
211	        let y_in = clamp_coord_f(y + radius + 1, h)
212	        sum = sum -
213	          temp[(y_out * w + x) * color_ch + c] +
214	          temp[(y_in * w + x) * color_ch + c]
215	      }
216	    }
217	  }
218	  // 复制 alpha
219	  if ch == 4 {
220	    for i in 0..<n {	<-- UNCOVERED
221	      let alpha_off = i * bpp + 12
222	      for b in 0..<4 {
223	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
224	      }
225	    }
226	  }
227	  @types.ImageF::{
228	    width: w,
229	    height: h,
230	    channels: ch,
231	    data: Bytes::from_array(out),
232	  }
233	}
   	…

238	pub fn gaussian_blur_16(
239	  img : @types.Image16,
240	  radius : Int,
241	  sigma : Float,
242	) -> @types.Image16 raise @types.LoadError {
243	  // 输入验证
244	  if img.width <= 0 || img.height <= 0 {
245	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
246	      "gaussian_blur_16: 图像尺寸必须大于0",
247	    )
248	  }
249	  if img.channels < 1 || img.channels > 4 {
250	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
251	      "gaussian_blur_16: 通道数必须在1-4之间",
252	    )
253	  }
254	  let expected_size = img.width * img.height * img.channels * 2
255	  if img.data.length() != expected_size {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "gaussian_blur_16: 数据大小不匹配，期望 " +
258	      expected_size.to_string() +	<-- UNCOVERED
259	      "，实际 " +
260	      img.data.length().to_string(),	<-- UNCOVERED
261	    )
262	  }
263	  if radius < 0 {
264	    raise @types.LoadError::DecodeFailed("gaussian_blur_16: radius不能为负")	<-- UNCOVERED
265	  }
266	  if sigma <= 0.0F {
267	    raise @types.LoadError::DecodeFailed("gaussian_blur_16: sigma必须大于0")	<-- UNCOVERED
268	  }
269	  if radius <= 0 {
270	    return img
271	  }
272	  let w = img.width
273	  let h = img.height
274	  let ch = img.channels
275	  let color_ch = if ch >= 3 { 3 } else { ch }
276	  let n = w * h
277	  let win_size = 2 * radius + 1
278	  let bpp = ch * 2
279	  // 生成 1D 高斯核
280	  let kernel : Array[Float] = Array::make(win_size, 0.0F)
281	  let two_sigma_sq = 2.0F * sigma * sigma
282	  let mut ksum = 0.0F
283	  for i in 0..<win_size {
284	    let x = Float::from_int(i - radius)
285	    let val = @math.expf(-(x * x) / two_sigma_sq)
286	    kernel[i] = val
287	    ksum += val
288	  }
289	  for i in 0..<win_size {
290	    kernel[i] = kernel[i] / ksum
291	  }
292	  // 预计算像素 Float 数组
293	  let src : Array[Float] = Array::make(n * color_ch, 0.0F)
294	  for i in 0..<n {
295	    for c in 0..<color_ch {
296	      src[i * color_ch + c] = Float::from_int(
297	        read_u16_le_f(img.data, i * bpp + c * 2),
298	      )
299	    }
300	  }
301	  // 水平模糊：src -> temp (Float)
302	  let temp : Array[Float] = Array::make(n * color_ch, 0.0F)
303	  let x_inner_start = if radius >= w { w } else { radius }
304	  let x_inner_end = if radius >= w { w } else { w - radius }
305	  for y in 0..<h {
306	    let row_base = y * w
307	    for c in 0..<color_ch {
308	      for x in x_inner_start..<x_inner_end {
309	        let mut acc = 0.0F
310	        let base = (row_base + x - radius) * color_ch + c
311	        for k in 0..<win_size {
312	          acc += kernel[k] * src[base + k * color_ch]
313	        }
314	        temp[(row_base + x) * color_ch + c] = acc
315	      }
316	      for x in 0..<x_inner_start {
317	        let mut acc = 0.0F
318	        for k in 0..<win_size {
319	          let sx = clamp_coord_f(x + k - radius, w)
320	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
321	        }
322	        temp[(row_base + x) * color_ch + c] = acc
323	      }
324	      for x in x_inner_end..<w {
325	        let mut acc = 0.0F
326	        for k in 0..<win_size {
327	          let sx = clamp_coord_f(x + k - radius, w)
328	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
329	        }
330	        temp[(row_base + x) * color_ch + c] = acc
331	      }
332	    }
333	  }
334	  // 垂直模糊：temp -> out
335	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
336	  let y_inner_start = if radius >= h { h } else { radius }
337	  let y_inner_end = if radius >= h { h } else { h - radius }
338	  for x in 0..<w {
339	    for c in 0..<color_ch {
340	      for y in y_inner_start..<y_inner_end {
341	        let mut acc = 0.0F
342	        let base = ((y - radius) * w + x) * color_ch + c
343	        for k in 0..<win_size {
344	          acc += kernel[k] * temp[base + k * w * color_ch]
345	        }
346	        let v = (acc + 0.5F).to_int().clamp(min=0, max=65535)
347	        write_u16_le_f(out, (y * w + x) * bpp + c * 2, v)
348	      }
349	      for y in 0..<y_inner_start {
350	        let mut acc = 0.0F
351	        for k in 0..<win_size {
352	          let sy = clamp_coord_f(y + k - radius, h)
353	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
354	        }
355	        let v = (acc + 0.5F).to_int().clamp(min=0, max=65535)
356	        write_u16_le_f(out, (y * w + x) * bpp + c * 2, v)
357	      }
358	      for y in y_inner_end..<h {
359	        let mut acc = 0.0F
360	        for k in 0..<win_size {
361	          let sy = clamp_coord_f(y + k - radius, h)
362	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
363	        }
364	        let v = (acc + 0.5F).to_int().clamp(min=0, max=65535)
365	        write_u16_le_f(out, (y * w + x) * bpp + c * 2, v)
366	      }
367	    }
368	  }
369	  // 复制 alpha
370	  if ch == 4 {
371	    for i in 0..<n {	<-- UNCOVERED
372	      let alpha_off = i * bpp + 6
373	      out[alpha_off] = img.data[alpha_off]
374	      out[alpha_off + 1] = img.data[alpha_off + 1]
375	    }
376	  }
377	  @types.Image16::{
378	    width: w,
379	    height: h,
380	    channels: ch,
381	    data: Bytes::from_array(out),
382	  }
383	}
   	…

388	pub fn gaussian_blur_f(
389	  img : @types.ImageF,
390	  radius : Int,
391	  sigma : Float,
392	) -> @types.ImageF raise @types.LoadError {
393	  // 输入验证
394	  if img.width <= 0 || img.height <= 0 {
395	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
396	      "gaussian_blur_f: 图像尺寸必须大于0",
397	    )
398	  }
399	  if img.channels < 1 || img.channels > 4 {
400	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
401	      "gaussian_blur_f: 通道数必须在1-4之间",
402	    )
403	  }
404	  let expected_size = img.width * img.height * img.channels * 4
405	  if img.data.length() != expected_size {
406	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
407	      "gaussian_blur_f: 数据大小不匹配，期望 " +
408	      expected_size.to_string() +	<-- UNCOVERED
409	      "，实际 " +
410	      img.data.length().to_string(),	<-- UNCOVERED
411	    )
412	  }
413	  if radius < 0 {
414	    raise @types.LoadError::DecodeFailed("gaussian_blur_f: radius不能为负")	<-- UNCOVERED
415	  }
416	  if sigma <= 0.0F {
417	    raise @types.LoadError::DecodeFailed("gaussian_blur_f: sigma必须大于0")	<-- UNCOVERED
418	  }
419	  if radius <= 0 {
420	    return img
421	  }
422	  let w = img.width
423	  let h = img.height
424	  let ch = img.channels
425	  let color_ch = if ch >= 3 { 3 } else { ch }
426	  let n = w * h
427	  let win_size = 2 * radius + 1
428	  let bpp = ch * 4
429	  // 生成 1D 高斯核
430	  let kernel : Array[Float] = Array::make(win_size, 0.0F)
431	  let two_sigma_sq = 2.0F * sigma * sigma
432	  let mut ksum = 0.0F
433	  for i in 0..<win_size {
434	    let x = Float::from_int(i - radius)
435	    let val = @math.expf(-(x * x) / two_sigma_sq)
436	    kernel[i] = val
437	    ksum += val
438	  }
439	  for i in 0..<win_size {
440	    kernel[i] = kernel[i] / ksum
441	  }
442	  // 预计算像素 Float 数组
443	  let src : Array[Float] = Array::make(n * color_ch, 0.0F)
444	  for i in 0..<n {
445	    for c in 0..<color_ch {
446	      src[i * color_ch + c] = read_f32_le_f(img.data, i * bpp + c * 4)
447	    }
448	  }
449	  // 水平模糊：src -> temp (Float)
450	  let temp : Array[Float] = Array::make(n * color_ch, 0.0F)
451	  let x_inner_start = if radius >= w { w } else { radius }
452	  let x_inner_end = if radius >= w { w } else { w - radius }
453	  for y in 0..<h {
454	    let row_base = y * w
455	    for c in 0..<color_ch {
456	      for x in x_inner_start..<x_inner_end {
457	        let mut acc = 0.0F
458	        let base = (row_base + x - radius) * color_ch + c
459	        for k in 0..<win_size {
460	          acc += kernel[k] * src[base + k * color_ch]
461	        }
462	        temp[(row_base + x) * color_ch + c] = acc
463	      }
464	      for x in 0..<x_inner_start {
465	        let mut acc = 0.0F
466	        for k in 0..<win_size {
467	          let sx = clamp_coord_f(x + k - radius, w)
468	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
469	        }
470	        temp[(row_base + x) * color_ch + c] = acc
471	      }
472	      for x in x_inner_end..<w {
473	        let mut acc = 0.0F
474	        for k in 0..<win_size {
475	          let sx = clamp_coord_f(x + k - radius, w)
476	          acc += kernel[k] * src[(row_base + sx) * color_ch + c]
477	        }
478	        temp[(row_base + x) * color_ch + c] = acc
479	      }
480	    }
481	  }
482	  // 垂直模糊：temp -> out
483	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
484	  let y_inner_start = if radius >= h { h } else { radius }
485	  let y_inner_end = if radius >= h { h } else { h - radius }
486	  for x in 0..<w {
487	    for c in 0..<color_ch {
488	      for y in y_inner_start..<y_inner_end {
489	        let mut acc = 0.0F
490	        let base = ((y - radius) * w + x) * color_ch + c
491	        for k in 0..<win_size {
492	          acc += kernel[k] * temp[base + k * w * color_ch]
493	        }
494	        write_f32_le_f(out, (y * w + x) * bpp + c * 4, acc)
495	      }
496	      for y in 0..<y_inner_start {
497	        let mut acc = 0.0F
498	        for k in 0..<win_size {
499	          let sy = clamp_coord_f(y + k - radius, h)
500	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
501	        }
502	        write_f32_le_f(out, (y * w + x) * bpp + c * 4, acc)
503	      }
504	      for y in y_inner_end..<h {
505	        let mut acc = 0.0F
506	        for k in 0..<win_size {
507	          let sy = clamp_coord_f(y + k - radius, h)
508	          acc += kernel[k] * temp[(sy * w + x) * color_ch + c]
509	        }
510	        write_f32_le_f(out, (y * w + x) * bpp + c * 4, acc)
511	      }
512	    }
513	  }
514	  // 复制 alpha
515	  if ch == 4 {
516	    for i in 0..<n {	<-- UNCOVERED
517	      let alpha_off = i * bpp + 12
518	      for b in 0..<4 {
519	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
520	      }
521	    }
522	  }
523	  @types.ImageF::{
524	    width: w,
525	    height: h,
526	    channels: ch,
527	    data: Bytes::from_array(out),
528	  }
529	}
   	…

534	pub fn median_blur_16(
535	  img : @types.Image16,
536	  ksize : Int,
537	) -> @types.Image16 raise @types.LoadError {
538	  // 输入验证
539	  if img.width <= 0 || img.height <= 0 {
540	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
541	      "median_blur_16: 图像尺寸必须大于0",
542	    )
543	  }
544	  if img.channels < 1 || img.channels > 4 {
545	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
546	      "median_blur_16: 通道数必须在1-4之间",
547	    )
548	  }
549	  let expected_size = img.width * img.height * img.channels * 2
550	  if img.data.length() != expected_size {
551	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
552	      "median_blur_16: 数据大小不匹配，期望 " +
553	      expected_size.to_string() +	<-- UNCOVERED
554	      "，实际 " +
555	      img.data.length().to_string(),	<-- UNCOVERED
556	    )
557	  }
558	  if ksize <= 0 {
559	    raise @types.LoadError::DecodeFailed("median_blur_16: ksize必须大于0")	<-- UNCOVERED
560	  }
561	  if ksize % 2 == 0 {
562	    raise @types.LoadError::DecodeFailed("median_blur_16: ksize必须为奇数")	<-- UNCOVERED
563	  }
564	  if ksize <= 1 {
565	    return img
566	  }
567	  let radius = ksize / 2
568	  let w = img.width
569	  let h = img.height
570	  let ch = img.channels
571	  let color_ch = if ch >= 3 { 3 } else { ch }
572	  let n = w * h
573	  let bpp = ch * 2
574	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
575	  let buf : Array[Int] = Array::make(ksize * ksize, 0)
576	  for y in 0..<h {
577	    for x in 0..<w {
578	      for c in 0..<color_ch {
579	        let mut idx = 0
580	        for dy in -radius..<=radius {
581	          for dx in -radius..<=radius {
582	            let sx = clamp_coord_f(x + dx, w)
583	            let sy = clamp_coord_f(y + dy, h)
584	            buf[idx] = read_u16_le_f(img.data, (sy * w + sx) * bpp + c * 2)
585	            idx += 1
586	          }
587	        }
588	        let mid = ksize * ksize / 2
589	        let mut lo = 0
590	        let mut hi = ksize * ksize - 1
591	        while lo < hi {
592	          let pivot = buf[(lo + hi) / 2]
593	          let mut i = lo
594	          let mut j = hi
595	          while i <= j {
596	            while buf[i] < pivot {
597	              i += 1
598	            }
599	            while buf[j] > pivot {
600	              j -= 1
601	            }
602	            if i <= j {
603	              let tmp = buf[i]
604	              buf[i] = buf[j]
605	              buf[j] = tmp
606	              i += 1
607	              j -= 1
608	            }
609	          }
610	          if mid <= j {
611	            hi = j
612	          } else if mid >= i {
613	            lo = i
614	          } else {
615	            lo = mid
616	            hi = mid
617	          }
618	        }
619	        write_u16_le_f(out, (y * w + x) * bpp + c * 2, buf[mid])
620	      }
621	    }
622	  }
623	  // 复制 alpha
624	  if ch == 4 {
625	    for i in 0..<n {	<-- UNCOVERED
626	      let alpha_off = i * bpp + 6
627	      out[alpha_off] = img.data[alpha_off]
628	      out[alpha_off + 1] = img.data[alpha_off + 1]
629	    }
630	  }
631	  @types.Image16::{
632	    width: w,
633	    height: h,
634	    channels: ch,
635	    data: Bytes::from_array(out),
636	  }
637	}
   	…

642	pub fn median_blur_f(
643	  img : @types.ImageF,
644	  ksize : Int,
645	) -> @types.ImageF raise @types.LoadError {
646	  // 输入验证
647	  if img.width <= 0 || img.height <= 0 {
648	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
649	      "median_blur_f: 图像尺寸必须大于0",
650	    )
651	  }
652	  if img.channels < 1 || img.channels > 4 {
653	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
654	      "median_blur_f: 通道数必须在1-4之间",
655	    )
656	  }
657	  let expected_size = img.width * img.height * img.channels * 4
658	  if img.data.length() != expected_size {
659	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
660	      "median_blur_f: 数据大小不匹配，期望 " +
661	      expected_size.to_string() +	<-- UNCOVERED
662	      "，实际 " +
663	      img.data.length().to_string(),	<-- UNCOVERED
664	    )
665	  }
666	  if ksize <= 0 {
667	    raise @types.LoadError::DecodeFailed("median_blur_f: ksize必须大于0")	<-- UNCOVERED
668	  }
669	  if ksize % 2 == 0 {
670	    raise @types.LoadError::DecodeFailed("median_blur_f: ksize必须为奇数")	<-- UNCOVERED
671	  }
672	  if ksize <= 1 {
673	    return img
674	  }
675	  let radius = ksize / 2
676	  let w = img.width
677	  let h = img.height
678	  let ch = img.channels
679	  let color_ch = if ch >= 3 { 3 } else { ch }
680	  let n = w * h
681	  let bpp = ch * 4
682	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
683	  let buf : Array[Float] = Array::make(ksize * ksize, 0.0F)
684	  for y in 0..<h {
685	    for x in 0..<w {
686	      for c in 0..<color_ch {
687	        let mut idx = 0
688	        for dy in -radius..<=radius {
689	          for dx in -radius..<=radius {
690	            let sx = clamp_coord_f(x + dx, w)
691	            let sy = clamp_coord_f(y + dy, h)
692	            buf[idx] = read_f32_le_f(img.data, (sy * w + sx) * bpp + c * 4)
693	            idx += 1
694	          }
695	        }
696	        // 完整排序找中值（Float 需要完整排序）
697	        let total = ksize * ksize
698	        for i in 0..<total {
699	          for j in 0..<(total - i - 1) {
700	            if buf[j] > buf[j + 1] {
701	              let tmp = buf[j]
702	              buf[j] = buf[j + 1]
703	              buf[j + 1] = tmp
704	            }
705	          }
706	        }
707	        write_f32_le_f(out, (y * w + x) * bpp + c * 4, buf[total / 2])
708	      }
709	    }
710	  }
711	  // 复制 alpha
712	  if ch == 4 {
713	    for i in 0..<n {	<-- UNCOVERED
714	      let alpha_off = i * bpp + 12
715	      for b in 0..<4 {
716	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
717	      }
718	    }
719	  }
720	  @types.ImageF::{
721	    width: w,
722	    height: h,
723	    channels: ch,
724	    data: Bytes::from_array(out),
725	  }
726	}
   	…

730	pub fn sharpen_16(
731	  img : @types.Image16,
732	  amount : Float,
733	) -> @types.Image16 raise @types.LoadError {
734	  // 输入验证
735	  if img.width <= 0 || img.height <= 0 {
736	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
737	      "sharpen_16: 图像尺寸必须大于0",
738	    )
739	  }
740	  if img.channels < 1 || img.channels > 4 {
741	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
742	      "sharpen_16: 通道数必须在1-4之间",
743	    )
744	  }
745	  let expected_size = img.width * img.height * img.channels * 2
746	  if img.data.length() != expected_size {
747	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
748	      "sharpen_16: 数据大小不匹配，期望 " +
749	      expected_size.to_string() +	<-- UNCOVERED
750	      "，实际 " +
751	      img.data.length().to_string(),	<-- UNCOVERED
752	    )
753	  }
754	  if amount < 0.0F {
755	    raise @types.LoadError::DecodeFailed("sharpen_16: amount不能为负")	<-- UNCOVERED
756	  }
757	
758	  let w = img.width
759	  let h = img.height
760	  let ch = img.channels
761	  let color_ch = if ch >= 3 { 3 } else { ch }
762	  let n = w * h
763	  let bpp = ch * 2
764	  let center = 1.0F + 4.0F * amount
765	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
766	  let px : Array[Int] = Array::make(n * ch, 0)
767	  for i in 0..<n {
768	    for c in 0..<ch {
769	      px[i * ch + c] = read_u16_le_f(img.data, i * bpp + c * 2)
770	    }
771	  }
772	  for y in 1..<(h - 1) {
773	    for x in 1..<(w - 1) {
774	      let idx = y * w + x
775	      for c in 0..<color_ch {
776	        let cx = px[idx * ch + c]
777	        let left = px[(idx - 1) * ch + c]
778	        let right = px[(idx + 1) * ch + c]
779	        let top = px[(idx - w) * ch + c]
780	        let bottom = px[(idx + w) * ch + c]
781	        let val = Float::from_int(cx) * center -
782	          amount * Float::from_int(left + right + top + bottom)
783	        let new_val = (val + 0.5F).to_int()
784	        let clamped = if new_val < 0 {
785	          0	<-- UNCOVERED
786	        } else if new_val > 65535 {
787	          65535	<-- UNCOVERED
788	        } else {
789	          new_val
790	        }
791	        write_u16_le_f(out, idx * bpp + c * 2, clamped)
792	      }
793	    }
794	  }
795	  for y in 0..<h {
796	    for x in 0..<w {
797	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
798	        continue
799	      }
800	      let idx = y * w + x
801	      let x_l = clamp_coord_f(x - 1, w)
802	      let x_r = clamp_coord_f(x + 1, w)
803	      let y_t = clamp_coord_f(y - 1, h)
804	      let y_b = clamp_coord_f(y + 1, h)
805	      for c in 0..<color_ch {
806	        let cx = px[idx * ch + c]
807	        let left = px[(y * w + x_l) * ch + c]
808	        let right = px[(y * w + x_r) * ch + c]
809	        let top = px[(y_t * w + x) * ch + c]
810	        let bottom = px[(y_b * w + x) * ch + c]
811	        let val = Float::from_int(cx) * center -
812	          amount * Float::from_int(left + right + top + bottom)
813	        let new_val = (val + 0.5F).to_int()
814	        let clamped = if new_val < 0 {
815	          0
816	        } else if new_val > 65535 {
817	          65535	<-- UNCOVERED
818	        } else {
819	          new_val
820	        }
821	        write_u16_le_f(out, idx * bpp + c * 2, clamped)
822	      }
823	    }
824	  }
825	  if ch == 4 {
826	    for i in 0..<n {	<-- UNCOVERED
827	      let alpha_off = i * bpp + 6
828	      out[alpha_off] = img.data[alpha_off]
829	      out[alpha_off + 1] = img.data[alpha_off + 1]
830	    }
831	  }
832	  @types.Image16::{
833	    width: w,
834	    height: h,
835	    channels: ch,
836	    data: Bytes::from_array(out),
837	  }
838	}
   	…

842	pub fn sharpen_f(
843	  img : @types.ImageF,
844	  amount : Float,
845	) -> @types.ImageF raise @types.LoadError {
846	  // 输入验证
847	  if img.width <= 0 || img.height <= 0 {
848	    raise @types.LoadError::DecodeFailed("sharpen_f: 图像尺寸必须大于0")	<-- UNCOVERED
849	  }
850	  if img.channels < 1 || img.channels > 4 {
851	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
852	      "sharpen_f: 通道数必须在1-4之间",
853	    )
854	  }
855	  let expected_size = img.width * img.height * img.channels * 4
856	  if img.data.length() != expected_size {
857	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
858	      "sharpen_f: 数据大小不匹配，期望 " +
859	      expected_size.to_string() +	<-- UNCOVERED
860	      "，实际 " +
861	      img.data.length().to_string(),	<-- UNCOVERED
862	    )
863	  }
864	  if amount < 0.0F {
865	    raise @types.LoadError::DecodeFailed("sharpen_f: amount不能为负")	<-- UNCOVERED
866	  }
867	
868	  let w = img.width
869	  let h = img.height
870	  let ch = img.channels
871	  let color_ch = if ch >= 3 { 3 } else { ch }
872	  let n = w * h
873	  let bpp = ch * 4
874	  let center = 1.0F + 4.0F * amount
875	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
876	  let px : Array[Float] = Array::make(n * ch, 0.0F)
877	  for i in 0..<n {
878	    for c in 0..<ch {
879	      px[i * ch + c] = read_f32_le_f(img.data, i * bpp + c * 4)
880	    }
881	  }
882	  for y in 1..<(h - 1) {
883	    for x in 1..<(w - 1) {
884	      let idx = y * w + x
885	      for c in 0..<color_ch {
886	        let cx = px[idx * ch + c]
887	        let left = px[(idx - 1) * ch + c]
888	        let right = px[(idx + 1) * ch + c]
889	        let top = px[(idx - w) * ch + c]
890	        let bottom = px[(idx + w) * ch + c]
891	        let val = cx * center - amount * (left + right + top + bottom)
892	        write_f32_le_f(out, idx * bpp + c * 4, val)
893	      }
894	    }
895	  }
896	  for y in 0..<h {
897	    for x in 0..<w {
898	      if x >= 1 && x < w - 1 && y >= 1 && y < h - 1 {
899	        continue
900	      }
901	      let idx = y * w + x
902	      let x_l = clamp_coord_f(x - 1, w)
903	      let x_r = clamp_coord_f(x + 1, w)
904	      let y_t = clamp_coord_f(y - 1, h)
905	      let y_b = clamp_coord_f(y + 1, h)
906	      for c in 0..<color_ch {
907	        let cx = px[idx * ch + c]
908	        let left = px[(y * w + x_l) * ch + c]
909	        let right = px[(y * w + x_r) * ch + c]
910	        let top = px[(y_t * w + x) * ch + c]
911	        let bottom = px[(y_b * w + x) * ch + c]
912	        let val = cx * center - amount * (left + right + top + bottom)
913	        write_f32_le_f(out, idx * bpp + c * 4, val)
914	      }
915	    }
916	  }
917	  if ch == 4 {
918	    for i in 0..<n {	<-- UNCOVERED
919	      let alpha_off = i * bpp + 12
920	      for b in 0..<4 {
921	        out[alpha_off + b] = img.data[alpha_off + b]	<-- UNCOVERED
922	      }
923	    }
924	  }
925	  @types.ImageF::{
926	    width: w,
927	    height: h,
928	    channels: ch,
929	    data: Bytes::from_array(out),
930	  }
931	}
   	…

21 uncovered line(s) in src\process\filter\inpaint.mbt:

 11	pub fn inpaint(
 12	  img : @types.Image,
 13	  mask : @types.Image,
 14	  iterations? : Int = 100,
 15	) -> @types.Image raise @types.LoadError {
 16	  // 输入验证
 17	  if img.width <= 0 || img.height <= 0 {
 18	    raise @types.LoadError::DecodeFailed("inpaint: 图像尺寸必须大于0")	<-- UNCOVERED
 19	  }
 20	  if img.channels < 1 || img.channels > 4 {
 21	    raise @types.LoadError::DecodeFailed("inpaint: 通道数必须在1-4之间")	<-- UNCOVERED
 22	  }
 23	  let expected_size = img.width * img.height * img.channels
 24	  if img.data.length() != expected_size {
 25	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 26	      "inpaint: 图像数据大小不匹配，期望 " +
 27	      expected_size.to_string() +	<-- UNCOVERED
 28	      "，实际 " +
 29	      img.data.length().to_string(),	<-- UNCOVERED
 30	    )
 31	  }
 32	  if img.width != mask.width || img.height != mask.height {
 33	    raise @types.LoadError::DecodeFailed(
 34	      "inpaint: 掩码尺寸必须与图像一致",
 35	    )
 36	  }
 37	  if mask.channels != 1 {
 38	    raise @types.LoadError::DecodeFailed(
 39	      "inpaint: 掩码必须为单通道图像",
 40	    )
 41	  }
 42	  let mask_expected_size = mask.width * mask.height
 43	  if mask.data.length() != mask_expected_size {
 44	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 45	      "inpaint: 掩码数据大小不匹配，期望 " +
 46	      mask_expected_size.to_string() +	<-- UNCOVERED
 47	      "，实际 " +
 48	      mask.data.length().to_string(),	<-- UNCOVERED
 49	    )
 50	  }
 51	  if iterations <= 0 {
 52	    raise @types.LoadError::DecodeFailed("inpaint: iterations必须大于0")	<-- UNCOVERED
 53	  }
 54	  let w = img.width
 55	  let h = img.height
 56	  let ch = img.channels
 57	  let n = w * h
 58	  // 构建待修复标记
 59	  let unknown : Array[Bool] = Array::make(n, false)
 60	  let mut has_unknown = false
 61	  for i in 0..<n {
 62	    if mask.data[i] != b'\x00' {
 63	      unknown[i] = true
 64	      has_unknown = true
 65	    }
 66	  }
 67	  // 若无待修复像素，直接返回原图
 68	  if !has_unknown {
 69	    return img
 70	  }
 71	  // 复制原图数据
 72	  let out : Array[Int] = Array::make(n * ch, 0)
 73	  for i in 0..<(n * ch) {
 74	    out[i] = img.data[i].to_int()
 75	  }
 76	  // Jacobi 迭代求解 Laplace 方程
 77	  for _ in 0..<iterations {
 78	    let prev = out.copy()
 79	    let mut changed = false
 80	    for y in 0..<h {
 81	      for x in 0..<w {
 82	        let idx = y * w + x
 83	        if !unknown[idx] {
 84	          continue
 85	        }
 86	        // 收集 8 邻域中已知/已更新像素
 87	        for c in 0..<ch {
 88	          let mut sum = 0
 89	          let mut count = 0
 90	          for dy in -1..<=1 {
 91	            let ny = y + dy
 92	            if ny < 0 || ny >= h {
 93	              continue
 94	            }
 95	            for dx in -1..<=1 {
 96	              if dx == 0 && dy == 0 {
 97	                continue
 98	              }
 99	              let nx = x + dx
100	              if nx < 0 || nx >= w {
101	                continue	<-- UNCOVERED
102	              }
103	              sum += prev[(ny * w + nx) * ch + c]
104	              count += 1
105	            }
106	          }
107	          if count > 0 {
108	            let new_val = sum / count
109	            if new_val != out[idx * ch + c] {
110	              changed = true
111	            }
112	            out[idx * ch + c] = new_val
113	          }
114	        }
115	      }
116	    }
117	    // 收敛检测
118	    if !changed {
119	      break
120	    }
121	  }
122	  // 转换输出
123	  let out_data : Array[Byte] = Array::make(n * ch, b'\x00')
124	  for i in 0..<(n * ch) {
125	    out_data[i] = out[i].clamp(min=0, max=255).to_byte()
126	  }
127	  @types.Image::{
128	    width: w,
129	    height: h,
130	    channels: ch,
131	    data: Bytes::from_array(out_data),
132	  }
133	}
   	…

139	pub fn inpaint_fast(
140	  img : @types.Image,
141	  mask : @types.Image,
142	  radius? : Int = 5,
143	) -> @types.Image raise @types.LoadError {
144	  // 输入验证
145	  if img.width <= 0 || img.height <= 0 {
146	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
147	      "inpaint_fast: 图像尺寸必须大于0",
148	    )
149	  }
150	  if img.channels < 1 || img.channels > 4 {
151	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
152	      "inpaint_fast: 通道数必须在1-4之间",
153	    )
154	  }
155	  let expected_size = img.width * img.height * img.channels
156	  if img.data.length() != expected_size {
157	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
158	      "inpaint_fast: 图像数据大小不匹配，期望 " +
159	      expected_size.to_string() +	<-- UNCOVERED
160	      "，实际 " +
161	      img.data.length().to_string(),	<-- UNCOVERED
162	    )
163	  }
164	  if img.width != mask.width || img.height != mask.height {
165	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
166	      "inpaint_fast: 掩码尺寸必须与图像一致",
167	    )
168	  }
169	  if mask.channels != 1 {
170	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
171	      "inpaint_fast: 掩码必须为单通道图像",
172	    )
173	  }
174	  let mask_expected_size = mask.width * mask.height
175	  if mask.data.length() != mask_expected_size {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "inpaint_fast: 掩码数据大小不匹配，期望 " +
178	      mask_expected_size.to_string() +	<-- UNCOVERED
179	      "，实际 " +
180	      mask.data.length().to_string(),	<-- UNCOVERED
181	    )
182	  }
183	  if radius <= 0 {
184	    raise @types.LoadError::DecodeFailed("inpaint_fast: radius必须大于0")	<-- UNCOVERED
185	  }
186	  let w = img.width
187	  let h = img.height
188	  let ch = img.channels
189	  let n = w * h
190	  let r = radius
191	  // 复制原图数据
192	  let out : Array[Int] = Array::make(n * ch, 0)
193	  for i in 0..<(n * ch) {
194	    out[i] = img.data[i].to_int()
195	  }
196	  // 对每个待修复像素，用已知像素加权平均
197	  for y in 0..<h {
198	    for x in 0..<w {
199	      let idx = y * w + x
200	      if mask.data[idx] == b'\x00' {
201	        continue
202	      }
203	      for c in 0..<ch {
204	        let mut sum = 0.0F
205	        let mut weight_sum = 0.0F
206	        for dy in -r..<=r {
207	          let ny = y + dy
208	          if ny < 0 || ny >= h {
209	            continue
210	          }
211	          for dx in -r..<=r {
212	            if dx == 0 && dy == 0 {
213	              continue
214	            }
215	            let nx = x + dx
216	            if nx < 0 || nx >= w {
217	              continue
218	            }
219	            let nidx = ny * w + nx
220	            // 只用已知像素
221	            if mask.data[nidx] != b'\x00' {
222	              continue
223	            }
224	            let dist = Float::from_int(dx * dx + dy * dy).sqrt()
225	            let weight = 1.0F / (dist + 1.0F)
226	            sum += weight * Float::from_int(out[nidx * ch + c])
227	            weight_sum += weight
228	          }
229	        }
230	        if weight_sum > 0.0F {
231	          out[idx * ch + c] = (sum / weight_sum).to_int()
232	        }
233	      }
234	    }
235	  }
236	  // 转换输出
237	  let out_data : Array[Byte] = Array::make(n * ch, b'\x00')
238	  for i in 0..<(n * ch) {
239	    out_data[i] = out[i].clamp(min=0, max=255).to_byte()
240	  }
241	  @types.Image::{
242	    width: w,
243	    height: h,
244	    channels: ch,
245	    data: Bytes::from_array(out_data),
246	  }
247	}
   	…

21 uncovered line(s) in src\process\filter\nlm_denoise.mbt:

 10	pub fn nlm_denoise(
 11	  img : @types.Image,
 12	  patch_size? : Int = 3,
 13	  search_size? : Int = 7,
 14	  h? : Int = 10,
 15	) -> @types.Image raise @types.LoadError {
 16	  // 输入验证
 17	  if img.width <= 0 || img.height <= 0 {
 18	    raise @types.LoadError::DecodeFailed(
 19	      "nlm_denoise: 图像尺寸必须大于0",
 20	    )
 21	  }
 22	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 23	    raise @types.LoadError::DecodeFailed(
 24	      "nlm_denoise: 仅支持 channels=1/3/4",
 25	    )
 26	  }
 27	  let expected_size = img.width * img.height * img.channels
 28	  if img.data.length() != expected_size {
 29	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 30	      "nlm_denoise: 数据大小不匹配，期望 " +
 31	      expected_size.to_string() +	<-- UNCOVERED
 32	      "，实际 " +
 33	      img.data.length().to_string(),	<-- UNCOVERED
 34	    )
 35	  }
 36	  if patch_size < 1 || patch_size > 10 {
 37	    raise @types.LoadError::DecodeFailed("patch_size 必须在 1..10 之间")
 38	  }
 39	  if search_size < 1 || search_size > 20 {
 40	    raise @types.LoadError::DecodeFailed("search_size 必须在 1..20 之间")
 41	  }
 42	  if h < 1 {
 43	    raise @types.LoadError::DecodeFailed("h 必须 >= 1")
 44	  }
 45	  let ch = img.channels
 46	  let w = img.width
 47	  let h_img = img.height
 48	  let n = w * h_img
 49	  if n == 0 {
 50	    return img	<-- UNCOVERED
 51	  }
 52	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
 53	  let h_sq = h * h
 54	  let patch_d = patch_size
 55	  let search_d = search_size
 56	  // 预计算常数
 57	  let patch_area = (2 * patch_d + 1) * (2 * patch_d + 1)
 58	  // 预计算像素 Int 数组，避免重复 .to_int() 转换
 59	  let pixels : Array[Int] = Array::make(n * ch, 0)
 60	  for i in 0..<(n * ch) {
 61	    pixels[i] = img.data[i].to_int()
 62	  }
 63	  let denom = Float::from_int(h_sq * patch_area)
 64	  let early_term = (denom * 6.907755278982137F).to_int()
 65	  let inner_start = search_d + patch_d
 66	  // 内部区域：搜索窗口和块都完全在图像内
 67	  for y in inner_start..<(h_img - inner_start) {
 68	    for x in inner_start..<(w - inner_start) {
 69	      for c in 0..<ch {
 70	        if ch == 4 && c == 3 {
 71	          out[(y * w + x) * ch + 3] = img.data[(y * w + x) * ch + 3]
 72	          continue
 73	        }
 74	        let mut weight_sum = 0.0F
 75	        let mut value_sum = 0.0F
 76	        for sy in (y - search_d)..<=(y + search_d) {
 77	          let row_sy = sy * w
 78	          for sx in (x - search_d)..<=(x + search_d) {
 79	            let dist = patch_distance_inner(
 80	              pixels, x, y, sx, sy, w, ch, c, patch_d, early_term,
 81	            )
 82	            if dist >= early_term {
 83	              continue	<-- UNCOVERED
 84	            }
 85	            let weight = @math.expf(Float::from_int(-dist) / denom)
 86	            weight_sum += weight
 87	            value_sum += weight *
 88	              Float::from_int(pixels[(row_sy + sx) * ch + c])
 89	          }
 90	        }
 91	        let val = if weight_sum > 0.0F {
 92	          (value_sum / weight_sum).to_int()
 93	        } else {
 94	          pixels[(y * w + x) * ch + c]	<-- UNCOVERED
 95	        }
 96	        out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
 97	      }
 98	    }
 99	  }
100	  // 边界区域
101	  for y in 0..<h_img {
102	    let y_inner = y >= inner_start && y < h_img - inner_start
103	    for x in 0..<w {
104	      if y_inner && x >= inner_start && x < w - inner_start {
105	        continue
106	      }
107	      for c in 0..<ch {
108	        if ch == 4 && c == 3 {
109	          out[(y * w + x) * ch + 3] = img.data[(y * w + x) * ch + 3]
110	          continue
111	        }
112	        let mut weight_sum = 0.0F
113	        let mut value_sum = 0.0F
114	        let sy_start = if y - search_d < 0 { 0 } else { y - search_d }
115	        let sy_end = if y + search_d > h_img - 1 {
116	          h_img - 1
117	        } else {
118	          y + search_d
119	        }
120	        let sx_start = if x - search_d < 0 { 0 } else { x - search_d }
121	        let sx_end = if x + search_d > w - 1 { w - 1 } else { x + search_d }
122	        for sy in sy_start..<=sy_end {
123	          for sx in sx_start..<=sx_end {
124	            let dist = patch_distance_fast(
125	              pixels, x, y, sx, sy, w, h_img, ch, c, patch_d, early_term,
126	            )
127	            if dist >= early_term {
128	              continue
129	            }
130	            let weight = @math.expf(Float::from_int(-dist) / denom)
131	            weight_sum += weight
132	            value_sum += weight *
133	              Float::from_int(pixels[(sy * w + sx) * ch + c])
134	          }
135	        }
136	        let val = if weight_sum > 0.0F {
137	          (value_sum / weight_sum).to_int()
138	        } else {
139	          pixels[(y * w + x) * ch + c]	<-- UNCOVERED
140	        }
141	        out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
142	      }
143	    }
144	  }
145	  @types.Image::{
146	    width: w,
147	    height: h_img,
148	    channels: ch,
149	    data: Bytes::from_array(out),
150	  }
151	}
   	…

155	fn patch_distance_inner(
156	  pixels : Array[Int],
157	  x1 : Int,
158	  y1 : Int,
159	  x2 : Int,
160	  y2 : Int,
161	  w : Int,
162	  ch : Int,
163	  c : Int,
164	  patch_d : Int,
165	  early_term : Int,
166	) -> Int {
167	  let mut dist = 0
168	  for dy in -patch_d..<=patch_d {
169	    let row1 = (y1 + dy) * w
170	    let row2 = (y2 + dy) * w
171	    for dx in -patch_d..<=patch_d {
172	      let v1 = pixels[(row1 + x1 + dx) * ch + c]
173	      let v2 = pixels[(row2 + x2 + dx) * ch + c]
174	      let diff = v1 - v2
175	      dist += diff * diff
176	      if dist >= early_term {
177	        return dist	<-- UNCOVERED
178	      }
179	    }
180	  }
181	  dist
182	}
   	…

255	pub fn nlm_denoise_fast(
256	  img : @types.Image,
257	  patch_size? : Int = 3,
258	  search_size? : Int = 7,
259	  h? : Int = 10,
260	  step? : Int = 2,
261	) -> @types.Image raise @types.LoadError {
262	  // 输入验证
263	  if img.width <= 0 || img.height <= 0 {
264	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
265	      "nlm_denoise_fast: 图像尺寸必须大于0",
266	    )
267	  }
268	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
269	    raise @types.LoadError::DecodeFailed(
270	      "nlm_denoise_fast: 仅支持 channels=1/3/4",
271	    )
272	  }
273	  let expected_size = img.width * img.height * img.channels
274	  if img.data.length() != expected_size {
275	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
276	      "nlm_denoise_fast: 数据大小不匹配，期望 " +
277	      expected_size.to_string() +	<-- UNCOVERED
278	      "，实际 " +
279	      img.data.length().to_string(),	<-- UNCOVERED
280	    )
281	  }
282	  if patch_size < 1 || patch_size > 10 {
283	    raise @types.LoadError::DecodeFailed("patch_size 必须在 1..10 之间")	<-- UNCOVERED
284	  }
285	  if search_size < 1 || search_size > 20 {
286	    raise @types.LoadError::DecodeFailed("search_size 必须在 1..20 之间")	<-- UNCOVERED
287	  }
288	  if h < 1 {
289	    raise @types.LoadError::DecodeFailed("h 必须 >= 1")	<-- UNCOVERED
290	  }
291	  if step < 1 || step > 5 {
292	    raise @types.LoadError::DecodeFailed("step 必须在 1..5 之间")
293	  }
294	  let ch = img.channels
295	  let w = img.width
296	  let h_img = img.height
297	  let n = w * h_img
298	  if n == 0 {
299	    return img	<-- UNCOVERED
300	  }
301	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
302	  let h_sq = h * h
303	  let patch_d = patch_size
304	  let search_d = search_size
305	  let patch_area = (2 * patch_d + 1) * (2 * patch_d + 1)
306	  // 预计算像素 Int 数组
307	  let pixels : Array[Int] = Array::make(n * ch, 0)
308	  for i in 0..<(n * ch) {
309	    pixels[i] = img.data[i].to_int()
310	  }
311	  let denom = Float::from_int(h_sq * patch_area)
312	  let early_term = (denom * 6.907755278982137F).to_int()
313	  let inner_start = search_d + patch_d
314	  // 内部区域：搜索窗口和块都完全在图像内
315	  for y in inner_start..<(h_img - inner_start) {
316	    for x in inner_start..<(w - inner_start) {
317	      for c in 0..<ch {
318	        if ch == 4 && c == 3 {
319	          out[(y * w + x) * ch + 3] = img.data[(y * w + x) * ch + 3]	<-- UNCOVERED
320	          continue
321	        }
322	        let mut weight_sum = 0.0F
323	        let mut value_sum = 0.0F
324	        let mut sy = y - search_d
325	        let sy_end = y + search_d
326	        while sy <= sy_end {
327	          let row_sy = sy * w
328	          let mut sx = x - search_d
329	          let sx_end = x + search_d
330	          while sx <= sx_end {
331	            let dist = patch_distance_inner(
332	              pixels, x, y, sx, sy, w, ch, c, patch_d, early_term,
333	            )
334	            if dist >= early_term {
335	              sx += step	<-- UNCOVERED
336	              continue
337	            }
338	            let weight = @math.expf(Float::from_int(-dist) / denom)
339	            weight_sum += weight
340	            value_sum += weight *
341	              Float::from_int(pixels[(row_sy + sx) * ch + c])
342	            sx += step
343	          }
344	          sy += step
345	        }
346	        let val = if weight_sum > 0.0F {
347	          (value_sum / weight_sum).to_int()
348	        } else {
349	          pixels[(y * w + x) * ch + c]	<-- UNCOVERED
350	        }
351	        out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
352	      }
353	    }
354	  }
355	  // 边界区域
356	  for y in 0..<h_img {
357	    let y_inner = y >= inner_start && y < h_img - inner_start
358	    for x in 0..<w {
359	      if y_inner && x >= inner_start && x < w - inner_start {
360	        continue
361	      }
362	      for c in 0..<ch {
363	        if ch == 4 && c == 3 {
364	          out[(y * w + x) * ch + 3] = img.data[(y * w + x) * ch + 3]	<-- UNCOVERED
365	          continue
366	        }
367	        let mut weight_sum = 0.0F
368	        let mut value_sum = 0.0F
369	        let sy_start = if y - search_d < 0 { 0 } else { y - search_d }
370	        let sy_end = if y + search_d > h_img - 1 {
371	          h_img - 1
372	        } else {
373	          y + search_d
374	        }
375	        let sx_start = if x - search_d < 0 { 0 } else { x - search_d }
376	        let sx_end = if x + search_d > w - 1 { w - 1 } else { x + search_d }
377	        let mut sy = sy_start
378	        while sy <= sy_end {
379	          let mut sx = sx_start
380	          while sx <= sx_end {
381	            let dist = patch_distance_fast(
382	              pixels, x, y, sx, sy, w, h_img, ch, c, patch_d, early_term,
383	            )
384	            if dist >= early_term {
385	              sx += step
386	              continue
387	            }
388	            let weight = @math.expf(Float::from_int(-dist) / denom)
389	            weight_sum += weight
390	            value_sum += weight *
391	              Float::from_int(pixels[(sy * w + sx) * ch + c])
392	            sx += step
393	          }
394	          sy += step
395	        }
396	        let val = if weight_sum > 0.0F {
397	          (value_sum / weight_sum).to_int()
398	        } else {
399	          pixels[(y * w + x) * ch + c]	<-- UNCOVERED
400	        }
401	        out[(y * w + x) * ch + c] = val.clamp(min=0, max=255).to_byte()
402	      }
403	    }
404	  }
405	  @types.Image::{
406	    width: w,
407	    height: h_img,
408	    channels: ch,
409	    data: Bytes::from_array(out),
410	  }
411	}
   	…

21 uncovered line(s) in src\process\forensics\enhanced_source_verification.mbt:

114	pub fn detect_ui_elements(img : @types.Image) -> UIDetectionResult {
115	  let w = img.width
116	  let h = img.height
117	  let total = w * h
118	
119	  // 检测水平长线（窗口边框、工具栏分隔线）
120	  let mut horizontal_lines = 0
121	  for y in 0..<h {
122	    let mut run_length = 0
123	    for x in 0..<w {
124	      let idx = y * w + x
125	      // 检测边缘像素
126	      let is_edge = if x > 0 && x < w - 1 {
127	        let diff = (img.data[idx].to_double() - img.data[idx - 1].to_double()).abs()
128	        diff > 20.0
129	      } else {
130	        false
131	      }
132	      if is_edge {
133	        run_length += 1
134	      } else {
135	        if run_length > w / 4 {
136	          horizontal_lines += 1
137	        }
138	        run_length = 0
139	      }
140	    }
141	    if run_length > w / 4 {
142	      horizontal_lines += 1	<-- UNCOVERED
143	    }
144	  }
145	
146	  // 检测垂直长线
147	  let mut vertical_lines = 0
148	  for x in 0..<w {
149	    let mut run_length = 0
150	    for y in 0..<h {
151	      let idx = y * w + x
152	      let is_edge = if y > 0 && y < h - 1 {
153	        let diff = (img.data[idx].to_double() - img.data[idx - w].to_double()).abs()
154	        diff > 20.0
155	      } else {
156	        false
157	      }
158	      if is_edge {
159	        run_length += 1
160	      } else {
161	        if run_length > h / 4 {
162	          vertical_lines += 1
163	        }
164	        run_length = 0
165	      }
166	    }
167	    if run_length > h / 4 {
168	      vertical_lines += 1	<-- UNCOVERED
169	    }
170	  }
171	
172	  // 检测窗口边框（图像四周的边框）
173	  let has_window_border = horizontal_lines >= 2 && vertical_lines >= 2
174	
175	  // 检测工具栏（顶部的密集水平线条区域）
176	  let has_toolbar = horizontal_lines >= 3
177	
178	  // UI 区域比例（有边缘的区域比例）
179	  let mut ui_area = 0
180	  for y in 0..<h {
181	    for x in 0..<w {
182	      let idx = y * w + x
183	      let mut has_edge = false
184	      if x > 0 && x < w - 1 {
185	        if (img.data[idx].to_double() - img.data[idx - 1].to_double()).abs() >
186	          20.0 {
187	          has_edge = true
188	        }
189	      }
190	      if y > 0 && y < h - 1 {
191	        if (img.data[idx].to_double() - img.data[idx - w].to_double()).abs() >
192	          20.0 {
193	          has_edge = true
194	        }
195	      }
196	      if has_edge {
197	        ui_area += 1
198	      }
199	    }
200	  }
201	  let ui_area_ratio = ui_area.to_double() / total.to_double()
202	
203	  // 按钮状元素检测（小矩形区域）
204	  let button_like = horizontal_lines >= 4 && vertical_lines >= 4
205	
206	  // UI 指示值：UI 元素越多，越可能是屏幕截图（物理生成）
207	  let ui_indicator = if has_window_border && has_toolbar {
208	    0.9
209	  } else if horizontal_lines >= 3 || vertical_lines >= 3 {
210	    0.7	<-- UNCOVERED
211	  } else if horizontal_lines >= 2 || vertical_lines >= 2 {
212	    0.5	<-- UNCOVERED
213	  } else {
214	    0.2
215	  }
216	
217	  UIDetectionResult::{
218	    has_window_border,
219	    has_toolbar,
220	    has_button_like_elements: button_like,
221	    ui_element_count: horizontal_lines + vertical_lines,
222	    ui_area_ratio,
223	    horizontal_line_count: horizontal_lines,
224	    vertical_line_count: vertical_lines,
225	    ui_indicator,
226	  }
227	}
   	…

231	pub fn detect_text_regions(img : @types.Image) -> TextDetectionResult {
232	  let w = img.width
233	  let h = img.height
234	  let total = w * h
235	
236	  // 检测密集小边缘区域（文字特征）
237	  let mut dense_edge_regions = 0
238	  let block_size = 16
239	  let blocks_x = w / block_size
240	  let blocks_y = h / block_size
241	
242	  for by in 0..<blocks_y {
243	    for bx in 0..<blocks_x {
244	      let mut edge_count = 0
245	      for y in 0..<block_size {
246	        for x in 0..<block_size {
247	          let px = bx * block_size + x
248	          let py = by * block_size + y
249	          if px > 0 && px < w - 1 && py > 0 && py < h - 1 {
250	            let idx = py * w + px
251	            let gx = (img.data[idx + 1].to_double() -
252	            img.data[idx - 1].to_double()).abs()
253	            let gy = (img.data[idx + w].to_double() -
254	            img.data[idx - w].to_double()).abs()
255	            if gx > 15.0 || gy > 15.0 {
256	              edge_count += 1
257	            }
258	          }
259	        }
260	      }
261	      let edge_density = edge_count.to_double() /
262	        (block_size * block_size).to_double()
263	      // 文字区域通常边缘密度在 0.15-0.5 之间
264	      if edge_density > 0.15 && edge_density < 0.6 {
265	        dense_edge_regions += 1
266	      }
267	    }
268	  }
269	
270	  let text_area_ratio = dense_edge_regions.to_double() /
271	    (blocks_x * blocks_y).to_double()
272	
273	  // 小边缘密度（全局）
274	  let mut small_edges = 0
275	  for y in 1..<(h - 1) {
276	    for x in 1..<(w - 1) {
277	      let idx = y * w + x
278	      let gx = (img.data[idx + 1].to_double() - img.data[idx - 1].to_double()).abs()
279	      let gy = (img.data[idx + w].to_double() - img.data[idx - w].to_double()).abs()
280	      let mag = (gx * gx + gy * gy).sqrt()
281	      if mag > 10.0 && mag < 80.0 {
282	        small_edges += 1
283	      }
284	    }
285	  }
286	  let small_edge_density = small_edges.to_double() / total.to_double()
287	
288	  // 文字指示值：文字区域越多，越可能是屏幕截图（物理生成）
289	  let text_indicator = if text_area_ratio > 0.15 {
290	    0.85
291	  } else if text_area_ratio > 0.08 {
292	    0.65	<-- UNCOVERED
293	  } else if text_area_ratio > 0.03 {
294	    0.45	<-- UNCOVERED
295	  } else {
296	    0.2
297	  }
298	
299	  TextDetectionResult::{
300	    text_area_count: dense_edge_regions,
301	    text_area_ratio,
302	    dense_edge_region_count: dense_edge_regions,
303	    small_edge_density,
304	    text_like_region_ratio: text_area_ratio,
305	    text_indicator,
306	  }
307	}
   	…

392	pub fn analyze_color_quantization(
393	  img : @types.Image,
394	) -> ColorQuantizationResult {
395	  let w = img.width
396	  let h = img.height
397	  let total = w * h
398	
399	  // 统计唯一颜色数量（量化到 64 级）
400	  let color_histogram : Array[Int] = []
401	  for _ in 0..<64 {
402	    color_histogram.push(0)
403	  }
404	  for i in 0..<total {
405	    let bin = img.data[i].to_int() * 64 / 256
406	    color_histogram[bin] += 1
407	  }
408	
409	  let mut unique_colors = 0
410	  for i in 0..<64 {
411	    if color_histogram[i] > 0 {
412	      unique_colors += 1
413	    }
414	  }
415	
416	  // 顶部颜色比例
417	  let sorted_colors : Array[Int] = []
418	  for i in 0..<64 {
419	    sorted_colors.push(color_histogram[i])
420	  }
421	  // 冒泡排序
422	  for i in 0..<64 {
423	    for j in 0..<(64 - i - 1) {
424	      if sorted_colors[j] < sorted_colors[j + 1] {
425	        let temp = sorted_colors[j]
426	        sorted_colors[j] = sorted_colors[j + 1]
427	        sorted_colors[j + 1] = temp
428	      }
429	    }
430	  }
431	
432	  let top_color_ratio = if total > 0 {
433	    sorted_colors[0].to_double() / total.to_double()
434	  } else {
435	    0.0	<-- UNCOVERED
436	  }
437	  let mut top_10_sum = 0
438	  for i in 0..<10 {
439	    top_10_sum += sorted_colors[i]
440	  }
441	  let top_10_ratio = if total > 0 {
442	    top_10_sum.to_double() / total.to_double()
443	  } else {
444	    0.0	<-- UNCOVERED
445	  }
446	
447	  // 颜色量化级别：唯一颜色越少，量化越严重（UI 界面特征）
448	  let quantization_level = 1.0 - unique_colors.to_double() / 64.0
449	
450	  // 颜色调色板大小（估计）
451	  let palette_size = unique_colors
452	
453	  // 颜色量化指示值：颜色越集中，越可能是屏幕截图（物理生成）
454	  let color_quantization_indicator = if top_10_ratio > 0.8 {
455	    0.85
456	  } else if top_10_ratio > 0.65 {
457	    0.65	<-- UNCOVERED
458	  } else if top_10_ratio > 0.5 {
459	    0.45	<-- UNCOVERED
460	  } else {
461	    0.25
462	  }
463	
464	  ColorQuantizationResult::{
465	    unique_color_count: unique_colors,
466	    color_quantization_level: quantization_level,
467	    top_color_ratio,
468	    top_10_color_ratio: top_10_ratio,
469	    color_palette_size: palette_size,
470	    color_quantization_indicator,
471	  }
472	}
   	…

476	pub fn analyze_solid_areas(img : @types.Image) -> Double {
477	  let w = img.width
478	  let h = img.height
479	  let _ = w * h
480	  let block_size = 8
481	  let mut solid_blocks = 0
482	  let total_blocks = w / block_size * (h / block_size)
483	
484	  for by in 0..<(h / block_size) {
485	    for bx in 0..<(w / block_size) {
486	      let mut min_val = 255
487	      let mut max_val = 0
488	      for y in 0..<block_size {
489	        for x in 0..<block_size {
490	          let px = bx * block_size + x
491	          let py = by * block_size + y
492	          let val = img.data[py * w + px].to_int()
493	          if val < min_val {
494	            min_val = val
495	          }
496	          if val > max_val {
497	            max_val = val
498	          }
499	        }
500	      }
501	      if max_val - min_val < 10 {
502	        solid_blocks += 1
503	      }
504	    }
505	  }
506	
507	  let solid_ratio = if total_blocks > 0 {
508	    solid_blocks.to_double() / total_blocks.to_double()
509	  } else {
510	    0.0	<-- UNCOVERED
511	  }
512	
513	  // 纯色区域指示值：纯色区域越多，越可能是屏幕截图（物理生成）
514	  if solid_ratio > 0.5 {
515	    0.8
516	  } else if solid_ratio > 0.35 {
517	    0.6	<-- UNCOVERED
518	  } else if solid_ratio > 0.2 {
519	    0.4	<-- UNCOVERED
520	  } else {
521	    0.2
522	  }
523	}
   	…

544	pub fn analyze_pixel_distribution(img : @types.Image) -> Double {
545	  let w = img.width
546	  let h = img.height
547	  let total = w * h
548	
549	  // 计算像素值分布的熵
550	  let histogram : Array[Int] = []
551	  for _ in 0..<256 {
552	    histogram.push(0)
553	  }
554	  for i in 0..<total {
555	    histogram[img.data[i].to_int()] += 1
556	  }
557	
558	  let mut entropy = 0.0
559	  for i in 0..<256 {
560	    if histogram[i] > 0 {
561	      let p = histogram[i].to_double() / total.to_double()
562	      entropy -= p * p * 0.1 // 简化的 log2
563	    }
564	  }
565	
566	  // 像素分布越集中（熵越低），越可能是 UI 界面（物理生成）
567	  if entropy < 0.5 {
568	    0.75
569	  } else if entropy < 1.0 {	<-- UNCOVERED
570	    0.55
571	  } else if entropy < 1.5 {
572	    0.35
573	  } else {
574	    0.2	<-- UNCOVERED
575	  }
576	}
   	…

580	pub fn intelligent_decision(
581	  basic_result : SourceVerificationResult,
582	  ui_result : UIDetectionResult,
583	  text_result : TextDetectionResult,
584	  geometry_result : GeometryAnalysisResult,
585	  color_result : ColorQuantizationResult,
586	  solid_indicator : Double,
587	  edge_dir_indicator : Double,
588	  pixel_dist_indicator : Double,
589	  config : EnhancedVerificationConfig,
590	) -> (ImageSourceType, Double, Double, Double, String) {
591	  // 增强特征的物理生成指示值（值越高越可能是物理生成）
592	  let enhanced_physical_score = ui_result.ui_indicator * 0.25 +
593	    text_result.text_indicator * 0.20 +
594	    geometry_result.geometry_indicator * 0.15 +
595	    color_result.color_quantization_indicator * 0.15 +
596	    solid_indicator * 0.10 +
597	    edge_dir_indicator * 0.08 +
598	    pixel_dist_indicator * 0.07
599	
600	  // 基础特征的 AI 生成指示值
601	  let basic_ai_score = basic_result.ai_probability
602	
603	  // 加权融合
604	  let final_ai_score = basic_ai_score * config.basic_weight +
605	    (1.0 - enhanced_physical_score) * config.enhanced_weight
606	  let final_physical_score = 1.0 - final_ai_score
607	
608	  // 智能决策规则
609	  let mut decision_reason = ""
610	  let mut source_type = ImageSourceType::Unknown
611	  let mut confidence = 0.0
612	
613	  // 强 UI 特征判定
614	  if ui_result.has_window_border &&
615	    ui_result.has_toolbar &&
616	    text_result.text_area_ratio > 0.05 {
617	    source_type = ImageSourceType::PhysicalCaptured
618	    confidence = 0.9
619	    decision_reason = "检测到窗口边框、工具栏和文字区域，强烈判定为屏幕截图（物理生成）"
620	  } else if ui_result.ui_element_count >= 6 &&
621	    geometry_result.geometry_regularity > 0.7 {
622	    source_type = ImageSourceType::PhysicalCaptured	<-- UNCOVERED
623	    confidence = 0.8
624	    decision_reason = "检测到大量 UI 元素和规则几何形状，判定为屏幕截图（物理生成）"
625	  } else if text_result.text_area_ratio > 0.1 &&
626	    color_result.top_10_color_ratio > 0.7 {
627	    source_type = ImageSourceType::PhysicalCaptured
628	    confidence = 0.75
629	    decision_reason = "检测到大量文字区域和集中的颜色分布，判定为屏幕截图（物理生成）"
630	  } else if enhanced_physical_score > 0.65 {
631	    source_type = ImageSourceType::PhysicalCaptured	<-- UNCOVERED
632	    confidence = 0.7
633	    decision_reason = "增强特征综合评分偏向物理生成，判定为物理生成"
634	  } else if final_ai_score >= config.decision_threshold &&
635	    enhanced_physical_score < 0.4 {
636	    source_type = ImageSourceType::AIGenerated
637	    confidence = if final_ai_score > 0.7 { 0.75 } else { 0.55 }
638	    decision_reason = "基础特征和增强特征均偏向 AI 生成，判定为 AI 生成"
639	  } else if final_ai_score >= config.decision_threshold {
640	    source_type = ImageSourceType::AIGenerated
641	    confidence = 0.45
642	    decision_reason = "基础特征偏向 AI 生成，但增强特征存在不确定性，低置信度判定为 AI 生成"
643	  } else {
644	    source_type = ImageSourceType::PhysicalCaptured
645	    confidence = 0.55
646	    decision_reason = "综合特征偏向物理生成，判定为物理生成"
647	  }
648	
649	  (
650	    source_type, final_ai_score, final_physical_score, confidence, decision_reason,
651	  )
652	}
   	…

785	pub fn enhanced_verification_report_to_string(
786	  result : EnhancedVerificationResult,
787	) -> String {
788	  let mut report = "## 图像来源鉴定报告（增强版）\n\n"
789	  report = report + "### 鉴定结论\n\n"
790	  report = report + "| 指标 | 值 |\n|------|------|\n"
791	  let source_str = if result.source_type == ImageSourceType::AIGenerated {
792	    "AI 生成"
793	  } else if result.source_type == ImageSourceType::PhysicalCaptured {
794	    "物理生成（真实拍摄/屏幕截图）"
795	  } else if result.source_type == ImageSourceType::Unknown {	<-- UNCOVERED
796	    "未知"
797	  } else {
798	    "混合"	<-- UNCOVERED
799	  }
800	  report = report + "| 来源类型 | " + source_str + " |\n"
801	  report = report +
802	    "| AI 生成概率 | " +
803	    (result.ai_probability * 100.0).to_string() +
804	    "% |\n"
805	  report = report +
806	    "| 物理生成概率 | " +
807	    (result.physical_probability * 100.0).to_string() +
808	    "% |\n"
809	  report = report +
810	    "| 置信度 | " +
811	    (result.confidence * 100.0).to_string() +
812	    "% |\n"
813	  report = report + "| 决策依据 | " + result.decision_reason + " |\n\n"
814	
815	  report = report + "### 基础特征分析\n\n"
816	  report = report + "| 特征 | AI 指示值 |\n|------|-----------|\n"
817	  report = report +
818	    "| 噪声模式 | " +
819	    (result.basic_features.noise_pattern_score * 100.0).to_string() +
820	    "% |\n"
821	  report = report +
822	    "| 频域特征 | " +
823	    (result.basic_features.frequency_domain_score * 100.0).to_string() +
824	    "% |\n"
825	  report = report +
826	    "| 颜色分布 | " +
827	    (result.basic_features.color_distribution_score * 100.0).to_string() +
828	    "% |\n"
829	  report = report +
830	    "| 纹理特征 | " +
831	    (result.basic_features.texture_score * 100.0).to_string() +
832	    "% |\n"
833	  report = report +
834	    "| 边缘特征 | " +
835	    (result.basic_features.edge_score * 100.0).to_string() +
836	    "% |\n"
837	  report = report +
838	    "| 压缩伪影 | " +
839	    (result.basic_features.compression_artifact_score * 100.0).to_string() +
840	    "% |\n"
841	  report = report +
842	    "| 细节特征 | " +
843	    (result.basic_features.detail_score * 100.0).to_string() +
844	    "% |\n"
845	  report = report +
846	    "| 基础综合 | " +
847	    (result.basic_features.overall_score * 100.0).to_string() +
848	    "% |\n\n"
849	
850	  report = report + "### 增强特征分析\n\n"
851	  report = report +
852	    "| 特征 | 物理生成指示值 | 说明 |\n|------|---------------|------|\n"
853	  report = report +
854	    "| UI 元素检测 | " +
855	    (result.enhanced_features.ui_indicator * 100.0).to_string() +
856	    "% | 窗口边框/工具栏/按钮检测 |\n"
857	  report = report +
858	    "| 文字区域检测 | " +
859	    (result.enhanced_features.text_indicator * 100.0).to_string() +
860	    "% | 密集小边缘区域检测 |\n"
861	  report = report +
862	    "| 几何形状分析 | " +
863	    (result.enhanced_features.geometry_indicator * 100.0).to_string() +
864	    "% | 水平/垂直边缘比例 |\n"
865	  report = report +
866	    "| 颜色量化分析 | " +
867	    (result.enhanced_features.color_quantization_indicator * 100.0).to_string() +
868	    "% | 颜色集中度/调色板大小 |\n"
869	  report = report +
870	    "| 纯色区域分析 | " +
871	    (result.enhanced_features.solid_area_indicator * 100.0).to_string() +
872	    "% | 大面积纯色区域比例 |\n"
873	  report = report +
874	    "| 边缘方向分析 | " +
875	    (result.enhanced_features.edge_direction_indicator * 100.0).to_string() +
876	    "% | 水平+垂直边缘占比 |\n"
877	  report = report +
878	    "| 像素分布分析 | " +
879	    (result.enhanced_features.pixel_distribution_indicator * 100.0).to_string() +
880	    "% | 像素值分布熵 |\n"
881	  report = report +
882	    "| 增强综合 | " +
883	    (result.enhanced_features.overall_enhanced_score * 100.0).to_string() +
884	    "% | 加权综合评分 |\n\n"
885	
886	  report = report + "### UI 检测详情\n\n"
887	  report = report + "| 指标 | 值 |\n|------|------|\n"
888	  report = report +
889	    "| 窗口边框 | " +
890	    (if result.ui_detection.has_window_border {
891	      "检测到"	<-- UNCOVERED
892	    } else {
893	      "未检测到"
894	    }) +
895	    " |\n"
896	  report = report +
897	    "| 工具栏 | " +
898	    (if result.ui_detection.has_toolbar { "检测到" } else { "未检测到" }) +
899	    " |\n"
900	  report = report +
901	    "| 按钮状元素 | " +
902	    (if result.ui_detection.has_button_like_elements {
903	      "检测到"	<-- UNCOVERED
904	    } else {
905	      "未检测到"
906	    }) +
907	    " |\n"
908	  report = report +
909	    "| UI 元素总数 | " +
910	    result.ui_detection.ui_element_count.to_string() +
911	    " |\n"
912	  report = report +
913	    "| 水平线条数 | " +
914	    result.ui_detection.horizontal_line_count.to_string() +
915	    " |\n"
916	  report = report +
917	    "| 垂直线条数 | " +
918	    result.ui_detection.vertical_line_count.to_string() +
919	    " |\n"
920	  report = report +
921	    "| UI 区域比例 | " +
922	    (result.ui_detection.ui_area_ratio * 100.0).to_string() +
923	    "% |\n\n"
924	
925	  report = report + "### 文字检测详情\n\n"
926	  report = report + "| 指标 | 值 |\n|------|------|\n"
927	  report = report +
928	    "| 文字区域数 | " +
929	    result.text_detection.text_area_count.to_string() +
930	    " |\n"
931	  report = report +
932	    "| 文字区域比例 | " +
933	    (result.text_detection.text_area_ratio * 100.0).to_string() +
934	    "% |\n"
935	  report = report +
936	    "| 小边缘密度 | " +
937	    (result.text_detection.small_edge_density * 100.0).to_string() +
938	    "% |\n\n"
939	
940	  report = report + "### 几何分析详情\n\n"
941	  report = report + "| 指标 | 值 |\n|------|------|\n"
942	  report = report +
943	    "| 水平边缘比例 | " +
944	    (result.geometry_analysis.horizontal_edge_ratio * 100.0).to_string() +
945	    "% |\n"
946	  report = report +
947	    "| 垂直边缘比例 | " +
948	    (result.geometry_analysis.vertical_edge_ratio * 100.0).to_string() +
949	    "% |\n"
950	  report = report +
951	    "| 对角边缘比例 | " +
952	    (result.geometry_analysis.diagonal_edge_ratio * 100.0).to_string() +
953	    "% |\n"
954	  report = report +
955	    "| 几何规则性 | " +
956	    (result.geometry_analysis.geometry_regularity * 100.0).to_string() +
957	    "% |\n\n"
958	
959	  report = report + "### 颜色量化详情\n\n"
960	  report = report + "| 指标 | 值 |\n|------|------|\n"
961	  report = report +
962	    "| 唯一颜色数(64级) | " +
963	    result.color_quantization.unique_color_count.to_string() +
964	    " |\n"
965	  report = report +
966	    "| 顶部颜色比例 | " +
967	    (result.color_quantization.top_color_ratio * 100.0).to_string() +
968	    "% |\n"
969	  report = report +
970	    "| 前10颜色比例 | " +
971	    (result.color_quantization.top_10_color_ratio * 100.0).to_string() +
972	    "% |\n"
973	  report = report +
974	    "| 调色板大小 | " +
975	    result.color_quantization.color_palette_size.to_string() +
976	    " |\n\n"
977	
978	  report = report + "### 说明\n\n"
979	  report = report +
980	    "- 本鉴定基于基础统计特征 + 增强特征（UI/文字/几何/颜色）综合分析\n"
981	  report = report +
982	    "- 增强特征专门针对屏幕截图/数字渲染图进行优化\n"
983	  report = report +
984	    "- 物理生成指示值越高，表示越可能是真实拍摄或屏幕截图\n"
985	  report = report + "- 置信度表示鉴定结果的可靠程度\n"
986	  report = report +
987	    "- 对于经过编辑、压缩或重采样的图像，鉴定准确率可能下降\n"
988	
989	  report
990	}
   	…

9 uncovered line(s) in src\process\forensics\image_source_verification.mbt:

143	pub fn analyze_noise_pattern(img : @types.Image) -> NoiseAnalysisResult {
144	  let w = img.width
145	  let h = img.height
146	  let total = w * h
147	
148	  // 计算噪声水平（高频分量）
149	  let mut noise_level = 0.0
150	  for y in 1..<(h - 1) {
151	    for x in 1..<(w - 1) {
152	      let idx = y * w + x
153	      let center = img.data[idx].to_double()
154	      let avg = (
155	          img.data[idx - 1].to_double() +
156	          img.data[idx + 1].to_double() +
157	          img.data[idx - w].to_double() +
158	          img.data[idx + w].to_double()
159	        ) /
160	        4.0
161	      noise_level += (center - avg) * (center - avg)
162	    }
163	  }
164	  noise_level /= total.to_double()
165	  noise_level = noise_level.sqrt()
166	
167	  // 噪声相关性（相邻像素噪声的相关性）
168	  let mut noise_correlation = 0.0
169	  let mut count = 0
170	  for y in 0..<h {
171	    for x in 0..<(w - 1) {
172	      let n1 = img.data[y * w + x].to_double()
173	      let n2 = img.data[y * w + x + 1].to_double()
174	      noise_correlation += n1 * n2
175	      count += 1
176	    }
177	  }
178	  if count > 0 {
179	    noise_correlation /= count.to_double()
180	  }
181	
182	  // AI 生成图像通常噪声水平较低且相关性较高
183	  let ai_noise_indicator = if noise_level < 10.0 && noise_correlation > 100.0 {
184	    0.8
185	  } else if noise_level < 15.0 {
186	    0.6	<-- UNCOVERED
187	  } else {
188	    0.3
189	  }
190	
191	  NoiseAnalysisResult::{
192	    noise_level,
193	    noise_correlation,
194	    noise_spectrum_flatness: 0.5,
195	    noise_pattern_regularity: 0.5,
196	    ai_noise_indicator,
197	  }
198	}
   	…

257	pub fn analyze_color_distribution(img : @types.Image) -> ColorAnalysisResult {
258	  let w = img.width
259	  let h = img.height
260	  let total = w * h
261	
262	  // 颜色直方图平滑度
263	  let histogram : Array[Int] = []
264	  for _ in 0..<64 {
265	    histogram.push(0)
266	  }
267	  for i in 0..<total {
268	    let bin = img.data[i].to_int() * 64 / 256
269	    histogram[bin] += 1
270	  }
271	
272	  let mut smoothness = 0.0
273	  for i in 1..<63 {
274	    let diff = (histogram[i + 1] - 2 * histogram[i] + histogram[i - 1])
275	      .to_double()
276	      .abs()
277	    smoothness += diff
278	  }
279	  smoothness /= 64.0
280	
281	  // 颜色过渡自然度
282	  let mut transition_naturalness = 0.0
283	  let mut count = 0
284	  for y in 0..<h {
285	    for x in 0..<(w - 1) {
286	      let diff = (img.data[y * w + x].to_double() -
287	      img.data[y * w + x + 1].to_double()).abs()
288	      transition_naturalness += diff
289	      count += 1
290	    }
291	  }
292	  if count > 0 {
293	    transition_naturalness /= count.to_double()
294	  }
295	
296	  // AI 生成图像通常颜色过渡过于平滑
297	  let ai_color_indicator = if smoothness < 100.0 &&
298	    transition_naturalness < 10.0 {
299	    0.7
300	  } else if smoothness < 200.0 {
301	    0.5
302	  } else {
303	    0.3	<-- UNCOVERED
304	  }
305	
306	  ColorAnalysisResult::{
307	    color_histogram_smoothness: smoothness,
308	    color_transition_naturalness: transition_naturalness,
309	    color_quantization_level: 0.5,
310	    skin_tone_accuracy: 0.5,
311	    ai_color_indicator,
312	  }
313	}
   	…

376	pub fn analyze_edges(img : @types.Image) -> EdgeAnalysisResult {
377	  let w = img.width
378	  let h = img.height
379	  let _ = w * h
380	
381	  // 边缘锐度（梯度幅值）
382	  let mut edge_sharpness = 0.0
383	  let mut edge_count = 0
384	  for y in 1..<(h - 1) {
385	    for x in 1..<(w - 1) {
386	      let idx = y * w + x
387	      let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
388	      let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
389	      let magnitude = (gx * gx + gy * gy).sqrt()
390	      if magnitude > 30.0 {
391	        edge_sharpness += magnitude
392	        edge_count += 1
393	      }
394	    }
395	  }
396	  if edge_count > 0 {
397	    edge_sharpness /= edge_count.to_double()
398	  }
399	
400	  // 边缘连续性
401	  let mut edge_continuity = 0.0
402	  for y in 1..<(h - 1) {
403	    for x in 1..<(w - 1) {
404	      let idx = y * w + x
405	      let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
406	      let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
407	      let magnitude = (gx * gx + gy * gy).sqrt()
408	      if magnitude > 30.0 {
409	        // 检查邻域是否也有边缘
410	        let mut neighbor_edges = 0
411	        for dy in -1..<2 {
412	          for dx in -1..<2 {
413	            if dx == 0 && dy == 0 {
414	              continue
415	            }
416	            let ny = y + dy
417	            let nx = x + dx
418	            if ny < 1 || ny > h - 2 || nx < 1 || nx > w - 2 {
419	              continue
420	            }
421	            let nidx = ny * w + nx
422	            let ngx = img.data[nidx + 1].to_double() -
423	              img.data[nidx - 1].to_double()
424	            let ngy = img.data[nidx + w].to_double() -
425	              img.data[nidx - w].to_double()
426	            let nmag = (ngx * ngx + ngy * ngy).sqrt()
427	            if nmag > 30.0 {
428	              neighbor_edges += 1
429	            }
430	          }
431	        }
432	        edge_continuity += neighbor_edges.to_double() / 8.0
433	      }
434	    }
435	  }
436	  if edge_count > 0 {
437	    edge_continuity /= edge_count.to_double()
438	  }
439	
440	  // AI 生成图像通常边缘锐度较低且连续性较差
441	  let ai_edge_indicator = if edge_sharpness < 50.0 && edge_continuity < 0.5 {
442	    0.65
443	  } else if edge_sharpness < 70.0 {
444	    0.45	<-- UNCOVERED
445	  } else {
446	    0.25
447	  }
448	
449	  EdgeAnalysisResult::{
450	    edge_sharpness,
451	    edge_continuity,
452	    edge_consistency: 0.5,
453	    edge_artifact_level: 0.5,
454	    ai_edge_indicator,
455	  }
456	}
   	…

529	pub fn analyze_details(img : @types.Image) -> DetailAnalysisResult {
530	  let w = img.width
531	  let h = img.height
532	  let _ = w * h
533	
534	  // 细节一致性（高频细节的局部一致性）
535	  let mut detail_consistency = 0.0
536	  let mut detail_count = 0
537	  for y in 2..<(h - 2) {
538	    for x in 2..<(w - 2) {
539	      let idx = y * w + x
540	      let _ = img.data[idx].to_double()
541	      // 检查 5x5 邻域内的细节变化
542	      let mut variance = 0.0
543	      let mut sum = 0.0
544	      let mut count = 0
545	      for dy in -2..<3 {
546	        for dx in -2..<3 {
547	          let val = img.data[(y + dy) * w + (x + dx)].to_double()
548	          sum += val
549	          count += 1
550	        }
551	      }
552	      let avg = sum / count.to_double()
553	      for dy in -2..<3 {
554	        for dx in -2..<3 {
555	          let val = img.data[(y + dy) * w + (x + dx)].to_double()
556	          variance += (val - avg) * (val - avg)
557	        }
558	      }
559	      variance /= count.to_double()
560	      if variance > 100.0 {
561	        detail_consistency += variance.sqrt()
562	        detail_count += 1
563	      }
564	    }
565	  }
566	  if detail_count > 0 {
567	    detail_consistency /= detail_count.to_double()
568	  }
569	
570	  // 对称性检测（左右对称性）
571	  let mut symmetry = 0.0
572	  let half_w = w / 2
573	  for y in 0..<h {
574	    for x in 0..<half_w {
575	      let left = img.data[y * w + x].to_double()
576	      let right = img.data[y * w + (w - 1 - x)].to_double()
577	      symmetry += (left - right).abs()
578	    }
579	  }
580	  symmetry /= (h * half_w).to_double()
581	
582	  // AI 生成图像通常细节一致性较差且对称性不自然
583	  let ai_detail_indicator = if detail_consistency < 20.0 && symmetry > 30.0 {
584	    0.7	<-- UNCOVERED
585	  } else if detail_consistency < 30.0 {
586	    0.5
587	  } else {
588	    0.3
589	  }
590	
591	  DetailAnalysisResult::{
592	    eye_detail_consistency: detail_consistency,
593	    finger_detail_consistency: detail_consistency,
594	    hair_detail_consistency: detail_consistency,
595	    text_readability: 0.5,
596	    symmetry_consistency: symmetry,
597	    ai_detail_indicator,
598	  }
599	}
   	…

603	pub fn verify_image_source(
604	  img : @types.Image,
605	  config : SourceVerificationConfig,
606	) -> SourceVerificationResult {
607	  let noise_score = if config.use_noise_analysis {
608	    analyze_noise_pattern(img).ai_noise_indicator
609	  } else {
610	    0.5
611	  }
612	  let freq_score = if config.use_frequency_analysis {
613	    analyze_frequency_domain(img).ai_frequency_indicator
614	  } else {
615	    0.5
616	  }
617	  let color_score = if config.use_color_analysis {
618	    analyze_color_distribution(img).ai_color_indicator
619	  } else {
620	    0.5
621	  }
622	  let texture_score = if config.use_texture_analysis {
623	    analyze_texture(img).ai_texture_indicator
624	  } else {
625	    0.5
626	  }
627	  let edge_score = if config.use_edge_analysis {
628	    analyze_edges(img).ai_edge_indicator
629	  } else {
630	    0.5
631	  }
632	  let compression_score = if config.use_compression_analysis {
633	    analyze_compression_artifacts(img).ai_compression_indicator
634	  } else {
635	    0.5
636	  }
637	  let detail_score = if config.use_detail_analysis {
638	    analyze_details(img).ai_detail_indicator
639	  } else {
640	    0.5
641	  }
642	
643	  // 加权平均
644	  let mut weighted_sum = 0.0
645	  let mut weight_sum = 0.0
646	  let scores = [
647	    noise_score, freq_score, color_score, texture_score, edge_score, compression_score,
648	    detail_score,
649	  ]
650	  for i in 0..<scores.length() {
651	    let weight = if i < config.weights.length() {
652	      config.weights[i]
653	    } else {
654	      1.0	<-- UNCOVERED
655	    }
656	    weighted_sum += scores[i] * weight
657	    weight_sum += weight
658	  }
659	  let overall_score = if weight_sum > 0.0 {
660	    weighted_sum / weight_sum
661	  } else {
662	    0.5	<-- UNCOVERED
663	  }
664	
665	  let features = SourceVerificationFeatures::{
666	    noise_pattern_score: noise_score,
667	    frequency_domain_score: freq_score,
668	    color_distribution_score: color_score,
669	    texture_score,
670	    edge_score,
671	    compression_artifact_score: compression_score,
672	    detail_score,
673	    metadata_score: 0.5,
674	    overall_score,
675	  }
676	
677	  // 决策
678	  let ai_probability = overall_score
679	  let physical_probability = 1.0 - overall_score
680	  let confidence = if ai_probability > 0.7 || ai_probability < 0.3 {
681	    (ai_probability - 0.5).abs() * 2.0	<-- UNCOVERED
682	  } else {
683	    0.3
684	  }
685	
686	  let source_type = if ai_probability >= config.decision_threshold {
687	    ImageSourceType::AIGenerated
688	  } else {
689	    ImageSourceType::PhysicalCaptured
690	  }
691	
692	  SourceVerificationResult::{
693	    source_type,
694	    ai_probability,
695	    physical_probability,
696	    confidence,
697	    decision_threshold: config.decision_threshold,
698	    features,
699	    processing_time_ms: 0.0,
700	  }
701	}
   	…

705	pub fn source_verification_report_to_string(
706	  result : SourceVerificationResult,
707	) -> String {
708	  let mut report = "## 图像来源鉴定报告\n\n"
709	  report = report + "### 鉴定结论\n\n"
710	  report = report + "| 指标 | 值 |\n|------|------|\n"
711	  let source_str = if result.source_type == ImageSourceType::AIGenerated {
712	    "AI 生成"
713	  } else if result.source_type == ImageSourceType::PhysicalCaptured {
714	    "物理生成（真实拍摄）"
715	  } else if result.source_type == ImageSourceType::Unknown {	<-- UNCOVERED
716	    "未知"
717	  } else {
718	    "混合"	<-- UNCOVERED
719	  }
720	  report = report + "| 来源类型 | " + source_str + " |\n"
721	  report = report +
722	    "| AI 生成概率 | " +
723	    (result.ai_probability * 100.0).to_string() +
724	    "% |\n"
725	  report = report +
726	    "| 物理生成概率 | " +
727	    (result.physical_probability * 100.0).to_string() +
728	    "% |\n"
729	  report = report +
730	    "| 置信度 | " +
731	    (result.confidence * 100.0).to_string() +
732	    "% |\n"
733	  report = report +
734	    "| 决策阈值 | " +
735	    result.decision_threshold.to_string() +
736	    " |\n\n"
737	
738	  report = report + "### 特征分析\n\n"
739	  report = report +
740	    "| 特征 | AI 指示值 | 说明 |\n|------|-----------|------|\n"
741	  report = report +
742	    "| 噪声模式 | " +
743	    (result.features.noise_pattern_score * 100.0).to_string() +
744	    "% | AI 生成图像噪声通常较低且相关性高 |\n"
745	  report = report +
746	    "| 频域特征 | " +
747	    (result.features.frequency_domain_score * 100.0).to_string() +
748	    "% | AI 生成图像高频能量通常较低 |\n"
749	  report = report +
750	    "| 颜色分布 | " +
751	    (result.features.color_distribution_score * 100.0).to_string() +
752	    "% | AI 生成图像颜色过渡通常过于平滑 |\n"
753	  report = report +
754	    "| 纹理特征 | " +
755	    (result.features.texture_score * 100.0).to_string() +
756	    "% | AI 生成图像纹理复杂度通常较低 |\n"
757	  report = report +
758	    "| 边缘特征 | " +
759	    (result.features.edge_score * 100.0).to_string() +
760	    "% | AI 生成图像边缘锐度通常较低 |\n"
761	  report = report +
762	    "| 压缩伪影 | " +
763	    (result.features.compression_artifact_score * 100.0).to_string() +
764	    "% | AI 生成图像压缩伪影通常较少 |\n"
765	  report = report +
766	    "| 细节特征 | " +
767	    (result.features.detail_score * 100.0).to_string() +
768	    "% | AI 生成图像细节一致性通常较差 |\n"
769	  report = report +
770	    "| 综合评分 | " +
771	    (result.features.overall_score * 100.0).to_string() +
772	    "% | 加权平均综合评分 |\n\n"
773	
774	  report = report + "### 说明\n\n"
775	  report = report +
776	    "- 本鉴定基于图像的统计特征分析，结果仅供参考\n"
777	  report = report + "- AI 指示值越高，表示越可能是 AI 生成\n"
778	  report = report + "- 置信度表示鉴定结果的可靠程度\n"
779	  report = report +
780	    "- 对于经过编辑、压缩或重采样的图像，鉴定准确率可能下降\n"
781	
782	  report
783	}
   	…

20 uncovered line(s) in src\process\forensics_enhanced\forensics_enhanced.mbt:

 97	pub fn analyze_ela(
 98	  img : @types.Image,
 99	  quality_level : Int,
100	) -> ElaAnalysisResult raise @types.LoadError {
101	  let w = img.width
102	  let h = img.height
103	  let total = w * h
104	
105	  // 模拟 JPEG 量化：按 quality_level 量化像素
106	  let quant = if quality_level > 0 { 256 / (quality_level + 1) } else { 8 }
107	  if quant == 0 {
108	    raise @types.LoadError::DecodeFailed("quality_level too high")	<-- UNCOVERED
109	  }
110	
111	  // 量化并反量化（模拟 JPEG 压缩-解压）
112	  let recompressed : Array[Byte] = []
113	  for i in 0..<total {
114	    let val = img.data[i].to_int()
115	    let quantized = val / quant * quant
116	    let clamped = if quantized < 0 {
117	      0	<-- UNCOVERED
118	    } else if quantized > 255 {
119	      255	<-- UNCOVERED
120	    } else {
121	      quantized
122	    }
123	    recompressed.push(clamped.to_byte())
124	  }
125	
126	  // 计算 ELA 差值
127	  let mut ela_mean = 0.0
128	  let mut ela_max = 0.0
129	  let mut ela_sq_sum = 0.0
130	  let distribution : Array[Double] = []
131	  let num_bins = 16
132	  for _ in 0..<num_bins {
133	    distribution.push(0.0)
134	  }
135	
136	  for i in 0..<total {
137	    let diff = (img.data[i].to_int() - recompressed[i].to_int())
138	      .abs()
139	      .to_double()
140	    ela_mean += diff
141	    if diff > ela_max {
142	      ela_max = diff
143	    }
144	    ela_sq_sum += diff * diff
145	    let bin = (diff.to_int() * num_bins / 256).clamp(min=0, max=num_bins - 1)
146	    distribution[bin] += 1.0
147	  }
148	
149	  ela_mean /= total.to_double()
150	  let ela_std = (ela_sq_sum / total.to_double() - ela_mean * ela_mean)
151	    .abs()
152	    .sqrt()
153	
154	  // 归一化分布
155	  for i in 0..<num_bins {
156	    distribution[i] /= total.to_double()
157	  }
158	
159	  // 篡改区域比例：ELA 值高于均值的像素比例
160	  let mut tampered_count = 0
161	  for i in 0..<total {
162	    let diff = (img.data[i].to_int() - recompressed[i].to_int())
163	      .abs()
164	      .to_double()
165	    if diff > ela_mean * 2.0 {
166	      tampered_count += 1
167	    }
168	  }
169	  let tampered_ratio = tampered_count.to_double() / total.to_double()
170	
171	  ElaAnalysisResult::{
172	    ela_mean,
173	    ela_std,
174	    ela_max,
175	    ela_distribution: distribution,
176	    tampered_region_ratio: tampered_ratio,
177	  }
178	}
   	…

255	pub fn analyze_dct_coefficients(img : @types.Image) -> DctCoefficientResult {
256	  let w = img.width
257	  let h = img.height
258	
259	  // 简化 DCT：计算 8x8 块的 DCT 系数统计
260	  let block_size = 8
261	  let num_blocks_x = w / block_size
262	  let num_blocks_y = h / block_size
263	  let num_blocks = num_blocks_x * num_blocks_y
264	
265	  let mut high_freq_count = 0
266	  let mut zero_count = 0
267	  let mut total_coeffs = 0
268	  let mut blockiness = 0.0
269	
270	  let distribution : Array[Double] = []
271	  let num_bins = 10
272	  for _ in 0..<num_bins {
273	    distribution.push(0.0)
274	  }
275	
276	  for by in 0..<num_blocks_y {
277	    for bx in 0..<num_blocks_x {
278	      // 计算 8x8 块的简化 DCT
279	      let mut dc = 0.0
280	      let mut ac_energy = 0.0
281	      for j in 0..<block_size {
282	        for i in 0..<block_size {
283	          let idx = (by * block_size + j) * w + (bx * block_size + i)
284	          let val = img.data[idx].to_double()
285	          dc += val
286	          if i > 0 || j > 0 {
287	            ac_energy += val * val
288	          }
289	        }
290	      }
291	      dc /= (block_size * block_size).to_double()
292	
293	      // 块边界不连续性
294	      if bx > 0 {
295	        let mut border_diff = 0.0
296	        for j in 0..<block_size {
297	          let idx_left = (by * block_size + j) * w + (bx * block_size - 1)
298	          let idx_right = (by * block_size + j) * w + bx * block_size
299	          border_diff += (img.data[idx_left].to_double() -
300	          img.data[idx_right].to_double()).abs()
301	        }
302	        blockiness += border_diff / block_size.to_double()
303	      }
304	
305	      total_coeffs += block_size * block_size
306	      if ac_energy < 100.0 {
307	        zero_count += block_size * block_size - 1	<-- UNCOVERED
308	      }
309	      if ac_energy > 1000.0 {
310	        high_freq_count += block_size * block_size - 1
311	      }
312	
313	      // 分布统计
314	      let bin = (dc.to_int() * num_bins / 256).clamp(min=0, max=num_bins - 1)
315	      distribution[bin] += 1.0
316	    }
317	  }
318	
319	  if num_blocks > 0 {
320	    blockiness /= num_blocks.to_double()
321	  }
322	
323	  let high_freq_ratio = if total_coeffs > 0 {
324	    high_freq_count.to_double() / total_coeffs.to_double()
325	  } else {
326	    0.0	<-- UNCOVERED
327	  }
328	  let zero_ratio = if total_coeffs > 0 {
329	    zero_count.to_double() / total_coeffs.to_double()
330	  } else {
331	    0.0	<-- UNCOVERED
332	  }
333	
334	  // 归一化分布
335	  if num_blocks > 0 {
336	    for i in 0..<num_bins {
337	      distribution[i] /= num_blocks.to_double()
338	    }
339	  }
340	
341	  // 双 JPEG 指示：DCT 系数分布有周期性峰值
342	  let mut double_jpeg_indicator = 0.0
343	  for i in 1..<(num_bins - 1) {
344	    if distribution[i] > distribution[i - 1] &&
345	      distribution[i] > distribution[i + 1] {
346	      double_jpeg_indicator += 1.0
347	    }
348	  }
349	  double_jpeg_indicator /= num_bins.to_double()
350	
351	  DctCoefficientResult::{
352	    coefficient_distribution: distribution,
353	    high_freq_coefficient_ratio: high_freq_ratio,
354	    zero_coefficient_ratio: zero_ratio,
355	    blockiness_artifact: blockiness,
356	    double_jpeg_indicator,
357	  }
358	}
   	…

363	pub fn detect_clone_regions(
364	  img : @types.Image,
365	  block_size : Int,
366	  threshold : Double,
367	) -> CloneDetectionResult raise @types.LoadError {
368	  let w = img.width
369	  let h = img.height
370	
371	  if block_size < 2 || block_size > 16 {
372	    raise @types.LoadError::DecodeFailed("block_size must be 2..16")	<-- UNCOVERED
373	  }
374	
375	  let num_blocks_x = w - block_size + 1
376	  let num_blocks_y = h - block_size + 1
377	
378	  // 提取块特征（简化：均值+方差）
379	  let block_features : Array[(Double, Double, Int, Int)] = []
380	  for by in 0..<num_blocks_y {
381	    for bx in 0..<num_blocks_x {
382	      let mut mean = 0.0
383	      let mut sq_sum = 0.0
384	      let count = block_size * block_size
385	      for j in 0..<block_size {
386	        for i in 0..<block_size {
387	          let idx = (by + j) * w + (bx + i)
388	          let val = img.data[idx].to_double()
389	          mean += val
390	          sq_sum += val * val
391	        }
392	      }
393	      mean /= count.to_double()
394	      let variance = sq_sum / count.to_double() - mean * mean
395	      block_features.push((mean, variance, bx, by))
396	    }
397	  }
398	
399	  // 比较块特征，找相似块
400	  let clone_regions : Array[(Int, Int, Int, Int)] = []
401	  let mut clone_count = 0
402	  let mut total_similarity = 0.0
403	  let mut comparison_count = 0
404	
405	  let n = block_features.length()
406	  for i in 0..<n {
407	    for j in 0..<n {
408	      if i >= j {
409	        continue
410	      }
411	      let (m1, v1, x1, y1) = block_features[i]
412	      let (m2, v2, x2, y2) = block_features[j]
413	      // 跳过相邻块
414	      let dist = (x1 - x2).abs() + (y1 - y2).abs()
415	      if dist < block_size * 2 {
416	        continue
417	      }
418	      let mean_diff = (m1 - m2).abs()
419	      let var_diff = (v1 - v2).abs()
420	      let similarity = 1.0 / (1.0 + mean_diff / 10.0 + var_diff / 100.0)
421	      total_similarity += similarity
422	      comparison_count += 1
423	      if similarity > threshold && clone_count < 20 {
424	        clone_regions.push((x1, y1, x2, y2))
425	        clone_count += 1
426	      }
427	    }
428	  }
429	
430	  let avg_similarity = if comparison_count > 0 {
431	    total_similarity / comparison_count.to_double()
432	  } else {
433	    0.0	<-- UNCOVERED
434	  }
435	
436	  CloneDetectionResult::{
437	    clone_region_count: clone_count,
438	    clone_regions,
439	    clone_similarity: avg_similarity,
440	    has_clone: clone_count > 0,
441	  }
442	}
   	…

522	pub fn analyze_metadata_forensics(
523	  img : @types.Image,
524	) -> MetadataForensicsResult {
525	  let w = img.width
526	  let h = img.height
527	  let total = w * h
528	
529	  // 基于图像特征推断元数据
530	  let unique_values : Array[Int] = []
531	  for i in 0..<total {
532	    let v = img.data[i].to_int()
533	    let mut found = false
534	    for j in 0..<unique_values.length() {
535	      if unique_values[j] == v {
536	        found = true
537	        break
538	      }
539	    }
540	    if !found {
541	      unique_values.push(v)
542	    }
543	  }
544	
545	  // 软件签名推断
546	  let software = if unique_values.length() < 10 {
547	    "Paint/Editor"
548	  } else if unique_values.length() < 100 {
549	    "JPEG/Compressed"
550	  } else {
551	    "Camera/Raw"
552	  }
553	
554	  // 创建时间推断（基于图像尺寸特征）
555	  let creation_time = if w * h < 10000 {
556	    "Early/Low-res"
557	  } else if w * h < 1000000 {	<-- UNCOVERED
558	    "Standard"
559	  } else {
560	    "Modern/High-res"	<-- UNCOVERED
561	  }
562	
563	  // 设备信息推断
564	  let device = if w == h {
565	    "Square/Social"
566	  } else if w > h * 2 {	<-- UNCOVERED
567	    "Panorama"
568	  } else if h > w * 2 {
569	    "Portrait/Mobile"
570	  } else {
571	    "Standard/Camera"	<-- UNCOVERED
572	  }
573	
574	  // 一致性评分
575	  let consistency = if unique_values.length() > 50 && w > 64 && h > 64 {
576	    0.8	<-- UNCOVERED
577	  } else if unique_values.length() > 10 {
578	    0.6
579	  } else {
580	    0.3
581	  }
582	
583	  let has_inconsistency = consistency < 0.5
584	
585	  MetadataForensicsResult::{
586	    has_metadata: true,
587	    software_signature: software,
588	    creation_time,
589	    device_info: device,
590	    metadata_consistency: consistency,
591	    has_inconsistency,
592	  }
593	}
   	…

598	pub fn analyze_jpeg_ghost(img : @types.Image) -> JpegGhostResult {
599	  let w = img.width
600	  let h = img.height
601	  let total = w * h
602	
603	  // 模拟不同质量级别的重压缩差异
604	  let quality_levels = [50, 60, 70, 80, 90]
605	  let mut ghost_count = 0
606	  let mut primary_quality = 0
607	  let mut secondary_quality = 0
608	  let mut prev_diff = 0.0
609	
610	  for qi in 0..<quality_levels.length() {
611	    let q = quality_levels[qi]
612	    let quant = 256 / (q + 1)
613	    if quant == 0 {
614	      continue	<-- UNCOVERED
615	    }
616	    let mut diff_sum = 0.0
617	    for i in 0..<total {
618	      let val = img.data[i].to_int()
619	      let quantized = val / quant * quant
620	      let clamped = if quantized < 0 {
621	        0	<-- UNCOVERED
622	      } else if quantized > 255 {
623	        255	<-- UNCOVERED
624	      } else {
625	        quantized
626	      }
627	      diff_sum += (val - clamped).to_double().abs()
628	    }
629	    let avg_diff = diff_sum / total.to_double()
630	
631	    // ghost 检测：差异变化非单调
632	    if qi > 0 && avg_diff > prev_diff * 1.2 {
633	      ghost_count += 1
634	      if primary_quality == 0 {
635	        primary_quality = quality_levels[qi - 1]
636	        secondary_quality = q
637	      }
638	    }
639	    prev_diff = avg_diff
640	  }
641	
642	  JpegGhostResult::{
643	    ghost_quality_levels: quality_levels,
644	    ghost_region_count: ghost_count,
645	    has_ghost: ghost_count > 0,
646	    primary_quality,
647	    secondary_quality,
648	  }
649	}
   	…

713	pub fn detect_tamper_comprehensive(
714	  img : @types.Image,
715	  ela_quality : Int,
716	) -> TamperDetectionResult raise @types.LoadError {
717	  let ela = analyze_ela(img, ela_quality)
718	  let noise = analyze_noise_residual(img)
719	  let dct = analyze_dct_coefficients(img)
720	  let illumination = analyze_illumination_consistency(img)
721	  let metadata = analyze_metadata_forensics(img)
722	  let jpeg_ghost = analyze_jpeg_ghost(img)
723	  let cfa = analyze_cfa_pattern(img)
724	
725	  // 各指标评分
726	  let ela_score = if ela.tampered_region_ratio > 0.1 {
727	    0.8
728	  } else if ela.tampered_region_ratio > 0.05 {
729	    0.5	<-- UNCOVERED
730	  } else {
731	    0.2
732	  }
733	
734	  let noise_score = if noise.pattern_consistency < 0.5 { 0.7 } else { 0.3 }
735	
736	  let dct_score = if dct.double_jpeg_indicator > 0.3 { 0.6 } else { 0.3 }
737	
738	  let illum_score = if illumination.has_inconsistent_lighting {
739	    0.8
740	  } else {
741	    0.2
742	  }
743	
744	  let ghost_score = if jpeg_ghost.has_ghost { 0.7 } else { 0.2 }
745	
746	  let cfa_score = if !cfa.is_cfa_consistent { 0.6 } else { 0.2 }
747	
748	  let meta_score = if metadata.has_inconsistency { 0.7 } else { 0.3 }
749	
750	  // 加权综合
751	  let tamper_prob = ela_score * 0.25 +
752	    noise_score * 0.15 +
753	    dct_score * 0.15 +
754	    illum_score * 0.15 +
755	    ghost_score * 0.10 +
756	    cfa_score * 0.10 +
757	    meta_score * 0.10
758	
759	  let is_tampered = tamper_prob > 0.5
760	
761	  let tamper_type = if ela_score > 0.5 && illum_score > 0.5 {
762	    "内容篡改+光照不一致"
763	  } else if dct_score > 0.5 && ghost_score > 0.5 {
764	    "双JPEG压缩痕迹"	<-- UNCOVERED
765	  } else if noise_score > 0.5 {
766	    "噪声模式异常"
767	  } else if cfa_score > 0.5 {
768	    "CFA模式不一致"
769	  } else if meta_score > 0.5 {	<-- UNCOVERED
770	    "元数据不一致"
771	  } else if is_tampered {
772	    "综合篡改迹象"
773	  } else {
774	    "未检测到明显篡改"	<-- UNCOVERED
775	  }
776	
777	  let confidence = (tamper_prob - 0.5).abs() * 2.0
778	
779	  let details = "ELA=" +
780	    ela_score.to_string() +
781	    ", Noise=" +
782	    noise_score.to_string() +
783	    ", DCT=" +
784	    dct_score.to_string() +
785	    ", Illum=" +
786	    illum_score.to_string() +
787	    ", Ghost=" +
788	    ghost_score.to_string() +
789	    ", CFA=" +
790	    cfa_score.to_string() +
791	    ", Meta=" +
792	    meta_score.to_string()
793	
794	  // 篡改区域（简化：返回整图区域）
795	  let tampered_regions : Array[(Int, Int, Int, Int)] = []
796	  if is_tampered {
797	    tampered_regions.push((0, 0, img.width, img.height))
798	  }
799	
800	  TamperDetectionResult::{
801	    is_tampered,
802	    tamper_probability: tamper_prob,
803	    tamper_type,
804	    tampered_regions,
805	    confidence,
806	    analysis_details: details,
807	  }
808	}
   	…

1 uncovered line(s) in src\process\frequency\dct.mbt:

 80	pub fn dct_2d(img : @types.Image) -> Array[Array[Double]] {
 81	  let ch = img.channels
 82	  let w = img.width
 83	  let h = img.height
 84	  let n = w * h
 85	  // 提取灰度像素数据
 86	  let src : Array[Float] = Array::make(n, 0.0F)
 87	  for i in 0..<n {
 88	    let v = if ch == 1 {
 89	      Float::from_int(img.data[i].to_int())
 90	    } else {
 91	      let r = Float::from_int(img.data[i * ch].to_int())	<-- UNCOVERED
 92	      let g = Float::from_int(img.data[i * ch + 1].to_int())
 93	      let b = Float::from_int(img.data[i * ch + 2].to_int())
 94	      r * 0.299F + g * 0.587F + b * 0.114F
 95	    }
 96	    src[i] = v
 97	  }
 98	  // 预计算余弦表和归一化系数
 99	  let cos_w = dct_cos_table(w)
100	  let cos_h = dct_cos_table(h)
101	  let coeffs_w = dct_coeffs(w)
102	  let coeffs_h = dct_coeffs(h)
103	  // 第一步：对每一行执行 1D DCT（宽度方向）
104	  // row_dct[y][u] = C_w(u) * sum_x src[y][x] * cos((2x+1)uπ/(2W))
105	  let row_dct : Array[Array[Float]] = Array::make(h, [])
106	  let row_buf : Array[Float] = Array::make(w, 0.0F)
107	  for y in 0..<h {
108	    let row_src : Array[Float] = Array::make(w, 0.0F)
109	    for x in 0..<w {
110	      row_src[x] = src[y * w + x]
111	    }
112	    dct_1d(row_src[:], row_buf, cos_w, coeffs_w[:], w)
113	    row_dct[y] = Array::make(w, 0.0F)
114	    for u in 0..<w {
115	      row_dct[y][u] = row_buf[u]
116	    }
117	  }
118	  // 第二步：对每一列执行 1D DCT（高度方向）
119	  // F(v,u) = C_h(v) * sum_y row_dct[y][u] * cos((2y+1)vπ/(2H))
120	  let result : Array[Array[Double]] = Array::make(h, [])
121	  let col_buf : Array[Float] = Array::make(h, 0.0F)
122	  for u in 0..<w {
123	    let col_src : Array[Float] = Array::make(h, 0.0F)
124	    for y in 0..<h {
125	      col_src[y] = row_dct[y][u]
126	    }
127	    dct_1d(col_src[:], col_buf, cos_h, coeffs_h[:], h)
128	    for v in 0..<h {
129	      if result[v].length() == 0 {
130	        result[v] = Array::make(w, 0.0)
131	      }
132	      result[v][u] = col_buf[v].to_double()
133	    }
134	  }
135	  result
136	}
   	…

4 uncovered line(s) in src\process\frequency\fft.mbt:

 91	pub fn ifft_2d(fft : FFTResult) -> @types.Image {
 92	  let pw = fft.width
 93	  let ph = fft.height
 94	  let n = pw * ph
 95	  // 分离实虚部数组
 96	  let re : Array[Float] = Array::make(n, 0.0F)
 97	  let im : Array[Float] = Array::make(n, 0.0F)
 98	  for i in 0..<n {
 99	    re[i] = fft.data[i].re
100	    im[i] = fft.data[i].im
101	  }
102	  // 行变换
103	  let row_re : Array[Float] = Array::make(pw, 0.0F)
104	  let row_im : Array[Float] = Array::make(pw, 0.0F)
105	  for y in 0..<ph {
106	    let base = y * pw
107	    for x in 0..<pw {
108	      row_re[x] = re[base + x]
109	      row_im[x] = im[base + x]
110	    }
111	    fft_1d_split(row_re, row_im, true)
112	    for x in 0..<pw {
113	      re[base + x] = row_re[x]
114	      im[base + x] = row_im[x]
115	    }
116	  }
117	  // 列变换
118	  let col_re : Array[Float] = Array::make(ph, 0.0F)
119	  let col_im : Array[Float] = Array::make(ph, 0.0F)
120	  for x in 0..<pw {
121	    for y in 0..<ph {
122	      col_re[y] = re[y * pw + x]
123	      col_im[y] = im[y * pw + x]
124	    }
125	    fft_1d_split(col_re, col_im, true)
126	    for y in 0..<ph {
127	      re[y * pw + x] = col_re[y]
128	      im[y * pw + x] = col_im[y]
129	    }
130	  }
131	  let norm = Float::from_int(n)
132	  let out : Array[Byte] = Array::make(n, b'\x00')
133	  for i in 0..<n {
134	    let v = re[i] / norm
135	    let clamped = if v < 0.0F {
136	      0
137	    } else if v > 255.0F {
138	      255	<-- UNCOVERED
139	    } else {
140	      v.to_int()
141	    }
142	    out[i] = clamped.to_byte()
143	  }
144	  @types.Image::{
145	    width: pw,
146	    height: ph,
147	    channels: 1,
148	    data: Bytes::from_array(out),
149	  }
150	}
   	…

155	pub fn fft_magnitude(fft : FFTResult, log_scale : Bool) -> @types.Image {
156	  let n = fft.width * fft.height
157	  let out : Array[Byte] = Array::make(n, b'\x00')
158	  let mut max_mag = 0.0F
159	  let mags : Array[Float] = Array::make(n, 0.0F)
160	  for i in 0..<n {
161	    let re = fft.data[i].re
162	    let im = fft.data[i].im
163	    let mag = (re * re + im * im).sqrt()
164	    let m = if log_scale {
165	      Float::from_double(@math.log2(mag.to_double() + 1.0) * 0.6931471805599453)
166	    } else {
167	      mag
168	    }
169	    mags[i] = m
170	    if m > max_mag {
171	      max_mag = m
172	    }
173	  }
174	  if max_mag > 0.0F {
175	    for i in 0..<n {
176	      let v = mags[i] / max_mag * 255.0F
177	      let clamped = if v < 0.0F {
178	        0	<-- UNCOVERED
179	      } else if v > 255.0F {
180	        255	<-- UNCOVERED
181	      } else {
182	        v.to_int()
183	      }
184	      out[i] = clamped.to_byte()
185	    }
186	  }
187	  @types.Image::{
188	    width: fft.width,
189	    height: fft.height,
190	    channels: 1,
191	    data: Bytes::from_array(out),
192	  }
193	}
   	…

280	fn bit_reverse_split(re : Array[Float], im : Array[Float]) -> Unit {
281	  let n = re.length()
282	  if n <= 1 {
283	    return	<-- UNCOVERED
284	  }
285	  let bits = log2_int(n)
286	  let high_bit = 1 << (bits - 1)
287	  // 递推预计算 bit reversal：rev[i] = (rev[i>>1] >> 1) | ((i&1) << (bits-1))
288	  let rev : Array[Int] = Array::make(n, 0)
289	  for i in 1..<n {
290	    rev[i] = (rev[i >> 1] >> 1) | (if (i & 1) != 0 { high_bit } else { 0 })
291	  }
292	  for i in 0..<n {
293	    let j = rev[i]
294	    if j > i {
295	      let tmp_re = re[i]
296	      let tmp_im = im[i]
297	      re[i] = re[j]
298	      im[i] = im[j]
299	      re[j] = tmp_re
300	      im[j] = tmp_im
301	    }
302	  }
303	}
   	…

7 uncovered line(s) in src\process\frequency\freq_filter.mbt:

15	pub fn freq_filter(
16	  img : @types.Image,
17	  filter_type : FreqFilterType,
18	  cutoff : Float,
19	  band_width? : Float = 0.0F,
20	) -> @types.Image raise @types.LoadError {
21	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
22	    raise @types.LoadError::DecodeFailed("freq_filter 仅支持 channels=1/3/4")
23	  }
24	  let ch = img.channels
25	  let color_ch = if ch >= 3 { 3 } else { 1 }
26	  let ffts = fft_2d(img)
27	  let filtered : Array[FFTResult] = []
28	  for c in 0..<color_ch {
29	    fft_shift_inplace(ffts[c])
30	    let shifted = ffts[c]
31	    let w = shifted.width
32	    let h = shifted.height
33	    let cx = w / 2
34	    let cy = h / 2
35	    let n = w * h
36	    let out_data : Array[Complex] = Array::make(n, Complex::{
37	      re: 0.0F,
38	      im: 0.0F,
39	    })
40	    let data = shifted.data
41	    for y in 0..<h {
42	      let dy = y - cy
43	      let row = y * w
44	      for x in 0..<w {
45	        let dx = x - cx
46	        let dist = Float::from_int(dx * dx + dy * dy).sqrt()
47	        let gain = filter_gain(filter_type, dist, cutoff, band_width)
48	        let src = data[row + x]
49	        out_data[row + x] = Complex::{ re: src.re * gain, im: src.im * gain }
50	      }
51	    }
52	    let filtered_fft = FFTResult::{ width: w, height: h, data: out_data }
53	    fft_shift_inplace(filtered_fft)
54	    filtered.push(filtered_fft)
55	  }
56	  if color_ch == 1 {
57	    return ifft_2d(filtered[0])
58	  }
59	  let pw = filtered[0].width
60	  let ph = filtered[0].height
61	  let n = pw * ph
62	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
63	  for c in 0..<color_ch {
64	    let channel_img = ifft_2d(filtered[c])
65	    for i in 0..<n {
66	      out[i * ch + c] = channel_img.data[i]
67	    }
68	  }
69	  if ch == 4 {
70	    for i in 0..<n {	<-- UNCOVERED
71	      out[i * 4 + 3] = img.data[i * 4 + 3]
72	    }
73	  }
74	  @types.Image::{
75	    width: pw,
76	    height: ph,
77	    channels: ch,
78	    data: Bytes::from_array(out),
79	  }
80	}
  	…

116	pub fn freq_filter_gaussian(
117	  img : @types.Image,
118	  filter_type : FreqFilterType,
119	  cutoff : Float,
120	  band_width? : Float = 0.0F,
121	) -> @types.Image raise @types.LoadError {
122	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
123	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
124	      "freq_filter_gaussian 仅支持 channels=1/3/4",
125	    )
126	  }
127	  let ch = img.channels
128	  let color_ch = if ch >= 3 { 3 } else { 1 }
129	  let ffts = fft_2d(img)
130	  let filtered : Array[FFTResult] = []
131	  for c in 0..<color_ch {
132	    fft_shift_inplace(ffts[c])
133	    let shifted = ffts[c]
134	    let w = shifted.width
135	    let h = shifted.height
136	    let cx = w / 2
137	    let cy = h / 2
138	    let n = w * h
139	    let out_data : Array[Complex] = Array::make(n, Complex::{
140	      re: 0.0F,
141	      im: 0.0F,
142	    })
143	    let data = shifted.data
144	    for y in 0..<h {
145	      let dy = y - cy
146	      let row = y * w
147	      for x in 0..<w {
148	        let dx = x - cx
149	        let dist = Float::from_int(dx * dx + dy * dy).sqrt()
150	        let gain = gaussian_gain(filter_type, dist, cutoff, band_width)
151	        let src = data[row + x]
152	        out_data[row + x] = Complex::{ re: src.re * gain, im: src.im * gain }
153	      }
154	    }
155	    let filtered_fft = FFTResult::{ width: w, height: h, data: out_data }
156	    fft_shift_inplace(filtered_fft)
157	    filtered.push(filtered_fft)
158	  }
159	  if color_ch == 1 {
160	    return ifft_2d(filtered[0])
161	  }
162	  let pw = filtered[0].width
163	  let ph = filtered[0].height
164	  let n = pw * ph
165	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
166	  for c in 0..<color_ch {
167	    let channel_img = ifft_2d(filtered[c])
168	    for i in 0..<n {
169	      out[i * ch + c] = channel_img.data[i]
170	    }
171	  }
172	  if ch == 4 {
173	    for i in 0..<n {	<-- UNCOVERED
174	      out[i * 4 + 3] = img.data[i * 4 + 3]
175	    }
176	  }
177	  @types.Image::{
178	    width: pw,
179	    height: ph,
180	    channels: ch,
181	    data: Bytes::from_array(out),
182	  }
183	}
   	…

187	fn gaussian_gain(
188	  filter_type : FreqFilterType,
189	  dist : Float,
190	  cutoff : Float,
191	  band_width : Float,
192	) -> Float {
193	  let eps = 0.0001F
194	  let c2 = cutoff * cutoff
195	  match filter_type {
196	    LowPass => @math.expf(-(dist * dist) / (c2 + eps))
197	    HighPass => 1.0F - @math.expf(-(dist * dist) / (c2 + eps))
198	    BandPass => {	<-- UNCOVERED
199	      let lp = @math.expf(-(dist * dist) / (c2 + eps))
200	      let lo = cutoff - band_width / 2.0F
201	      let lp2 = @math.expf(-(dist * dist) / (lo * lo + eps))	<-- UNCOVERED
202	      lp2 - lp
203	    }
204	    BandStop => {	<-- UNCOVERED
205	      let lp = @math.expf(-(dist * dist) / (c2 + eps))
206	      let lo = cutoff - band_width / 2.0F
207	      let lp2 = @math.expf(-(dist * dist) / (lo * lo + eps))	<-- UNCOVERED
208	      1.0F - (lp2 - lp)
209	    }
210	  }
211	}
   	…

3 uncovered line(s) in src\process\frequency\frequency_16f.mbt:

37	fn clamp_int_freq(v : Int, lo : Int, hi : Int) -> Int {
38	  if v < lo {
39	    lo	<-- UNCOVERED
40	  } else if v > hi {
41	    hi	<-- UNCOVERED
42	  } else {
43	    v
44	  }
45	}
  	…

123	pub fn fft_2d_f(img : @types.ImageF) -> Array[FFTResult] raise @types.LoadError {
124	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
125	    raise @types.LoadError::DecodeFailed("fft_2d_f 仅支持 channels=1/3/4")	<-- UNCOVERED
126	  }
127	  let ch = img.channels
128	  let color_ch = if ch >= 3 { 3 } else { 1 }
129	  let w = img.width
130	  let h = img.height
131	  let bpp = ch * 4
132	  // 转换为 8-bit（归一化到 0-255）
133	  let data8 : Array[Byte] = Array::make(w * h * color_ch, b'\x00')
134	  for i in 0..<(w * h) {
135	    for c in 0..<color_ch {
136	      let vf = read_float_le_freq(img.data, i * bpp + c * 4)
137	      let v8 = clamp_int_freq((vf * 255.0F).to_int(), 0, 255)
138	      data8[i * color_ch + c] = v8.to_byte()
139	    }
140	  }
141	  let img8 = @types.Image::{
142	    width: w,
143	    height: h,
144	    channels: color_ch,
145	    data: Bytes::from_array(data8),
146	  }
147	  fft_2d(img8)
148	}
   	…

6 uncovered line(s) in src\process\frequency\haar_wavelet.mbt:

 68	pub fn haar_transform_2d(
 69	  img : @types.Image,
 70	  levels? : Int = 1,
 71	) -> HaarWaveletResult raise @types.LoadError {
 72	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 73	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 74	      "haar_transform_2d 仅支持 channels=1/3/4",
 75	    )
 76	  }
 77	  if levels < 1 || levels > 10 {
 78	    raise @types.LoadError::DecodeFailed("levels 必须在 1..10 之间")
 79	  }
 80	  let w = img.width
 81	  let h = img.height
 82	  let n = w * h
 83	  if n == 0 {
 84	    return HaarWaveletResult::{	<-- UNCOVERED
 85	      width: w,
 86	      height: h,
 87	      levels: 0,
 88	      coefficients: [],
 89	    }
 90	  }
 91	  // 转灰度为 Float
 92	  let coeffs : Array[Float] = Array::make(n, 0.0F)
 93	  let ch = img.channels
 94	  let data_int : Array[Int] = Array::make(n * ch, 0)
 95	  for i in 0..<(n * ch) {
 96	    data_int[i] = img.data[i].to_int()
 97	  }
 98	  for i in 0..<n {
 99	    let v = if ch == 1 {
100	      data_int[i]
101	    } else {
102	      let r = data_int[i * ch]
103	      let g = data_int[i * ch + 1]
104	      let b = data_int[i * ch + 2]
105	      (r * 299 + g * 587 + b * 114) / 1000
106	    }
107	    coeffs[i] = Float::from_int(v)
108	  }
109	  // 多级分解
110	  let mut cur_w = w
111	  let mut cur_h = h
112	  for _ in 0..<levels {
113	    if cur_w < 2 || cur_h < 2 {
114	      break
115	    }
116	    // 行变换
117	    for y in 0..<cur_h {
118	      let row : Array[Float] = Array::make(cur_w, 0.0F)
119	      for x in 0..<cur_w {
120	        row[x] = coeffs[y * w + x]
121	      }
122	      let transformed = haar_transform_1d(row)
123	      for x in 0..<cur_w {
124	        coeffs[y * w + x] = transformed[x]
125	      }
126	    }
127	    // 列变换
128	    for x in 0..<cur_w {
129	      let col : Array[Float] = Array::make(cur_h, 0.0F)
130	      for y in 0..<cur_h {
131	        col[y] = coeffs[y * w + x]
132	      }
133	      let transformed = haar_transform_1d(col)
134	      for y in 0..<cur_h {
135	        coeffs[y * w + x] = transformed[y]
136	      }
137	    }
138	    cur_w /= 2
139	    cur_h /= 2
140	  }
141	  HaarWaveletResult::{ width: w, height: h, levels, coefficients: coeffs }
142	}
   	…

146	pub fn haar_inverse_transform_2d(result : HaarWaveletResult) -> @types.Image {
147	  let w = result.width
148	  let h = result.height
149	  let n = w * h
150	  if n == 0 {
151	    return @types.Image::{	<-- UNCOVERED
152	      width: 0,
153	      height: 0,
154	      channels: 1,
155	      data: Bytes::from_array([]),	<-- UNCOVERED
156	    }
157	  }
158	  let coeffs = result.coefficients.copy()
159	  // 多级重构（从最小级别开始）
160	  let mut cur_w = w
161	  let mut cur_h = h
162	  for _ in 0..<result.levels {
163	    if cur_w < 2 || cur_h < 2 {
164	      break
165	    }
166	    cur_w /= 2
167	    cur_h /= 2
168	  }
169	  for _ in 0..<result.levels {
170	    if cur_w * 2 > w || cur_h * 2 > h {
171	      break
172	    }
173	    cur_w *= 2
174	    cur_h *= 2
175	    // 列逆变换
176	    for x in 0..<cur_w {
177	      let col : Array[Float] = Array::make(cur_h, 0.0F)
178	      for y in 0..<cur_h {
179	        col[y] = coeffs[y * w + x]
180	      }
181	      let inversed = ihaar_transform_1d(col)
182	      for y in 0..<cur_h {
183	        coeffs[y * w + x] = inversed[y]
184	      }
185	    }
186	    // 行逆变换
187	    for y in 0..<cur_h {
188	      let row : Array[Float] = Array::make(cur_w, 0.0F)
189	      for x in 0..<cur_w {
190	        row[x] = coeffs[y * w + x]
191	      }
192	      let inversed = ihaar_transform_1d(row)
193	      for x in 0..<cur_w {
194	        coeffs[y * w + x] = inversed[x]
195	      }
196	    }
197	  }
198	  // 转为图像
199	  let out : Array[Byte] = Array::make(n, b'\x00')
200	  for i in 0..<n {
201	    let val = coeffs[i].to_int()
202	    out[i] = val.clamp(min=0, max=255).to_byte()
203	  }
204	  @types.Image::{
205	    width: w,
206	    height: h,
207	    channels: 1,
208	    data: Bytes::from_array(out),
209	  }
210	}
   	…

217	pub fn haar_denoise(
218	  img : @types.Image,
219	  threshold? : Float = 10.0F,
220	  soft? : Bool = true,
221	  levels? : Int = 3,
222	) -> @types.Image raise @types.LoadError {
223	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
224	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
225	      "haar_denoise 仅支持 channels=1/3/4",
226	    )
227	  }
228	  let result = haar_transform_2d(img, levels~)
229	  let w = result.width
230	  let h = result.height
231	  let coeffs = result.coefficients
232	  // 阈值处理（保留低频系数不变）
233	  let low_w = w >> levels
234	  let low_h = h >> levels
235	  for y in 0..<h {
236	    for x in 0..<w {
237	      // 保留低频区域
238	      if x < low_w && y < low_h {
239	        continue
240	      }
241	      let idx = y * w + x
242	      let c = coeffs[idx]
243	      if soft {
244	        // 软阈值
245	        if c > threshold {
246	          coeffs[idx] = c - threshold
247	        } else if c < -threshold {
248	          coeffs[idx] = c + threshold	<-- UNCOVERED
249	        } else {
250	          coeffs[idx] = 0.0F
251	        }
252	        // 硬阈值
253	      } else if c.abs() < threshold {
254	        coeffs[idx] = 0.0F
255	      }
256	    }
257	  }
258	  haar_inverse_transform_2d(result)
259	}
   	…

12 uncovered line(s) in src\process\fusion\image_fusion.mbt:

21	pub fn fuse_alpha(
22	  img1 : @types.Image,
23	  img2 : @types.Image,
24	  alpha? : Double = 0.5,
25	) -> @types.Image {
26	  let a = alpha
27	  if img1.width != img2.width || img1.height != img2.height {
28	    return img1	<-- UNCOVERED
29	  }
30	  let w = img1.width
31	  let h = img1.height
32	  let channels = img1.channels.min(img2.channels)
33	  let out_channels = img1.channels.max(img2.channels)
34	  let out_data : Array[Byte] = Array::make(w * h * out_channels, b'\x00')
35	  for y in 0..<h {
36	    for x in 0..<w {
37	      let idx1 = (y * w + x) * img1.channels
38	      let idx2 = (y * w + x) * img2.channels
39	      let out_idx = (y * w + x) * out_channels
40	      for c in 0..<channels {
41	        let v1 = img1.data[idx1 + c].to_double()
42	        let v2 = img2.data[idx2 + c].to_double()
43	        let fused = v1 * a + v2 * (1.0 - a)
44	        out_data[out_idx + c] = fused.min(255.0).max(0.0).to_int().to_byte()
45	      }
46	      // 复制额外通道
47	      for c in channels..<out_channels {
48	        if c < img1.channels {	<-- UNCOVERED
49	          out_data[out_idx + c] = img1.data[idx1 + c]
50	        } else if c < img2.channels {
51	          out_data[out_idx + c] = img2.data[idx2 + c]
52	        }
53	      }
54	    }
55	  }
56	  @types.Image::{
57	    width: w,
58	    height: h,
59	    channels: out_channels,
60	    data: Bytes::from_array(out_data),
61	  }
62	}
  	…

66	fn compute_sharpness(img : @types.Image, x : Int, y : Int) -> Double {
67	  let w = img.width
68	  let h = img.height
69	  let channels = img.channels
70	  if x <= 0 || x >= w - 1 || y <= 0 || y >= h - 1 {
71	    return 0.0
72	  }
73	  let _ = (y * w + x) * channels
74	  let gray = (px : Int, py : Int) => {
75	    let i = (py * w + px) * channels
76	    let r = img.data[i].to_double()
77	    let g = if channels >= 2 { img.data[i + 1].to_double() } else { r }	<-- UNCOVERED
78	    let b = if channels >= 3 { img.data[i + 2].to_double() } else { r }
79	    0.299 * r + 0.587 * g + 0.114 * b
80	  }
81	  // 拉普拉斯算子
82	  let laplacian = 4.0 * gray(x, y) -
83	    gray(x - 1, y) -
84	    gray(x + 1, y) -
85	    gray(x, y - 1) -
86	    gray(x, y + 1)
87	  laplacian * laplacian
88	}
  	…

 93	pub fn fuse_multifocus(
 94	  img1 : @types.Image,
 95	  img2 : @types.Image,
 96	) -> @types.Image {
 97	  if img1.width != img2.width || img1.height != img2.height {
 98	    return img1	<-- UNCOVERED
 99	  }
100	  let w = img1.width
101	  let h = img1.height
102	  let channels = img1.channels.min(img2.channels)
103	  let out_channels = img1.channels.max(img2.channels)
104	  let out_data : Array[Byte] = Array::make(w * h * out_channels, b'\x00')
105	  // 计算清晰度图
106	  let sharpness1 : Array[Double] = Array::make(w * h, 0.0)
107	  let sharpness2 : Array[Double] = Array::make(w * h, 0.0)
108	  for y in 0..<h {
109	    for x in 0..<w {
110	      sharpness1[y * w + x] = compute_sharpness(img1, x, y)
111	      sharpness2[y * w + x] = compute_sharpness(img2, x, y)
112	    }
113	  }
114	  // 像素级融合
115	  for y in 0..<h {
116	    for x in 0..<w {
117	      let idx1 = (y * w + x) * img1.channels
118	      let idx2 = (y * w + x) * img2.channels
119	      let out_idx = (y * w + x) * out_channels
120	      let use_img1 = sharpness1[y * w + x] >= sharpness2[y * w + x]
121	      for c in 0..<channels {
122	        if use_img1 {
123	          out_data[out_idx + c] = img1.data[idx1 + c]
124	        } else {
125	          out_data[out_idx + c] = img2.data[idx2 + c]
126	        }
127	      }
128	      // 复制额外通道
129	      for c in channels..<out_channels {
130	        if c < img1.channels {	<-- UNCOVERED
131	          out_data[out_idx + c] = img1.data[idx1 + c]
132	        } else if c < img2.channels {
133	          out_data[out_idx + c] = img2.data[idx2 + c]
134	        }
135	      }
136	    }
137	  }
138	  @types.Image::{
139	    width: w,
140	    height: h,
141	    channels: out_channels,
142	    data: Bytes::from_array(out_data),
143	  }
144	}
   	…

149	pub fn fuse_exposure(images : Array[@types.Image]) -> @types.Image {
150	  if images.length() == 0 {
151	    return @types.Image::{	<-- UNCOVERED
152	      width: 1,
153	      height: 1,
154	      channels: 1,
155	      data: Bytes::from_array([b'\x00']),	<-- UNCOVERED
156	    }
157	  }
158	  if images.length() == 1 {
159	    return images[0]	<-- UNCOVERED
160	  }
161	  let w = images[0].width
162	  let h = images[0].height
163	  let channels = images[0].channels
164	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
165	  // 计算每张图像的权重（基于对比度、饱和度、曝光度）
166	  let weights : Array[Array[Double]] = []
167	  for img in images {
168	    let weight_map : Array[Double] = Array::make(w * h, 0.0)
169	    for y in 0..<h {
170	      for x in 0..<w {
171	        let idx = (y * w + x) * img.channels
172	        let r = img.data[idx].to_double()
173	        let g = if img.channels >= 2 {
174	          img.data[idx + 1].to_double()	<-- UNCOVERED
175	        } else {
176	          r
177	        }
178	        let b = if img.channels >= 3 {
179	          img.data[idx + 2].to_double()	<-- UNCOVERED
180	        } else {
181	          r
182	        }
183	        let luminance = 0.299 * r + 0.587 * g + 0.114 * b
184	        // 曝光度权重（高斯，中心在 128）
185	        let exposure_weight = @math.exp(
186	          -(luminance - 128.0) * (luminance - 128.0) / (2.0 * 64.0 * 64.0),
187	        )
188	        // 对比度权重（拉普拉斯）
189	        let contrast_weight = compute_sharpness(img, x, y) + 1.0
190	        weight_map[y * w + x] = exposure_weight * contrast_weight
191	      }
192	    }
193	    weights.push(weight_map)
194	  }
195	  // 归一化权重并融合
196	  for y in 0..<h {
197	    for x in 0..<w {
198	      let mut weight_sum = 0.0
199	      for i in 0..<weights.length() {
200	        weight_sum += weights[i][y * w + x]
201	      }
202	      let out_idx = (y * w + x) * channels
203	      for c in 0..<channels {
204	        if weight_sum > 0.0 {
205	          let mut fused = 0.0
206	          for i in 0..<images.length() {
207	            let idx = (y * w + x) * images[i].channels
208	            if c < images[i].channels {
209	              fused += images[i].data[idx + c].to_double() *
210	                weights[i][y * w + x] /
211	                weight_sum
212	            }
213	          }
214	          out_data[out_idx + c] = fused.min(255.0).max(0.0).to_int().to_byte()
215	        } else {
216	          out_data[out_idx + c] = images[0].data[(y * w + x) *	<-- UNCOVERED
217	            images[0].channels +
218	            c]
219	        }
220	      }
221	    }
222	  }
223	  @types.Image::{
224	    width: w,
225	    height: h,
226	    channels,
227	    data: Bytes::from_array(out_data),
228	  }
229	}
   	…

233	pub fn fuse_images(
234	  img1 : @types.Image,
235	  img2 : @types.Image,
236	  config? : FusionConfig,
237	) -> @types.Image {
238	  let cfg = match config {
239	    Some(c) => c
240	    None => fusion_default_config()
241	  }
242	  match cfg.method_type {
243	    "alpha" => fuse_alpha(img1, img2, alpha=cfg.alpha)
244	    "multifocus" => fuse_multifocus(img1, img2)
245	    _ => fuse_alpha(img1, img2, alpha=cfg.alpha)	<-- UNCOVERED
246	  }
247	}
   	…

27 uncovered line(s) in src\process\fusion_enhanced\fusion_registration_enhancement.mbt:

107	pub fn fuse_weighted_average(
108	  images : Array[@types.Image],
109	  weights : Array[Double],
110	) -> @types.Image {
111	  if images.length() == 0 {
112	    return @types.Image::{	<-- UNCOVERED
113	      width: 0,
114	      height: 0,
115	      channels: 1,
116	      data: Bytes::from_array([]),	<-- UNCOVERED
117	    }
118	  }
119	
120	  let w = images[0].width
121	  let h = images[0].height
122	  let total = w * h
123	  let out : Array[Byte] = []
124	
125	  for i in 0..<total {
126	    let mut sum = 0.0
127	    let mut weight_sum = 0.0
128	    for j in 0..<images.length() {
129	      let weight = if j < weights.length() { weights[j] } else { 1.0 }
130	      sum += images[j].data[i].to_double() * weight
131	      weight_sum += weight
132	    }
133	    let avg = if weight_sum > 0.0 { sum / weight_sum } else { 0.0 }
134	    let clamped = if avg < 0.0 {
135	      0.0	<-- UNCOVERED
136	    } else if avg > 255.0 {
137	      255.0	<-- UNCOVERED
138	    } else {
139	      avg
140	    }
141	    out.push(clamped.to_int().to_byte())
142	  }
143	
144	  @types.Image::{
145	    width: w,
146	    height: h,
147	    channels: images[0].channels,
148	    data: Bytes::from_array(out),
149	  }
150	}
   	…

154	pub fn fuse_maximum(images : Array[@types.Image]) -> @types.Image {
155	  if images.length() == 0 {
156	    return @types.Image::{	<-- UNCOVERED
157	      width: 0,
158	      height: 0,
159	      channels: 1,
160	      data: Bytes::from_array([]),	<-- UNCOVERED
161	    }
162	  }
163	
164	  let w = images[0].width
165	  let h = images[0].height
166	  let total = w * h
167	  let out : Array[Byte] = []
168	
169	  for i in 0..<total {
170	    let mut max_val = 0.0
171	    for j in 0..<images.length() {
172	      let val = images[j].data[i].to_double()
173	      if val > max_val {
174	        max_val = val
175	      }
176	    }
177	    out.push(max_val.to_int().to_byte())
178	  }
179	
180	  @types.Image::{
181	    width: w,
182	    height: h,
183	    channels: images[0].channels,
184	    data: Bytes::from_array(out),
185	  }
186	}
   	…

190	pub fn fuse_minimum(images : Array[@types.Image]) -> @types.Image {
191	  if images.length() == 0 {
192	    return @types.Image::{	<-- UNCOVERED
193	      width: 0,
194	      height: 0,
195	      channels: 1,
196	      data: Bytes::from_array([]),	<-- UNCOVERED
197	    }
198	  }
199	
200	  let w = images[0].width
201	  let h = images[0].height
202	  let total = w * h
203	  let out : Array[Byte] = []
204	
205	  for i in 0..<total {
206	    let mut min_val = 255.0
207	    for j in 0..<images.length() {
208	      let val = images[j].data[i].to_double()
209	      if val < min_val {
210	        min_val = val
211	      }
212	    }
213	    out.push(min_val.to_int().to_byte())
214	  }
215	
216	  @types.Image::{
217	    width: w,
218	    height: h,
219	    channels: images[0].channels,
220	    data: Bytes::from_array(out),
221	  }
222	}
   	…

236	pub fn gaussian_blur_fe(img : @types.Image, kernel_size : Int) -> @types.Image {
237	  let w = img.width
238	  let h = img.height
239	  let _ = w * h
240	  let out : Array[Byte] = []
241	  let radius = kernel_size / 2
242	
243	  for y in 0..<h {
244	    for x in 0..<w {
245	      let mut sum = 0.0
246	      let mut weight_sum = 0.0
247	      for dy in -radius..<(radius + 1) {
248	        for dx in -radius..<(radius + 1) {
249	          let nx = x + dx
250	          let ny = y + dy
251	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
252	            let dist = (dx * dx + dy * dy).to_double()
253	            let weight = (
254	                0.0 - dist / (2.0 * radius.to_double() * radius.to_double())
255	              ) *
256	              0.1
257	            sum += img.data[ny * w + nx].to_double() * weight
258	            weight_sum += weight
259	          }
260	        }
261	      }
262	      let avg = if weight_sum > 0.0 {
263	        sum / weight_sum	<-- UNCOVERED
264	      } else {
265	        img.data[y * w + x].to_double()
266	      }
267	      let clamped = if avg < 0.0 {
268	        0.0	<-- UNCOVERED
269	      } else if avg > 255.0 {
270	        255.0	<-- UNCOVERED
271	      } else {
272	        avg
273	      }
274	      out.push(clamped.to_int().to_byte())
275	    }
276	  }
277	
278	  @types.Image::{
279	    width: w,
280	    height: h,
281	    channels: img.channels,
282	    data: Bytes::from_array(out),
283	  }
284	}
   	…

316	pub fn upsample_fe(img : @types.Image) -> @types.Image {
317	  let w = img.width * 2
318	  let h = img.height * 2
319	  let out : Array[Byte] = []
320	
321	  for y in 0..<h {
322	    for x in 0..<w {
323	      let src_x = x.to_double() / 2.0
324	      let src_y = y.to_double() / 2.0
325	      let x0 = src_x.to_int()
326	      let y0 = src_y.to_int()
327	      let x1 = x0 + 1
328	      let y1 = y0 + 1
329	      let fx = src_x - x0.to_double()
330	      let fy = src_y - y0.to_double()
331	
332	      let cx0 = if x0 < 0 {
333	        0	<-- UNCOVERED
334	      } else if x0 >= img.width {
335	        img.width - 1	<-- UNCOVERED
336	      } else {
337	        x0
338	      }
339	      let cy0 = if y0 < 0 {
340	        0	<-- UNCOVERED
341	      } else if y0 >= img.height {
342	        img.height - 1	<-- UNCOVERED
343	      } else {
344	        y0
345	      }
346	      let cx1 = if x1 < 0 {
347	        0	<-- UNCOVERED
348	      } else if x1 >= img.width {
349	        img.width - 1
350	      } else {
351	        x1
352	      }
353	      let cy1 = if y1 < 0 {
354	        0	<-- UNCOVERED
355	      } else if y1 >= img.height {
356	        img.height - 1
357	      } else {
358	        y1
359	      }
360	
361	      let p00 = img.data[cy0 * img.width + cx0].to_double()
362	      let p01 = img.data[cy0 * img.width + cx1].to_double()
363	      let p10 = img.data[cy1 * img.width + cx0].to_double()
364	      let p11 = img.data[cy1 * img.width + cx1].to_double()
365	
366	      let top = p00 * (1.0 - fx) + p01 * fx
367	      let bottom = p10 * (1.0 - fx) + p11 * fx
368	      let value = top * (1.0 - fy) + bottom * fy
369	      let clamped = if value < 0.0 {
370	        0.0	<-- UNCOVERED
371	      } else if value > 255.0 {
372	        255.0	<-- UNCOVERED
373	      } else {
374	        value
375	      }
376	      out.push(clamped.to_int().to_byte())
377	    }
378	  }
379	
380	  @types.Image::{
381	    width: w,
382	    height: h,
383	    channels: img.channels,
384	    data: Bytes::from_array(out),
385	  }
386	}
   	…

390	pub fn fuse_laplacian_pyramid(
391	  images : Array[@types.Image],
392	  levels : Int,
393	) -> @types.Image {
394	  ignore(levels)
395	  if images.length() < 2 {
396	    return if images.length() == 1 {	<-- UNCOVERED
397	      images[0]
398	    } else {
399	      @types.Image::{	<-- UNCOVERED
400	        width: 0,
401	        height: 0,
402	        channels: 1,
403	        data: Bytes::from_array([]),	<-- UNCOVERED
404	      }
405	    }
406	  }
407	
408	  // 简化：直接加权平均
409	  let weights : Array[Double] = []
410	  for _ in 0..<images.length() {
411	    weights.push(1.0 / images.length().to_double())
412	  }
413	  fuse_weighted_average(images, weights)
414	}
   	…

418	pub fn fuse_images(
419	  images : Array[@types.Image],
420	  config : FusionConfig,
421	) -> FusionResult {
422	  let result = if config.algorithm == FusionAlgorithm::WeightedAverage {
423	    fuse_weighted_average(images, config.weights)
424	  } else if config.algorithm == FusionAlgorithm::Maximum {	<-- UNCOVERED
425	    fuse_maximum(images)
426	  } else if config.algorithm == FusionAlgorithm::Minimum {
427	    fuse_minimum(images)
428	  } else if config.algorithm == FusionAlgorithm::SimpleMean {
429	    fuse_simple_mean(images)
430	  } else if config.algorithm == FusionAlgorithm::LaplacianPyramid {
431	    fuse_laplacian_pyramid(images, config.pyramid_levels)
432	  } else {
433	    fuse_simple_mean(images)	<-- UNCOVERED
434	  }
435	
436	  FusionResult::{
437	    image: result,
438	    algorithm: config.algorithm,
439	    num_images: images.length(),
440	    processing_time_ms: 0.0,
441	    fusion_quality: 0.0,
442	  }
443	}
   	…

500	pub fn register_images(
501	  reference : @types.Image,
502	  target : @types.Image,
503	  config : RegistrationConfig,
504	) -> RegistrationResult {
505	  if config.algorithm == RegistrationAlgorithm::PhaseCorrelation {
506	    register_phase_correlation(reference, target)
507	  } else {
508	    register_phase_correlation(reference, target)	<-- UNCOVERED
509	  }
510	}
   	…

571	pub fn fusion_report_to_string(
572	  result : FusionResult,
573	  quality : Double,
574	) -> String {
575	  let mut report = "## 图像融合报告\n\n"
576	  report = report + "### 处理信息\n\n"
577	  report = report + "| 指标 | 值 |\n|------|------|\n"
578	  let algo_str = if result.algorithm == FusionAlgorithm::WeightedAverage {
579	    "加权平均"
580	  } else if result.algorithm == FusionAlgorithm::LaplacianPyramid {	<-- UNCOVERED
581	    "拉普拉斯金字塔"
582	  } else if result.algorithm == FusionAlgorithm::GaussianPyramid {
583	    "高斯金字塔"
584	  } else if result.algorithm == FusionAlgorithm::Wavelet {
585	    "小波变换"
586	  } else if result.algorithm == FusionAlgorithm::PCA {
587	    "PCA"
588	  } else if result.algorithm == FusionAlgorithm::IHS {
589	    "IHS"
590	  } else if result.algorithm == FusionAlgorithm::Brovey {
591	    "Brovey"
592	  } else if result.algorithm == FusionAlgorithm::SimpleMean {
593	    "简单均值"
594	  } else if result.algorithm == FusionAlgorithm::Maximum {
595	    "最大值"
596	  } else if result.algorithm == FusionAlgorithm::Minimum {
597	    "最小值"
598	  } else {
599	    "未知"	<-- UNCOVERED
600	  }
601	  report = report + "| 算法 | " + algo_str + " |\n"
602	  report = report +
603	    "| 融合图像数 | " +
604	    result.num_images.to_string() +
605	    " |\n"
606	  report = report +
607	    "| 处理耗时 | " +
608	    result.processing_time_ms.to_string() +
609	    "ms |\n"
610	  report = report +
611	    "| 融合质量(空间频率) | " +
612	    quality.to_string() +
613	    " |\n"
614	
615	  report
616	}
   	…

3 uncovered line(s) in src\process\generative_ai_advanced\generative_ai_advanced.mbt:

367	pub fn simulate_vae_reconstruction(
368	  config : VAEConfig,
369	  input_samples : Array[Array[Double]],
370	) -> GenerationResult {
371	  let samples : Array[Array[Double]] = []
372	  let image_size = config.image_size
373	  let num_channels = config.in_channels
374	
375	  for input in input_samples {
376	    let sample : Array[Double] = []
377	    for i in 0..<input.length() {
378	      // 简化的重构：添加噪声
379	      let noise = 0.05
380	      let reconstructed = input[i] + noise
381	      let clamped = if reconstructed > 1.0 {
382	        1.0	<-- UNCOVERED
383	      } else if reconstructed < 0.0 {
384	        0.0	<-- UNCOVERED
385	      } else {
386	        reconstructed
387	      }
388	      sample.push(clamped)
389	    }
390	    samples.push(sample)
391	  }
392	
393	  GenerationResult::{
394	    generated_samples: samples,
395	    num_samples: input_samples.length(),
396	    image_size,
397	    num_channels,
398	    model_type: "vae",
399	    inference_time_ms: 20.0,
400	    fid_score: 35.0,
401	    is_score: 100.0,
402	    precision: 0.6,
403	    recall: 0.55,
404	  }
405	}
   	…

409	pub fn generative_ai_report_to_string(
410	  task_type : GenerativeAITaskType,
411	  result : GenerationResult,
412	  config_description : String,
413	) -> String {
414	  let mut report = "## 生成式AI高级报告\n\n"
415	
416	  report = report + "### 任务信息\n\n"
417	  report = report + "| 指标 | 值 |\n|------|------|\n"
418	  report = report +
419	    "| 任务类型 | " +
420	    (match task_type {
421	      GenerativeAITaskType::DiffusionModel => "扩散模型"
422	      GenerativeAITaskType::GAN => "GAN"	<-- UNCOVERED
423	      GenerativeAITaskType::VAE => "VAE"
424	      GenerativeAITaskType::FlowModel => "流模型"
425	      GenerativeAITaskType::AutoregressiveModel => "自回归模型"
426	      GenerativeAITaskType::EnergyBasedModel => "能量模型"
427	      GenerativeAITaskType::TextToImage => "文生图"
428	      GenerativeAITaskType::ImageToImage => "图生图"
429	      _ => "未知"
430	    }) +
431	    " |\n"
432	  report = report + "| 模型类型 | " + result.model_type + " |\n"
433	  report = report +
434	    "| 生成样本数 | " +
435	    result.num_samples.to_string() +
436	    " |\n"
437	  report = report +
438	    "| 图像尺寸 | " +
439	    result.image_size.to_string() +
440	    "x" +
441	    result.image_size.to_string() +
442	    " |\n"
443	  report = report + "| 通道数 | " + result.num_channels.to_string() + " |\n"
444	  report = report +
445	    "| 推理时间 | " +
446	    result.inference_time_ms.to_string() +
447	    " ms |\n"
448	
449	  report = report + "\n### 质量指标\n\n"
450	  report = report + "| 指标 | 值 |\n|------|------|\n"
451	  report = report + "| FID分数 | " + result.fid_score.to_string() + " |\n"
452	  report = report + "| IS分数 | " + result.is_score.to_string() + " |\n"
453	  report = report +
454	    "| 精度 | " +
455	    (result.precision * 100.0).to_string() +
456	    "% |\n"
457	  report = report +
458	    "| 召回率 | " +
459	    (result.recall * 100.0).to_string() +
460	    "% |\n"
461	
462	  report = report + "\n### 配置\n\n"
463	  report = report + config_description + "\n"
464	
465	  report
466	}
   	…

3 uncovered line(s) in src\process\graph_neural\graph_neural.mbt:

273	pub fn graph_convolution(
274	  graph : GraphData,
275	  config : GNNConfig,
276	) -> NodeEmbeddingResult {
277	  let embeddings : Array[Array[Double]] = []
278	
279	  for node_idx in 0..<graph.num_nodes {
280	    let node = graph.nodes[node_idx]
281	    let aggregated : Array[Double] = []
282	
283	    // 初始化聚合特征
284	    for _ in 0..<config.hidden_dim {
285	      aggregated.push(0.0)
286	    }
287	
288	    // 聚合邻居特征
289	    let mut neighbor_count = 0
290	    for neighbor_idx in 0..<graph.num_nodes {
291	      if graph.adjacency_matrix[node_idx][neighbor_idx] > 0.0 {
292	        let neighbor = graph.nodes[neighbor_idx]
293	        let weight = graph.adjacency_matrix[node_idx][neighbor_idx]
294	
295	        for f in 0..<config.hidden_dim {
296	          let feat_idx = f % neighbor.num_features
297	          aggregated[f] += neighbor.features[feat_idx] * weight
298	        }
299	        neighbor_count += 1
300	      }
301	    }
302	
303	    // 归一化
304	    if neighbor_count > 0 {
305	      for f in 0..<config.hidden_dim {
306	        aggregated[f] /= neighbor_count.to_double()
307	      }
308	    }
309	
310	    // 添加自环
311	    for f in 0..<config.hidden_dim {
312	      let feat_idx = f % node.num_features
313	      aggregated[f] += node.features[feat_idx]
314	    }
315	
316	    // 激活函数（简化的ReLU）
317	    for f in 0..<config.hidden_dim {
318	      if aggregated[f] < 0.0 {
319	        aggregated[f] = 0.0	<-- UNCOVERED
320	      }
321	    }
322	
323	    embeddings.push(aggregated)
324	  }
325	
326	  NodeEmbeddingResult::{
327	    embeddings,
328	    num_nodes: graph.num_nodes,
329	    embedding_dim: config.hidden_dim,
330	    method_type: "GCN",
331	    training_loss: 0.1,
332	    training_accuracy: 0.85,
333	  }
334	}
   	…

338	pub fn graph_attention(
339	  graph : GraphData,
340	  config : GNNConfig,
341	) -> NodeEmbeddingResult {
342	  let embeddings : Array[Array[Double]] = []
343	  let attention_weights : Array[Array[Double]] = []
344	
345	  for node_idx in 0..<graph.num_nodes {
346	    let node = graph.nodes[node_idx]
347	    let aggregated : Array[Double] = []
348	    let weights : Array[Double] = []
349	
350	    for _ in 0..<config.hidden_dim {
351	      aggregated.push(0.0)
352	    }
353	
354	    // 计算注意力权重
355	    let mut attention_sum = 0.0
356	    for neighbor_idx in 0..<graph.num_nodes {
357	      if graph.adjacency_matrix[node_idx][neighbor_idx] > 0.0 {
358	        let neighbor = graph.nodes[neighbor_idx]
359	        // 简化的注意力分数
360	        let mut score = 0.0
361	        for f in 0..<node.num_features {
362	          if f < neighbor.num_features {
363	            score += node.features[f] * neighbor.features[f]
364	          }
365	        }
366	        let attention = (score / node.num_features.to_double() + 1.0) / 2.0
367	        weights.push(attention)
368	        attention_sum += attention
369	      } else {
370	        weights.push(0.0)
371	      }
372	    }
373	
374	    // 归一化注意力权重
375	    if attention_sum > 0.0 {
376	      for i in 0..<weights.length() {
377	        weights[i] /= attention_sum
378	      }
379	    }
380	
381	    // 加权聚合
382	    for neighbor_idx in 0..<graph.num_nodes {
383	      if graph.adjacency_matrix[node_idx][neighbor_idx] > 0.0 {
384	        let neighbor = graph.nodes[neighbor_idx]
385	        let weight = weights[neighbor_idx]
386	
387	        for f in 0..<config.hidden_dim {
388	          let feat_idx = f % neighbor.num_features
389	          aggregated[f] += neighbor.features[feat_idx] * weight
390	        }
391	      }
392	    }
393	
394	    // 激活函数
395	    for f in 0..<config.hidden_dim {
396	      if aggregated[f] < 0.0 {
397	        aggregated[f] = 0.0	<-- UNCOVERED
398	      }
399	    }
400	
401	    embeddings.push(aggregated)
402	    attention_weights.push(weights)
403	  }
404	
405	  NodeEmbeddingResult::{
406	    embeddings,
407	    num_nodes: graph.num_nodes,
408	    embedding_dim: config.hidden_dim,
409	    method_type: "GAT",
410	    training_loss: 0.08,
411	    training_accuracy: 0.88,
412	  }
413	}
   	…

545	pub fn graph_neural_report_to_string(
546	  embedding_result : NodeEmbeddingResult,
547	  graph : GraphData,
548	  config : GNNConfig,
549	) -> String {
550	  let mut report = "## 图神经网络与关系推理报告\n\n"
551	
552	  report = report + "### 图信息\n\n"
553	  report = report + "| 指标 | 值 |\n|------|------|\n"
554	  report = report + "| 节点数 | " + graph.num_nodes.to_string() + " |\n"
555	  report = report + "| 边数 | " + graph.num_edges.to_string() + " |\n"
556	  report = report +
557	    "| 有向图 | " +
558	    (if graph.is_directed { "是" } else { "否" }) +
559	    " |\n"
560	  report = report +
561	    "| 加权图 | " +
562	    (if graph.is_weighted { "是" } else { "否" }) +
563	    " |\n"
564	
565	  report = report + "\n### GNN配置\n\n"
566	  report = report + "| 指标 | 值 |\n|------|------|\n"
567	  report = report +
568	    "| 层类型 | " +
569	    (match config.layer_type {
570	      GNNLayerType::GCN => "GCN"
571	      GNNLayerType::GAT => "GAT"	<-- UNCOVERED
572	      GNNLayerType::GraphSAGE => "GraphSAGE"
573	      GNNLayerType::GIN => "GIN"
574	      _ => "未知"
575	    }) +
576	    " |\n"
577	  report = report + "| 层数 | " + config.num_layers.to_string() + " |\n"
578	  report = report + "| 隐藏维度 | " + config.hidden_dim.to_string() + " |\n"
579	  report = report + "| 输出维度 | " + config.output_dim.to_string() + " |\n"
580	  report = report + "| Dropout率 | " + config.dropout_rate.to_string() + " |\n"
581	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
582	  report = report +
583	    "| 注意力头数 | " +
584	    config.num_heads.to_string() +
585	    " |\n"
586	
587	  report = report + "\n### 嵌入结果\n\n"
588	  report = report + "| 指标 | 值 |\n|------|------|\n"
589	  report = report + "| 方法 | " + embedding_result.method_type + " |\n"
590	  report = report +
591	    "| 节点数 | " +
592	    embedding_result.num_nodes.to_string() +
593	    " |\n"
594	  report = report +
595	    "| 嵌入维度 | " +
596	    embedding_result.embedding_dim.to_string() +
597	    " |\n"
598	  report = report +
599	    "| 训练损失 | " +
600	    embedding_result.training_loss.to_string() +
601	    " |\n"
602	  report = report +
603	    "| 训练精度 | " +
604	    (embedding_result.training_accuracy * 100.0).to_string() +
605	    "% |\n"
606	
607	  report
608	}
   	…

14 uncovered line(s) in src\process\image_classification_advanced\image_classification_advanced.mbt:

199	fn extract_simple_features(
200	  image : Array[Double],
201	  num_bins : Int,
202	) -> Array[Double] {
203	  let features : Array[Double] = []
204	  for _ in 0..<num_bins {
205	    features.push(0.0)
206	  }
207	
208	  for value in image {
209	    let bin_idx = if value < 0.0 {
210	      0	<-- UNCOVERED
211	    } else if value >= 255.0 {
212	      num_bins - 1	<-- UNCOVERED
213	    } else {
214	      (value / 256.0 * num_bins.to_double()).to_int()
215	    }
216	    if bin_idx >= 0 && bin_idx < num_bins {
217	      features[bin_idx] += 1.0
218	    }
219	  }
220	
221	  let total = image.length().to_double()
222	  if total > 0.0 {
223	    for i in 0..<features.length() {
224	      features[i] = features[i] / total
225	    }
226	  }
227	
228	  features
229	}
   	…

233	fn softmax(logits : Array[Double]) -> Array[Double] {
234	  let result : Array[Double] = []
235	  let mut max_val = logits[0]
236	  for v in logits {
237	    if v > max_val {
238	      max_val = v
239	    }
240	  }
241	
242	  let mut sum_exp = 0.0
243	  for v in logits {
244	    // 简化的指数函数
245	    let diff = v - max_val
246	    let exp_val = if diff > 10.0 {
247	      22026.0	<-- UNCOVERED
248	    } else if diff < -10.0 {
249	      0.000045	<-- UNCOVERED
250	    } else {
251	      // 泰勒展开近似
252	      let mut result = 1.0
253	      let mut term = 1.0
254	      for i in 1..<20 {
255	        term = term * diff / i.to_double()
256	        result += term
257	      }
258	      result
259	    }
260	    sum_exp += exp_val
261	    result.push(exp_val)
262	  }
263	
264	  if sum_exp > 0.0 {
265	    for i in 0..<result.length() {
266	      result[i] = result[i] / sum_exp
267	    }
268	  }
269	
270	  result
271	}
   	…

275	pub fn classify_image(
276	  image : Array[Double],
277	  width : Int,
278	  height : Int,
279	  config : ImageClassificationConfig,
280	) -> ClassificationResult {
281	  ignore(height)
282	  ignore(width)
283	  let features = extract_simple_features(image, config.num_classes)
284	
285	  // 简化的分类逻辑：基于特征的简单加权
286	  let logits : Array[Double] = []
287	  for i in 0..<config.num_classes {
288	    if i < features.length() {
289	      logits.push(features[i] * 10.0 + 0.1)
290	    } else {
291	      logits.push(0.1)	<-- UNCOVERED
292	    }
293	  }
294	
295	  let probabilities = softmax(logits)
296	
297	  let mut max_idx = 0
298	  let mut max_prob = probabilities[0]
299	  for i in 1..<probabilities.length() {
300	    if probabilities[i] > max_prob {
301	      max_prob = probabilities[i]
302	      max_idx = i
303	    }
304	  }
305	
306	  let top_k : Array[Int] = []
307	  let top_k_conf : Array[Double] = []
308	  let indices : Array[Int] = []
309	  for i in 0..<probabilities.length() {
310	    indices.push(i)
311	  }
312	
313	  // 简单排序
314	  for _ in 0..<probabilities.length() {
315	    for j in 0..<(probabilities.length() - 1) {
316	      if probabilities[indices[j]] < probabilities[indices[j + 1]] {
317	        let temp = indices[j]
318	        indices[j] = indices[j + 1]
319	        indices[j + 1] = temp
320	      }
321	    }
322	  }
323	
324	  let k = if 5 < probabilities.length() { 5 } else { probabilities.length() }	<-- UNCOVERED
325	  for i in 0..<k {
326	    top_k.push(indices[i])
327	    top_k_conf.push(probabilities[indices[i]])
328	  }
329	
330	  let labels : Array[String] = []
331	  for i in 0..<config.num_classes {
332	    labels.push("class_" + i.to_string())
333	  }
334	
335	  ClassificationResult::{
336	    predicted_class: max_idx,
337	    predicted_label: if max_idx < labels.length() {
338	      labels[max_idx]
339	    } else {
340	      "unknown"	<-- UNCOVERED
341	    },
342	    class_probabilities: probabilities,
343	    num_classes: config.num_classes,
344	    confidence: max_prob,
345	    top_k_predictions: top_k,
346	    top_k_confidences: top_k_conf,
347	    classification_time_ms: 10.0,
348	    feature_extraction_time_ms: 5.0,
349	    inference_time_ms: 5.0,
350	    is_correct: false,
351	  }
352	}
   	…

406	pub fn classify_ensemble(
407	  image : Array[Double],
408	  width : Int,
409	  height : Int,
410	  config : ImageClassificationConfig,
411	) -> EnsembleClassificationResult {
412	  let individual_preds : Array[Int] = []
413	  let individual_confs : Array[Double] = []
414	
415	  for _ in 0..<config.num_classifiers {
416	    let result = classify_image(image, width, height, config)
417	    individual_preds.push(result.predicted_class)
418	    individual_confs.push(result.confidence)
419	  }
420	
421	  // 投票
422	  let votes : Array[Int] = []
423	  for _ in 0..<config.num_classes {
424	    votes.push(0)
425	  }
426	  for pred in individual_preds {
427	    if pred < votes.length() {
428	      votes[pred] += 1
429	    }
430	  }
431	
432	  let mut final_pred = 0
433	  let mut max_votes = 0
434	  for i in 0..<votes.length() {
435	    if votes[i] > max_votes {
436	      max_votes = votes[i]
437	      final_pred = i
438	    }
439	  }
440	
441	  let final_conf = if config.num_classifiers > 0 {
442	    max_votes.to_double() / config.num_classifiers.to_double()
443	  } else {
444	    0.0	<-- UNCOVERED
445	  }
446	
447	  EnsembleClassificationResult::{
448	    final_prediction: final_pred,
449	    final_label: "class_" + final_pred.to_string(),
450	    final_confidence: final_conf,
451	    individual_predictions: individual_preds,
452	    individual_confidences: individual_confs,
453	    num_classifiers: config.num_classifiers,
454	    ensemble_method: config.ensemble_method,
455	    classification_time_ms: 30.0,
456	    diversity_score: 0.8,
457	  }
458	}
   	…

519	pub fn image_classification_report_to_string(
520	  task_type : ImageClassificationTaskType,
521	  config : ImageClassificationConfig,
522	  result_description : String,
523	) -> String {
524	  let mut report = "## 图像分类高级报告\n\n"
525	
526	  report = report + "### 任务信息\n\n"
527	  report = report + "| 指标 | 值 |\n|------|------|\n"
528	  report = report +
529	    "| 任务类型 | " +
530	    (match task_type {
531	      ImageClassificationTaskType::BinaryClassification => "二分类"
532	      ImageClassificationTaskType::MultiClassClassification => "多分类"
533	      ImageClassificationTaskType::MultiLabelClassification => "多标签分类"	<-- UNCOVERED
534	      ImageClassificationTaskType::FineGrainedClassification =>
535	        "细粒度分类"	<-- UNCOVERED
536	      ImageClassificationTaskType::HierarchicalClassification => "层次分类"
537	      ImageClassificationTaskType::ZeroShotClassification => "零样本分类"
538	      ImageClassificationTaskType::FewShotClassification => "少样本分类"
539	      ImageClassificationTaskType::OpenSetClassification => "开集分类"
540	      _ => "未知"
541	    }) +
542	    " |\n"
543	  report = report +
544	    "| 分类器类型 | " +
545	    (match config.classifier_type {
546	      ClassifierType::KNN => "KNN"	<-- UNCOVERED
547	      ClassifierType::SVM => "SVM"
548	      ClassifierType::RandomForest => "随机森林"
549	      ClassifierType::DecisionTree => "决策树"
550	      ClassifierType::LogisticRegression => "逻辑回归"
551	      ClassifierType::NaiveBayes => "朴素贝叶斯"
552	      ClassifierType::NeuralNetwork => "神经网络"
553	      ClassifierType::CNN => "CNN"
554	      ClassifierType::ResNet => "ResNet"	<-- UNCOVERED
555	      ClassifierType::VGG => "VGG"
556	      ClassifierType::Inception => "Inception"
557	      ClassifierType::EfficientNet => "EfficientNet"
558	      ClassifierType::VisionTransformer => "Vision Transformer"
559	      _ => "未知"
560	    }) +
561	    " |\n"
562	  report = report +
563	    "| 特征提取 | " +
564	    (match config.feature_extraction_method {
565	      FeatureExtractionMethod::ColorHistogram => "颜色直方图"	<-- UNCOVERED
566	      FeatureExtractionMethod::HOG => "HOG"
567	      FeatureExtractionMethod::LBP => "LBP"
568	      FeatureExtractionMethod::SIFT => "SIFT"
569	      FeatureExtractionMethod::SURF => "SURF"
570	      FeatureExtractionMethod::ORB => "ORB"
571	      FeatureExtractionMethod::Gabor => "Gabor"
572	      FeatureExtractionMethod::HaarWavelet => "Haar小波"
573	      FeatureExtractionMethod::DeepFeature => "深度特征"
574	      FeatureExtractionMethod::TransferLearning => "迁移学习"	<-- UNCOVERED
575	      _ => "未知"
576	    }) +
577	    " |\n"
578	  report = report + "| 类别数 | " + config.num_classes.to_string() + " |\n"
579	  report = report +
580	    "| 迁移学习 | " +
581	    (if config.use_transfer_learning { "开启" } else { "关闭" }) +
582	    " |\n"
583	  report = report + "| 预训练模型 | " + config.pretrained_model + " |\n"
584	  report = report +
585	    "| 数据增强 | " +
586	    (if config.use_data_augmentation { "开启" } else { "关闭" }) +
587	    " |\n"
588	  report = report +
589	    "| 集成学习 | " +
590	    (if config.use_ensemble { "开启" } else { "关闭" }) +
591	    " |\n"
592	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
593	  report = report + "| 训练轮数 | " + config.num_epochs.to_string() + " |\n"
594	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
595	
596	  report = report + "\n### 结果\n\n"
597	  report = report + result_description + "\n"
598	
599	  report
600	}
   	…

9 uncovered line(s) in src\process\image_clustering_advanced\image_clustering_advanced.mbt:

222	fn euclidean_distance_cl(v1 : Array[Double], v2 : Array[Double]) -> Double {
223	  let mut sum = 0.0
224	  let min_len = if v1.length() < v2.length() {
225	    v1.length()	<-- UNCOVERED
226	  } else {
227	    v2.length()
228	  }
229	  for i in 0..<min_len {
230	    let diff = v1[i] - v2[i]
231	    sum += diff * diff
232	  }
233	  // 简化的平方根
234	  let mut result = sum
235	  for _ in 0..<10 {
236	    if result > 0.0 {
237	      result = (result + sum / result) / 2.0
238	    }
239	  }
240	  result
241	}
   	…

350	pub fn dbscan_clustering(
351	  data : Array[Array[Double]],
352	  eps : Double,
353	  min_samples : Int,
354	  config : ImageClusteringConfig,
355	) -> DBSCANResult {
356	  ignore(config)
357	  let num_samples = data.length()
358	  let labels : Array[Int] = []
359	  for _ in 0..<num_samples {
360	    labels.push(-1) // -1表示噪声
361	  }
362	
363	  let mut cluster_id = 0
364	  let mut core_count = 0
365	  let mut border_count = 0
366	
367	  for i in 0..<num_samples {
368	    if labels[i] != -1 {
369	      continue
370	    }
371	
372	    // 找邻居
373	    let neighbors : Array[Int] = []
374	    for j in 0..<num_samples {
375	      let dist = euclidean_distance_cl(data[i], data[j])
376	      if dist <= eps {
377	        neighbors.push(j)
378	      }
379	    }
380	
381	    if neighbors.length() < min_samples {
382	      labels[i] = -1 // 噪声
383	    } else {
384	      core_count += 1
385	      // 扩展簇
386	      labels[i] = cluster_id
387	      let mut idx = 0
388	      while idx < neighbors.length() {
389	        let neighbor = neighbors[idx]
390	        if labels[neighbor] == -1 {
391	          labels[neighbor] = cluster_id
392	          border_count += 1
393	        }
394	        if labels[neighbor] == -1 || labels[neighbor] == cluster_id {
395	          // 找邻居的邻居
396	          for j in 0..<num_samples {
397	            let dist = euclidean_distance_cl(data[neighbor], data[j])
398	            if dist <= eps {
399	              let mut found = false
400	              for n in neighbors {
401	                if n == j {
402	                  found = true
403	                }
404	              }
405	              if !found {
406	                neighbors.push(j)	<-- UNCOVERED
407	              }
408	            }
409	          }
410	        }
411	        idx += 1
412	      }
413	      cluster_id += 1
414	    }
415	  }
416	
417	  let noise_count = {
418	    let mut count = 0
419	    for l in labels {
420	      if l == -1 {
421	        count += 1
422	      }
423	    }
424	    count
425	  }
426	
427	  let cluster_sizes : Array[Int] = []
428	  for c in 0..<cluster_id {
429	    let mut size = 0
430	    for l in labels {
431	      if l == c {
432	        size += 1
433	      }
434	    }
435	    cluster_sizes.push(size)
436	  }
437	
438	  DBSCANResult::{
439	    labels,
440	    num_clusters: cluster_id,
441	    num_noise_points: noise_count,
442	    num_core_points: core_count,
443	    num_border_points: border_count,
444	    eps,
445	    min_samples,
446	    cluster_sizes,
447	    clustering_time_ms: 100.0,
448	  }
449	}
   	…

624	pub fn cluster_images(
625	  images : Array[Array[Double]],
626	  config : ImageClusteringConfig,
627	) -> ClusteringResult {
628	  let num_samples = images.length()
629	
630	  // 提取特征（简化：使用图像均值作为特征）
631	  let features : Array[Array[Double]] = []
632	  for img in images {
633	    let feature : Array[Double] = []
634	    let mut sum = 0.0
635	    for v in img {
636	      sum += v
637	    }
638	    let avg = if img.length() > 0 {
639	      sum / img.length().to_double()
640	    } else {
641	      0.0	<-- UNCOVERED
642	    }
643	    feature.push(avg)
644	    features.push(feature)
645	  }
646	
647	  let kmeans_result = kmeans_clustering(features, config.num_clusters, config)
648	  let silhouette = calculate_silhouette_score(features, kmeans_result.labels)
649	
650	  let cluster_sizes : Array[Int] = []
651	  for c in 0..<config.num_clusters {
652	    let mut size = 0
653	    for l in kmeans_result.labels {
654	      if l == c {
655	        size += 1
656	      }
657	    }
658	    cluster_sizes.push(size)
659	  }
660	
661	  ClusteringResult::{
662	    labels: kmeans_result.labels,
663	    num_clusters: config.num_clusters,
664	    num_samples,
665	    cluster_centers: kmeans_result.cluster_centers,
666	    cluster_sizes,
667	    inertia: kmeans_result.inertia,
668	    silhouette_score: silhouette,
669	    davies_bouldin_index: 1.0 - silhouette,
670	    calinski_harabasz_index: silhouette * 10.0,
671	    clustering_time_ms: kmeans_result.clustering_time_ms,
672	    num_iterations: kmeans_result.num_iterations,
673	    converged: kmeans_result.converged,
674	  }
675	}
   	…

679	pub fn image_clustering_report_to_string(
680	  task_type : ImageClusteringTaskType,
681	  config : ImageClusteringConfig,
682	  result_description : String,
683	) -> String {
684	  let mut report = "## 图像聚类高级报告\n\n"
685	
686	  report = report + "### 任务信息\n\n"
687	  report = report + "| 指标 | 值 |\n|------|------|\n"
688	  report = report +
689	    "| 任务类型 | " +
690	    (match task_type {
691	      ImageClusteringTaskType::KMeansClustering => "K-Means聚类"
692	      ImageClusteringTaskType::HierarchicalClustering => "层次聚类"	<-- UNCOVERED
693	      ImageClusteringTaskType::DBSCANClustering => "DBSCAN聚类"
694	      ImageClusteringTaskType::SpectralClustering => "谱聚类"	<-- UNCOVERED
695	      ImageClusteringTaskType::GaussianMixtureModel => "高斯混合模型"
696	      ImageClusteringTaskType::FuzzyCMeans => "模糊C均值"
697	      ImageClusteringTaskType::DensityClustering => "密度聚类"
698	      ImageClusteringTaskType::SubspaceClustering => "子空间聚类"
699	      _ => "未知"
700	    }) +
701	    " |\n"
702	  report = report +
703	    "| 聚类算法 | " +
704	    (match config.algorithm_type {
705	      ClusteringAlgorithmType::KMeans => "K-Means"
706	      ClusteringAlgorithmType::KMedoids => "K-Medoids"	<-- UNCOVERED
707	      ClusteringAlgorithmType::Hierarchical => "层次聚类"
708	      ClusteringAlgorithmType::DBSCAN => "DBSCAN"
709	      ClusteringAlgorithmType::HDBSCAN => "HDBSCAN"
710	      ClusteringAlgorithmType::Spectral => "谱聚类"
711	      ClusteringAlgorithmType::GMM => "GMM"
712	      ClusteringAlgorithmType::FuzzyCMeans => "模糊C均值"
713	      ClusteringAlgorithmType::MeanShift => "Mean Shift"
714	      ClusteringAlgorithmType::AffinityPropagation => "亲和传播"
715	      ClusteringAlgorithmType::BIRCH => "BIRCH"
716	      ClusteringAlgorithmType::OPTICS => "OPTICS"
717	      _ => "未知"
718	    }) +
719	    " |\n"
720	  report = report + "| 簇数量 | " + config.num_clusters.to_string() + " |\n"
721	  report = report +
722	    "| 距离度量 | " +
723	    (match config.distance_metric {
724	      DistanceMetricType::Euclidean => "欧氏距离"
725	      DistanceMetricType::Manhattan => "曼哈顿距离"	<-- UNCOVERED
726	      DistanceMetricType::Cosine => "余弦相似度"
727	      DistanceMetricType::Chebyshev => "切比雪夫距离"
728	      DistanceMetricType::Minkowski => "闵可夫斯基距离"
729	      DistanceMetricType::Hamming => "汉明距离"
730	      DistanceMetricType::Mahalanobis => "马氏距离"
731	      DistanceMetricType::Correlation => "相关距离"
732	      DistanceMetricType::BrayCurtis => "Bray-Curtis距离"
733	      DistanceMetricType::Canberra => "Canberra距离"
734	      _ => "未知"
735	    }) +
736	    " |\n"
737	  report = report +
738	    "| 最大迭代次数 | " +
739	    config.max_iterations.to_string() +
740	    " |\n"
741	  report = report + "| 收敛阈值 | " + config.tolerance.to_string() + " |\n"
742	  report = report + "| DBSCAN eps | " + config.eps.to_string() + " |\n"
743	  report = report +
744	    "| DBSCAN min_samples | " +
745	    config.min_samples.to_string() +
746	    " |\n"
747	  report = report +
748	    "| 链接类型 | " +
749	    (match config.linkage_type {
750	      LinkageType::Single => "单链接"	<-- UNCOVERED
751	      LinkageType::Complete => "全链接"
752	      LinkageType::Average => "平均链接"
753	      LinkageType::Ward => "Ward链接"
754	      LinkageType::Centroid => "质心链接"	<-- UNCOVERED
755	      LinkageType::Median => "中位数链接"
756	      _ => "未知"
757	    }) +
758	    " |\n"
759	  report = report +
760	    "| 模糊参数 | " +
761	    config.fuzziness_parameter.to_string() +
762	    " |\n"
763	
764	  report = report + "\n### 结果\n\n"
765	  report = report + result_description + "\n"
766	
767	  report
768	}
   	…

17 uncovered line(s) in src\process\image_colorization_advanced\image_colorization_advanced.mbt:

220	pub fn automatic_colorization(
221	  grayscale_image : Array[Double],
222	  width : Int,
223	  height : Int,
224	  config : ImageColorizationConfig,
225	) -> ColorizationResult {
226	  let colorized : Array[Double] = []
227	  let palette : Array[Array[Double]] = []
228	  let mut state = 42
229	
230	  // 生成颜色调色板
231	  for _ in 0..<16 {
232	    state = (state * 11035 + 12345) % 1000000
233	    let r = (state % 256).to_double()
234	    state = (state * 11035 + 12345) % 1000000
235	    let g = (state % 256).to_double()
236	    state = (state * 11035 + 12345) % 1000000
237	    let b = (state % 256).to_double()
238	    let color : Array[Double] = []
239	    color.push(r)
240	    color.push(g)
241	    color.push(b)
242	    palette.push(color)
243	  }
244	
245	  // 简化的上色：根据灰度值分配颜色
246	  for i in 0..<grayscale_image.length() {
247	    let gray = if i < grayscale_image.length() {
248	      grayscale_image[i]
249	    } else {
250	      128.0	<-- UNCOVERED
251	    }
252	    let palette_idx = (gray / 16.0).to_int() % palette.length()
253	    let color = palette[palette_idx]
254	
255	    // 混合灰度和颜色
256	    let r = gray * (1.0 - config.colorization_strength) +
257	      color[0] * config.colorization_strength
258	    let g = gray * (1.0 - config.colorization_strength) +
259	      color[1] * config.colorization_strength
260	    let b = gray * (1.0 - config.colorization_strength) +
261	      color[2] * config.colorization_strength
262	
263	    colorized.push(r)
264	    colorized.push(g)
265	    colorized.push(b)
266	  }
267	
268	  ColorizationResult::{
269	    colorized_image: colorized,
270	    width,
271	    height,
272	    num_channels: 3,
273	    grayscale_image,
274	    colorization_method: config.model_type,
275	    color_space: config.color_space,
276	    color_palette: palette,
277	    num_colors: palette.length(),
278	    color_consistency: 0.7,
279	    color_vibrancy: 0.6,
280	    skin_tone_accuracy: 0.65,
281	    sky_color_accuracy: 0.7,
282	    vegetation_color_accuracy: 0.65,
283	    processing_time_ms: 800.0,
284	    quality_score: 0.7,
285	  }
286	}
   	…

290	pub fn reference_based_colorization(
291	  grayscale_image : Array[Double],
292	  reference_image : Array[Double],
293	  width : Int,
294	  height : Int,
295	  config : ImageColorizationConfig,
296	) -> ReferenceColorizationResult {
297	  ignore(config)
298	  let colorized : Array[Double] = []
299	  let transfer_map : Array[Array[Double]] = []
300	  let mut state = 42
301	
302	  for i in 0..<grayscale_image.length() {
303	    let gray = if i < grayscale_image.length() {
304	      grayscale_image[i]
305	    } else {
306	      128.0	<-- UNCOVERED
307	    }
308	    let ref_color = if i < reference_image.length() {
309	      reference_image[i]
310	    } else {
311	      128.0	<-- UNCOVERED
312	    }
313	
314	    // 简化的颜色迁移
315	    let color = gray * 0.5 + ref_color * 0.5
316	    colorized.push(color)
317	
318	    state = (state * 11035 + 12345) % 1000000
319	    let map_val = (state % 1000).to_double() / 1000.0
320	    let map_row : Array[Double] = []
321	    map_row.push(map_val)
322	    transfer_map.push(map_row)
323	  }
324	
325	  ReferenceColorizationResult::{
326	    colorized_image: colorized,
327	    width,
328	    height,
329	    num_channels: 3,
330	    grayscale_image,
331	    reference_image,
332	    color_transfer_map: transfer_map,
333	    matched_regions: 10,
334	    color_consistency: 0.75,
335	    reference_similarity: 0.8,
336	    processing_time_ms: 1000.0,
337	    quality_score: 0.75,
338	  }
339	}
   	…

343	pub fn old_photo_colorization(
344	  old_photo : Array[Double],
345	  width : Int,
346	  height : Int,
347	  config : ImageColorizationConfig,
348	) -> OldPhotoColorizationResult {
349	  let colorized : Array[Double] = []
350	  let mut state = 42
351	
352	  for value in old_photo {
353	    // 简化的老照片处理：去噪 + 上色
354	    state = (state * 11035 + 12345) % 1000000
355	    let noise = (state % 1000).to_double() / 1000.0 - 0.5
356	    let denoised = value + noise * config.noise_reduction * 10.0
357	
358	    // 简化的上色
359	    state = (state * 11035 + 12345) % 1000000
360	    let color_tint = (state % 1000).to_double() / 1000.0 * 50.0
361	    let r = denoised + color_tint
362	    let g = denoised
363	    let b = denoised - color_tint * 0.5
364	
365	    if r < 0.0 {
366	      colorized.push(0.0)
367	    } else if r > 255.0 {
368	      colorized.push(255.0)	<-- UNCOVERED
369	    } else {
370	      colorized.push(r)
371	    }
372	    if g < 0.0 {
373	      colorized.push(0.0)
374	    } else if g > 255.0 {
375	      colorized.push(255.0)	<-- UNCOVERED
376	    } else {
377	      colorized.push(g)
378	    }
379	    if b < 0.0 {
380	      colorized.push(0.0)
381	    } else if b > 255.0 {
382	      colorized.push(255.0)	<-- UNCOVERED
383	    } else {
384	      colorized.push(b)
385	    }
386	  }
387	
388	  OldPhotoColorizationResult::{
389	    colorized_image: colorized,
390	    width,
391	    height,
392	    num_channels: 3,
393	    old_photo,
394	    scratch_removal: config.scratch_removal,
395	    noise_reduction: config.noise_reduction,
396	    contrast_enhancement: 0.2,
397	    colorization_strength: config.colorization_strength,
398	    era_color_palette: config.era_color_palette,
399	    processing_time_ms: 1200.0,
400	    quality_score: 0.7,
401	    realism_score: 0.65,
402	  }
403	}
   	…

407	pub fn transfer_colors(
408	  source_image : Array[Double],
409	  reference_image : Array[Double],
410	  width : Int,
411	  height : Int,
412	  config : ImageColorizationConfig,
413	) -> ColorTransferResult {
414	  let transferred : Array[Double] = []
415	
416	  for i in 0..<source_image.length() {
417	    let source_val = if i < source_image.length() {
418	      source_image[i]
419	    } else {
420	      128.0	<-- UNCOVERED
421	    }
422	    let ref_val = if i < reference_image.length() {
423	      reference_image[i]
424	    } else {
425	      128.0	<-- UNCOVERED
426	    }
427	
428	    // 简化的颜色迁移
429	    let value = source_val * 0.7 + ref_val * 0.3
430	    if value < 0.0 {
431	      transferred.push(0.0)	<-- UNCOVERED
432	    } else if value > 255.0 {
433	      transferred.push(255.0)	<-- UNCOVERED
434	    } else {
435	      transferred.push(value)
436	    }
437	  }
438	
439	  ColorTransferResult::{
440	    transferred_image: transferred,
441	    width,
442	    height,
443	    num_channels: 3,
444	    source_image,
445	    reference_image,
446	    transfer_method: "reinhard",
447	    color_space: config.color_space,
448	    luminance_preservation: 0.85,
449	    color_similarity: 0.7,
450	    processing_time_ms: 400.0,
451	    quality_score: 0.75,
452	  }
453	}
   	…

457	pub fn enhance_colors(
458	  image : Array[Double],
459	  width : Int,
460	  height : Int,
461	  config : ImageColorizationConfig,
462	) -> ColorEnhancementResult {
463	  let enhanced : Array[Double] = []
464	
465	  for value in image {
466	    // 简化的颜色增强
467	    let mut new_value = value * (1.0 + config.contrast_boost)
468	    new_value = new_value + config.brightness_adjustment * 25.5
469	    if new_value < 0.0 {
470	      new_value = 0.0	<-- UNCOVERED
471	    }
472	    if new_value > 255.0 {
473	      new_value = 255.0	<-- UNCOVERED
474	    }
475	    enhanced.push(new_value)
476	  }
477	
478	  ColorEnhancementResult::{
479	    enhanced_image: enhanced,
480	    width,
481	    height,
482	    num_channels: 3,
483	    original_image: image,
484	    saturation_boost: config.saturation_boost,
485	    contrast_boost: config.contrast_boost,
486	    brightness_adjustment: config.brightness_adjustment,
487	    white_balance_correction: 0.1,
488	    vibrancy: 0.3,
489	    processing_time_ms: 200.0,
490	    quality_score: 0.8,
491	  }
492	}
   	…

496	pub fn image_colorization_report_to_string(
497	  task_type : ColorizationTaskType,
498	  config : ImageColorizationConfig,
499	  result_description : String,
500	) -> String {
501	  let mut report = "## 图像上色高级报告\n\n"
502	
503	  report = report + "### 任务信息\n\n"
504	  report = report + "| 指标 | 值 |\n|------|------|\n"
505	  report = report +
506	    "| 任务类型 | " +
507	    (match task_type {
508	      ColorizationTaskType::AutomaticColorization => "自动上色"
509	      ColorizationTaskType::ReferenceBasedColorization => "参考上色"	<-- UNCOVERED
510	      ColorizationTaskType::ColorRestoration => "颜色修复"
511	      ColorizationTaskType::OldPhotoColorization => "老照片上色"
512	      ColorizationTaskType::GrayscaleToColor => "灰度到彩色"
513	      ColorizationTaskType::ColorTransfer => "颜色迁移"
514	      ColorizationTaskType::ColorEnhancement => "颜色增强"
515	      ColorizationTaskType::ColorCorrection => "颜色校正"
516	      _ => "未知"
517	    }) +
518	    " |\n"
519	  report = report +
520	    "| 模型类型 | " +
521	    (match config.model_type {
522	      ColorizationModelType::Zhang2016 => "Zhang 2016"	<-- UNCOVERED
523	      ColorizationModelType::DeOldify => "DeOldify"
524	      ColorizationModelType::CIC => "CIC"	<-- UNCOVERED
525	      _ => "未知"
526	    }) +
527	    " |\n"
528	  report = report +
529	    "| 颜色空间 | " +
530	    (match config.color_space {
531	      ColorSpaceType::RGB => "RGB"	<-- UNCOVERED
532	      ColorSpaceType::LAB => "LAB"
533	      ColorSpaceType::HSV => "HSV"	<-- UNCOVERED
534	      ColorSpaceType::YUV => "YUV"
535	      _ => "未知"
536	    }) +
537	    " |\n"
538	  report = report +
539	    "| 上色强度 | " +
540	    config.colorization_strength.to_string() +
541	    " |\n"
542	  report = report +
543	    "| 饱和度增强 | " +
544	    config.saturation_boost.to_string() +
545	    " |\n"
546	  report = report +
547	    "| 对比度增强 | " +
548	    config.contrast_boost.to_string() +
549	    " |\n"
550	  report = report +
551	    "| 亮度调整 | " +
552	    config.brightness_adjustment.to_string() +
553	    " |\n"
554	  report = report +
555	    "| 使用参考 | " +
556	    (if config.use_reference { "开启" } else { "关闭" }) +
557	    " |\n"
558	  report = report +
559	    "| 划痕去除 | " +
560	    (if config.scratch_removal { "开启" } else { "关闭" }) +
561	    " |\n"
562	  report = report +
563	    "| 降噪强度 | " +
564	    config.noise_reduction.to_string() +
565	    " |\n"
566	  report = report + "| 年代调色板 | " + config.era_color_palette + " |\n"
567	
568	  report = report + "\n### 结果\n\n"
569	  report = report + result_description + "\n"
570	
571	  report
572	}
   	…

18 uncovered line(s) in src\process\image_compression_advanced\image_compression_advanced.mbt:

162	fn sqrt_approx_comp(x : Double) -> Double {
163	  if x <= 0.0 {
164	    0.0	<-- UNCOVERED
165	  } else {
166	    let mut z = x
167	    let mut y = (z + 1.0) / 2.0
168	    for _ in 0..<20 {
169	      z = y
170	      y = (z + x / z) / 2.0
171	    }
172	    y
173	  }
174	}
   	…

178	fn log_approx_comp(x : Double) -> Double {
179	  if x <= 0.0 {
180	    -100.0	<-- UNCOVERED
181	  } else {
182	    let mut result = 0.0
183	    let mut y = x
184	    while y > 1.0 {
185	      y = y / 2.71828
186	      result = result + 1.0
187	    }
188	    result + (y - 1.0) / y
189	  }
190	}
   	…

228	fn quantize(block : Array[Double], quality : Int) -> Array[Int] {
229	  let result : Array[Int] = []
230	  let scale = if quality < 50 {
231	    5000.0 / quality.to_double()	<-- UNCOVERED
232	  } else {
233	    200.0 - 2.0 * quality.to_double()
234	  }
235	
236	  for i in 0..<block.length() {
237	    let value = block[i] * scale / 100.0
238	    let quantized = if value >= 0.0 {
239	      (value + 0.5).to_int()
240	    } else {
241	      (value - 0.5).to_int()
242	    }
243	    result.push(quantized)
244	  }
245	
246	  result
247	}
   	…

251	pub fn compress_image(
252	  image : Array[Double],
253	  width : Int,
254	  height : Int,
255	  config : ImageCompressionConfig,
256	) -> CompressionResult {
257	  let compressed : Array[Byte] = []
258	  let block_size = config.block_size
259	
260	  let mut _total_coefficients = 0
261	  let mut _nonzero_coefficients = 0
262	
263	  let mut y = 0
264	  while y < height {
265	    let mut x = 0
266	    while x < width {
267	      let block : Array[Double] = []
268	      for by in 0..<block_size {
269	        for bx in 0..<block_size {
270	          let px = x + bx
271	          let py = y + by
272	          if px < width && py < height {
273	            let idx = py * width + px
274	            if idx < image.length() {
275	              block.push(image[idx] - 128.0)
276	            } else {
277	              block.push(0.0)	<-- UNCOVERED
278	            }
279	          } else {
280	            block.push(0.0)	<-- UNCOVERED
281	          }
282	        }
283	      }
284	
285	      let dct_block = dct_2d(block, block_size)
286	      let quantized = quantize(dct_block, config.quality)
287	
288	      for q in quantized {
289	        _total_coefficients += 1
290	        if q != 0 {
291	          _nonzero_coefficients += 1
292	        }
293	        let byte_val = if q > 127 { 127 } else if q < -128 { -128 } else { q }
294	        let b : Byte = byte_val.to_byte()
295	        compressed.push(b)
296	      }
297	
298	      x = x + block_size
299	    }
300	    y = y + block_size
301	  }
302	
303	  let original_size = width * height * 3
304	  let compressed_size = compressed.length()
305	  let ratio = if compressed_size > 0 {
306	    original_size.to_double() / compressed_size.to_double()
307	  } else {
308	    1.0	<-- UNCOVERED
309	  }
310	  let bpp = if original_size > 0 {
311	    compressed_size.to_double() * 8.0 / (width * height).to_double()
312	  } else {
313	    0.0	<-- UNCOVERED
314	  }
315	
316	  CompressionResult::{
317	    compressed_data: compressed,
318	    compressed_size_bytes: compressed_size,
319	    original_size_bytes: original_size,
320	    compression_ratio: ratio,
321	    bits_per_pixel: bpp,
322	    compression_mode: config.compression_mode,
323	    compression_standard: config.compression_standard,
324	    quality: config.quality,
325	    psnr: 30.0 + config.quality.to_double() * 0.1,
326	    ssim: 0.8 + config.quality.to_double() * 0.002,
327	    encoding_time_ms: 100.0,
328	    decoding_time_ms: 50.0,
329	  }
330	}
   	…

334	pub fn decompress_image(
335	  compressed_data : Array[Byte],
336	  width : Int,
337	  height : Int,
338	  config : ImageCompressionConfig,
339	) -> DecompressionResult {
340	  let decompressed : Array[Double] = []
341	
342	  for i in 0..<(width * height * 3) {
343	    if i < compressed_data.length() {
344	      let value = compressed_data[i].to_int().to_double() + 128.0
345	      if value < 0.0 {
346	        decompressed.push(0.0)	<-- UNCOVERED
347	      } else if value > 255.0 {
348	        decompressed.push(255.0)	<-- UNCOVERED
349	      } else {
350	        decompressed.push(value)
351	      }
352	    } else {
353	      decompressed.push(128.0)
354	    }
355	  }
356	
357	  DecompressionResult::{
358	    decompressed_image: decompressed,
359	    width,
360	    height,
361	    num_channels: 3,
362	    decompression_time_ms: 50.0,
363	    compression_standard: config.compression_standard,
364	    quality_score: 0.8,
365	  }
366	}
   	…

446	pub fn image_compression_report_to_string(
447	  task_type : ImageCompressionTaskType,
448	  config : ImageCompressionConfig,
449	  result_description : String,
450	) -> String {
451	  let mut report = "## 图像压缩高级报告\n\n"
452	
453	  report = report + "### 任务信息\n\n"
454	  report = report + "| 指标 | 值 |\n|------|------|\n"
455	  report = report +
456	    "| 任务类型 | " +
457	    (match task_type {
458	      ImageCompressionTaskType::LosslessCompression => "无损压缩"
459	      ImageCompressionTaskType::LossyCompression => "有损压缩"	<-- UNCOVERED
460	      ImageCompressionTaskType::JPEGCompression => "JPEG压缩"
461	      ImageCompressionTaskType::PNGCompression => "PNG压缩"	<-- UNCOVERED
462	      ImageCompressionTaskType::WebPCompression => "WebP压缩"
463	      ImageCompressionTaskType::HEICCompression => "HEIC压缩"	<-- UNCOVERED
464	      ImageCompressionTaskType::AVIFCompression => "AVIF压缩"
465	      ImageCompressionTaskType::FractalCompression => "分形压缩"
466	      ImageCompressionTaskType::WaveletCompression => "小波压缩"
467	      ImageCompressionTaskType::NeuralCompression => "神经网络压缩"
468	      _ => "未知"
469	    }) +
470	    " |\n"
471	  report = report +
472	    "| 压缩标准 | " +
473	    (match config.compression_standard {
474	      CompressionStandardType::JPEG => "JPEG"
475	      CompressionStandardType::JPEG2000 => "JPEG2000"	<-- UNCOVERED
476	      CompressionStandardType::PNG => "PNG"
477	      CompressionStandardType::WebP => "WebP"
478	      CompressionStandardType::HEIC => "HEIC"
479	      CompressionStandardType::AVIF => "AVIF"
480	      _ => "未知"
481	    }) +
482	    " |\n"
483	  report = report +
484	    "| 压缩模式 | " +
485	    (match config.compression_mode {
486	      CompressionMode::Lossless => "无损"	<-- UNCOVERED
487	      CompressionMode::Lossy => "有损"
488	      CompressionMode::NearLossless => "近无损"	<-- UNCOVERED
489	      _ => "未知"
490	    }) +
491	    " |\n"
492	  report = report + "| 质量 | " + config.quality.to_string() + " |\n"
493	  report = report +
494	    "| 变换类型 | " +
495	    (match config.transform_type {
496	      TransformType::DCT => "DCT"
497	      TransformType::DWT => "DWT"	<-- UNCOVERED
498	      TransformType::DFT => "DFT"
499	      TransformType::Hadamard => "Hadamard"
500	      TransformType::KLT => "KLT"
501	      TransformType::PCA => "PCA"
502	      _ => "未知"
503	    }) +
504	    " |\n"
505	  report = report + "| 块大小 | " + config.block_size.to_string() + " |\n"
506	  report = report +
507	    "| 色度子采样 | " +
508	    (if config.use_chroma_subsampling {
509	      config.chroma_subsampling
510	    } else {
511	      "4:4:4"	<-- UNCOVERED
512	    }) +
513	    " |\n"
514	  report = report +
515	    "| 熵编码 | " +
516	    (if config.use_entropy_coding {
517	      config.entropy_coding_method
518	    } else {
519	      "关闭"	<-- UNCOVERED
520	    }) +
521	    " |\n"
522	  report = report +
523	    "| 预测 | " +
524	    (if config.use_prediction { config.prediction_method } else { "关闭" }) +
525	    " |\n"
526	  report = report +
527	    "| 环路滤波 | " +
528	    (if config.use_loop_filter { "开启" } else { "关闭" }) +
529	    " |\n"
530	
531	  report = report + "\n### 结果\n\n"
532	  report = report + result_description + "\n"
533	
534	  report
535	}
   	…

7 uncovered line(s) in src\process\image_dimensionality_reduction\image_dimensionality_reduction.mbt:

257	fn center_data(
258	  data : Array[Array[Double]],
259	  mean : Array[Double],
260	) -> Array[Array[Double]] {
261	  let centered : Array[Array[Double]] = []
262	  for sample in data {
263	    let c : Array[Double] = []
264	    for d in 0..<sample.length() {
265	      if d < mean.length() {
266	        c.push(sample[d] - mean[d])
267	      } else {
268	        c.push(sample[d])	<-- UNCOVERED
269	      }
270	    }
271	    centered.push(c)
272	  }
273	  centered
274	}
   	…

302	pub fn pca_reduce(
303	  data : Array[Array[Double]],
304	  num_components : Int,
305	  config : DimensionalityReductionConfig,
306	) -> PCAResult {
307	  ignore(config)
308	  let num_samples = data.length()
309	  let original_dim = if num_samples > 0 { data[0].length() } else { 0 }
310	
311	  let mean = calculate_mean(data, original_dim)
312	  let centered = center_data(data, mean)
313	  let cov = calculate_covariance(centered, original_dim)
314	
315	  // 简化的特征值分解：使用幂迭代法
316	  let components : Array[Array[Double]] = []
317	  let explained_variance : Array[Double] = []
318	  let explained_variance_ratio : Array[Double] = []
319	  let singular_values : Array[Double] = []
320	
321	  let mut total_variance = 0.0
322	  for i in 0..<original_dim {
323	    if i < cov.length() && i < cov[i].length() {
324	      total_variance += cov[i][i]
325	    }
326	  }
327	
328	  for _ in 0..<num_components {
329	    // 幂迭代法
330	    let mut v : Array[Double] = []
331	    for _ in 0..<original_dim {
332	      v.push(1.0 / original_dim.to_double())
333	    }
334	
335	    for _ in 0..<100 {
336	      let new_v : Array[Double] = []
337	      for i in 0..<original_dim {
338	        let mut sum = 0.0
339	        for j in 0..<original_dim {
340	          if i < cov.length() && j < cov[i].length() {
341	            sum += cov[i][j] * v[j]
342	          }
343	        }
344	        new_v.push(sum)
345	      }
346	
347	      // 归一化
348	      let mut norm = 0.0
349	      for val in new_v {
350	        norm += val * val
351	      }
352	      norm = norm
353	
354	      if norm > 0.0 {
355	        for i in 0..<new_v.length() {
356	          new_v[i] = new_v[i] / norm
357	        }
358	      }
359	      v = new_v
360	    }
361	
362	    // 计算特征值
363	    let mut eigenvalue = 0.0
364	    for i in 0..<original_dim {
365	      let mut sum = 0.0
366	      for j in 0..<original_dim {
367	        if i < cov.length() && j < cov[i].length() {
368	          sum += cov[i][j] * v[j]
369	        }
370	      }
371	      eigenvalue += v[i] * sum
372	    }
373	
374	    components.push(v)
375	    explained_variance.push(eigenvalue)
376	    singular_values.push(eigenvalue)
377	
378	    if total_variance > 0.0 {
379	      explained_variance_ratio.push(eigenvalue / total_variance)
380	    } else {
381	      explained_variance_ratio.push(0.0)	<-- UNCOVERED
382	    }
383	
384	    // 减去已找到的成分（deflation）
385	    for i in 0..<original_dim {
386	      for j in 0..<original_dim {
387	        if i < cov.length() && j < cov[i].length() {
388	          cov[i][j] = cov[i][j] - eigenvalue * v[i] * v[j]
389	        }
390	      }
391	    }
392	  }
393	
394	  // 投影数据
395	  let reduced : Array[Array[Double]] = []
396	  for sample in centered {
397	    let r : Array[Double] = []
398	    for k in 0..<num_components {
399	      let mut sum = 0.0
400	      for d in 0..<original_dim {
401	        if d < sample.length() &&
402	          k < components.length() &&
403	          d < components[k].length() {
404	          sum += sample[d] * components[k][d]
405	        }
406	      }
407	      r.push(sum)
408	    }
409	    reduced.push(r)
410	  }
411	
412	  let mut cumulative = 0.0
413	  for ratio in explained_variance_ratio {
414	    cumulative += ratio
415	  }
416	
417	  PCAResult::{
418	    reduced_data: reduced,
419	    num_components,
420	    original_dimension: original_dim,
421	    num_samples,
422	    explained_variance,
423	    explained_variance_ratio,
424	    cumulative_explained_variance: cumulative,
425	    components,
426	    singular_values,
427	    mean,
428	    noise_variance: 0.0,
429	    reconstruction_error: 0.01,
430	    reduction_time_ms: 50.0,
431	  }
432	}
   	…

553	pub fn select_features(
554	  data : Array[Array[Double]],
555	  threshold : Double,
556	) -> FeatureSelectionResult {
557	  let num_samples = data.length()
558	  let num_features = if num_samples > 0 { data[0].length() } else { 0 }
559	
560	  // 计算每个特征的方差
561	  let scores : Array[Double] = []
562	  for f in 0..<num_features {
563	    let mut sum = 0.0
564	    for sample in data {
565	      if f < sample.length() {
566	        sum += sample[f]
567	      }
568	    }
569	    let mean = if num_samples > 0 { sum / num_samples.to_double() } else { 0.0 }
570	
571	    let mut var_sum = 0.0
572	    for sample in data {
573	      if f < sample.length() {
574	        let diff = sample[f] - mean
575	        var_sum += diff * diff
576	      }
577	    }
578	    let variance = if num_samples > 0 {
579	      var_sum / num_samples.to_double()
580	    } else {
581	      0.0	<-- UNCOVERED
582	    }
583	    scores.push(variance)
584	  }
585	
586	  // 选择方差大于阈值的特征
587	  let selected : Array[Int] = []
588	  let ranking : Array[Int] = []
589	  for f in 0..<num_features {
590	    ranking.push(f)
591	  }
592	
593	  // 简单排序
594	  for _ in 0..<num_features {
595	    for j in 0..<(num_features - 1) {
596	      if scores[ranking[j]] < scores[ranking[j + 1]] {
597	        let temp = ranking[j]	<-- UNCOVERED
598	        ranking[j] = ranking[j + 1]
599	        ranking[j + 1] = temp
600	      }
601	    }
602	  }
603	
604	  for f in 0..<num_features {
605	    if scores[f] > threshold {
606	      selected.push(f)
607	    }
608	  }
609	
610	  FeatureSelectionResult::{
611	    selected_features: selected,
612	    num_selected: selected.length(),
613	    original_num_features: num_features,
614	    feature_scores: scores,
615	    feature_ranking: ranking,
616	    selection_method: "variance",
617	    threshold,
618	    selection_time_ms: 20.0,
619	  }
620	}
   	…

624	pub fn dimensionality_reduction_report_to_string(
625	  task_type : DimensionalityReductionTaskType,
626	  config : DimensionalityReductionConfig,
627	  result_description : String,
628	) -> String {
629	  let mut report = "## 图像降维高级报告\n\n"
630	
631	  report = report + "### 任务信息\n\n"
632	  report = report + "| 指标 | 值 |\n|------|------|\n"
633	  report = report +
634	    "| 任务类型 | " +
635	    (match task_type {
636	      DimensionalityReductionTaskType::PCA => "PCA"
637	      DimensionalityReductionTaskType::LDA => "LDA"	<-- UNCOVERED
638	      DimensionalityReductionTaskType::TSNE => "t-SNE"
639	      DimensionalityReductionTaskType::UMAP => "UMAP"
640	      DimensionalityReductionTaskType::ISOMAP => "ISOMAP"
641	      DimensionalityReductionTaskType::LLE => "LLE"
642	      DimensionalityReductionTaskType::Autoencoder => "自动编码器"
643	      DimensionalityReductionTaskType::ManifoldLearning => "流形学习"
644	      DimensionalityReductionTaskType::FeatureSelection => "特征选择"
645	      _ => "未知"
646	    }) +
647	    " |\n"
648	  report = report +
649	    "| 降维算法 | " +
650	    (match config.algorithm_type {
651	      DimensionalityReductionAlgorithmType::PCA => "PCA"
652	      DimensionalityReductionAlgorithmType::KernelPCA => "核PCA"	<-- UNCOVERED
653	      DimensionalityReductionAlgorithmType::IncrementalPCA => "增量PCA"
654	      DimensionalityReductionAlgorithmType::SparsePCA => "稀疏PCA"
655	      DimensionalityReductionAlgorithmType::TruncatedSVD => "截断SVD"
656	      DimensionalityReductionAlgorithmType::LDA => "LDA"
657	      DimensionalityReductionAlgorithmType::TSNE => "t-SNE"
658	      DimensionalityReductionAlgorithmType::UMAP => "UMAP"
659	      DimensionalityReductionAlgorithmType::ISOMAP => "ISOMAP"
660	      DimensionalityReductionAlgorithmType::LLE => "LLE"
661	      DimensionalityReductionAlgorithmType::MDS => "MDS"
662	      DimensionalityReductionAlgorithmType::SpectralEmbedding => "谱嵌入"
663	      DimensionalityReductionAlgorithmType::Autoencoder => "自动编码器"
664	      _ => "未知"
665	    }) +
666	    " |\n"
667	  report = report +
668	    "| 目标维度 | " +
669	    config.num_components.to_string() +
670	    " |\n"
671	  report = report +
672	    "| 核函数 | " +
673	    (match config.kernel_type {
674	      KernelType::Linear => "线性"
675	      KernelType::Polynomial => "多项式"	<-- UNCOVERED
676	      KernelType::RBF => "RBF"
677	      KernelType::Sigmoid => "Sigmoid"
678	      KernelType::Cosine => "余弦"
679	      _ => "未知"
680	    }) +
681	    " |\n"
682	  report = report + "| 困惑度 | " + config.perplexity.to_string() + " |\n"
683	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
684	  report = report +
685	    "| 迭代次数 | " +
686	    config.num_iterations.to_string() +
687	    " |\n"
688	  report = report + "| 邻居数 | " + config.n_neighbors.to_string() + " |\n"
689	  report = report + "| 最小距离 | " + config.min_dist.to_string() + " |\n"
690	  report = report +
691	    "| 白化 | " +
692	    (if config.use_whitening { "开启" } else { "关闭" }) +
693	    " |\n"
694	
695	  report = report + "\n### 结果\n\n"
696	  report = report + result_description + "\n"
697	
698	  report
699	}
   	…

9 uncovered line(s) in src\process\image_editing_advanced\image_editing_advanced.mbt:

265	pub fn smart_cutout(
266	  image : Array[Double],
267	  width : Int,
268	  height : Int,
269	  config : ImageEditingConfig,
270	) -> CutoutResult {
271	  let foreground : Array[Double] = []
272	  let alpha : Array[Double] = []
273	  let mut fg_pixels = 0
274	  let mut state = 42
275	
276	  for y in 0..<height {
277	    for x in 0..<width {
278	      state = (state * 11035 + 12345) % 1000000
279	      let dist_center = ((x - width / 2) * (x - width / 2) +
280	      (y - height / 2) * (y - height / 2)).to_double()
281	      let max_dist = (width * width / 4 + height * height / 4).to_double()
282	      let alpha_val = 1.0 - dist_center / max_dist
283	      let final_alpha = if alpha_val < 0.0 {
284	        0.0	<-- UNCOVERED
285	      } else if alpha_val > 1.0 {
286	        1.0	<-- UNCOVERED
287	      } else {
288	        alpha_val
289	      }
290	
291	      alpha.push(final_alpha)
292	      let idx = y * width + x
293	      if idx < image.length() {
294	        foreground.push(image[idx] * final_alpha)
295	        if final_alpha > 0.5 {
296	          fg_pixels += 1
297	        }
298	      } else {
299	        foreground.push(0.0)	<-- UNCOVERED
300	      }
301	    }
302	  }
303	
304	  CutoutResult::{
305	    foreground,
306	    alpha_mask: alpha,
307	    width,
308	    height,
309	    num_channels: 3,
310	    cutout_method: config.cutout_method,
311	    foreground_pixels: fg_pixels,
312	    background_pixels: width * height - fg_pixels,
313	    edge_quality: 0.85,
314	    hair_detail: 0.7,
315	    processing_time_ms: 500.0,
316	  }
317	}
   	…

408	pub fn beauty_retouch(
409	  image : Array[Double],
410	  width : Int,
411	  height : Int,
412	  config : ImageEditingConfig,
413	) -> BeautyRetouchResult {
414	  let retouched : Array[Double] = []
415	
416	  for value in image {
417	    // 简化的美颜：提亮 + 平滑
418	    let mut new_value = value * (1.0 + config.whitening * 0.1)
419	    new_value = new_value + config.skin_smoothing * 5.0
420	    if new_value < 0.0 {
421	      new_value = 0.0	<-- UNCOVERED
422	    }
423	    if new_value > 255.0 {
424	      new_value = 255.0	<-- UNCOVERED
425	    }
426	    retouched.push(new_value)
427	  }
428	
429	  let types : Array[BeautyType] = []
430	  types.push(BeautyType::SkinSmoothing)
431	  types.push(BeautyType::Whitening)
432	  types.push(BeautyType::FaceThinning)
433	  types.push(BeautyType::EyeEnlargement)
434	
435	  let strengths : Array[Double] = []
436	  strengths.push(config.skin_smoothing)
437	  strengths.push(config.whitening)
438	  strengths.push(config.face_thinning)
439	  strengths.push(config.eye_enlargement)
440	
441	  BeautyRetouchResult::{
442	    retouched_image: retouched,
443	    width,
444	    height,
445	    num_channels: 3,
446	    beauty_types: types,
447	    beauty_strengths: strengths,
448	    skin_smoothness: config.skin_smoothing,
449	    skin_brightness: config.whitening,
450	    face_thinness: config.face_thinning,
451	    eye_size: config.eye_enlargement,
452	    processing_time_ms: 400.0,
453	    naturalness_score: 0.85,
454	  }
455	}
   	…

459	pub fn image_editing_report_to_string(
460	  task_type : ImageEditingTaskType,
461	  config : ImageEditingConfig,
462	  result_description : String,
463	) -> String {
464	  let mut report = "## 图像编辑高级报告\n\n"
465	
466	  report = report + "### 任务信息\n\n"
467	  report = report + "| 指标 | 值 |\n|------|------|\n"
468	  report = report +
469	    "| 任务类型 | " +
470	    (match task_type {
471	      ImageEditingTaskType::SmartCutout => "智能抠图"
472	      ImageEditingTaskType::BackgroundReplacement => "背景替换"	<-- UNCOVERED
473	      ImageEditingTaskType::ObjectRemoval => "物体移除"
474	      ImageEditingTaskType::FaceEditing => "人脸编辑"
475	      ImageEditingTaskType::BeautyRetouch => "美颜"
476	      ImageEditingTaskType::Makeup => "妆容"
477	      ImageEditingTaskType::AgeTransformation => "年龄变换"
478	      ImageEditingTaskType::ExpressionEditing => "表情编辑"
479	      _ => "未知"
480	    }) +
481	    " |\n"
482	  report = report +
483	    "| 抠图方法 | " +
484	    (match config.cutout_method {
485	      CutoutMethod::GrabCut => "GrabCut"	<-- UNCOVERED
486	      CutoutMethod::DeepLab => "DeepLab"
487	      CutoutMethod::U2Net => "U2Net"
488	      CutoutMethod::MODNet => "MODNet"	<-- UNCOVERED
489	      _ => "未知"
490	    }) +
491	    " |\n"
492	  report = report +
493	    "| 移除方法 | " +
494	    (match config.removal_method {
495	      ObjectRemovalMethod::Inpainting => "图像修复"
496	      ObjectRemovalMethod::ContentAwareFill => "内容感知填充"	<-- UNCOVERED
497	      ObjectRemovalMethod::PatchMatch => "PatchMatch"
498	      _ => "未知"
499	    }) +
500	    " |\n"
501	  report = report +
502	    "| 美颜强度 | " +
503	    config.beauty_strength.to_string() +
504	    " |\n"
505	  report = report +
506	    "| 磨皮强度 | " +
507	    config.skin_smoothing.to_string() +
508	    " |\n"
509	  report = report + "| 美白强度 | " + config.whitening.to_string() + " |\n"
510	  report = report +
511	    "| 瘦脸强度 | " +
512	    config.face_thinning.to_string() +
513	    " |\n"
514	  report = report +
515	    "| 大眼强度 | " +
516	    config.eye_enlargement.to_string() +
517	    " |\n"
518	  report = report +
519	    "| 妆容强度 | " +
520	    config.makeup_strength.to_string() +
521	    " |\n"
522	  report = report + "| 目标年龄 | " + config.target_age.to_string() + " |\n"
523	  report = report + "| 目标表情 | " + config.target_expression + " |\n"
524	
525	  report = report + "\n### 结果\n\n"
526	  report = report + result_description + "\n"
527	
528	  report
529	}
   	…

16 uncovered line(s) in src\process\image_encryption_advanced\image_encryption_advanced.mbt:

251	fn log_approx_enc(x : Double) -> Double {
252	  if x <= 0.0 {
253	    -100.0	<-- UNCOVERED
254	  } else {
255	    let mut result = 0.0
256	    let mut y = x
257	    while y > 1.0 {
258	      y = y / 2.71828	<-- UNCOVERED
259	      result = result + 1.0
260	    }
261	    result + (y - 1.0) / y
262	  }
263	}
   	…

267	pub fn encrypt_image(
268	  image : Array[Double],
269	  width : Int,
270	  height : Int,
271	  key : Array[Byte],
272	  config : ImageEncryptionConfig,
273	) -> EncryptionResult {
274	  ignore(height)
275	  ignore(width)
276	  let image_bytes : Array[Byte] = []
277	  for value in image {
278	    let int_val = if value < 0.0 {
279	      0	<-- UNCOVERED
280	    } else if value > 255.0 {
281	      255	<-- UNCOVERED
282	    } else {
283	      value.to_int()
284	    }
285	    let b : Byte = int_val.to_byte()
286	    image_bytes.push(b)
287	  }
288	
289	  let encrypted = xor_encrypt(image_bytes, key)
290	  let entropy = calculate_entropy(encrypted)
291	
292	  let iv : Array[Byte] = []
293	  for i in 0..<config.iv_size {
294	    let b : Byte = (i % 256).to_byte()
295	    iv.push(b)
296	  }
297	
298	  let salt : Array[Byte] = []
299	  for i in 0..<config.salt_size {
300	    let b : Byte = ((i + 128) % 256).to_byte()
301	    salt.push(b)
302	  }
303	
304	  EncryptionResult::{
305	    encrypted_data: encrypted,
306	    encrypted_size_bytes: encrypted.length(),
307	    original_size_bytes: image_bytes.length(),
308	    encryption_algorithm: config.encryption_algorithm,
309	    key_size: config.key_size,
310	    iv,
311	    salt,
312	    encryption_time_ms: 50.0,
313	    entropy,
314	    correlation_coefficient: 0.01,
315	    npcr: 99.5,
316	    uaci: 33.3,
317	  }
318	}
   	…

351	pub fn chaotic_encrypt(
352	  image : Array[Double],
353	  width : Int,
354	  height : Int,
355	  config : ImageEncryptionConfig,
356	) -> ChaoticEncryptionResult {
357	  ignore(height)
358	  ignore(width)
359	  let image_bytes : Array[Byte] = []
360	  for value in image {
361	    let int_val = if value < 0.0 {
362	      0	<-- UNCOVERED
363	    } else if value > 255.0 {
364	      255	<-- UNCOVERED
365	    } else {
366	      value.to_int()
367	    }
368	    let b : Byte = int_val.to_byte()
369	    image_bytes.push(b)
370	  }
371	
372	  // 简化的Logistic混沌序列
373	  let chaotic_key : Array[Byte] = []
374	  let mut x = if config.initial_condition.length() > 0 {
375	    config.initial_condition[0]
376	  } else {
377	    0.5	<-- UNCOVERED
378	  }
379	  let r = if config.control_parameters.length() > 0 {
380	    config.control_parameters[0]
381	  } else {
382	    3.99	<-- UNCOVERED
383	  }
384	
385	  for _ in 0..<image_bytes.length() {
386	    x = r * x * (1.0 - x)
387	    let byte_val = (x * 256.0).to_int() % 256
388	    let b : Byte = byte_val.to_byte()
389	    chaotic_key.push(b)
390	  }
391	
392	  let encrypted = xor_encrypt(image_bytes, chaotic_key)
393	  let entropy = calculate_entropy(encrypted)
394	
395	  ChaoticEncryptionResult::{
396	    encrypted_data: encrypted,
397	    encrypted_size_bytes: encrypted.length(),
398	    original_size_bytes: image_bytes.length(),
399	    chaotic_system: config.chaotic_system,
400	    initial_condition: config.initial_condition,
401	    control_parameters: config.control_parameters,
402	    num_iterations: config.num_chaotic_iterations,
403	    key_space_size: 10000000000000000.0,
404	    encryption_time_ms: 100.0,
405	    entropy,
406	    correlation_coefficient: 0.005,
407	    npcr: 99.8,
408	    uaci: 33.5,
409	  }
410	}
   	…

414	pub fn embed_steganography(
415	  image : Array[Double],
416	  width : Int,
417	  height : Int,
418	  hidden_data : Array[Byte],
419	  config : ImageEncryptionConfig,
420	) -> SteganographyResult {
421	  let stego : Array[Double] = []
422	  let mut data_idx = 0
423	  let mut bit_idx = 0
424	
425	  for value in image {
426	    if data_idx < hidden_data.length() {
427	      let byte_val = hidden_data[data_idx].to_int()
428	      let bit = (byte_val >> (7 - bit_idx)) & 1
429	      let pixel_val = value.to_int()
430	      let new_pixel = (pixel_val & 0xFE) | bit
431	      stego.push(new_pixel.to_double())
432	      bit_idx += 1
433	      if bit_idx >= 8 {
434	        bit_idx = 0
435	        data_idx += 1
436	      }
437	    } else {
438	      stego.push(value)
439	    }
440	  }
441	
442	  let capacity = width * height * 3
443	  let embedding_rate = if capacity > 0 {
444	    hidden_data.length().to_double() * 8.0 / capacity.to_double()
445	  } else {
446	    0.0	<-- UNCOVERED
447	  }
448	
449	  SteganographyResult::{
450	    stego_image: stego,
451	    width,
452	    height,
453	    num_channels: 3,
454	    hidden_data,
455	    hidden_data_size_bytes: hidden_data.length(),
456	    steganography_type: config.steganography_type,
457	    capacity_bits: capacity,
458	    embedding_rate,
459	    psnr: 50.0,
460	    ssim: 0.99,
461	    is_robust: false,
462	  }
463	}
   	…

467	pub fn embed_watermark(
468	  image : Array[Double],
469	  width : Int,
470	  height : Int,
471	  watermark : Array[Byte],
472	  config : ImageEncryptionConfig,
473	) -> DigitalWatermarkResult {
474	  let watermarked : Array[Double] = []
475	  let strength = config.watermark_strength
476	
477	  for i in 0..<image.length() {
478	    let wm_idx = i % watermark.length()
479	    let wm_bit = if watermark[wm_idx].to_int() > 128 { 1.0 } else { -1.0 }
480	    let value = image[i] + wm_bit * strength * 10.0
481	    if value < 0.0 {
482	      watermarked.push(0.0)
483	    } else if value > 255.0 {
484	      watermarked.push(255.0)	<-- UNCOVERED
485	    } else {
486	      watermarked.push(value)
487	    }
488	  }
489	
490	  DigitalWatermarkResult::{
491	    watermarked_image: watermarked,
492	    width,
493	    height,
494	    num_channels: 3,
495	    watermark_data: watermark,
496	    watermark_size_bytes: watermark.length(),
497	    watermark_strength: strength,
498	    embedding_domain: "spatial",
499	    psnr: 40.0,
500	    ssim: 0.95,
501	    robustness_score: 0.7,
502	    imperceptibility_score: 0.9,
503	    capacity_bits: width * height * 3,
504	  }
505	}
   	…

509	pub fn image_encryption_report_to_string(
510	  task_type : ImageEncryptionTaskType,
511	  config : ImageEncryptionConfig,
512	  result_description : String,
513	) -> String {
514	  let mut report = "## 图像加密高级报告\n\n"
515	
516	  report = report + "### 任务信息\n\n"
517	  report = report + "| 指标 | 值 |\n|------|------|\n"
518	  report = report +
519	    "| 任务类型 | " +
520	    (match task_type {
521	      ImageEncryptionTaskType::SymmetricEncryption => "对称加密"
522	      ImageEncryptionTaskType::AsymmetricEncryption => "非对称加密"	<-- UNCOVERED
523	      ImageEncryptionTaskType::ChaoticEncryption => "混沌加密"
524	      ImageEncryptionTaskType::DNAEncryption => "DNA加密"
525	      ImageEncryptionTaskType::OpticalEncryption => "光学加密"
526	      ImageEncryptionTaskType::Steganography => "隐写术"
527	      ImageEncryptionTaskType::DigitalWatermark => "数字水印"
528	      ImageEncryptionTaskType::HomomorphicEncryption => "同态加密"
529	      _ => "未知"
530	    }) +
531	    " |\n"
532	  report = report +
533	    "| 加密算法 | " +
534	    (match config.encryption_algorithm {
535	      EncryptionAlgorithmType::AES => "AES"
536	      EncryptionAlgorithmType::DES => "DES"	<-- UNCOVERED
537	      EncryptionAlgorithmType::RSA => "RSA"
538	      EncryptionAlgorithmType::ECC => "ECC"
539	      EncryptionAlgorithmType::ChaCha20 => "ChaCha20"
540	      EncryptionAlgorithmType::Blowfish => "Blowfish"
541	      EncryptionAlgorithmType::Twofish => "Twofish"
542	      EncryptionAlgorithmType::Serpent => "Serpent"
543	      _ => "未知"
544	    }) +
545	    " |\n"
546	  report = report +
547	    "| 密钥长度 | " +
548	    config.key_size.to_string() +
549	    " bits |\n"
550	  report = report +
551	    "| 混沌系统 | " +
552	    (match config.chaotic_system {
553	      ChaoticSystemType::Logistic => "Logistic"
554	      ChaoticSystemType::Henon => "Henon"	<-- UNCOVERED
555	      ChaoticSystemType::Lorenz => "Lorenz"
556	      ChaoticSystemType::Rossler => "Rossler"
557	      ChaoticSystemType::Chen => "Chen"
558	      ChaoticSystemType::Hyperchaotic => "超混沌"
559	      _ => "未知"
560	    }) +
561	    " |\n"
562	  report = report +
563	    "| DNA编码 | " +
564	    (match config.dna_encoding {
565	      DNAEncodingType::Binary => "二进制"	<-- UNCOVERED
566	      DNAEncodingType::Complement => "互补"
567	      DNAEncodingType::Addition => "加法"
568	      DNAEncodingType::Subtraction => "减法"
569	      DNAEncodingType::XOR => "异或"
570	      _ => "未知"	<-- UNCOVERED
571	    }) +
572	    " |\n"
573	  report = report +
574	    "| 隐写类型 | " +
575	    (match config.steganography_type {
576	      SteganographyType::LSB => "LSB"
577	      SteganographyType::MSB => "MSB"	<-- UNCOVERED
578	      SteganographyType::DCTDomain => "DCT域"
579	      SteganographyType::DWTomain => "DWT域"
580	      SteganographyType::FFTomain => "FFT域"
581	      SteganographyType::SpreadSpectrum => "扩频"
582	      _ => "未知"
583	    }) +
584	    " |\n"
585	  report = report +
586	    "| 水印强度 | " +
587	    config.watermark_strength.to_string() +
588	    " |\n"
589	  report = report +
590	    "| 使用盐 | " +
591	    (if config.use_salt { "开启" } else { "关闭" }) +
592	    " |\n"
593	  report = report +
594	    "| 使用IV | " +
595	    (if config.use_iv { "开启" } else { "关闭" }) +
596	    " |\n"
597	
598	  report = report + "\n### 结果\n\n"
599	  report = report + result_description + "\n"
600	
601	  report
602	}
   	…

14 uncovered line(s) in src\process\image_generation\image_generation_editing.mbt:

233	pub fn inpaint_image(
234	  img : @types.Image,
235	  mask : @types.Image,
236	  config : EditingConfig,
237	) -> EditingResult {
238	  ignore(config)
239	  let w = img.width
240	  let h = img.height
241	  let total = w * h
242	  let out : Array[Byte] = []
243	
244	  for i in 0..<total {
245	    let mask_value = mask.data[i].to_int()
246	    if mask_value > 128 {
247	      // 需要修复的区域：使用邻域平均
248	      let x = i % w	<-- UNCOVERED
249	      let y = i / w
250	      let mut sum = 0.0
251	      let mut count = 0
252	      for dy in -3..<4 {
253	        for dx in -3..<4 {	<-- UNCOVERED
254	          let nx = x + dx
255	          let ny = y + dy
256	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
257	            let nidx = ny * w + nx	<-- UNCOVERED
258	            if mask.data[nidx].to_int() <= 128 {
259	              sum += img.data[nidx].to_double()
260	              count += 1
261	            }
262	          }
263	        }
264	      }
265	      let value = if count > 0 { sum / count.to_double() } else { 128.0 }	<-- UNCOVERED
266	      out.push(value.to_int().to_byte())
267	    } else {
268	      // 保留原始像素
269	      out.push(img.data[i])
270	    }
271	  }
272	
273	  let result_image = @types.Image::{
274	    width: w,
275	    height: h,
276	    channels: img.channels,
277	    data: Bytes::from_array(out),
278	  }
279	
280	  EditingResult::{
281	    image: result_image,
282	    algorithm: ImageEditingAlgorithm::Inpainting,
283	    original_image: img,
284	    mask,
285	    editing_time_ms: 0.0,
286	    quality_score: 0.75,
287	    consistency_score: 0.8,
288	  }
289	}
   	…

432	pub fn compose_images(
433	  background : @types.Image,
434	  foreground : @types.Image,
435	  mask : @types.Image,
436	  config : CompositionConfig,
437	) -> @types.Image {
438	  let w = background.width
439	  let h = background.height
440	  let total = w * h
441	  let out : Array[Byte] = []
442	
443	  for i in 0..<total {
444	    let mask_value = mask.data[i].to_double() / 255.0
445	    let bg = background.data[i].to_double()
446	    let fg = foreground.data[i].to_double()
447	
448	    // Alpha 混合
449	    let alpha = mask_value * config.opacity
450	    let value = bg * (1.0 - alpha) + fg * alpha
451	    let clamped = if value < 0.0 {
452	      0.0	<-- UNCOVERED
453	    } else if value > 255.0 {
454	      255.0	<-- UNCOVERED
455	    } else {
456	      value
457	    }
458	    out.push(clamped.to_int().to_byte())
459	  }
460	
461	  @types.Image::{
462	    width: w,
463	    height: h,
464	    channels: background.channels,
465	    data: Bytes::from_array(out),
466	  }
467	}
   	…

471	pub fn remove_background(
472	  img : @types.Image,
473	  config : EditingConfig,
474	) -> EditingResult {
475	  ignore(config)
476	  let w = img.width
477	  let h = img.height
478	  let _ = w * h
479	  let out : Array[Byte] = []
480	  let mask : Array[Byte] = []
481	
482	  // 简化版：基于边缘检测的背景移除
483	  // 实际实现应该使用语义分割模型
484	
485	  // 计算图像的边缘强度
486	  for y in 0..<h {
487	    for x in 0..<w {
488	      let idx = y * w + x
489	      let mut edge_strength = 0.0
490	
491	      if x > 0 && x < w - 1 && y > 0 && y < h - 1 {
492	        let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
493	        let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
494	        edge_strength = (gx * gx + gy * gy).sqrt()
495	      }
496	
497	      // 简化的前景判断：边缘强度高的区域为前景
498	      let is_foreground = edge_strength > 20.0
499	      if is_foreground {
500	        out.push(img.data[idx])	<-- UNCOVERED
501	        mask.push(b'\xFF')
502	      } else {
503	        out.push(b'\x00')
504	        mask.push(b'\x00')
505	      }
506	    }
507	  }
508	
509	  let result_image = @types.Image::{
510	    width: w,
511	    height: h,
512	    channels: img.channels,
513	    data: Bytes::from_array(out),
514	  }
515	  let mask_image = @types.Image::{
516	    width: w,
517	    height: h,
518	    channels: 1,
519	    data: Bytes::from_array(mask),
520	  }
521	
522	  EditingResult::{
523	    image: result_image,
524	    algorithm: ImageEditingAlgorithm::BackgroundRemoval,
525	    original_image: img,
526	    mask: mask_image,
527	    editing_time_ms: 0.0,
528	    quality_score: 0.6,
529	    consistency_score: 0.65,
530	  }
531	}
   	…

535	pub fn edit_image(
536	  img : @types.Image,
537	  mask : @types.Image,
538	  config : EditingConfig,
539	) -> EditingResult {
540	  if config.algorithm == ImageEditingAlgorithm::Inpainting {
541	    inpaint_image(img, mask, config)
542	  } else if config.algorithm == ImageEditingAlgorithm::Colorization {
543	    colorize_image(img, config)
544	  } else if config.algorithm == ImageEditingAlgorithm::BackgroundRemoval {	<-- UNCOVERED
545	    remove_background(img, config)
546	  } else {
547	    inpaint_image(img, mask, config)	<-- UNCOVERED
548	  }
549	}
   	…

553	pub fn generation_report_to_string(result : GenerationResult) -> String {
554	  let mut report = "## 图像生成报告\n\n"
555	  report = report + "### 生成信息\n\n"
556	  report = report + "| 指标 | 值 |\n|------|------|\n"
557	  report = report +
558	    "| 算法 | " +
559	    (if result.algorithm == ImageGenerationAlgorithm::GAN {
560	      "GAN"	<-- UNCOVERED
561	    } else if result.algorithm == ImageGenerationAlgorithm::StyleGAN {
562	      "StyleGAN"
563	    } else if result.algorithm == ImageGenerationAlgorithm::CycleGAN {	<-- UNCOVERED
564	      "CycleGAN"
565	    } else if result.algorithm == ImageGenerationAlgorithm::DiffusionModel {
566	      "扩散模型"
567	    } else if result.algorithm == ImageGenerationAlgorithm::VAE {
568	      "VAE"
569	    } else {
570	      "未知"	<-- UNCOVERED
571	    }) +
572	    " |\n"
573	  report = report + "| 随机种子 | " + result.seed.to_string() + " |\n"
574	  report = report +
575	    "| 潜空间维度 | " +
576	    result.latent_vector.length().to_string() +
577	    " |\n"
578	  report = report +
579	    "| 生成时间 | " +
580	    result.generation_time_ms.to_string() +
581	    " ms |\n"
582	  report = report +
583	    "| 质量评分 | " +
584	    (result.quality_score * 100.0).to_string() +
585	    "% |\n"
586	  report = report +
587	    "| 多样性评分 | " +
588	    (result.diversity_score * 100.0).to_string() +
589	    "% |\n"
590	
591	  report
592	}
   	…

596	pub fn editing_report_to_string(result : EditingResult) -> String {
597	  let mut report = "## 图像编辑报告\n\n"
598	  report = report + "### 编辑信息\n\n"
599	  report = report + "| 指标 | 值 |\n|------|------|\n"
600	  report = report +
601	    "| 算法 | " +
602	    (if result.algorithm == ImageEditingAlgorithm::Inpainting {
603	      "图像修复"
604	    } else if result.algorithm == ImageEditingAlgorithm::Outpainting {	<-- UNCOVERED
605	      "图像外扩"
606	    } else if result.algorithm == ImageEditingAlgorithm::Colorization {
607	      "图像上色"
608	    } else if result.algorithm == ImageEditingAlgorithm::StyleTransfer {
609	      "风格迁移"
610	    } else if result.algorithm == ImageEditingAlgorithm::BackgroundRemoval {
611	      "背景移除"
612	    } else if result.algorithm == ImageEditingAlgorithm::ObjectRemoval {
613	      "物体移除"
614	    } else {
615	      "未知"	<-- UNCOVERED
616	    }) +
617	    " |\n"
618	  report = report +
619	    "| 编辑时间 | " +
620	    result.editing_time_ms.to_string() +
621	    " ms |\n"
622	  report = report +
623	    "| 质量评分 | " +
624	    (result.quality_score * 100.0).to_string() +
625	    "% |\n"
626	  report = report +
627	    "| 一致性评分 | " +
628	    (result.consistency_score * 100.0).to_string() +
629	    "% |\n"
630	
631	  report
632	}
   	…

9 uncovered line(s) in src\process\image_generation_advanced\image_generation_advanced.mbt:

423	pub fn transfer_style(
424	  content_image : Array[Double],
425	  style_image : Array[Double],
426	  width : Int,
427	  height : Int,
428	  config : ImageGenerationConfig,
429	) -> StyleTransferResult {
430	  let stylized : Array[Double] = []
431	
432	  for i in 0..<content_image.length() {
433	    let content_val = if i < content_image.length() {
434	      content_image[i]
435	    } else {
436	      0.0	<-- UNCOVERED
437	    }
438	    let style_val = if i < style_image.length() { style_image[i] } else { 0.0 }
439	    let mixed = content_val * config.content_strength +
440	      style_val * config.style_strength
441	    let total = config.content_strength + config.style_strength
442	    let final_val = if total > 0.0 { mixed / total } else { content_val }
443	    if final_val < 0.0 {
444	      stylized.push(0.0)	<-- UNCOVERED
445	    } else if final_val > 255.0 {
446	      stylized.push(255.0)	<-- UNCOVERED
447	    } else {
448	      stylized.push(final_val)
449	    }
450	  }
451	
452	  StyleTransferResult::{
453	    stylized_image: stylized,
454	    width,
455	    height,
456	    num_channels: 3,
457	    content_image,
458	    style_image,
459	    style_strength: config.style_strength,
460	    content_strength: config.content_strength,
461	    transfer_method: "gatys",
462	    model_type: config.gan_type,
463	    num_iterations: 100,
464	    transfer_time_ms: 1000.0,
465	    content_loss: 10.0,
466	    style_loss: 5.0,
467	    total_loss: 15.0,
468	  }
469	}
   	…

473	pub fn image_generation_report_to_string(
474	  task_type : ImageGenerationTaskType,
475	  config : ImageGenerationConfig,
476	  result_description : String,
477	) -> String {
478	  let mut report = "## 图像生成高级报告\n\n"
479	
480	  report = report + "### 任务信息\n\n"
481	  report = report + "| 指标 | 值 |\n|------|------|\n"
482	  report = report +
483	    "| 任务类型 | " +
484	    (match task_type {
485	      ImageGenerationTaskType::TextToImage => "文本到图像"
486	      ImageGenerationTaskType::ImageToImage => "图像到图像"	<-- UNCOVERED
487	      ImageGenerationTaskType::Inpainting => "图像修复"
488	      ImageGenerationTaskType::Outpainting => "图像外扩"
489	      ImageGenerationTaskType::StyleTransfer => "风格迁移"
490	      ImageGenerationTaskType::SuperResolution => "超分辨率"
491	      ImageGenerationTaskType::Colorization => "图像上色"
492	      _ => "未知"
493	    }) +
494	    " |\n"
495	  report = report +
496	    "| 模型类型 | " +
497	    (match config.model_type {
498	      GenerativeModelType::Diffusion => "扩散模型"
499	      GenerativeModelType::GAN => "GAN"	<-- UNCOVERED
500	      GenerativeModelType::VAE => "VAE"
501	      GenerativeModelType::Flow => "流模型"
502	      GenerativeModelType::Autoregressive => "自回归"
503	      _ => "未知"
504	    }) +
505	    " |\n"
506	  report = report +
507	    "| 扩散模型 | " +
508	    (match config.diffusion_model {
509	      DiffusionModelType::DDPM => "DDPM"	<-- UNCOVERED
510	      DiffusionModelType::DDIM => "DDIM"
511	      DiffusionModelType::StableDiffusion => "Stable Diffusion"
512	      DiffusionModelType::ControlNet => "ControlNet"	<-- UNCOVERED
513	      _ => "未知"
514	    }) +
515	    " |\n"
516	  report = report +
517	    "| GAN类型 | " +
518	    (match config.gan_type {
519	      GANType::StyleGAN => "StyleGAN"	<-- UNCOVERED
520	      GANType::StyleGAN2 => "StyleGAN2"
521	      GANType::CycleGAN => "CycleGAN"	<-- UNCOVERED
522	      GANType::Pix2Pix => "Pix2Pix"
523	      _ => "未知"
524	    }) +
525	    " |\n"
526	  report = report +
527	    "| 分辨率 | " +
528	    config.width.to_string() +
529	    "x" +
530	    config.height.to_string() +
531	    " |\n"
532	  report = report +
533	    "| 推理步数 | " +
534	    config.num_inference_steps.to_string() +
535	    " |\n"
536	  report = report +
537	    "| 引导系数 | " +
538	    config.guidance_scale.to_string() +
539	    " |\n"
540	  report = report + "| 生成数量 | " + config.num_images.to_string() + " |\n"
541	  report = report + "| 随机种子 | " + config.seed.to_string() + " |\n"
542	  report = report + "| 提示词 | " + config.prompt + " |\n"
543	
544	  report = report + "\n### 结果\n\n"
545	  report = report + result_description + "\n"
546	
547	  report
548	}
   	…

13 uncovered line(s) in src\process\image_restoration_advanced\image_restoration_advanced.mbt:

312	pub fn deblur_image(
313	  blurry_image : Array[Double],
314	  width : Int,
315	  height : Int,
316	  config : ImageRestorationConfig,
317	) -> DeblurringResult {
318	  let deblurred : Array[Double] = []
319	  let kernel : Array[Array[Double]] = []
320	
321	  // 简化的锐化去模糊
322	  for y in 0..<height {
323	    for x in 0..<width {
324	      let idx = y * width + x
325	      if idx < blurry_image.length() {
326	        let center = blurry_image[idx]
327	        let mut neighbor_sum = 0.0
328	        let mut neighbor_count = 0
329	
330	        for dy in -1..<2 {
331	          for dx in -1..<2 {
332	            if !(dx == 0 && dy == 0) {
333	              let nx = x + dx
334	              let ny = y + dy
335	              if nx >= 0 && nx < width && ny >= 0 && ny < height {
336	                let nidx = ny * width + nx
337	                if nidx < blurry_image.length() {
338	                  neighbor_sum += blurry_image[nidx]
339	                  neighbor_count += 1
340	                }
341	              }
342	            }
343	          }
344	        }
345	
346	        let neighbor_avg = if neighbor_count > 0 {
347	          neighbor_sum / neighbor_count.to_double()
348	        } else {
349	          center	<-- UNCOVERED
350	        }
351	        let sharpened = center * 2.0 - neighbor_avg
352	        if sharpened < 0.0 {
353	          deblurred.push(0.0)
354	        } else if sharpened > 255.0 {
355	          deblurred.push(255.0)	<-- UNCOVERED
356	        } else {
357	          deblurred.push(sharpened)
358	        }
359	      } else {
360	        deblurred.push(0.0)	<-- UNCOVERED
361	      }
362	    }
363	  }
364	
365	  // 生成估计的核
366	  for i in 0..<config.kernel_size {
367	    let row : Array[Double] = []
368	    for j in 0..<config.kernel_size {
369	      let dist = ((i - config.kernel_size / 2) * (i - config.kernel_size / 2) +
370	      (j - config.kernel_size / 2) * (j - config.kernel_size / 2)).to_double()
371	      let value = 1.0 / (1.0 + dist)
372	      row.push(value)
373	    }
374	    kernel.push(row)
375	  }
376	
377	  DeblurringResult::{
378	    deblurred_image: deblurred,
379	    width,
380	    height,
381	    num_channels: 3,
382	    blurry_image,
383	    deblurring_method: config.deblurring_method,
384	    blur_type: config.blur_type,
385	    kernel_size: config.kernel_size,
386	    kernel_estimate: kernel,
387	    psnr: 28.0,
388	    ssim: 0.8,
389	    sharpness_improvement: 0.3,
390	    iterations: 10,
391	    processing_time_ms: 800.0,
392	  }
393	}
   	…

454	pub fn dehaze_image(
455	  hazy_image : Array[Double],
456	  width : Int,
457	  height : Int,
458	  config : ImageRestorationConfig,
459	) -> DehazingResult {
460	  ignore(config)
461	  let dehazed : Array[Double] = []
462	  let transmission : Array[Double] = []
463	  let atmospheric : Array[Double] = []
464	  atmospheric.push(240.0)
465	  atmospheric.push(240.0)
466	  atmospheric.push(240.0)
467	
468	  for i in 0..<hazy_image.length() {
469	    let value = if i < hazy_image.length() { hazy_image[i] } else { 128.0 }
470	    let channel = i % 3
471	    let a = atmospheric[channel]
472	
473	    // 简化的暗通道先验
474	    let t = 1.0 - 0.95 * (value / 255.0)
475	    transmission.push(t)
476	
477	    // 去雾公式
478	    let dehazed_value = if t > 0.1 { (value - a) / t + a } else { value }
479	    if dehazed_value < 0.0 {
480	      dehazed.push(0.0)	<-- UNCOVERED
481	    } else if dehazed_value > 255.0 {
482	      dehazed.push(255.0)	<-- UNCOVERED
483	    } else {
484	      dehazed.push(dehazed_value)
485	    }
486	  }
487	
488	  DehazingResult::{
489	    dehazed_image: dehazed,
490	    width,
491	    height,
492	    num_channels: 3,
493	    hazy_image,
494	    transmission_map: transmission,
495	    atmospheric_light: atmospheric,
496	    haze_density: 0.5,
497	    visibility_improvement: 50.0,
498	    contrast_improvement: 20.0,
499	    processing_time_ms: 400.0,
500	  }
501	}
   	…

505	pub fn restore_old_photo(
506	  old_photo : Array[Double],
507	  width : Int,
508	  height : Int,
509	  config : ImageRestorationConfig,
510	) -> OldPhotoRestorationResult {
511	  let restored : Array[Double] = []
512	  let mut state = 42
513	
514	  for value in old_photo {
515	    // 简化的老照片修复：去噪 + 对比度增强
516	    state = (state * 11035 + 12345) % 1000000
517	    let noise = (state % 1000).to_double() / 1000.0 - 0.5
518	    let denoised = value + noise * config.noise_reduction * 10.0
519	
520	    // 对比度增强
521	    let enhanced = (denoised - 128.0) * (1.0 + config.contrast_enhancement) +
522	      128.0
523	
524	    if enhanced < 0.0 {
525	      restored.push(0.0)
526	    } else if enhanced > 255.0 {	<-- UNCOVERED
527	      restored.push(255.0)
528	    } else {
529	      restored.push(enhanced)	<-- UNCOVERED
530	    }
531	  }
532	
533	  OldPhotoRestorationResult::{
534	    restored_image: restored,
535	    width,
536	    height,
537	    num_channels: 3,
538	    old_photo,
539	    scratch_removal: config.scratch_removal,
540	    noise_reduction: config.noise_reduction,
541	    contrast_enhancement: config.contrast_enhancement,
542	    colorization: config.colorization,
543	    face_enhancement: config.face_enhancement,
544	    processing_time_ms: 1500.0,
545	    quality_score: 0.75,
546	    realism_score: 0.7,
547	  }
548	}
   	…

552	pub fn image_restoration_report_to_string(
553	  task_type : ImageRestorationTaskType,
554	  config : ImageRestorationConfig,
555	  result_description : String,
556	) -> String {
557	  let mut report = "## 图像修复高级报告\n\n"
558	
559	  report = report + "### 任务信息\n\n"
560	  report = report + "| 指标 | 值 |\n|------|------|\n"
561	  report = report +
562	    "| 任务类型 | " +
563	    (match task_type {
564	      ImageRestorationTaskType::Denoising => "图像去噪"
565	      ImageRestorationTaskType::Deblurring => "图像去模糊"	<-- UNCOVERED
566	      ImageRestorationTaskType::SuperResolution => "超分辨率"
567	      ImageRestorationTaskType::Dehazing => "去雾"
568	      ImageRestorationTaskType::Deraining => "去雨"
569	      ImageRestorationTaskType::Desnowing => "去雪"
570	      ImageRestorationTaskType::Deocclusion => "去遮挡"
571	      ImageRestorationTaskType::ScratchRemoval => "划痕修复"
572	      ImageRestorationTaskType::OldPhotoRestoration => "老照片修复"
573	      _ => "未知"
574	    }) +
575	    " |\n"
576	  report = report +
577	    "| 去噪方法 | " +
578	    (match config.denoising_method {
579	      DenoisingMethod::BM3D => "BM3D"
580	      DenoisingMethod::NLM => "非局部均值"	<-- UNCOVERED
581	      DenoisingMethod::Wiener => "维纳滤波"
582	      DenoisingMethod::TotalVariation => "全变分"
583	      DenoisingMethod::DeepLearning => "深度学习"
584	      _ => "未知"
585	    }) +
586	    " |\n"
587	  report = report +
588	    "| 去模糊方法 | " +
589	    (match config.deblurring_method {
590	      DeblurringMethod::Wiener => "维纳滤波"	<-- UNCOVERED
591	      DeblurringMethod::RichardsonLucy => "Richardson-Lucy"
592	      DeblurringMethod::BlindDeconvolution => "盲去卷积"	<-- UNCOVERED
593	      DeblurringMethod::DeepLearning => "深度学习"
594	      _ => "未知"
595	    }) +
596	    " |\n"
597	  report = report +
598	    "| 噪声类型 | " +
599	    (match config.noise_type {
600	      NoiseType::Gaussian => "高斯噪声"
601	      NoiseType::SaltAndPepper => "椒盐噪声"	<-- UNCOVERED
602	      NoiseType::Poisson => "泊松噪声"
603	      NoiseType::Speckle => "斑点噪声"
604	      _ => "未知"
605	    }) +
606	    " |\n"
607	  report = report +
608	    "| 噪声水平 | " +
609	    config.noise_level.to_string() +
610	    " |\n"
611	  report = report +
612	    "| 模糊类型 | " +
613	    (match config.blur_type {
614	      BlurType::Gaussian => "高斯模糊"
615	      BlurType::Motion => "运动模糊"	<-- UNCOVERED
616	      BlurType::Defocus => "散焦模糊"
617	      _ => "未知"
618	    }) +
619	    " |\n"
620	  report = report + "| 核大小 | " + config.kernel_size.to_string() + " |\n"
621	  report = report +
622	    "| 超分倍率 | " +
623	    config.scale_factor.to_string() +
624	    "x |\n"
625	  report = report +
626	    "| 划痕去除 | " +
627	    (if config.scratch_removal { "开启" } else { "关闭" }) +
628	    " |\n"
629	  report = report +
630	    "| 降噪强度 | " +
631	    config.noise_reduction.to_string() +
632	    " |\n"
633	  report = report +
634	    "| 对比度增强 | " +
635	    config.contrast_enhancement.to_string() +
636	    " |\n"
637	  report = report +
638	    "| 上色 | " +
639	    (if config.colorization { "开启" } else { "关闭" }) +
640	    " |\n"
641	  report = report +
642	    "| 人脸增强 | " +
643	    (if config.face_enhancement { "开启" } else { "关闭" }) +
644	    " |\n"
645	
646	  report = report + "\n### 结果\n\n"
647	  report = report + result_description + "\n"
648	
649	  report
650	}
   	…

16 uncovered line(s) in src\process\image_retrieval_advanced\image_retrieval_advanced.mbt:

197	fn extract_color_histogram(
198	  image : Array[Double],
199	  num_bins : Int,
200	) -> Array[Double] {
201	  let histogram : Array[Double] = []
202	  for _ in 0..<num_bins {
203	    histogram.push(0.0)
204	  }
205	
206	  for value in image {
207	    let bin_idx = if value < 0.0 {
208	      0	<-- UNCOVERED
209	    } else if value >= 255.0 {
210	      num_bins - 1	<-- UNCOVERED
211	    } else {
212	      (value / 256.0 * num_bins.to_double()).to_int()
213	    }
214	    if bin_idx >= 0 && bin_idx < num_bins {
215	      histogram[bin_idx] += 1.0
216	    }
217	  }
218	
219	  // 归一化
220	  let total = image.length().to_double()
221	  if total > 0.0 {
222	    for i in 0..<histogram.length() {
223	      histogram[i] = histogram[i] / total
224	    }
225	  }
226	
227	  histogram
228	}
   	…

232	fn euclidean_distance(v1 : Array[Double], v2 : Array[Double]) -> Double {
233	  let mut sum = 0.0
234	  let min_len = if v1.length() < v2.length() {
235	    v1.length()	<-- UNCOVERED
236	  } else {
237	    v2.length()
238	  }
239	  for i in 0..<min_len {
240	    let diff = v1[i] - v2[i]
241	    sum += diff * diff
242	  }
243	  // 简化的平方根
244	  let mut result = sum
245	  for _ in 0..<10 {
246	    if result > 0.0 {
247	      result = (result + sum / result) / 2.0
248	    }
249	  }
250	  result
251	}
   	…

255	fn cosine_similarity(v1 : Array[Double], v2 : Array[Double]) -> Double {
256	  let mut dot = 0.0
257	  let mut norm1 = 0.0
258	  let mut norm2 = 0.0
259	  let min_len = if v1.length() < v2.length() {
260	    v1.length()	<-- UNCOVERED
261	  } else {
262	    v2.length()
263	  }
264	  for i in 0..<min_len {
265	    dot += v1[i] * v2[i]
266	    norm1 += v1[i] * v1[i]
267	    norm2 += v2[i] * v2[i]
268	  }
269	  let denom = norm1.sqrt() * norm2.sqrt()
270	  if denom > 0.0 {
271	    dot / denom
272	  } else {
273	    0.0	<-- UNCOVERED
274	  }
275	}
   	…

305	pub fn calculate_similarity(
306	  features1 : Array[Double],
307	  features2 : Array[Double],
308	  metric : SimilarityMetricType,
309	) -> Double {
310	  match metric {
311	    SimilarityMetricType::EuclideanDistance =>
312	      euclidean_distance(features1, features2)
313	    SimilarityMetricType::CosineSimilarity =>
314	      cosine_similarity(features1, features2)
315	    SimilarityMetricType::ManhattanDistance => {
316	      let mut sum = 0.0
317	      let min_len = if features1.length() < features2.length() {
318	        features1.length()	<-- UNCOVERED
319	      } else {
320	        features2.length()
321	      }
322	      for i in 0..<min_len {
323	        sum += (features1[i] - features2[i]).abs()
324	      }
325	      sum
326	    }
327	    _ => euclidean_distance(features1, features2)	<-- UNCOVERED
328	  }
329	}
   	…

333	pub fn retrieve_images(
334	  query_image : Array[Double],
335	  query_width : Int,
336	  query_height : Int,
337	  database_images : Array[Array[Double]],
338	  config : ImageRetrievalConfig,
339	) -> RetrievalResult {
340	  let query_features = extract_color_histogram(
341	    query_image,
342	    config.feature_dimension,
343	  )
344	
345	  let scores : Array[Double] = []
346	  let indices : Array[Int] = []
347	
348	  for i in 0..<database_images.length() {
349	    let db_features = extract_color_histogram(
350	      database_images[i],
351	      config.feature_dimension,
352	    )
353	    let score = calculate_similarity(
354	      query_features,
355	      db_features,
356	      config.similarity_metric,
357	    )
358	    scores.push(score)
359	    indices.push(i)
360	  }
361	
362	  // 简单排序（冒泡排序）
363	  for _ in 0..<scores.length() {
364	    for j in 0..<(scores.length() - 1) {
365	      if scores[j] > scores[j + 1] {
366	        let temp_score = scores[j]	<-- UNCOVERED
367	        scores[j] = scores[j + 1]
368	        scores[j + 1] = temp_score
369	        let temp_idx = indices[j]
370	        indices[j] = indices[j + 1]
371	        indices[j + 1] = temp_idx
372	      }
373	    }
374	  }
375	
376	  let num_results = if config.num_results < scores.length() {
377	    config.num_results	<-- UNCOVERED
378	  } else {
379	    scores.length()
380	  }
381	
382	  let retrieved_images : Array[Array[Double]] = []
383	  let retrieved_indices : Array[Int] = []
384	  let retrieved_scores : Array[Double] = []
385	
386	  for i in 0..<num_results {
387	    if i < database_images.length() {
388	      retrieved_images.push(database_images[indices[i]])
389	      retrieved_indices.push(indices[i])
390	      retrieved_scores.push(scores[i])
391	    }
392	  }
393	
394	  RetrievalResult::{
395	    query_image,
396	    query_width,
397	    query_height,
398	    retrieved_images,
399	    retrieved_indices,
400	    retrieved_scores,
401	    num_results,
402	    total_candidates: database_images.length(),
403	    retrieval_time_ms: 50.0,
404	    feature_extraction_time_ms: 10.0,
405	    similarity_search_time_ms: 40.0,
406	    average_precision: 0.7,
407	    recall_at_k: 0.6,
408	    precision_at_k: 0.8,
409	  }
410	}
   	…

523	pub fn image_retrieval_report_to_string(
524	  task_type : ImageRetrievalTaskType,
525	  config : ImageRetrievalConfig,
526	  result_description : String,
527	) -> String {
528	  let mut report = "## 图像检索高级报告\n\n"
529	
530	  report = report + "### 任务信息\n\n"
531	  report = report + "| 指标 | 值 |\n|------|------|\n"
532	  report = report +
533	    "| 任务类型 | " +
534	    (match task_type {
535	      ImageRetrievalTaskType::ContentBasedRetrieval =>
536	        "基于内容的图像检索"
537	      ImageRetrievalTaskType::FeatureExtraction => "特征提取"
538	      ImageRetrievalTaskType::SimilaritySearch => "相似度搜索"	<-- UNCOVERED
539	      ImageRetrievalTaskType::IndexConstruction => "索引构建"
540	      ImageRetrievalTaskType::QueryExpansion => "查询扩展"	<-- UNCOVERED
541	      ImageRetrievalTaskType::RelevanceFeedback => "相关反馈"
542	      ImageRetrievalTaskType::ImageClustering => "图像聚类"
543	      ImageRetrievalTaskType::ImageClassification => "图像分类"
544	      _ => "未知"
545	    }) +
546	    " |\n"
547	  report = report +
548	    "| 特征类型 | " +
549	    (match config.feature_type {
550	      FeatureDescriptorType::SIFT => "SIFT"	<-- UNCOVERED
551	      FeatureDescriptorType::SURF => "SURF"
552	      FeatureDescriptorType::ORB => "ORB"
553	      FeatureDescriptorType::HOG => "HOG"
554	      FeatureDescriptorType::LBP => "LBP"
555	      FeatureDescriptorType::Gabor => "Gabor"
556	      FeatureDescriptorType::ColorHistogram => "颜色直方图"
557	      FeatureDescriptorType::TextureDescriptor => "纹理描述符"	<-- UNCOVERED
558	      FeatureDescriptorType::ShapeDescriptor => "形状描述符"
559	      FeatureDescriptorType::DeepFeature => "深度特征"
560	      _ => "未知"
561	    }) +
562	    " |\n"
563	  report = report +
564	    "| 相似度度量 | " +
565	    (match config.similarity_metric {
566	      SimilarityMetricType::EuclideanDistance => "欧氏距离"
567	      SimilarityMetricType::CosineSimilarity => "余弦相似度"	<-- UNCOVERED
568	      SimilarityMetricType::ManhattanDistance => "曼哈顿距离"
569	      SimilarityMetricType::ChebyshevDistance => "切比雪夫距离"
570	      SimilarityMetricType::MinkowskiDistance => "闵可夫斯基距离"
571	      SimilarityMetricType::HammingDistance => "汉明距离"
572	      SimilarityMetricType::ChiSquareDistance => "卡方距离"
573	      SimilarityMetricType::CorrelationCoefficient => "相关系数"
574	      SimilarityMetricType::BhattacharyyaDistance => "Bhattacharyya距离"
575	      SimilarityMetricType::EarthMoverDistance => "推土机距离"
576	      _ => "未知"
577	    }) +
578	    " |\n"
579	  report = report +
580	    "| 索引结构 | " +
581	    (match config.index_type {
582	      IndexStructureType::KDTree => "KD树"	<-- UNCOVERED
583	      IndexStructureType::BallTree => "球树"
584	      IndexStructureType::VPree => "VP树"
585	      IndexStructureType::LSH => "LSH"
586	      IndexStructureType::IVF => "IVF"
587	      IndexStructureType::HNSW => "HNSW"
588	      IndexStructureType::Annoy => "Annoy"
589	      IndexStructureType::FlatIndex => "扁平索引"
590	      IndexStructureType::InvertedIndex => "倒排索引"	<-- UNCOVERED
591	      _ => "未知"
592	    }) +
593	    " |\n"
594	  report = report +
595	    "| 返回结果数 | " +
596	    config.num_results.to_string() +
597	    " |\n"
598	  report = report +
599	    "| 特征维度 | " +
600	    config.feature_dimension.to_string() +
601	    " |\n"
602	  report = report +
603	    "| 使用PCA | " +
604	    (if config.use_pca { "开启" } else { "关闭" }) +
605	    " |\n"
606	  report = report +
607	    "| 使用归一化 | " +
608	    (if config.use_normalization { "开启" } else { "关闭" }) +
609	    " |\n"
610	  report = report +
611	    "| 查询扩展 | " +
612	    (if config.use_query_expansion { "开启" } else { "关闭" }) +
613	    " |\n"
614	  report = report +
615	    "| 相关反馈 | " +
616	    (if config.use_relevance_feedback { "开启" } else { "关闭" }) +
617	    " |\n"
618	
619	  report = report + "\n### 结果\n\n"
620	  report = report + result_description + "\n"
621	
622	  report
623	}
   	…

30 uncovered line(s) in src\process\image_translation_advanced\image_translation_advanced.mbt:

251	pub fn translate_image(
252	  image : Array[Double],
253	  width : Int,
254	  height : Int,
255	  config : ImageTranslationConfig,
256	) -> ImageTranslationResult {
257	  let translated : Array[Double] = []
258	  let mut state = 42
259	
260	  for value in image {
261	    state = (state * 11035 + 12345) % 1000000
262	    let noise = (state % 1000).to_double() / 1000.0 - 0.5
263	    let translated_value = value *
264	      (1.0 + noise * config.translation_strength * 0.1)
265	    if translated_value < 0.0 {
266	      translated.push(0.0)	<-- UNCOVERED
267	    } else if translated_value > 255.0 {
268	      translated.push(255.0)	<-- UNCOVERED
269	    } else {
270	      translated.push(translated_value)
271	    }
272	  }
273	
274	  ImageTranslationResult::{
275	    translated_image: translated,
276	    width,
277	    height,
278	    num_channels: 3,
279	    source_domain: config.source_domain,
280	    target_domain: config.target_domain,
281	    translation_model: config.model_type,
282	    translation_strength: config.translation_strength,
283	    content_preservation: 0.8,
284	    style_similarity: 0.7,
285	    num_iterations: config.num_iterations,
286	    translation_time_ms: config.num_iterations.to_double() * 10.0,
287	    quality_score: 0.75,
288	  }
289	}
   	…

293	pub fn advanced_style_transfer(
294	  content_image : Array[Double],
295	  style_image : Array[Double],
296	  width : Int,
297	  height : Int,
298	  config : ImageTranslationConfig,
299	) -> AdvancedStyleTransferResult {
300	  let stylized : Array[Double] = []
301	
302	  for i in 0..<content_image.length() {
303	    let content_val = if i < content_image.length() {
304	      content_image[i]
305	    } else {
306	      0.0	<-- UNCOVERED
307	    }
308	    let style_val = if i < style_image.length() { style_image[i] } else { 0.0 }
309	    let total_weight = config.content_weight + config.style_weight
310	    let mixed = if total_weight > 0.0 {
311	      (content_val * config.content_weight + style_val * config.style_weight) /
312	      total_weight
313	    } else {
314	      content_val	<-- UNCOVERED
315	    }
316	    if mixed < 0.0 {
317	      stylized.push(0.0)	<-- UNCOVERED
318	    } else if mixed > 255.0 {
319	      stylized.push(255.0)	<-- UNCOVERED
320	    } else {
321	      stylized.push(mixed)
322	    }
323	  }
324	
325	  AdvancedStyleTransferResult::{
326	    stylized_image: stylized,
327	    width,
328	    height,
329	    num_channels: 3,
330	    content_image,
331	    style_image,
332	    style_strength: config.style_weight,
333	    content_strength: config.content_weight,
334	    total_variation_weight: config.total_variation_weight,
335	    transfer_method: config.model_type,
336	    num_iterations: config.num_iterations,
337	    content_loss: 10.0,
338	    style_loss: 5.0,
339	    total_loss: 15.0,
340	    transfer_time_ms: config.num_iterations.to_double() * 10.0,
341	  }
342	}
   	…

346	pub fn transfer_season(
347	  image : Array[Double],
348	  width : Int,
349	  height : Int,
350	  config : ImageTranslationConfig,
351	) -> SeasonTransferResult {
352	  let translated : Array[Double] = []
353	  let palette_shift : Array[Double] = []
354	
355	  // 根据目标季节调整颜色
356	  let (r_shift, g_shift, b_shift) = match config.target_season {
357	    SeasonType::Spring => (0.1, 0.2, 0.0)	<-- UNCOVERED
358	    SeasonType::Summer => (0.0, 0.1, 0.0)
359	    SeasonType::Autumn => (0.2, 0.1, -0.1)
360	    SeasonType::Winter => (-0.1, -0.1, 0.2)
361	    _ => (0.0, 0.0, 0.0)	<-- UNCOVERED
362	  }
363	
364	  palette_shift.push(r_shift)
365	  palette_shift.push(g_shift)
366	  palette_shift.push(b_shift)
367	
368	  for i in 0..<image.length() {
369	    let channel = i % 3
370	    let shift = if channel == 0 {
371	      r_shift
372	    } else if channel == 1 {
373	      g_shift
374	    } else {
375	      b_shift
376	    }
377	    let value = image[i] * (1.0 + shift * config.translation_strength)
378	    if value < 0.0 {
379	      translated.push(0.0)	<-- UNCOVERED
380	    } else if value > 255.0 {
381	      translated.push(255.0)	<-- UNCOVERED
382	    } else {
383	      translated.push(value)
384	    }
385	  }
386	
387	  SeasonTransferResult::{
388	    translated_image: translated,
389	    width,
390	    height,
391	    num_channels: 3,
392	    source_season: config.source_season,
393	    target_season: config.target_season,
394	    translation_strength: config.translation_strength,
395	    vegetation_change: 0.3,
396	    color_palette_shift: palette_shift,
397	    content_preservation: 0.85,
398	    translation_time_ms: 500.0,
399	    realism_score: 0.8,
400	  }
401	}
   	…

405	pub fn transfer_weather(
406	  image : Array[Double],
407	  width : Int,
408	  height : Int,
409	  config : ImageTranslationConfig,
410	) -> WeatherTransferResult {
411	  let translated : Array[Double] = []
412	  let mut state = 42
413	
414	  for value in image {
415	    state = (state * 11035 + 12345) % 1000000
416	    let noise = (state % 1000).to_double() / 1000.0
417	
418	    let mut new_value = value
419	    match config.target_weather {
420	      WeatherType::Rainy => new_value = value * 0.9 + noise * 20.0
421	      WeatherType::Snowy => {	<-- UNCOVERED
422	        new_value = value * 1.1 + noise * 30.0
423	        if new_value > 255.0 {
424	          new_value = 255.0	<-- UNCOVERED
425	        }
426	      }
427	      WeatherType::Foggy => new_value = value * 0.7 + 128.0 * 0.3	<-- UNCOVERED
428	      WeatherType::Cloudy => new_value = value * 0.85
429	      _ => ()
430	    }
431	
432	    if new_value < 0.0 {
433	      translated.push(0.0)
434	    } else if new_value > 255.0 {
435	      translated.push(255.0)	<-- UNCOVERED
436	    } else {
437	      translated.push(new_value)
438	    }
439	  }
440	
441	  WeatherTransferResult::{
442	    translated_image: translated,
443	    width,
444	    height,
445	    num_channels: 3,
446	    source_weather: config.source_weather,
447	    target_weather: config.target_weather,
448	    translation_strength: config.translation_strength,
449	    rain_intensity: 0.5,
450	    snow_intensity: 0.5,
451	    fog_density: 0.3,
452	    content_preservation: 0.8,
453	    translation_time_ms: 600.0,
454	    realism_score: 0.75,
455	  }
456	}
   	…

460	pub fn transfer_day_night(
461	  image : Array[Double],
462	  width : Int,
463	  height : Int,
464	  config : ImageTranslationConfig,
465	) -> DayNightTransferResult {
466	  let translated : Array[Double] = []
467	
468	  for value in image {
469	    let mut new_value = value
470	    if config.target_is_night {
471	      // 降低亮度，增加蓝色调
472	      new_value = value * 0.3	<-- UNCOVERED
473	      let channel = 0 // 简化
474	      if channel == 2 {
475	        new_value = new_value * 1.5	<-- UNCOVERED
476	      }
477	    } else {
478	      // 增加亮度
479	      new_value = value * 1.5
480	      if new_value > 255.0 {
481	        new_value = 255.0	<-- UNCOVERED
482	      }
483	    }
484	    translated.push(new_value)
485	  }
486	
487	  DayNightTransferResult::{
488	    translated_image: translated,
489	    width,
490	    height,
491	    num_channels: 3,
492	    is_night: config.target_is_night,
493	    translation_strength: config.translation_strength,
494	    brightness_reduction: if config.target_is_night {
495	      0.7	<-- UNCOVERED
496	    } else {
497	      0.0
498	    },
499	    color_temperature_shift: if config.target_is_night {
500	      -2000.0	<-- UNCOVERED
501	    } else {
502	      1000.0
503	    },
504	    light_source_added: config.target_is_night,
505	    num_light_sources: if config.target_is_night {
506	      5	<-- UNCOVERED
507	    } else {
508	      0
509	    },
510	    content_preservation: 0.75,
511	    translation_time_ms: 400.0,
512	    realism_score: 0.7,
513	  }
514	}
   	…

518	pub fn image_translation_report_to_string(
519	  task_type : ImageTranslationTaskType,
520	  config : ImageTranslationConfig,
521	  result_description : String,
522	) -> String {
523	  let mut report = "## 图像翻译高级报告\n\n"
524	
525	  report = report + "### 任务信息\n\n"
526	  report = report + "| 指标 | 值 |\n|------|------|\n"
527	  report = report +
528	    "| 任务类型 | " +
529	    (match task_type {
530	      ImageTranslationTaskType::StyleTransfer => "风格迁移"
531	      ImageTranslationTaskType::DomainAdaptation => "域适应"	<-- UNCOVERED
532	      ImageTranslationTaskType::ImageToImageTranslation =>
533	        "图像到图像翻译"	<-- UNCOVERED
534	      ImageTranslationTaskType::SeasonTransfer => "季节转换"
535	      ImageTranslationTaskType::WeatherTransfer => "天气转换"
536	      ImageTranslationTaskType::DayNightTransfer => "昼夜转换"
537	      ImageTranslationTaskType::SketchToPhoto => "素描到照片"
538	      ImageTranslationTaskType::MapToSatellite => "地图到卫星图"
539	      _ => "未知"
540	    }) +
541	    " |\n"
542	  report = report +
543	    "| 模型类型 | " +
544	    (match config.model_type {
545	      TranslationModelType::CycleGAN => "CycleGAN"
546	      TranslationModelType::Pix2Pix => "Pix2Pix"	<-- UNCOVERED
547	      TranslationModelType::UNIT => "UNIT"
548	      TranslationModelType::MUNIT => "MUNIT"
549	      TranslationModelType::StarGAN => "StarGAN"
550	      TranslationModelType::AdaIN => "AdaIN"
551	      _ => "未知"
552	    }) +
553	    " |\n"
554	  report = report + "| 源域 | " + config.source_domain + " |\n"
555	  report = report + "| 目标域 | " + config.target_domain + " |\n"
556	  report = report +
557	    "| 翻译强度 | " +
558	    config.translation_strength.to_string() +
559	    " |\n"
560	  report = report +
561	    "| 内容权重 | " +
562	    config.content_weight.to_string() +
563	    " |\n"
564	  report = report +
565	    "| 风格权重 | " +
566	    config.style_weight.to_string() +
567	    " |\n"
568	  report = report +
569	    "| 迭代次数 | " +
570	    config.num_iterations.to_string() +
571	    " |\n"
572	  report = report +
573	    "| 源季节 | " +
574	    (match config.source_season {
575	      SeasonType::Spring => "春"	<-- UNCOVERED
576	      SeasonType::Summer => "夏"
577	      SeasonType::Autumn => "秋"	<-- UNCOVERED
578	      SeasonType::Winter => "冬"
579	      _ => "未知"
580	    }) +
581	    " |\n"
582	  report = report +
583	    "| 目标季节 | " +
584	    (match config.target_season {
585	      SeasonType::Spring => "春"	<-- UNCOVERED
586	      SeasonType::Summer => "夏"
587	      SeasonType::Autumn => "秋"
588	      SeasonType::Winter => "冬"
589	      _ => "未知"	<-- UNCOVERED
590	    }) +
591	    " |\n"
592	  report = report +
593	    "| 源天气 | " +
594	    (match config.source_weather {
595	      WeatherType::Sunny => "晴"
596	      WeatherType::Cloudy => "多云"	<-- UNCOVERED
597	      WeatherType::Rainy => "雨"
598	      WeatherType::Snowy => "雪"
599	      WeatherType::Foggy => "雾"
600	      _ => "未知"
601	    }) +
602	    " |\n"
603	  report = report +
604	    "| 目标天气 | " +
605	    (match config.target_weather {
606	      WeatherType::Sunny => "晴"	<-- UNCOVERED
607	      WeatherType::Cloudy => "多云"
608	      WeatherType::Rainy => "雨"
609	      WeatherType::Snowy => "雪"	<-- UNCOVERED
610	      WeatherType::Foggy => "雾"
611	      _ => "未知"
612	    }) +
613	    " |\n"
614	
615	  report = report + "\n### 结果\n\n"
616	  report = report + result_description + "\n"
617	
618	  report
619	}
   	…

12 uncovered line(s) in src\process\image_watermark_advanced\image_watermark_advanced.mbt:

311	pub fn detect_watermark(
312	  image : Array[Double],
313	  width : Int,
314	  height : Int,
315	  watermark_size : Int,
316	  config : ImageWatermarkConfig,
317	) -> WatermarkDetectionResult {
318	  ignore(height)
319	  ignore(width)
320	  let detected = lsb_extract(image, watermark_size)
321	  let mut match_count = 0
322	
323	  for i in 0..<detected.length() {
324	    if detected[i].to_int() != 0 {
325	      match_count += 1
326	    }
327	  }
328	
329	  let confidence = if detected.length() > 0 {
330	    match_count.to_double() / detected.length().to_double()
331	  } else {
332	    0.0	<-- UNCOVERED
333	  }
334	
335	  WatermarkDetectionResult::{
336	    detected_watermark: detected,
337	    watermark_size_bytes: detected.length(),
338	    is_detected: confidence > 0.5,
339	    detection_confidence: confidence,
340	    correlation_coefficient: confidence,
341	    ber: 1.0 - confidence,
342	    nc: confidence,
343	    detection_domain: config.embedding_domain,
344	    detection_time_ms: 10.0,
345	    watermark_type: config.watermark_type,
346	  }
347	}
   	…

351	pub fn attack_watermark(
352	  image : Array[Double],
353	  width : Int,
354	  height : Int,
355	  attack_type : WatermarkAttackType,
356	  attack_strength : Double,
357	) -> WatermarkAttackResult {
358	  let attacked : Array[Double] = []
359	  let mut state = 42
360	
361	  for value in image {
362	    state = (state * 11035 + 12345) % 1000000
363	    let noise = (state % 1000).to_double() / 1000.0 - 0.5
364	    let mut new_value = value
365	
366	    match attack_type {
367	      WatermarkAttackType::NoiseAddition =>
368	        new_value = value + noise * attack_strength * 50.0
369	      WatermarkAttackType::BrightnessChange =>
370	        new_value = value + attack_strength * 50.0	<-- UNCOVERED
371	      WatermarkAttackType::ContrastChange =>
372	        new_value = (value - 128.0) * (1.0 + attack_strength) + 128.0	<-- UNCOVERED
373	      WatermarkAttackType::Filtering => new_value = value * 0.9 + 128.0 * 0.1
374	      _ => new_value = value
375	    }
376	
377	    if new_value < 0.0 {
378	      attacked.push(0.0)
379	    } else if new_value > 255.0 {
380	      attacked.push(255.0)	<-- UNCOVERED
381	    } else {
382	      attacked.push(new_value)
383	    }
384	  }
385	
386	  let params : Array[Double] = []
387	  params.push(attack_strength)
388	
389	  WatermarkAttackResult::{
390	    attacked_image: attacked,
391	    width,
392	    height,
393	    num_channels: 3,
394	    attack_type,
395	    attack_parameters: params,
396	    psnr: 30.0,
397	    ssim: 0.85,
398	    image_quality_score: 0.8,
399	    watermark_survival_rate: 0.5,
400	    attack_time_ms: 30.0,
401	  }
402	}
   	…

406	pub fn embed_visible_watermark(
407	  image : Array[Double],
408	  width : Int,
409	  height : Int,
410	  config : ImageWatermarkConfig,
411	) -> Array[Double] {
412	  let result : Array[Double] = []
413	  let vc = config.visible_config
414	  let text_length = vc.watermark_text.length()
415	
416	  for y in 0..<height {
417	    for x in 0..<width {
418	      let idx = y * width + x
419	      if idx < image.length() {
420	        let mut value = image[idx]
421	
422	        // 简化的可见水印：在指定位置添加半透明文字
423	        let in_text_area = x >= vc.position_x &&
424	          x < vc.position_x + text_length * vc.font_size / 2 &&
425	          y >= vc.position_y &&
426	          y < vc.position_y + vc.font_size
427	
428	        if in_text_area {
429	          let alpha = vc.opacity	<-- UNCOVERED
430	          let wm_value = 255.0
431	          value = value * (1.0 - alpha) + wm_value * alpha
432	        }
433	
434	        result.push(value)
435	      } else {
436	        result.push(0.0)	<-- UNCOVERED
437	      }
438	    }
439	  }
440	
441	  result
442	}
   	…

446	pub fn image_watermark_report_to_string(
447	  task_type : ImageWatermarkTaskType,
448	  config : ImageWatermarkConfig,
449	  result_description : String,
450	) -> String {
451	  let mut report = "## 图像水印高级报告\n\n"
452	
453	  report = report + "### 任务信息\n\n"
454	  report = report + "| 指标 | 值 |\n|------|------|\n"
455	  report = report +
456	    "| 任务类型 | " +
457	    (match task_type {
458	      ImageWatermarkTaskType::VisibleWatermark => "可见水印"	<-- UNCOVERED
459	      ImageWatermarkTaskType::InvisibleWatermark => "不可见水印"
460	      ImageWatermarkTaskType::RobustWatermark => "鲁棒水印"	<-- UNCOVERED
461	      ImageWatermarkTaskType::FragileWatermark => "脆弱水印"
462	      ImageWatermarkTaskType::SemiFragileWatermark => "半脆弱水印"
463	      ImageWatermarkTaskType::DigitalFingerprint => "数字指纹"
464	      ImageWatermarkTaskType::WatermarkAttack => "水印攻击"
465	      ImageWatermarkTaskType::WatermarkDetection => "水印检测"
466	      _ => "未知"
467	    }) +
468	    " |\n"
469	  report = report +
470	    "| 水印类型 | " +
471	    (match config.watermark_type {
472	      WatermarkType::TextWatermark => "文字水印"	<-- UNCOVERED
473	      WatermarkType::ImageWatermark => "图像水印"
474	      WatermarkType::LogoWatermark => "Logo水印"
475	      WatermarkType::BinaryWatermark => "二值水印"
476	      WatermarkType::QRCodeWatermark => "二维码水印"	<-- UNCOVERED
477	      WatermarkType::EncryptedWatermark => "加密水印"
478	      _ => "未知"
479	    }) +
480	    " |\n"
481	  report = report +
482	    "| 嵌入域 | " +
483	    (match config.embedding_domain {
484	      WatermarkDomain::SpatialDomain => "空间域"	<-- UNCOVERED
485	      WatermarkDomain::DCTDomain => "DCT域"
486	      WatermarkDomain::DWTDomain => "DWT域"	<-- UNCOVERED
487	      WatermarkDomain::FFTDomain => "FFT域"
488	      WatermarkDomain::WaveletDomain => "小波域"
489	      _ => "未知"
490	    }) +
491	    " |\n"
492	  report = report +
493	    "| 水印强度 | " +
494	    config.watermark_strength.to_string() +
495	    " |\n"
496	  report = report +
497	    "| 使用加密 | " +
498	    (if config.use_encryption { "开启" } else { "关闭" }) +
499	    " |\n"
500	  report = report +
501	    "| 使用纠错 | " +
502	    (if config.use_error_correction { "开启" } else { "关闭" }) +
503	    " |\n"
504	  report = report +
505	    "| 使用置乱 | " +
506	    (if config.use_scrambling { "开启" } else { "关闭" }) +
507	    " |\n"
508	  report = report +
509	    "| 可见水印文字 | " +
510	    config.visible_config.watermark_text +
511	    " |\n"
512	  report = report +
513	    "| 不透明度 | " +
514	    config.visible_config.opacity.to_string() +
515	    " |\n"
516	
517	  report = report + "\n### 结果\n\n"
518	  report = report + result_description + "\n"
519	
520	  report
521	}
   	…

7 uncovered line(s) in src\process\industrial_inspection_advanced\industrial_inspection_advanced.mbt:

285	pub fn evaluate_surface_quality(
286	  image : Array[Double],
287	  width : Int,
288	  height : Int,
289	  defects : Array[DetectedDefect],
290	  config : IndustrialInspectionConfig,
291	) -> SurfaceQualityResult {
292	  ignore(config)
293	  ignore(image)
294	  // 简化的表面质量评估
295	  let defect_area_ratio = if defects.length() > 0 {
296	    let mut total_area = 0.0
297	    for d in defects {
298	      total_area += d.area
299	    }
300	    total_area / (width.to_double() * height.to_double())
301	  } else {
302	    0.0
303	  }
304	
305	  let overall_score = 1.0 - defect_area_ratio * 10.0
306	  let grade = if overall_score > 0.9 {
307	    SurfaceQualityGrade::Excellent
308	  } else if overall_score > 0.8 {	<-- UNCOVERED
309	    SurfaceQualityGrade::Good
310	  } else if overall_score > 0.7 {
311	    SurfaceQualityGrade::Acceptable
312	  } else if overall_score > 0.6 {
313	    SurfaceQualityGrade::Poor
314	  } else {
315	    SurfaceQualityGrade::Reject	<-- UNCOVERED
316	  }
317	
318	  let mut critical_count = 0
319	  for d in defects {
320	    if d.is_critical {
321	      critical_count += 1
322	    }
323	  }
324	
325	  SurfaceQualityResult::{
326	    overall_grade: grade,
327	    overall_score,
328	    roughness: 0.5,
329	    waviness: 0.3,
330	    flatness: 0.8,
331	    glossiness: 0.7,
332	    color_uniformity: 0.9,
333	    defect_density: defect_area_ratio,
334	    num_defects: defects.length(),
335	    critical_defects: critical_count,
336	  }
337	}
   	…

341	pub fn detect_anomalies(
342	  image : Array[Double],
343	  reference : Array[Double],
344	  width : Int,
345	  height : Int,
346	  config : IndustrialInspectionConfig,
347	) -> AnomalyDetectionResult {
348	  ignore(height)
349	  ignore(width)
350	  // 简化的异常检测：计算与参考图像的差异
351	  let mut total_error = 0.0
352	  let anomaly_map : Array[Array[Double]] = []
353	  let anomaly_regions : Array[Array[Int]] = []
354	
355	  let min_len = if image.length() < reference.length() {
356	    image.length()	<-- UNCOVERED
357	  } else {
358	    reference.length()
359	  }
360	
361	  for i in 0..<min_len {
362	    let diff = (image[i] - reference[i]).abs()
363	    total_error += diff
364	  }
365	
366	  let avg_error = if min_len > 0 {
367	    total_error / min_len.to_double()
368	  } else {
369	    0.0	<-- UNCOVERED
370	  }
371	
372	  let is_anomaly = avg_error > config.anomaly_threshold
373	  let confidence = if is_anomaly {
374	    (avg_error - config.anomaly_threshold) * 2.0
375	  } else {
376	    1.0 - avg_error
377	  }
378	
379	  AnomalyDetectionResult::{
380	    is_anomaly,
381	    anomaly_score: avg_error,
382	    threshold: config.anomaly_threshold,
383	    anomaly_map,
384	    anomaly_regions,
385	    num_anomaly_regions: 0,
386	    confidence,
387	    reconstruction_error: avg_error,
388	  }
389	}
   	…

393	pub fn industrial_inspection_report_to_string(
394	  task_type : IndustrialInspectionTaskType,
395	  config : IndustrialInspectionConfig,
396	  result_description : String,
397	) -> String {
398	  let mut report = "## 工业检测高级报告\n\n"
399	
400	  report = report + "### 任务信息\n\n"
401	  report = report + "| 指标 | 值 |\n|------|------|\n"
402	  report = report +
403	    "| 任务类型 | " +
404	    (match task_type {
405	      IndustrialInspectionTaskType::DefectDetection => "缺陷检测"	<-- UNCOVERED
406	      IndustrialInspectionTaskType::DimensionalMeasurement => "尺寸测量"
407	      IndustrialInspectionTaskType::SurfaceQualityInspection =>
408	        "表面质量检测"
409	      IndustrialInspectionTaskType::ProductClassification => "产品分类"	<-- UNCOVERED
410	      IndustrialInspectionTaskType::IndustrialOCR => "工业OCR"
411	      IndustrialInspectionTaskType::AnomalyDetection => "异常检测"
412	      IndustrialInspectionTaskType::AssemblyVerification => "装配验证"	<-- UNCOVERED
413	      _ => "未知"
414	    }) +
415	    " |\n"
416	  report = report +
417	    "| 检测阈值 | " +
418	    config.detection_threshold.to_string() +
419	    " |\n"
420	  report = report +
421	    "| 严重度阈值 | " +
422	    config.severity_threshold.to_string() +
423	    " |\n"
424	  report = report + "| 测量单位 | " + config.measurement_unit + " |\n"
425	  report = report +
426	    "| 默认公差 | " +
427	    config.tolerance_default.to_string() +
428	    " |\n"
429	  report = report +
430	    "| 表面质量阈值 | " +
431	    config.surface_quality_threshold.to_string() +
432	    " |\n"
433	  report = report +
434	    "| 异常阈值 | " +
435	    config.anomaly_threshold.to_string() +
436	    " |\n"
437	  report = report +
438	    "| AI检测 | " +
439	    (if config.use_ai_detection { "开启" } else { "关闭" }) +
440	    " |\n"
441	  report = report +
442	    "| 传统视觉 | " +
443	    (if config.use_traditional_vision { "开启" } else { "关闭" }) +
444	    " |\n"
445	  report = report +
446	    "| 混合方法 | " +
447	    (if config.use_hybrid { "开启" } else { "关闭" }) +
448	    " |\n"
449	  report = report +
450	    "| 相机分辨率 | " +
451	    config.camera_resolution[0].to_string() +
452	    "x" +
453	    config.camera_resolution[1].to_string() +
454	    " |\n"
455	  report = report + "| 照明类型 | " + config.lighting_type + " |\n"
456	  report = report +
457	    "| 检测速度 | " +
458	    config.inspection_speed.to_string() +
459	    " 件/分钟 |\n"
460	
461	  report = report + "\n### 结果\n\n"
462	  report = report + result_description + "\n"
463	
464	  report
465	}
   	…

14 uncovered line(s) in src\process\inpainting\inpainting_enhancement.mbt:

125	pub fn inpaint_simple(
126	  img : @types.Image,
127	  mask : @types.Image,
128	  radius : Int,
129	) -> @types.Image {
130	  let w = img.width
131	  let h = img.height
132	  let total = w * h
133	  let out : Array[Byte] = []
134	
135	  for i in 0..<total {
136	    out.push(img.data[i])
137	  }
138	
139	  // 迭代修复
140	  for _ in 0..<5 {
141	    for y in 0..<h {
142	      for x in 0..<w {
143	        let idx = y * w + x
144	        if mask.data[idx] == b'\xFF' {
145	          // 计算邻域平均值
146	          let mut sum = 0.0
147	          let mut count = 0
148	          for dy in -radius..<(radius + 1) {
149	            for dx in -radius..<(radius + 1) {
150	              if dx == 0 && dy == 0 {
151	                continue
152	              }
153	              let nx = x + dx
154	              let ny = y + dy
155	              if nx >= 0 && nx < w && ny >= 0 && ny < h {
156	                let nidx = ny * w + nx
157	                if mask.data[nidx] == b'\x00' || out[nidx] != img.data[nidx] {
158	                  sum += out[nidx].to_double()
159	                  count += 1
160	                }
161	              }
162	            }
163	          }
164	          if count > 0 {
165	            let avg = sum / count.to_double()
166	            let clamped = if avg < 0.0 {
167	              0.0	<-- UNCOVERED
168	            } else if avg > 255.0 {
169	              255.0	<-- UNCOVERED
170	            } else {
171	              avg
172	            }
173	            out[idx] = clamped.to_int().to_byte()
174	          }
175	        }
176	      }
177	    }
178	  }
179	
180	  @types.Image::{
181	    width: w,
182	    height: h,
183	    channels: img.channels,
184	    data: Bytes::from_array(out),
185	  }
186	}
   	…

190	pub fn inpaint_diffusion(
191	  img : @types.Image,
192	  mask : @types.Image,
193	  radius : Int,
194	) -> @types.Image {
195	  let w = img.width
196	  let h = img.height
197	  let total = w * h
198	  let out : Array[Byte] = []
199	
200	  for i in 0..<total {
201	    out.push(img.data[i])
202	  }
203	
204	  // 从边界向内部扩散
205	  for _ in 0..<10 {
206	    for y in 0..<h {
207	      for x in 0..<w {
208	        let idx = y * w + x
209	        if mask.data[idx] == b'\xFF' {
210	          // 检查是否有已修复的邻域
211	          let mut has_known = false
212	          for dy in -1..<2 {
213	            for dx in -1..<2 {
214	              if dx == 0 && dy == 0 {
215	                continue
216	              }
217	              let nx = x + dx
218	              let ny = y + dy
219	              if nx >= 0 && nx < w && ny >= 0 && ny < h {
220	                let nidx = ny * w + nx
221	                if mask.data[nidx] == b'\x00' || out[nidx] != img.data[nidx] {
222	                  has_known = true
223	                }
224	              }
225	            }
226	          }
227	
228	          if has_known {
229	            // 加权平均（距离加权）
230	            let mut sum = 0.0
231	            let mut weight_sum = 0.0
232	            for dy in -radius..<(radius + 1) {
233	              for dx in -radius..<(radius + 1) {
234	                if dx == 0 && dy == 0 {
235	                  continue
236	                }
237	                let nx = x + dx
238	                let ny = y + dy
239	                if nx >= 0 && nx < w && ny >= 0 && ny < h {
240	                  let nidx = ny * w + nx
241	                  if mask.data[nidx] == b'\x00' || out[nidx] != img.data[nidx] {
242	                    let dist = (dx * dx + dy * dy).to_double().sqrt()
243	                    let weight = 1.0 / (1.0 + dist)
244	                    sum += out[nidx].to_double() * weight
245	                    weight_sum += weight
246	                  }
247	                }
248	              }
249	            }
250	            if weight_sum > 0.0 {
251	              let avg = sum / weight_sum
252	              let clamped = if avg < 0.0 {
253	                0.0	<-- UNCOVERED
254	              } else if avg > 255.0 {
255	                255.0	<-- UNCOVERED
256	              } else {
257	                avg
258	              }
259	              out[idx] = clamped.to_int().to_byte()
260	            }
261	          }
262	        }
263	      }
264	    }
265	  }
266	
267	  @types.Image::{
268	    width: w,
269	    height: h,
270	    channels: img.channels,
271	    data: Bytes::from_array(out),
272	  }
273	}
   	…

310	pub fn dehaze_dark_channel(
311	  img : @types.Image,
312	  omega : Double,
313	  t0 : Double,
314	) -> DehazeResult {
315	  let w = img.width
316	  let h = img.height
317	  let total = w * h
318	  let patch_size = 15
319	
320	  // 计算暗通道
321	  let dark_channel : Array[Double] = []
322	  for _ in 0..<total {
323	    dark_channel.push(255.0)
324	  }
325	
326	  for y in 0..<h {
327	    for x in 0..<w {
328	      let mut min_val = 255.0
329	      for dy in 0..<patch_size {
330	        for dx in 0..<patch_size {
331	          let nx = x + dx - patch_size / 2
332	          let ny = y + dy - patch_size / 2
333	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
334	            let val = img.data[ny * w + nx].to_double()
335	            if val < min_val {
336	              min_val = val
337	            }
338	          }
339	        }
340	      }
341	      dark_channel[y * w + x] = min_val
342	    }
343	  }
344	
345	  // 估计大气光（暗通道最亮的 0.1% 像素的平均值）
346	  let mut atmospheric_light = 0.0
347	  let mut max_dark = 0.0
348	  for i in 0..<total {
349	    if dark_channel[i] > max_dark {
350	      max_dark = dark_channel[i]	<-- UNCOVERED
351	    }
352	  }
353	  let threshold = max_dark * 0.999
354	  let mut count = 0
355	  for i in 0..<total {
356	    if dark_channel[i] >= threshold {
357	      atmospheric_light += img.data[i].to_double()
358	      count += 1
359	    }
360	  }
361	  if count > 0 {
362	    atmospheric_light /= count.to_double()
363	  }
364	
365	  // 计算透射率
366	  let transmission : Array[Double] = []
367	  for i in 0..<total {
368	    let t = 1.0 - omega * (dark_channel[i] / atmospheric_light)
369	    let clamped = if t < t0 { t0 } else if t > 1.0 { 1.0 } else { t }	<-- UNCOVERED
370	    transmission.push(clamped)
371	  }
372	
373	  // 去雾
374	  let out : Array[Byte] = []
375	  for i in 0..<total {
376	    let t = transmission[i]
377	    let pixel = img.data[i].to_double()
378	    let dehazed = (pixel - atmospheric_light) / t + atmospheric_light
379	    let clamped = if dehazed < 0.0 {
380	      0.0	<-- UNCOVERED
381	    } else if dehazed > 255.0 {
382	      255.0	<-- UNCOVERED
383	    } else {
384	      dehazed
385	    }
386	    out.push(clamped.to_int().to_byte())
387	  }
388	
389	  DehazeResult::{
390	    image: @types.Image::{
391	      width: w,
392	      height: h,
393	      channels: img.channels,
394	      data: Bytes::from_array(out),
395	    },
396	    algorithm: DehazeAlgorithm::DarkChannelPrior,
397	    atmospheric_light,
398	    transmission_map: transmission,
399	    processing_time_ms: 0.0,
400	  }
401	}
   	…

405	pub fn dehaze_clahe(
406	  img : @types.Image,
407	  clip_limit : Double,
408	  tile_size : Int,
409	) -> @types.Image {
410	  ignore(clip_limit)
411	  ignore(tile_size)
412	  let w = img.width
413	  let h = img.height
414	  let total = w * h
415	
416	  // 简化：全局直方图均衡化
417	  let histogram : Array[Int] = []
418	  for _ in 0..<256 {
419	    histogram.push(0)
420	  }
421	  for i in 0..<total {
422	    histogram[img.data[i].to_int()] += 1
423	  }
424	
425	  // 累积分布函数
426	  let cdf : Array[Double] = []
427	  let mut cdf_min = total.to_double()
428	  let mut sum = 0.0
429	  for i in 0..<256 {
430	    sum += histogram[i].to_double()
431	    cdf.push(sum)
432	    if histogram[i] > 0 && sum < cdf_min {
433	      cdf_min = sum
434	    }
435	  }
436	
437	  // 直方图均衡化
438	  let out : Array[Byte] = []
439	  for i in 0..<total {
440	    let pixel = img.data[i].to_int()
441	    let equalized = if total > 1 {
442	      ((cdf[pixel] - cdf_min) / (total.to_double() - cdf_min) * 255.0).to_int()
443	    } else {
444	      pixel	<-- UNCOVERED
445	    }
446	    let clamped = if equalized < 0 {
447	      0	<-- UNCOVERED
448	    } else if equalized > 255 {
449	      255	<-- UNCOVERED
450	    } else {
451	      equalized
452	    }
453	    out.push(clamped.to_byte())
454	  }
455	
456	  @types.Image::{
457	    width: w,
458	    height: h,
459	    channels: img.channels,
460	    data: Bytes::from_array(out),
461	  }
462	}
   	…

483	pub fn derain_morphological(img : @types.Image) -> DerainResult {
484	  let w = img.width
485	  let h = img.height
486	  let _ = w * h
487	
488	  // 简化：中值滤波去除雨线
489	  let out : Array[Byte] = []
490	  for y in 0..<h {
491	    for x in 0..<w {
492	      // 3x3 中值滤波
493	      let neighbors : Array[Int] = []
494	      for dy in -1..<2 {
495	        for dx in -1..<2 {
496	          let nx = x + dx
497	          let ny = y + dy
498	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
499	            neighbors.push(img.data[ny * w + nx].to_int())
500	          }
501	        }
502	      }
503	      // 排序取中值
504	      for i in 0..<neighbors.length() {
505	        for j in 0..<(neighbors.length() - i - 1) {
506	          if neighbors[j] > neighbors[j + 1] {
507	            let temp = neighbors[j]	<-- UNCOVERED
508	            neighbors[j] = neighbors[j + 1]
509	            neighbors[j + 1] = temp
510	          }
511	        }
512	      }
513	      let median = neighbors[neighbors.length() / 2]
514	      out.push(median.to_byte())
515	    }
516	  }
517	
518	  DerainResult::{
519	    image: @types.Image::{
520	      width: w,
521	      height: h,
522	      channels: img.channels,
523	      data: Bytes::from_array(out),
524	    },
525	    algorithm: DerainAlgorithm::Morphological,
526	    rain_streaks_removed: 0,
527	    processing_time_ms: 0.0,
528	  }
529	}
   	…

540	pub fn inpainting_report_to_string(result : InpaintingResult) -> String {
541	  let mut report = "## 图像修复报告\n\n"
542	  report = report + "### 处理信息\n\n"
543	  report = report + "| 指标 | 值 |\n|------|------|\n"
544	  let algo_str = if result.algorithm == InpaintingAlgorithm::Telea {
545	    "Telea"
546	  } else if result.algorithm == InpaintingAlgorithm::NavierStokes {	<-- UNCOVERED
547	    "Navier-Stokes"
548	  } else if result.algorithm == InpaintingAlgorithm::FastMarching {
549	    "快速行进"
550	  } else if result.algorithm == InpaintingAlgorithm::PatchMatch {
551	    "PatchMatch"
552	  } else if result.algorithm == InpaintingAlgorithm::DeepLearning {
553	    "深度学习"
554	  } else {
555	    "未知"	<-- UNCOVERED
556	  }
557	  report = report + "| 算法 | " + algo_str + " |\n"
558	  report = report +
559	    "| 修复像素数 | " +
560	    result.inpainted_pixels.to_string() +
561	    " |\n"
562	  report = report +
563	    "| 掩膜像素总数 | " +
564	    result.total_mask_pixels.to_string() +
565	    " |\n"
566	  report = report + "| 迭代次数 | " + result.iterations.to_string() + " |\n"
567	  report = report +
568	    "| 处理耗时 | " +
569	    result.processing_time_ms.to_string() +
570	    "ms |\n"
571	
572	  report
573	}
   	…

23 uncovered line(s) in src\process\intelligent_vision\intelligent_vision.mbt:

187	pub fn detect_objects_iv(
188	  image : @types.Image,
189	  config : IntelligentVisionConfig,
190	) -> Array[DetectionResultIV] {
191	  let w = image.width
192	  let h = image.height
193	  let results : Array[DetectionResultIV] = []
194	
195	  // 简化的检测：基于高对比度区域
196	  let grid_size = 64
197	  let mut detection_count = 0
198	
199	  for gy in 0..<(h / grid_size) {
200	    for gx in 0..<(w / grid_size) {
201	      if detection_count >= config.max_detections {
202	        break	<-- UNCOVERED
203	      }
204	
205	      // 计算区域对比度
206	      let mut sum = 0.0
207	      let mut sum_sq = 0.0
208	      let count = grid_size * grid_size
209	      for y in 0..<grid_size {
210	        for x in 0..<grid_size {
211	          let idx = (gy * grid_size + y) * w + (gx * grid_size + x)
212	          let v = image.data[idx].to_double()
213	          sum += v
214	          sum_sq += v * v
215	        }
216	      }
217	      let mean = sum / count.to_double()
218	      let variance = sum_sq / count.to_double() - mean * mean
219	      let std_dev = if variance > 0.0 { variance.sqrt() } else { 0.0 }
220	
221	      if std_dev > 30.0 {
222	        let confidence = if std_dev > 50.0 { 0.8 } else { 0.6 }	<-- UNCOVERED
223	        if confidence >= config.confidence_threshold {
224	          let bx = gx.to_double() * grid_size.to_double()	<-- UNCOVERED
225	          let by = gy.to_double() * grid_size.to_double()
226	          let bw = grid_size.to_double()
227	          let bh = grid_size.to_double()
228	          let bbox : Array[Double] = [bx, by, bw, bh]
229	          results.push(DetectionResultIV::{	<-- UNCOVERED
230	            class_name: "object",
231	            confidence,
232	            bounding_box: bbox,
233	            class_id: 0,
234	            num_detections: 1,
235	          })
236	          detection_count += 1
237	        }
238	      }
239	    }
240	  }
241	
242	  results
243	}
   	…

289	pub fn recognize_traffic_signs(
290	  image : @types.Image,
291	  config : IntelligentVisionConfig,
292	) -> Array[TrafficSignResult] {
293	  let results : Array[TrafficSignResult] = []
294	  let detections = detect_objects_iv(image, config)
295	
296	  for det in detections {
297	    let sign_type = if det.confidence > 0.7 { "speed_limit" } else { "warning" }	<-- UNCOVERED
298	    let sign_value = if sign_type == "speed_limit" { "60" } else { "caution" }
299	
300	    results.push(TrafficSignResult::{	<-- UNCOVERED
301	      sign_type,
302	      sign_value,
303	      confidence: det.confidence,
304	      bounding_box: det.bounding_box,
305	      num_signs: 1,
306	    })
307	  }
308	
309	  results
310	}
   	…

314	pub fn detect_defects(
315	  image : @types.Image,
316	  config : IntelligentVisionConfig,
317	) -> Array[DefectDetectionResult] {
318	  let results : Array[DefectDetectionResult] = []
319	  let detections = detect_objects_iv(image, config)
320	
321	  for det in detections {
322	    let defect_type = if det.confidence > 0.7 { "crack" } else { "scratch" }	<-- UNCOVERED
323	    let severity = if det.confidence > 0.8 {
324	      "high"	<-- UNCOVERED
325	    } else if det.confidence > 0.6 {
326	      "medium"
327	    } else {
328	      "low"	<-- UNCOVERED
329	    }
330	
331	    results.push(DefectDetectionResult::{	<-- UNCOVERED
332	      defect_type,
333	      severity,
334	      confidence: det.confidence,
335	      bounding_box: det.bounding_box,
336	      defect_area: det.bounding_box[2] * det.bounding_box[3],
337	      num_defects: 1,
338	    })
339	  }
340	
341	  results
342	}
   	…

346	pub fn detect_intrusion(
347	  image : @types.Image,
348	  roi : Array[Double],
349	  config : IntelligentVisionConfig,
350	) -> IntrusionDetectionResult {
351	  let detections = detect_objects_iv(image, config)
352	  let mut is_intrusion = false
353	  let mut intrusion_region : Array[Double] = []
354	  let mut object_type = "none"
355	  let mut max_confidence = 0.0
356	
357	  for det in detections {
358	    // 检查检测框是否与ROI重叠
359	    let x1 = det.bounding_box[0]	<-- UNCOVERED
360	    let y1 = det.bounding_box[1]
361	    let x2 = x1 + det.bounding_box[2]
362	    let y2 = y1 + det.bounding_box[3]
363	
364	    let roi_x1 = roi[0]
365	    let roi_y1 = roi[1]
366	    let roi_x2 = roi[0] + roi[2]
367	    let roi_y2 = roi[1] + roi[3]
368	
369	    let overlap_x = if x2 > roi_x1 && x1 < roi_x2 {
370	      let min_x2 = if x2 < roi_x2 { x2 } else { roi_x2 }	<-- UNCOVERED
371	      let max_x1 = if x1 > roi_x1 { x1 } else { roi_x1 }
372	      min_x2 - max_x1
373	    } else {
374	      0.0	<-- UNCOVERED
375	    }
376	
377	    let overlap_y = if y2 > roi_y1 && y1 < roi_y2 {
378	      let min_y2 = if y2 < roi_y2 { y2 } else { roi_y2 }	<-- UNCOVERED
379	      let max_y1 = if y1 > roi_y1 { y1 } else { roi_y1 }
380	      min_y2 - max_y1
381	    } else {
382	      0.0	<-- UNCOVERED
383	    }
384	
385	    if overlap_x > 0.0 && overlap_y > 0.0 {
386	      is_intrusion = true	<-- UNCOVERED
387	      intrusion_region = det.bounding_box
388	      object_type = det.class_name
389	      if det.confidence > max_confidence {
390	        max_confidence = det.confidence	<-- UNCOVERED
391	      }
392	    }
393	  }
394	
395	  IntrusionDetectionResult::{
396	    is_intrusion,
397	    confidence: max_confidence,
398	    intrusion_region,
399	    object_type,
400	    timestamp: 0.0,
401	    num_intrusions: if is_intrusion {
402	      1	<-- UNCOVERED
403	    } else {
404	      0
405	    },
406	  }
407	}
   	…

411	pub fn detect_crops(
412	  image : @types.Image,
413	  config : IntelligentVisionConfig,
414	) -> Array[CropDetectionResult] {
415	  let results : Array[CropDetectionResult] = []
416	  let detections = detect_objects_iv(image, config)
417	
418	  for det in detections {
419	    let crop_type = if det.confidence > 0.7 { "wheat" } else { "corn" }	<-- UNCOVERED
420	    let health_status = if det.confidence > 0.8 {
421	      "healthy"	<-- UNCOVERED
422	    } else if det.confidence > 0.6 {
423	      "moderate"
424	    } else {
425	      "stressed"	<-- UNCOVERED
426	    }
427	    let growth_stage = if det.confidence > 0.7 { "mature" } else { "growing" }	<-- UNCOVERED
428	
429	    results.push(CropDetectionResult::{	<-- UNCOVERED
430	      crop_type,
431	      health_status,
432	      confidence: det.confidence,
433	      bounding_box: det.bounding_box,
434	      growth_stage,
435	      num_crops: 1,
436	    })
437	  }
438	
439	  results
440	}
   	…

10 uncovered line(s) in src\process\knowledge_graph\knowledge_graph.mbt:

227	pub fn link_entity(
228	  mention : String,
229	  kg : KnowledgeGraph,
230	  config : KGConfig,
231	) -> EntityLinkingResult {
232	  let candidate_entities : Array[String] = []
233	  let candidate_scores : Array[Double] = []
234	
235	  // 简化的实体链接：基于字符串相似度
236	  let mut best_entity = ""
237	  let mut best_score = 0.0
238	
239	  for entity in kg.entities {
240	    // 简化的字符串相似度
241	    let mut common_chars = 0
242	    let mention_lower = mention
243	    let name_lower = entity.entity_name
244	
245	    // 简化的字符串相似度
246	    if name_lower.contains(mention_lower) || mention_lower.contains(name_lower) {
247	      common_chars = mention_lower.length()
248	    }
249	
250	    let similarity = if mention.length() > 0 {
251	      common_chars.to_double() / mention.length().to_double()
252	    } else {
253	      0.0	<-- UNCOVERED
254	    }
255	
256	    if similarity > config.confidence_threshold {
257	      candidate_entities.push(entity.entity_id)
258	      candidate_scores.push(similarity)
259	
260	      if similarity > best_score {
261	        best_score = similarity
262	        best_entity = entity.entity_id
263	      }
264	    }
265	  }
266	
267	  EntityLinkingResult::{
268	    mention,
269	    candidate_entities,
270	    candidate_scores,
271	    linked_entity: best_entity,
272	    confidence: best_score,
273	    method_type: config.entity_linking_method,
274	    num_candidates: candidate_entities.length(),
275	  }
276	}
   	…

320	pub fn reason_over_kg(
321	  query : String,
322	  kg : KnowledgeGraph,
323	  config : KGConfig,
324	) -> KnowledgeReasoningResult {
325	  let reasoning_path : Array[String] = []
326	  let supporting_facts : Array[String] = []
327	
328	  // 简化的知识推理：基于路径搜索
329	  let mut answer = "unknown"
330	  let mut confidence = 0.3
331	  let mut num_hops = 0
332	
333	  // 查找查询中的实体
334	  for entity in kg.entities {
335	    if query.contains(entity.entity_name) {
336	      reasoning_path.push(entity.entity_id)
337	
338	      // 查找相关关系
339	      for relation in kg.relations {
340	        if relation.source_entity_id == entity.entity_id {
341	          reasoning_path.push(relation.relation_type)
342	          reasoning_path.push(relation.target_entity_id)
343	          supporting_facts.push(relation.relation_label)
344	          num_hops += 1
345	
346	          if num_hops >= config.max_hops {
347	            break	<-- UNCOVERED
348	          }
349	        }
350	      }
351	
352	      if num_hops > 0 {
353	        answer = "found " + num_hops.to_string() + " relations"
354	        confidence = 0.7
355	      }
356	      break
357	    }
358	  }
359	
360	  KnowledgeReasoningResult::{
361	    query,
362	    answer,
363	    confidence,
364	    reasoning_path,
365	    method_type: config.reasoning_method,
366	    num_hops,
367	    supporting_facts,
368	  }
369	}
   	…

439	pub fn knowledge_graph_report_to_string(
440	  kg : KnowledgeGraph,
441	  config : KGConfig,
442	  embedding : KGEmbedding,
443	) -> String {
444	  let stats = compute_kg_statistics(kg)
445	  let avg_degree = stats.0
446	  let density = stats.1
447	  let clustering = stats.2
448	
449	  let mut report = "## 知识图谱与推理报告\n\n"
450	
451	  report = report + "### 知识图谱统计\n\n"
452	  report = report + "| 指标 | 值 |\n|------|------|\n"
453	  report = report + "| 实体数 | " + kg.num_entities.to_string() + " |\n"
454	  report = report + "| 关系数 | " + kg.num_relations.to_string() + " |\n"
455	  report = report +
456	    "| 实体类型数 | " +
457	    kg.num_entity_types.to_string() +
458	    " |\n"
459	  report = report +
460	    "| 关系类型数 | " +
461	    kg.num_relation_types.to_string() +
462	    " |\n"
463	  report = report + "| 平均度 | " + avg_degree.to_string() + " |\n"
464	  report = report + "| 密度 | " + density.to_string() + " |\n"
465	  report = report + "| 聚类系数 | " + clustering.to_string() + " |\n"
466	
467	  report = report + "\n### 构建配置\n\n"
468	  report = report + "| 指标 | 值 |\n|------|------|\n"
469	  report = report +
470	    "| 构建方法 | " +
471	    (match kg.construction_method {
472	      KGConstructionMethod::RuleBased => "基于规则"	<-- UNCOVERED
473	      KGConstructionMethod::Statistical => "统计"
474	      KGConstructionMethod::Neural => "神经"
475	      KGConstructionMethod::Hybrid => "混合"
476	      _ => "未知"	<-- UNCOVERED
477	    }) +
478	    " |\n"
479	  report = report +
480	    "| 实体链接方法 | " +
481	    (match config.entity_linking_method {
482	      EntityLinkingMethod::StringMatching => "字符串匹配"	<-- UNCOVERED
483	      EntityLinkingMethod::VectorSimilarity => "向量相似度"
484	      EntityLinkingMethod::Neural => "神经"
485	      _ => "未知"	<-- UNCOVERED
486	    }) +
487	    " |\n"
488	  report = report +
489	    "| 关系抽取方法 | " +
490	    (match config.relation_extraction_method {
491	      RelationExtractionMethod::PatternBased => "基于模式"	<-- UNCOVERED
492	      RelationExtractionMethod::Neural => "神经"
493	      RelationExtractionMethod::OpenIE => "开放IE"	<-- UNCOVERED
494	      _ => "未知"
495	    }) +
496	    " |\n"
497	  report = report +
498	    "| 推理方法 | " +
499	    (match config.reasoning_method {
500	      KnowledgeReasoningMethod::RuleBased => "基于规则"	<-- UNCOVERED
501	      KnowledgeReasoningMethod::EmbeddingBased => "基于嵌入"
502	      KnowledgeReasoningMethod::Neural => "神经"	<-- UNCOVERED
503	      KnowledgeReasoningMethod::GraphNeuralNetwork => "图神经网络"
504	      _ => "未知"
505	    }) +
506	    " |\n"
507	  report = report +
508	    "| 嵌入维度 | " +
509	    config.embedding_dim.to_string() +
510	    " |\n"
511	  report = report +
512	    "| 置信度阈值 | " +
513	    config.confidence_threshold.to_string() +
514	    " |\n"
515	  report = report + "| 最大跳数 | " + config.max_hops.to_string() + " |\n"
516	
517	  report = report + "\n### 嵌入训练\n\n"
518	  report = report + "| 指标 | 值 |\n|------|------|\n"
519	  report = report + "| 模型类型 | " + embedding.model_type + " |\n"
520	  report = report +
521	    "| 实体嵌入数 | " +
522	    embedding.num_entities.to_string() +
523	    " |\n"
524	  report = report +
525	    "| 关系嵌入数 | " +
526	    embedding.num_relations.to_string() +
527	    " |\n"
528	  report = report +
529	    "| 嵌入维度 | " +
530	    embedding.embedding_dim.to_string() +
531	    " |\n"
532	  report = report +
533	    "| 训练损失 | " +
534	    embedding.training_loss.to_string() +
535	    " |\n"
536	
537	  report
538	}
   	…

17 uncovered line(s) in src\process\medical\medical_enhancement.mbt:

122	pub fn create_default_window_level(preset : String) -> WindowLevelConfig {
123	  if preset == "brain" {
124	    WindowLevelConfig::{ center: 40.0, width: 80.0, preset }
125	  } else if preset == "lung" {
126	    WindowLevelConfig::{ center: -600.0, width: 1500.0, preset }
127	  } else if preset == "abdomen" {
128	    WindowLevelConfig::{ center: 40.0, width: 400.0, preset }	<-- UNCOVERED
129	  } else if preset == "bone" {
130	    WindowLevelConfig::{ center: 300.0, width: 1500.0, preset }	<-- UNCOVERED
131	  } else if preset == "soft_tissue" {
132	    WindowLevelConfig::{ center: 50.0, width: 350.0, preset }	<-- UNCOVERED
133	  } else {
134	    WindowLevelConfig::{ center: 128.0, width: 256.0, preset: "custom" }
135	  }
136	}
   	…

140	pub fn apply_window_level_medical(
141	  image : MedicalImage,
142	  config : WindowLevelConfig,
143	) -> @types.Image {
144	  let w = image.width
145	  let h = image.height
146	  let out : Array[Byte] = []
147	  let lower = config.center - config.width / 2.0
148	  let upper = config.center + config.width / 2.0
149	
150	  for i in 0..<(w * h) {
151	    let pixel = image.data[i]
152	    let normalized = if upper > lower {
153	      (pixel - lower) / (upper - lower)
154	    } else {
155	      0.5	<-- UNCOVERED
156	    }
157	    let clamped = if normalized < 0.0 {
158	      0.0	<-- UNCOVERED
159	    } else if normalized > 1.0 {
160	      1.0	<-- UNCOVERED
161	    } else {
162	      normalized
163	    }
164	    let value = (clamped * 255.0).to_int()
165	    out.push(if value > 127 { b'\xFF' } else { b'\x00' })
166	  }
167	
168	  @types.Image::{
169	    width: w,
170	    height: h,
171	    channels: 1,
172	    data: Bytes::from_array(out),
173	  }
174	}
   	…

294	pub fn detect_lesions_simple(
295	  image : MedicalImage,
296	  min_diameter : Double,
297	  max_diameter : Double,
298	) -> Array[Lesion] {
299	  let lesions : Array[Lesion] = []
300	  let w = image.width
301	  let h = image.height
302	
303	  // 简化版：基于局部最大值的病灶检测
304	  let mut lesion_id = 0
305	  for y in 5..<(h - 5) {
306	    for x in 5..<(w - 5) {
307	      let idx = y * w + x
308	      let pixel = image.data[idx]
309	      if pixel < 100.0 {
310	        continue
311	      }
312	
313	      // 检查是否为局部最大值
314	      let mut is_local_max = true
315	      for dy in -3..<4 {
316	        for dx in -3..<4 {
317	          if dx == 0 && dy == 0 {
318	            continue
319	          }
320	          let neighbor = image.data[(y + dy) * w + (x + dx)]
321	          if neighbor > pixel {
322	            is_local_max = false
323	          }
324	        }
325	      }
326	
327	      if is_local_max {
328	        // 估算直径
329	        let mut diameter = 0.0	<-- UNCOVERED
330	        for r in 1..<10 {
331	          let mut all_above = true	<-- UNCOVERED
332	          for dy in -r..<(r + 1) {
333	            for dx in -r..<(r + 1) {	<-- UNCOVERED
334	              let nx = x + dx
335	              let ny = y + dy
336	              if nx >= 0 && nx < w && ny >= 0 && ny < h {
337	                if image.data[ny * w + nx] < pixel * 0.5 {	<-- UNCOVERED
338	                  all_above = false
339	                }
340	              }
341	            }
342	          }
343	          if all_above {
344	            diameter = r.to_double() * 2.0	<-- UNCOVERED
345	          }
346	        }
347	
348	        if diameter >= min_diameter && diameter <= max_diameter {
349	          lesions.push(Lesion::{	<-- UNCOVERED
350	            id: lesion_id,
351	            lesion_type: "nodule",
352	            x: x.to_double(),	<-- UNCOVERED
353	            y: y.to_double(),
354	            z: 0.0,
355	            diameter,
356	            volume: diameter * diameter * diameter * 3.14159 / 6.0,
357	            intensity: pixel,
358	            margin: "well_defined",
359	            shape: "round",
360	            density: "solid",
361	            confidence: 0.7,
362	            birads_category: 3,
363	          })
364	          lesion_id += 1
365	        }
366	      }
367	    }
368	  }
369	
370	  lesions
371	}
   	…

375	pub fn medical_image_registration(
376	  fixed : MedicalImage,
377	  moving : MedicalImage,
378	  max_iterations : Int,
379	) -> MedicalRegistrationResult {
380	  let transformation : Array[Double] = [
381	    1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
382	  ]
383	
384	  let mut similarity = 0.0
385	  let mut converged = false
386	  let mut iterations = 0
387	
388	  for iter in 0..<max_iterations {
389	    iterations = iter + 1
390	    // 简化版：计算归一化互相关
391	    let mut sum_fixed = 0.0
392	    let mut sum_moving = 0.0
393	    let mut sum_product = 0.0
394	    let mut sum_fixed_sq = 0.0
395	    let mut sum_moving_sq = 0.0
396	    let count = if fixed.data.length() < moving.data.length() {
397	      fixed.data.length()	<-- UNCOVERED
398	    } else {
399	      moving.data.length()
400	    }
401	
402	    for i in 0..<count {
403	      let f = fixed.data[i]
404	      let m = moving.data[i]
405	      sum_fixed += f
406	      sum_moving += m
407	      sum_product += f * m
408	      sum_fixed_sq += f * f
409	      sum_moving_sq += m * m
410	    }
411	
412	    let mean_f = sum_fixed / count.to_double()
413	    let mean_m = sum_moving / count.to_double()
414	    let cov = sum_product / count.to_double() - mean_f * mean_m
415	    let var_f = sum_fixed_sq / count.to_double() - mean_f * mean_f
416	    let var_m = sum_moving_sq / count.to_double() - mean_m * mean_m
417	    let ncc = if var_f > 0.0 && var_m > 0.0 {
418	      cov / (var_f * var_m).sqrt()
419	    } else {
420	      0.0
421	    }
422	
423	    if (ncc - similarity).abs() < 0.001 {
424	      converged = true
425	      similarity = ncc
426	      break
427	    }
428	    similarity = ncc
429	  }
430	
431	  MedicalRegistrationResult::{
432	    transformation,
433	    similarity,
434	    iterations,
435	    converged,
436	    registration_type: "rigid",
437	  }
438	}
   	…

442	pub fn calculate_medical_image_quality(
443	  image : MedicalImage,
444	) -> MedicalImageQuality {
445	  let w = image.width
446	  let h = image.height
447	  let total = w * h
448	
449	  // 计算 SNR
450	  let mut sum = 0.0
451	  let mut sum_sq = 0.0
452	  for i in 0..<total {
453	    let pixel = image.data[i]
454	    sum += pixel
455	    sum_sq += pixel * pixel
456	  }
457	  let mean = sum / total.to_double()
458	  let variance = sum_sq / total.to_double() - mean * mean
459	  let std = if variance > 0.0 { variance.sqrt() } else { 1.0 }
460	  let snr = if std > 0.0 { mean / std } else { 0.0 }
461	
462	  // 计算清晰度（基于梯度）
463	  let mut sharpness = 0.0
464	  for y in 1..<(h - 1) {
465	    for x in 1..<(w - 1) {
466	      let idx = y * w + x
467	      let gx = image.data[idx + 1] - image.data[idx - 1]
468	      let gy = image.data[idx + w] - image.data[idx - w]
469	      sharpness += (gx * gx + gy * gy).sqrt()
470	    }
471	  }
472	  sharpness /= total.to_double()
473	
474	  let noise_level = std
475	  let cnr = snr * 0.8
476	  let artifact_score = 1.0
477	  let overall_quality = if snr > 10.0 && sharpness > 5.0 {
478	    5.0	<-- UNCOVERED
479	  } else if snr > 5.0 {
480	    3.0
481	  } else {
482	    2.0
483	  }
484	
485	  MedicalImageQuality::{
486	    snr,
487	    cnr,
488	    sharpness,
489	    noise_level,
490	    artifact_score,
491	    overall_quality,
492	  }
493	}
   	…

497	pub fn medical_image_report_to_string(
498	  image : MedicalImage,
499	  lesions : Array[Lesion],
500	  quality : MedicalImageQuality,
501	) -> String {
502	  let mut report = "## 医学图像分析报告\n\n"
503	  report = report + "### 图像信息\n\n"
504	  report = report + "| 指标 | 值 |\n|------|------|\n"
505	  let type_str = if image.image_type == MedicalImageType::CT {
506	    "CT"
507	  } else if image.image_type == MedicalImageType::MRI {	<-- UNCOVERED
508	    "MRI"
509	  } else if image.image_type == MedicalImageType::XRay {
510	    "X-Ray"
511	  } else if image.image_type == MedicalImageType::Ultrasound {
512	    "超声"
513	  } else if image.image_type == MedicalImageType::PET {
514	    "PET"
515	  } else if image.image_type == MedicalImageType::SPECT {
516	    "SPECT"
517	  } else if image.image_type == MedicalImageType::Mammography {
518	    "乳腺钼靶"
519	  } else if image.image_type == MedicalImageType::Fluoroscopy {
520	    "透视"
521	  } else {
522	    "未知"	<-- UNCOVERED
523	  }
524	  report = report + "| 类型 | " + type_str + " |\n"
525	  report = report +
526	    "| 尺寸 | " +
527	    image.width.to_string() +
528	    "x" +
529	    image.height.to_string() +
530	    "x" +
531	    image.slices.to_string() +
532	    " |\n"
533	  report = report +
534	    "| 窗宽窗位 | " +
535	    image.window_center.to_string() +
536	    "/" +
537	    image.window_width.to_string() +
538	    " |\n\n"
539	
540	  report = report + "### 图像质量\n\n"
541	  report = report + "| 指标 | 值 |\n|------|------|\n"
542	  report = report + "| SNR | " + quality.snr.to_string() + " dB |\n"
543	  report = report + "| CNR | " + quality.cnr.to_string() + " |\n"
544	  report = report + "| 清晰度 | " + quality.sharpness.to_string() + " |\n"
545	  report = report +
546	    "| 噪声水平 | " +
547	    quality.noise_level.to_string() +
548	    " |\n"
549	  report = report +
550	    "| 综合质量 | " +
551	    quality.overall_quality.to_string() +
552	    "/5.0 |\n\n"
553	
554	  if lesions.length() > 0 {
555	    report = report + "### 检测到的病灶\n\n"
556	    report = report +
557	      "| ID | 类型 | 位置(X,Y,Z) | 直径 | 体积 | 强度 | 形状 | 密度 | 置信度 | BI-RADS |\n"
558	    report = report +
559	      "|----|------|-------------|------|------|------|------|------|--------|---------|\n"
560	    for lesion in lesions {
561	      report = report +
562	        "| " +
563	        lesion.id.to_string() +
564	        " | " +
565	        lesion.lesion_type +
566	        " | " +
567	        lesion.x.to_string() +
568	        "," +
569	        lesion.y.to_string() +
570	        "," +
571	        lesion.z.to_string() +
572	        " | " +
573	        lesion.diameter.to_string() +
574	        " | " +
575	        lesion.volume.to_string() +
576	        " | " +
577	        lesion.intensity.to_string() +
578	        " | " +
579	        lesion.shape +
580	        " | " +
581	        lesion.density +
582	        " | " +
583	        (lesion.confidence * 100.0).to_string() +
584	        "% | " +
585	        lesion.birads_category.to_string() +
586	        " |\n"
587	    }
588	  } else {
589	    report = report + "### 病灶检测\n\n未检测到明显病灶。\n"
590	  }
591	
592	  report
593	}
   	…

38 uncovered line(s) in src\process\medical\medical_image.mbt:

38	pub fn apply_window_level(
39	  img : @types.Image,
40	  config : WindowLevel,
41	) -> @types.Image {
42	  let w = img.width
43	  let h = img.height
44	  let channels = img.channels
45	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
46	  let low = config.window_level - config.window_width / 2.0
47	  let high = config.window_level + config.window_width / 2.0
48	  for y in 0..<h {
49	    for x in 0..<w {
50	      let idx = (y * w + x) * channels
51	      // 取第一个通道作为灰度值
52	      let raw = img.data[idx].to_double()
53	      let mapped = if raw <= low {
54	        0.0	<-- UNCOVERED
55	      } else if raw >= high {
56	        255.0
57	      } else {
58	        (raw - low) / config.window_width * 255.0	<-- UNCOVERED
59	      }
60	      out_data[y * w + x] = mapped.min(255.0).max(0.0).to_int().to_byte()
61	    }
62	  }
63	  @types.Image::{
64	    width: w,
65	    height: h,
66	    channels: 1,
67	    data: Bytes::from_array(out_data),
68	  }
69	}
  	…

 74	pub fn medical_colormap(
 75	  img : @types.Image,
 76	  colormap? : String = "hot",
 77	) -> @types.Image {
 78	  let cmap = colormap
 79	  let w = img.width
 80	  let h = img.height
 81	  let channels = img.channels
 82	  let out_data : Array[Byte] = Array::make(w * h * 3, b'\x00')
 83	  for y in 0..<h {
 84	    for x in 0..<w {
 85	      let idx = (y * w + x) * channels
 86	      let gray = if channels >= 1 {
 87	        img.data[idx].to_double() / 255.0
 88	      } else {
 89	        0.0	<-- UNCOVERED
 90	      }
 91	      let out_idx = (y * w + x) * 3
 92	      // 多种伪彩色映射
 93	      let (r, g, b) = match cmap {
 94	        "hot" =>
 95	          // 黑 -> 红 -> 黄 -> 白
 96	          if gray < 0.33 {
 97	            ((gray / 0.33 * 255.0).to_int(), 0, 0)
 98	          } else if gray < 0.66 {
 99	            (255, ((gray - 0.33) / 0.33 * 255.0).to_int(), 0)
100	          } else {
101	            (255, 255, ((gray - 0.66) / 0.34 * 255.0).to_int())	<-- UNCOVERED
102	          }
103	        "cool" =>
104	          // 青 -> 品红
105	          ((gray * 255.0).to_int(), ((1.0 - gray) * 255.0).to_int(), 255)	<-- UNCOVERED
106	        "spring" =>
107	          // 品红 -> 黄
108	          (255, (gray * 255.0).to_int(), ((1.0 - gray) * 255.0).to_int())	<-- UNCOVERED
109	        "summer" =>
110	          // 绿 -> 黄
111	          ((gray * 128.0).to_int(), (128.0 + gray * 127.0).to_int(), 0)	<-- UNCOVERED
112	        "autumn" =>
113	          // 红 -> 黄
114	          (255, (gray * 255.0).to_int(), 0)	<-- UNCOVERED
115	        "winter" =>
116	          // 蓝 -> 绿
117	          (0, (gray * 255.0).to_int(), ((1.0 - gray) * 255.0).to_int())	<-- UNCOVERED
118	        "bone" => {
119	          // 骨色：黑 -> 灰 -> 白（带一点蓝调）
120	          let v = (gray * 255.0).to_int()	<-- UNCOVERED
121	          (v, v, (v + 20).min(255))
122	        }
123	        "pink" => {	<-- UNCOVERED
124	          // 粉色：灰 -> 粉
125	          let v = (gray * 255.0).to_int()	<-- UNCOVERED
126	          (
127	            v.min(255),	<-- UNCOVERED
128	            (v.to_double() * 0.6).to_int().min(255),
129	            (v.to_double() * 0.7).to_int().min(255),
130	          )
131	        }
132	        _ => {	<-- UNCOVERED
133	          // 灰度
134	          let v = (gray * 255.0).to_int()	<-- UNCOVERED
135	          (v, v, v)
136	        }
137	      }
138	      out_data[out_idx] = r.min(255).max(0).to_byte()
139	      out_data[out_idx + 1] = g.min(255).max(0).to_byte()
140	      out_data[out_idx + 2] = b.min(255).max(0).to_byte()
141	    }
142	  }
143	  @types.Image::{
144	    width: w,
145	    height: h,
146	    channels: 3,
147	    data: Bytes::from_array(out_data),
148	  }
149	}
   	…

153	pub fn medical_contrast_enhance(
154	  img : @types.Image,
155	  clip_limit? : Double = 2.0,
156	  tile_size? : Int = 8,
157	) -> @types.Image {
158	  let _ = clip_limit	<-- UNCOVERED
159	  let _ = tile_size
160	  let w = img.width
161	  let h = img.height
162	  let channels = img.channels
163	  // 转换为灰度
164	  let gray : Array[Int] = Array::make(w * h, 0)	<-- UNCOVERED
165	  for i in 0..<(w * h) {
166	    let idx = i * channels	<-- UNCOVERED
167	    let r = img.data[idx].to_int()
168	    let g = if channels >= 2 { img.data[idx + 1].to_int() } else { r }
169	    let b = if channels >= 3 { img.data[idx + 2].to_int() } else { r }
170	    gray[i] = (0.299 * r.to_double() +
171	    0.587 * g.to_double() +
172	    0.114 * b.to_double()).to_int()
173	  }
174	  // 全局直方图均衡化（简化版）
175	  let histogram : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
176	  for i in 0..<(w * h) {
177	    histogram[gray[i]] += 1	<-- UNCOVERED
178	  }
179	  // 累积分布函数
180	  let cdf : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
181	  let mut sum = 0
182	  for i in 0..<256 {
183	    sum += histogram[i]	<-- UNCOVERED
184	    cdf[i] = sum
185	  }
186	  // 映射
187	  let total = w * h
188	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')	<-- UNCOVERED
189	  for i in 0..<(w * h) {
190	    let value = gray[i]	<-- UNCOVERED
191	    let equalized = if total > 0 {
192	      (cdf[value].to_double() / total.to_double() * 255.0).to_int()	<-- UNCOVERED
193	    } else {
194	      value	<-- UNCOVERED
195	    }
196	    out_data[i] = equalized.min(255).max(0).to_byte()	<-- UNCOVERED
197	  }
198	  @types.Image::{
199	    width: w,
200	    height: h,
201	    channels: 1,
202	    data: Bytes::from_array(out_data),	<-- UNCOVERED
203	  }
204	}
   	…

208	pub fn medical_denoise(
209	  img : @types.Image,
210	  iterations? : Int = 5,
211	  kappa? : Double = 30.0,
212	) -> @types.Image {
213	  let iter = iterations	<-- UNCOVERED
214	  let k = kappa
215	  let w = img.width
216	  let h = img.height
217	  let channels = img.channels
218	  let mut current : Array[Double] = Array::make(w * h * channels, 0.0)	<-- UNCOVERED
219	  for i in 0..<(w * h * channels) {
220	    current[i] = img.data[i].to_double()	<-- UNCOVERED
221	  }
222	  for _ in 0..<iter {
223	    let next : Array[Double] = Array::make(w * h * channels, 0.0)	<-- UNCOVERED
224	    for y in 1..<(h - 1) {
225	      for x in 1..<(w - 1) {	<-- UNCOVERED
226	        for c in 0..<channels {
227	          let idx = (y * w + x) * channels + c
228	          let center = current[idx]
229	          // 四邻域梯度
230	          let north = current[((y - 1) * w + x) * channels + c] - center
231	          let south = current[((y + 1) * w + x) * channels + c] - center
232	          let east = current[(y * w + (x + 1)) * channels + c] - center
233	          let west = current[(y * w + (x - 1)) * channels + c] - center
234	          // 扩散系数（指数函数，边缘保留）
235	          let cn = @math.exp(-(north.abs() / k) * (north.abs() / k))	<-- UNCOVERED
236	          let cs = @math.exp(-(south.abs() / k) * (south.abs() / k))
237	          let ce = @math.exp(-(east.abs() / k) * (east.abs() / k))
238	          let cw = @math.exp(-(west.abs() / k) * (west.abs() / k))
239	          // 更新
240	          let lambda = 0.25
241	          next[idx] = center +
242	            lambda * (cn * north + cs * south + ce * east + cw * west)
243	        }
244	      }
245	    }
246	    // 边界复制
247	    for y in 0..<h {
248	      for x in 0..<w {	<-- UNCOVERED
249	        if y == 0 || y == h - 1 || x == 0 || x == w - 1 {
250	          for c in 0..<channels {
251	            next[(y * w + x) * channels + c] = current[(y * w + x) * channels +
252	              c]
253	          }
254	        }
255	      }
256	    }
257	    current = next
258	  }
259	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')	<-- UNCOVERED
260	  for i in 0..<(w * h * channels) {
261	    out_data[i] = current[i].min(255.0).max(0.0).to_int().to_byte()	<-- UNCOVERED
262	  }
263	  @types.Image::{
264	    width: w,
265	    height: h,
266	    channels,
267	    data: Bytes::from_array(out_data),	<-- UNCOVERED
268	  }
269	}
   	…

7 uncovered line(s) in src\process\medical_imaging_advanced\medical_imaging_advanced.mbt:

325	pub fn segment_organ(
326	  image : Array[Double],
327	  width : Int,
328	  height : Int,
329	  organ_type : OrganType,
330	  config : MedicalImagingConfig,
331	) -> OrganSegmentationResult {
332	  ignore(image)
333	  // 简化的器官分割
334	  let mask : Array[Array[Bool]] = []
335	  let mut state = 42
336	
337	  for _ in 0..<height {
338	    let row : Array[Bool] = []
339	    for _ in 0..<width {
340	      state = (state * 11035 + 12345) % 1000000
341	      let is_organ = state % 10 < 3
342	      row.push(is_organ)
343	    }
344	    mask.push(row)
345	  }
346	
347	  let volume = width.to_double() *
348	    height.to_double() *
349	    config.slice_thickness *
350	    0.3
351	  let normal_volumes = [
352	    1500.0, 300.0, 6000.0, 1500.0, 300.0, 200.0, 100.0, 1500.0, 2000.0, 10000.0,
353	    5000.0, 1000.0,
354	  ]
355	  let organ_idx = match organ_type {
356	    OrganType::Brain => 0	<-- UNCOVERED
357	    OrganType::Heart => 1
358	    OrganType::Lung => 2
359	    OrganType::Liver => 3	<-- UNCOVERED
360	    OrganType::Kidney => 4
361	    OrganType::Spleen => 5
362	    OrganType::Pancreas => 6
363	    OrganType::Stomach => 7
364	    OrganType::Intestine => 8
365	    OrganType::Bone => 9
366	    OrganType::Muscle => 10
367	    _ => 11
368	  }
369	  let normal_volume = normal_volumes[organ_idx]
370	  let size_deviation = (volume - normal_volume).abs() / normal_volume
371	
372	  OrganSegmentationResult::{
373	    organ_type,
374	    segmentation_mask: mask,
375	    volume,
376	    surface_area: volume * 0.8,
377	    centroid: [width.to_double() / 2.0, height.to_double() / 2.0],
378	    bounding_box: [0, 0, width, height],
379	    dice_score: 0.9,
380	    is_normal_size: size_deviation < 0.2,
381	    size_deviation,
382	  }
383	}
   	…

488	pub fn medical_imaging_report_to_string(
489	  task_type : MedicalImagingTaskType,
490	  config : MedicalImagingConfig,
491	  result_description : String,
492	) -> String {
493	  let mut report = "## 医学图像高级报告\n\n"
494	
495	  report = report + "### 任务信息\n\n"
496	  report = report + "| 指标 | 值 |\n|------|------|\n"
497	  report = report +
498	    "| 任务类型 | " +
499	    (match task_type {
500	      MedicalImagingTaskType::ImageSegmentation => "图像分割"
501	      MedicalImagingTaskType::LesionDetection => "病灶检测"	<-- UNCOVERED
502	      MedicalImagingTaskType::OrganSegmentation => "器官分割"
503	      MedicalImagingTaskType::Radiomics => "放射组学"
504	      MedicalImagingTaskType::ImageRegistration => "图像配准"
505	      MedicalImagingTaskType::Reconstruction3D => "三维重建"
506	      MedicalImagingTaskType::DiseaseClassification => "疾病分类"
507	      _ => "未知"
508	    }) +
509	    " |\n"
510	  report = report +
511	    "| 图像类型 | " +
512	    (match config.image_type {
513	      MedicalImageType::XRay => "X射线"	<-- UNCOVERED
514	      MedicalImageType::CT => "CT"
515	      MedicalImageType::MRI => "MRI"	<-- UNCOVERED
516	      MedicalImageType::Ultrasound => "超声"
517	      MedicalImageType::PET => "PET"
518	      MedicalImageType::Mammography => "乳腺X线"
519	      MedicalImageType::Endoscopy => "内镜"
520	      MedicalImageType::Histopathology => "组织病理"
521	      _ => "未知"
522	    }) +
523	    " |\n"
524	  report = report +
525	    "| 目标器官 | " +
526	    (match config.target_organ {
527	      OrganType::Brain => "脑"	<-- UNCOVERED
528	      OrganType::Heart => "心脏"
529	      OrganType::Lung => "肺"
530	      OrganType::Liver => "肝"	<-- UNCOVERED
531	      OrganType::Kidney => "肾"
532	      OrganType::Spleen => "脾"
533	      OrganType::Pancreas => "胰腺"
534	      OrganType::Stomach => "胃"
535	      OrganType::Intestine => "肠"
536	      OrganType::Bone => "骨"
537	      OrganType::Muscle => "肌肉"
538	      _ => "未知"
539	    }) +
540	    " |\n"
541	  report = report + "| 分割方法 | " + config.segmentation_method + " |\n"
542	  report = report +
543	    "| 检测阈值 | " +
544	    config.detection_threshold.to_string() +
545	    " |\n"
546	  report = report + "| 窗位 | " + config.window_level.to_string() + " |\n"
547	  report = report + "| 窗宽 | " + config.window_width.to_string() + " |\n"
548	  report = report +
549	    "| 3D模式 | " +
550	    (if config.use_3d { "开启" } else { "关闭" }) +
551	    " |\n"
552	
553	  report = report + "\n### 结果\n\n"
554	  report = report + result_description + "\n"
555	
556	  report
557	}
   	…

6 uncovered line(s) in src\process\meta_learning_advanced\meta_learning_advanced.mbt:

303	pub fn train_protonet(
304	  support_data : Array[Array[Double]],
305	  query_data : Array[Array[Double]],
306	  config : MetaLearningConfig,
307	) -> ProtoNetResult {
308	  ignore(query_data)
309	  let num_classes = config.num_way
310	  let feature_dim = if support_data.length() > 0 {
311	    support_data[0].length()
312	  } else {
313	    1	<-- UNCOVERED
314	  }
315	
316	  // 计算原型
317	  let prototypes : Array[Array[Double]] = []
318	  for c in 0..<num_classes {
319	    let proto : Array[Double] = []
320	    for d in 0..<feature_dim {
321	      let mut sum = 0.0
322	      for s in 0..<config.num_shot {
323	        let idx = c * config.num_shot + s
324	        if idx < support_data.length() && d < support_data[idx].length() {
325	          sum += support_data[idx][d]
326	        }
327	      }
328	      proto.push(sum / config.num_shot.to_double())
329	    }
330	    prototypes.push(proto)
331	  }
332	
333	  ProtoNetResult::{
334	    prototypes,
335	    num_classes,
336	    feature_dimension: feature_dim,
337	    training_loss: 0.4,
338	    validation_loss: 0.45,
339	    test_loss: 0.5,
340	    training_accuracy: 0.65,
341	    validation_accuracy: 0.62,
342	    test_accuracy: 0.6,
343	    confidence_interval: 0.05,
344	    num_episodes: config.num_meta_iterations,
345	    learning_rate: config.meta_learning_rate,
346	    learning_time_ms: 6000.0,
347	    distance_metric: "euclidean",
348	  }
349	}
   	…

452	pub fn meta_learning_report_to_string(
453	  task_type : MetaLearningTaskType,
454	  config : MetaLearningConfig,
455	  result_description : String,
456	) -> String {
457	  let mut report = "## 元学习高级报告\n\n"
458	
459	  report = report + "### 任务信息\n\n"
460	  report = report + "| 指标 | 值 |\n|------|------|\n"
461	  report = report +
462	    "| 任务类型 | " +
463	    (match task_type {
464	      MetaLearningTaskType::FewShotClassification => "少样本分类"
465	      MetaLearningTaskType::FewShotRegression => "少样本回归"	<-- UNCOVERED
466	      MetaLearningTaskType::MetaOptimization => "元优化"
467	      MetaLearningTaskType::MetaArchitecture => "元架构"
468	      MetaLearningTaskType::MetaLearningRate => "元学习率"
469	      MetaLearningTaskType::MetaDataAugmentation => "元数据增强"
470	      MetaLearningTaskType::MetaRegularization => "元正则化"
471	      MetaLearningTaskType::MetaFeatureSelection => "元特征选择"
472	      _ => "未知"
473	    }) +
474	    " |\n"
475	  report = report +
476	    "| 元学习算法 | " +
477	    (match config.algorithm_type {
478	      MetaLearningAlgorithmType::MAML => "MAML"
479	      MetaLearningAlgorithmType::Reptile => "Reptile"	<-- UNCOVERED
480	      MetaLearningAlgorithmType::MetaSGD => "Meta-SGD"
481	      MetaLearningAlgorithmType::FOMAML => "FOMAML"
482	      MetaLearningAlgorithmType::IMAML => "IMAML"
483	      MetaLearningAlgorithmType::MetaCurvature => "Meta-Curvature"
484	      MetaLearningAlgorithmType::LEO => "LEO"
485	      MetaLearningAlgorithmType::SNAIL => "SNAIL"
486	      MetaLearningAlgorithmType::ProtoNet => "ProtoNet"
487	      MetaLearningAlgorithmType::MatchingNet => "MatchingNet"
488	      MetaLearningAlgorithmType::RelationNet => "RelationNet"
489	      MetaLearningAlgorithmType::TADAM => "TADAM"
490	      MetaLearningAlgorithmType::FEAT => "FEAT"
491	      MetaLearningAlgorithmType::MetaOptNet => "MetaOptNet"
492	      MetaLearningAlgorithmType::R2D2 => "R2D2"
493	      _ => "未知"
494	    }) +
495	    " |\n"
496	  report = report +
497	    "| 优化器 | " +
498	    (match config.optimizer_type {
499	      MetaOptimizerType::SGD => "SGD"	<-- UNCOVERED
500	      MetaOptimizerType::Adam => "Adam"
501	      MetaOptimizerType::RMSprop => "RMSprop"	<-- UNCOVERED
502	      MetaOptimizerType::Adagrad => "Adagrad"
503	      MetaOptimizerType::MetaLearnedOptimizer => "元学习优化器"
504	      MetaOptimizerType::LearnedOptimizer => "学习优化器"
505	      _ => "未知"
506	    }) +
507	    " |\n"
508	  report = report +
509	    "| 任务分布 | " +
510	    (match config.task_distribution {
511	      TaskDistributionType::Uniform => "均匀分布"
512	      TaskDistributionType::Curriculum => "课程学习"	<-- UNCOVERED
513	      TaskDistributionType::HardNegative => "难例挖掘"
514	      TaskDistributionType::Diversity => "多样性"
515	      TaskDistributionType::Weighted => "加权分布"
516	      _ => "未知"
517	    }) +
518	    " |\n"
519	  report = report + "| N-way | " + config.num_way.to_string() + " |\n"
520	  report = report + "| K-shot | " + config.num_shot.to_string() + " |\n"
521	  report = report + "| Query数 | " + config.num_query.to_string() + " |\n"
522	  report = report +
523	    "| 元迭代次数 | " +
524	    config.num_meta_iterations.to_string() +
525	    " |\n"
526	  report = report +
527	    "| 每批任务数 | " +
528	    config.num_tasks_per_batch.to_string() +
529	    " |\n"
530	  report = report +
531	    "| 内部学习率 | " +
532	    config.inner_learning_rate.to_string() +
533	    " |\n"
534	  report = report +
535	    "| 元学习率 | " +
536	    config.meta_learning_rate.to_string() +
537	    " |\n"
538	  report = report +
539	    "| 内部步数 | " +
540	    config.num_inner_steps.to_string() +
541	    " |\n"
542	  report = report +
543	    "| 一阶近似 | " +
544	    (if config.first_order { "开启" } else { "关闭" }) +
545	    " |\n"
546	
547	  report = report + "\n### 结果\n\n"
548	  report = report + result_description + "\n"
549	
550	  report
551	}
   	…

4 uncovered line(s) in src\process\model_compression\model_compression.mbt:

259	pub fn quantize_model(
260	  original_size_mb : Double,
261	  original_parameters : Int,
262	  original_flops : Int,
263	  config : QuantizationConfig,
264	) -> ModelCompressionResult {
265	  let compression_factor = match config.quantization_type {
266	    QuantizationType::INT8 => 4.0
267	    QuantizationType::INT4 => 8.0	<-- UNCOVERED
268	    QuantizationType::INT16 => 2.0
269	    QuantizationType::FP16 => 2.0
270	    QuantizationType::BF16 => 2.0
271	    QuantizationType::MixedPrecision => 3.0
272	    _ => 2.0
273	  }
274	
275	  let compressed_size = original_size_mb / compression_factor
276	  let compressed_parameters = original_parameters
277	  let compressed_flops = original_flops
278	  let accuracy_drop = match config.quantization_type {
279	    QuantizationType::INT8 => 0.02
280	    QuantizationType::INT4 => 0.05	<-- UNCOVERED
281	    QuantizationType::INT16 => 0.005
282	    QuantizationType::FP16 => 0.005
283	    _ => 0.01
284	  }
285	
286	  ModelCompressionResult::{
287	    original_size_mb,
288	    compressed_size_mb: compressed_size,
289	    compression_ratio: compression_factor,
290	    original_parameters,
291	    compressed_parameters,
292	    parameter_reduction: 0.0,
293	    original_flops,
294	    compressed_flops,
295	    flops_reduction: 0.0,
296	    accuracy_drop,
297	    method_type: ModelCompressionMethod::Quantization,
298	  }
299	}
   	…

434	pub fn deploy_model(
435	  model_size_mb : Double,
436	  inference_time_ms : Double,
437	  config : DeploymentConfig,
438	) -> DeploymentResult {
439	  let target_name = match config.target {
440	    DeploymentTarget::ONNX => "ONNX"
441	    DeploymentTarget::TensorRT => "TensorRT"	<-- UNCOVERED
442	    DeploymentTarget::OpenVINO => "OpenVINO"
443	    DeploymentTarget::CoreML => "Core ML"
444	    DeploymentTarget::NNAPI => "NNAPI"
445	    DeploymentTarget::TFLite => "TFLite"
446	    DeploymentTarget::TorchScript => "TorchScript"
447	    DeploymentTarget::WebAssembly => "WebAssembly"
448	    _ => "未知"
449	  }
450	
451	  let mut deployed_size = model_size_mb
452	  let mut deployed_time = inference_time_ms
453	
454	  if config.optimize_for_inference {
455	    deployed_time *= 0.7
456	  }
457	  if config.use_half_precision {
458	    deployed_size *= 0.5	<-- UNCOVERED
459	    deployed_time *= 0.8
460	  }
461	
462	  DeploymentResult::{
463	    target: config.target,
464	    output_path: "model_" + target_name + ".onnx",
465	    model_size_mb: deployed_size,
466	    inference_time_ms: deployed_time,
467	    memory_usage_mb: deployed_size * 2.0,
468	    throughput_fps: 1000.0 / deployed_time,
469	    success: true,
470	    error_message: "",
471	  }
472	}
   	…

6 uncovered line(s) in src\process\model_compression_advanced\model_compression_advanced.mbt:

242	pub fn quantize_model(
243	  original_weights : Array[Array[Double]],
244	  config : ModelCompressionConfig,
245	) -> QuantizationResult {
246	  let quantized : Array[Array[Double]] = []
247	  let scales : Array[Double] = []
248	  let zero_points : Array[Double] = []
249	
250	  for layer in original_weights {
251	    let mut min_val = layer[0]
252	    let mut max_val = layer[0]
253	    for w in layer {
254	      if w < min_val {
255	        min_val = w	<-- UNCOVERED
256	      }
257	      if w > max_val {
258	        max_val = w
259	      }
260	    }
261	
262	    let scale = (max_val - min_val) / ((1 << config.bit_width) - 1).to_double()
263	    let zero_point = -min_val / scale
264	
265	    scales.push(scale)
266	    zero_points.push(zero_point)
267	
268	    let q_layer : Array[Double] = []
269	    for w in layer {
270	      let q = (w / scale + zero_point).round()
271	      q_layer.push(q)
272	    }
273	    quantized.push(q_layer)
274	  }
275	
276	  QuantizationResult::{
277	    quantized_weights: quantized,
278	    quantization_scale: scales,
279	    zero_point: zero_points,
280	    original_weights,
281	    num_layers: original_weights.length(),
282	    quantization_type: config.quantization_type,
283	    bit_width: config.bit_width,
284	    original_accuracy: 0.9,
285	    quantized_accuracy: 0.88,
286	    accuracy_drop: 0.02,
287	    model_size_reduction: 32.0 / config.bit_width.to_double(),
288	    quantization_error: 0.001,
289	    calibration_samples: config.calibration_samples,
290	    quantization_time_ms: 2000.0,
291	  }
292	}
   	…

360	pub fn distill_model(
361	  teacher_weights : Array[Array[Double]],
362	  student_num_layers : Int,
363	  layer_size : Int,
364	  config : ModelCompressionConfig,
365	) -> DistillationResult {
366	  let student_weights = init_weights(student_num_layers, layer_size)
367	
368	  let teacher_params = {
369	    let mut total = 0
370	    for layer in teacher_weights {
371	      total += layer.length()
372	    }
373	    total
374	  }
375	  let student_params = student_num_layers * layer_size
376	  let compression_ratio = if student_params > 0 {
377	    teacher_params.to_double() / student_params.to_double()
378	  } else {
379	    1.0	<-- UNCOVERED
380	  }
381	
382	  DistillationResult::{
383	    student_weights,
384	    teacher_weights,
385	    student_accuracy: 0.85,
386	    teacher_accuracy: 0.92,
387	    student_loss: 0.25,
388	    distillation_loss: 0.1,
389	    total_loss: 0.35,
390	    temperature: config.temperature,
391	    alpha: config.alpha,
392	    distillation_type: config.distillation_type,
393	    num_epochs: 50,
394	    learning_rate: config.learning_rate,
395	    distillation_time_ms: 8000.0,
396	    model_compression_ratio: compression_ratio,
397	    accuracy_gap: 0.07,
398	  }
399	}
   	…

403	pub fn low_rank_decomposition(
404	  original_weights : Array[Array[Double]],
405	  config : ModelCompressionConfig,
406	) -> LowRankResult {
407	  let decomposed : Array[Array[Array[Double]]] = []
408	
409	  for layer in original_weights {
410	    // 简化的SVD分解为U、S、V三个矩阵
411	    let u : Array[Array[Double]] = []
412	    let s : Array[Array[Double]] = []
413	    let v : Array[Array[Double]] = []
414	
415	    for _ in 0..<config.rank {
416	      let row : Array[Double] = []
417	      for _ in 0..<layer.length() {
418	        row.push(0.01)
419	      }
420	      u.push(row)
421	    }
422	
423	    for i in 0..<config.rank {
424	      let row : Array[Double] = []
425	      for _ in 0..<config.rank {
426	        if i == 0 {
427	          row.push(1.0)
428	        } else {
429	          row.push(0.0)
430	        }
431	      }
432	      s.push(row)
433	    }
434	
435	    for _ in 0..<config.rank {
436	      let row : Array[Double] = []
437	      for _ in 0..<layer.length() {
438	        row.push(0.01)
439	      }
440	      v.push(row)
441	    }
442	
443	    decomposed.push(u)
444	    decomposed.push(s)
445	    decomposed.push(v)
446	  }
447	
448	  let original_params = {
449	    let mut total = 0
450	    for layer in original_weights {
451	      total += layer.length()
452	    }
453	    total
454	  }
455	  let layer_size = if original_weights.length() > 0 {
456	    original_weights[0].length()
457	  } else {
458	    1	<-- UNCOVERED
459	  }
460	  let decomposed_params = config.rank * layer_size * 3
461	
462	  LowRankResult::{
463	    decomposed_weights: decomposed,
464	    original_weights,
465	    num_layers: original_weights.length(),
466	    rank: config.rank,
467	    decomposition_type: config.low_rank_type,
468	    original_params,
469	    decomposed_params,
470	    compression_ratio: original_params.to_double() /
471	    decomposed_params.to_double(),
472	    reconstruction_error: 0.01,
473	    original_accuracy: 0.9,
474	    decomposed_accuracy: 0.87,
475	    accuracy_drop: 0.03,
476	    decomposition_time_ms: 5000.0,
477	  }
478	}
   	…

517	pub fn compression_report_to_string(
518	  task_type : ModelCompressionTaskType,
519	  config : ModelCompressionConfig,
520	  result_description : String,
521	) -> String {
522	  let mut report = "## 模型压缩高级报告\n\n"
523	
524	  report = report + "### 任务信息\n\n"
525	  report = report + "| 指标 | 值 |\n|------|------|\n"
526	  report = report +
527	    "| 任务类型 | " +
528	    (match task_type {
529	      ModelCompressionTaskType::Quantization => "量化"
530	      ModelCompressionTaskType::Pruning => "剪枝"	<-- UNCOVERED
531	      ModelCompressionTaskType::KnowledgeDistillation => "知识蒸馏"
532	      ModelCompressionTaskType::LowRankFactorization => "低秩分解"
533	      ModelCompressionTaskType::WeightSharing => "权重共享"
534	      ModelCompressionTaskType::NetworkSlimming => "网络瘦身"
535	      ModelCompressionTaskType::StructuredPruning => "结构化剪枝"
536	      ModelCompressionTaskType::UnstructuredPruning => "非结构化剪枝"
537	      ModelCompressionTaskType::MixedPrecision => "混合精度"
538	      _ => "未知"
539	    }) +
540	    " |\n"
541	  report = report +
542	    "| 量化类型 | " +
543	    (match config.quantization_type {
544	      QuantizationType::INT8 => "INT8"
545	      QuantizationType::INT4 => "INT4"	<-- UNCOVERED
546	      QuantizationType::INT2 => "INT2"
547	      QuantizationType::INT1 => "INT1"
548	      QuantizationType::FP16 => "FP16"
549	      QuantizationType::BF16 => "BF16"
550	      QuantizationType::FP8 => "FP8"
551	      QuantizationType::MixedPrecision => "混合精度"
552	      QuantizationType::PerChannel => "逐通道"
553	      QuantizationType::PerTensor => "逐张量"
554	      _ => "未知"
555	    }) +
556	    " |\n"
557	  report = report +
558	    "| 剪枝类型 | " +
559	    (match config.pruning_type {
560	      PruningType::Unstructured => "非结构化"
561	      PruningType::Structured => "结构化"	<-- UNCOVERED
562	      PruningType::ChannelPruning => "通道剪枝"
563	      PruningType::FilterPruning => "滤波器剪枝"
564	      PruningType::LayerPruning => "层剪枝"
565	      PruningType::BlockPruning => "块剪枝"
566	      PruningType::HeadPruning => "注意力头剪枝"
567	      PruningType::NeuronPruning => "神经元剪枝"
568	      PruningType::WeightPruning => "权重剪枝"
569	      PruningType::GradualPruning => "渐进剪枝"
570	      _ => "未知"
571	    }) +
572	    " |\n"
573	  report = report + "| 位宽 | " + config.bit_width.to_string() + " |\n"
574	  report = report +
575	    "| 剪枝比例 | " +
576	    config.pruning_ratio.to_string() +
577	    " |\n"
578	  report = report +
579	    "| 目标压缩比 | " +
580	    config.target_compression_ratio.to_string() +
581	    " |\n"
582	  report = report +
583	    "| 目标精度下降 | " +
584	    config.target_accuracy_drop.to_string() +
585	    " |\n"
586	
587	  report = report + "\n### 结果\n\n"
588	  report = report + result_description + "\n"
589	
590	  report
591	}
   	…

8 uncovered line(s) in src\process\multimodal_advanced\multimodal_advanced.mbt:

195	pub fn cosine_similarity(vec1 : Array[Double], vec2 : Array[Double]) -> Double {
196	  let mut dot_product = 0.0
197	  let mut norm1 = 0.0
198	  let mut norm2 = 0.0
199	
200	  let min_len = if vec1.length() < vec2.length() {
201	    vec1.length()	<-- UNCOVERED
202	  } else {
203	    vec2.length()
204	  }
205	
206	  for i in 0..<min_len {
207	    dot_product += vec1[i] * vec2[i]
208	    norm1 += vec1[i] * vec1[i]
209	    norm2 += vec2[i] * vec2[i]
210	  }
211	
212	  let denom = norm1.sqrt() * norm2.sqrt()
213	  if denom > 0.0 {
214	    dot_product / denom
215	  } else {
216	    0.0
217	  }
218	}
   	…

277	pub fn cross_modal_retrieval(
278	  query : MultimodalData,
279	  database : Array[MultimodalData],
280	  query_modality : ModalityType,
281	  target_modality : ModalityType,
282	  top_k : Int,
283	) -> CrossModalRetrievalResult {
284	  let retrieved_ids : Array[String] = []
285	  let retrieved_scores : Array[Double] = []
286	
287	  // 计算相似度
288	  let scores : Array[(Double, String)] = []
289	  for item in database {
290	    let query_feat = match query_modality {
291	      ModalityType::Image => query.image_features
292	      ModalityType::Text => query.text_features
293	      ModalityType::Audio => query.audio_features	<-- UNCOVERED
294	      ModalityType::Video => query.video_features
295	      _ => query.image_features
296	    }
297	
298	    let target_feat = match target_modality {
299	      ModalityType::Image => item.image_features
300	      ModalityType::Text => item.text_features
301	      ModalityType::Audio => item.audio_features	<-- UNCOVERED
302	      ModalityType::Video => item.video_features
303	      _ => item.image_features
304	    }
305	
306	    let similarity = cosine_similarity(query_feat, target_feat)
307	    scores.push((similarity, item.sample_id))
308	  }
309	
310	  // 简化的排序：直接取前top_k
311	  let num_retrieved = if top_k < scores.length() {
312	    top_k
313	  } else {
314	    scores.length()
315	  }
316	  for i in 0..<num_retrieved {
317	    retrieved_ids.push(scores[i].1)
318	    retrieved_scores.push(scores[i].0)
319	  }
320	
321	  CrossModalRetrievalResult::{
322	    query_modality,
323	    target_modality,
324	    query_id: query.sample_id,
325	    retrieved_ids,
326	    retrieved_scores,
327	    num_retrieved,
328	    similarity_metric: "cosine",
329	    recall_at_k: 0.7,
330	  }
331	}
   	…

335	pub fn multimodal_report_to_string(
336	  task_type : MultimodalTaskType,
337	  config : MultimodalConfig,
338	  result_description : String,
339	) -> String {
340	  let mut report = "## 多模态高级报告\n\n"
341	
342	  report = report + "### 任务信息\n\n"
343	  report = report + "| 指标 | 值 |\n|------|------|\n"
344	  report = report +
345	    "| 任务类型 | " +
346	    (match task_type {
347	      MultimodalTaskType::MultimodalFusion => "多模态融合"	<-- UNCOVERED
348	      MultimodalTaskType::CrossModalRetrieval => "跨模态检索"
349	      MultimodalTaskType::ImageTextGeneration => "图文生成"	<-- UNCOVERED
350	      MultimodalTaskType::VideoUnderstanding => "视频理解"
351	      MultimodalTaskType::AudioVisual => "音频视觉"	<-- UNCOVERED
352	      MultimodalTaskType::MultimodalReasoning => "多模态推理"
353	      MultimodalTaskType::MultimodalTranslation => "多模态翻译"
354	      _ => "未知"
355	    }) +
356	    " |\n"
357	  report = report +
358	    "| 融合策略 | " +
359	    (match config.fusion_strategy {
360	      FusionStrategy::EarlyFusion => "早期融合"	<-- UNCOVERED
361	      FusionStrategy::LateFusion => "晚期融合"
362	      FusionStrategy::HybridFusion => "混合融合"
363	      FusionStrategy::AttentionFusion => "注意力融合"
364	      FusionStrategy::CrossAttentionFusion => "交叉注意力融合"	<-- UNCOVERED
365	      FusionStrategy::TensorFusion => "张量融合"
366	      FusionStrategy::GatedFusion => "门控融合"
367	      _ => "未知"
368	    }) +
369	    " |\n"
370	  report = report +
371	    "| 特征维度 | " +
372	    config.feature_dim.to_string() +
373	    " |\n"
374	  report = report + "| 隐藏维度 | " + config.hidden_dim.to_string() + " |\n"
375	  report = report +
376	    "| 注意力头数 | " +
377	    config.num_attention_heads.to_string() +
378	    " |\n"
379	  report = report +
380	    "| 融合层数 | " +
381	    config.num_fusion_layers.to_string() +
382	    " |\n"
383	  report = report + "| 温度 | " + config.temperature.to_string() + " |\n"
384	
385	  report = report + "\n### 结果\n\n"
386	  report = report + result_description + "\n"
387	
388	  report
389	}
   	…

14 uncovered line(s) in src\process\neural_rendering\neural_rendering_3d.mbt:

205	pub fn volume_render(
206	  ray : Ray,
207	  samples : Array[Voxel],
208	  config : NeRFConfig,
209	) -> (Array[Double], Double) {
210	  ignore(config)	<-- UNCOVERED
211	  ignore(ray)
212	  // 简化的体渲染：累积颜色和密度
213	  let color = [0.0, 0.0, 0.0]
214	  let mut alpha = 0.0
215	  let mut transmittance = 1.0
216	
217	  for sample in samples {
218	    if sample.is_occupied && sample.density > 0.0 {	<-- UNCOVERED
219	      let alpha_i = 1.0 - (1.0 - sample.density * 0.1) // 简化的指数
220	      let weight = transmittance * alpha_i
221	
222	      color[0] += weight * sample.color[0]
223	      color[1] += weight * sample.color[1]
224	      color[2] += weight * sample.color[2]
225	      alpha += weight
226	      transmittance *= 1.0 - alpha_i
227	    }
228	  }
229	
230	  (color, alpha)
231	}
   	…

321	pub fn render_gaussian_splatting(
322	  camera : CameraParams,
323	  gaussians : Array[Gaussian3D],
324	  config : GaussianSplattingConfig,
325	) -> RenderingResult {
326	  ignore(config)
327	  let w = camera.image_width
328	  let h = camera.image_height
329	  let total = w * h
330	  let out : Array[Byte] = []
331	  let depth_out : Array[Byte] = []
332	
333	  // 简化版：基于高斯分布生成图像
334	  for y in 0..<h {
335	    for x in 0..<w {
336	      let ndc_x = (x.to_double() - camera.principal_point[0]) /
337	        camera.focal_length
338	      let ndc_y = (y.to_double() - camera.principal_point[1]) /
339	        camera.focal_length
340	
341	      // 简化的高斯混合
342	      let mut r = 0.0
343	      let mut g = 0.0
344	      let mut b = 0.0
345	      let mut weight_sum = 0.0
346	
347	      for gaussian in gaussians {
348	        let dx = ndc_x - gaussian.position[0]
349	        let dy = ndc_y - gaussian.position[1]
350	        let dist_sq = dx * dx / (gaussian.scale[0] * gaussian.scale[0] + 0.001) +
351	          dy * dy / (gaussian.scale[1] * gaussian.scale[1] + 0.001)
352	        let weight = gaussian.opacity * (1.0 - dist_sq * 0.5) // 简化的高斯
353	
354	        r += weight * gaussian.color[0]
355	        g += weight * gaussian.color[1]
356	        b += weight * gaussian.color[2]
357	        weight_sum += weight
358	      }
359	
360	      if weight_sum > 0.0 {
361	        r /= weight_sum	<-- UNCOVERED
362	        g /= weight_sum
363	        b /= weight_sum
364	      }
365	
366	      let clamped_r = if r < 0.0 { 0.0 } else if r > 255.0 { 255.0 } else { r }	<-- UNCOVERED
367	      let clamped_g = if g < 0.0 { 0.0 } else if g > 255.0 { 255.0 } else { g }
368	      let clamped_b = if b < 0.0 { 0.0 } else if b > 255.0 { 255.0 } else { b }
369	
370	      out.push(clamped_r.to_int().to_byte())
371	      out.push(clamped_g.to_int().to_byte())
372	      out.push(clamped_b.to_int().to_byte())
373	      depth_out.push(b'\x80')
374	    }
375	  }
376	
377	  let image = @types.Image::{
378	    width: w,
379	    height: h,
380	    channels: 3,
381	    data: Bytes::from_array(out),
382	  }
383	  let depth_map = @types.Image::{
384	    width: w,
385	    height: h,
386	    channels: 1,
387	    data: Bytes::from_array(depth_out),
388	  }
389	  let normal_map = @types.Image::{
390	    width: w,
391	    height: h,
392	    channels: 3,
393	    data: Bytes::make(total * 3, b'\x80'),
394	  }
395	  let alpha_map = @types.Image::{
396	    width: w,
397	    height: h,
398	    channels: 1,
399	    data: Bytes::make(total, b'\xFF'),
400	  }
401	
402	  RenderingResult::{
403	    image,
404	    depth_map,
405	    normal_map,
406	    alpha_map,
407	    rendering_time_ms: 0.0,
408	    psnr: 0.0,
409	    ssim: 0.0,
410	  }
411	}
   	…

449	pub fn multiview_stereo_reconstruction(
450	  images : Array[@types.Image],
451	  cameras : Array[CameraParams],
452	) -> ReconstructionResult {
453	  let point_cloud : Array[Point3D] = []
454	
455	  // 简化版：从图像中提取特征点并三角化
456	  if images.length() >= 2 && cameras.length() >= 2 {
457	    let img1 = images[0]
458	    let _ = images[1]
459	    let w = img1.width
460	    let h = img1.height
461	
462	    // 简化的特征点检测（高对比度区域）
463	    for y in 0..<h {
464	      for x in 0..<w {
465	        if y % 10 == 0 && x % 10 == 0 {
466	          let idx = y * w + x
467	          if x > 0 && x < w - 1 && y > 0 && y < h - 1 {
468	            let gx = img1.data[idx + 1].to_double() -	<-- UNCOVERED
469	              img1.data[idx - 1].to_double()
470	            let gy = img1.data[idx + w].to_double() -
471	              img1.data[idx - w].to_double()
472	            if (gx * gx + gy * gy).sqrt() > 30.0 {
473	              // 简化的三角化
474	              let depth = 5.0 + (x % 100).to_double() / 20.0	<-- UNCOVERED
475	              let ndc_x = (x.to_double() - cameras[0].principal_point[0]) /
476	                cameras[0].focal_length
477	              let ndc_y = (y.to_double() - cameras[0].principal_point[1]) /	<-- UNCOVERED
478	                cameras[0].focal_length
479	
480	              point_cloud.push(Point3D::{	<-- UNCOVERED
481	                position: [ndc_x * depth, ndc_y * depth, depth],
482	                color: [
483	                  img1.data[idx].to_double(),	<-- UNCOVERED
484	                  img1.data[idx].to_double(),
485	                  img1.data[idx].to_double(),
486	                ],
487	                normal: [0.0, 0.0, 1.0],
488	                confidence: 0.7,
489	              })
490	            }
491	          }
492	        }
493	      }
494	    }
495	  }
496	
497	  ReconstructionResult::{
498	    point_cloud,
499	    num_points: point_cloud.length(),
500	    cameras,
501	    num_cameras: cameras.length(),
502	    reconstruction_time_ms: 0.0,
503	    coverage: 0.6,
504	    accuracy: 0.7,
505	  }
506	}
   	…

510	pub fn visualize_point_cloud(
511	  point_cloud : Array[Point3D],
512	  width : Int,
513	  height : Int,
514	) -> @types.Image {
515	  let total = width * height
516	  let out : Array[Byte] = []
517	
518	  // 初始化黑色背景
519	  for _ in 0..<total {
520	    out.push(b'\x00')
521	    out.push(b'\x00')
522	    out.push(b'\x00')
523	  }
524	
525	  // 投影点云到图像
526	  for point in point_cloud {
527	    if point.position[2] > 0.1 {
528	      let focal = 500.0
529	      let px = (point.position[0] * focal / point.position[2] +
530	      width.to_double() / 2.0).to_int()
531	      let py = (point.position[1] * focal / point.position[2] +
532	      height.to_double() / 2.0).to_int()
533	
534	      if px >= 0 && px < width && py >= 0 && py < height {
535	        let idx = (py * width + px) * 3	<-- UNCOVERED
536	        out[idx] = point.color[0].to_int().to_byte()
537	        out[idx + 1] = point.color[1].to_int().to_byte()
538	        out[idx + 2] = point.color[2].to_int().to_byte()
539	      }
540	    }
541	  }
542	
543	  @types.Image::{ width, height, channels: 3, data: Bytes::from_array(out) }
544	}
   	…

548	pub fn neural_rendering_report_to_string(
549	  result : RenderingResult,
550	  algorithm : NeuralRenderingAlgorithm,
551	) -> String {
552	  let mut report = "## 神经渲染报告\n\n"
553	  report = report + "### 渲染信息\n\n"
554	  report = report + "| 指标 | 值 |\n|------|------|\n"
555	  report = report +
556	    "| 算法 | " +
557	    (if algorithm == NeuralRenderingAlgorithm::NeRF {
558	      "NeRF"
559	    } else if algorithm == NeuralRenderingAlgorithm::GaussianSplatting {	<-- UNCOVERED
560	      "3D 高斯泼溅"
561	    } else if algorithm == NeuralRenderingAlgorithm::InstantNGP {
562	      "Instant-NGP"
563	    } else if algorithm == NeuralRenderingAlgorithm::TensoRF {
564	      "TensoRF"
565	    } else {
566	      "未知"	<-- UNCOVERED
567	    }) +
568	    " |\n"
569	  report = report +
570	    "| 渲染时间 | " +
571	    result.rendering_time_ms.to_string() +
572	    " ms |\n"
573	  report = report + "| PSNR | " + result.psnr.to_string() + " dB |\n"
574	  report = report + "| SSIM | " + result.ssim.to_string() + " |\n"
575	  report = report +
576	    "| 图像尺寸 | " +
577	    result.image.width.to_string() +
578	    "x" +
579	    result.image.height.to_string() +
580	    " |\n"
581	
582	  report
583	}
   	…

587	pub fn reconstruction_report_to_string(
588	  result : ReconstructionResult,
589	  algorithm : ReconstructionAlgorithm,
590	) -> String {
591	  let mut report = "## 3D 重建报告\n\n"
592	  report = report + "### 重建信息\n\n"
593	  report = report + "| 指标 | 值 |\n|------|------|\n"
594	  report = report +
595	    "| 算法 | " +
596	    (if algorithm == ReconstructionAlgorithm::MultiViewStereo {
597	      "多视图立体"
598	    } else if algorithm == ReconstructionAlgorithm::StructureFromMotion {	<-- UNCOVERED
599	      "运动恢复结构"
600	    } else if algorithm == ReconstructionAlgorithm::PhotometricStereo {
601	      "光度立体"
602	    } else {
603	      "未知"	<-- UNCOVERED
604	    }) +
605	    " |\n"
606	  report = report + "| 点云数量 | " + result.num_points.to_string() + " |\n"
607	  report = report +
608	    "| 相机数量 | " +
609	    result.num_cameras.to_string() +
610	    " |\n"
611	  report = report +
612	    "| 重建时间 | " +
613	    result.reconstruction_time_ms.to_string() +
614	    " ms |\n"
615	  report = report +
616	    "| 覆盖率 | " +
617	    (result.coverage * 100.0).to_string() +
618	    "% |\n"
619	  report = report +
620	    "| 精度 | " +
621	    (result.accuracy * 100.0).to_string() +
622	    "% |\n"
623	
624	  report
625	}
   	…

8 uncovered line(s) in src\process\nlp_advanced\nlp_advanced.mbt:

265	pub fn recognize_entities(text : String) -> NERResult {
266	  let entities : Array[String] = []
267	  let entity_types : Array[NEREntityType] = []
268	  let entity_spans : Array[Array[Int]] = []
269	  let confidences : Array[Double] = []
270	
271	  let tokens = tokenize(text)
272	  let mut position = 0
273	
274	  for token in tokens {
275	    let token_len = token.length()
276	    let start = position
277	    let end = position + token_len
278	
279	    // 简化的实体识别：基于首字母大写
280	    let mut is_entity = false
281	    let mut entity_type = NEREntityType::Unknown
282	
283	    if token.length() > 0 {
284	      // 简化的首字母大写判断
285	      if token.length() > 1 {
286	        is_entity = true
287	        // 简化的实体类型判断
288	        if token.contains("Inc") ||
289	          token.contains("Corp") ||
290	          token.contains("Company") {
291	          entity_type = NEREntityType::Organization
292	        } else if token.contains("Street") ||
293	          token.contains("Avenue") ||
294	          token.contains("City") {
295	          entity_type = NEREntityType::Location	<-- UNCOVERED
296	        } else {
297	          entity_type = NEREntityType::Person
298	        }
299	      }
300	    }
301	
302	    if is_entity {
303	      entities.push(token)
304	      entity_types.push(entity_type)
305	      let span : Array[Int] = []
306	      span.push(start)
307	      span.push(end)
308	      entity_spans.push(span)
309	      confidences.push(0.7)
310	    }
311	
312	    position = end + 1
313	  }
314	
315	  NERResult::{
316	    text,
317	    entities,
318	    entity_types,
319	    entity_spans,
320	    confidences,
321	    num_entities: entities.length(),
322	  }
323	}
   	…

327	pub fn analyze_sentiment(text : String) -> SentimentAnalysisResult {
328	  let tokens = tokenize(text)
329	
330	  let positive_words = [
331	    "good", "great", "excellent", "love", "happy", "amazing", "wonderful", "fantastic",
332	    "best", "perfect",
333	  ]
334	  let negative_words = [
335	    "bad", "terrible", "awful", "hate", "sad", "horrible", "worst", "poor", "disappointing",
336	    "ugly",
337	  ]
338	
339	  let mut positive_count = 0
340	  let mut negative_count = 0
341	
342	  for token in tokens {
343	    let token_lower = token
344	    for pw in positive_words {
345	      if token_lower.contains(pw) {
346	        positive_count += 1
347	      }
348	    }
349	    for nw in negative_words {
350	      if token_lower.contains(nw) {
351	        negative_count += 1
352	      }
353	    }
354	  }
355	
356	  let total = positive_count + negative_count
357	  let positive_score = if total > 0 {
358	    positive_count.to_double() / total.to_double()
359	  } else {
360	    0.5
361	  }
362	  let negative_score = if total > 0 {
363	    negative_count.to_double() / total.to_double()
364	  } else {
365	    0.5
366	  }
367	  let neutral_score = 1.0 - positive_score - negative_score
368	
369	  let sentiment_score = positive_score - negative_score
370	  let sentiment = if sentiment_score > 0.5 {
371	    SentimentType::VeryPositive
372	  } else if sentiment_score > 0.1 {
373	    SentimentType::Positive	<-- UNCOVERED
374	  } else if sentiment_score > -0.1 {
375	    SentimentType::Neutral
376	  } else if sentiment_score > -0.5 {
377	    SentimentType::Negative	<-- UNCOVERED
378	  } else {
379	    SentimentType::VeryNegative
380	  }
381	
382	  let confidence = if total > 0 {
383	    (positive_score - negative_score).abs()
384	  } else {
385	    0.3
386	  }
387	
388	  SentimentAnalysisResult::{
389	    text,
390	    sentiment,
391	    sentiment_score,
392	    confidence,
393	    positive_score,
394	    negative_score,
395	    neutral_score,
396	    aspect_sentiments: [],
397	  }
398	}
   	…

402	pub fn summarize_text(
403	  text : String,
404	  max_sentences : Int,
405	) -> TextSummarizationResult {
406	  // 简化的句子分割：按句号分割
407	  let sentences : Array[String] = []
408	  let parts = text.split(".")
409	  for part in parts {
410	    let current_sentence = part.to_owned() + "."
411	    if current_sentence.length() > 10 {
412	      sentences.push(current_sentence)
413	    }
414	  }
415	
416	  // 简化的句子评分：基于长度
417	  let scored_sentences : Array[(Double, String)] = []
418	  for sentence in sentences {
419	    let score = sentence.length().to_double()
420	    scored_sentences.push((score, sentence))
421	  }
422	
423	  // 选择Top-N句子
424	  let num_selected = if max_sentences < sentences.length() {
425	    max_sentences
426	  } else {
427	    sentences.length()
428	  }
429	
430	  let mut summary = ""
431	  for i in 0..<num_selected {
432	    summary = summary + scored_sentences[i].1
433	  }
434	
435	  let original_length = text.length()
436	  let summary_length = summary.length()
437	  let compression_ratio = if original_length > 0 {
438	    1.0 - summary_length.to_double() / original_length.to_double()
439	  } else {
440	    0.0	<-- UNCOVERED
441	  }
442	
443	  TextSummarizationResult::{
444	    original_text: text,
445	    summary,
446	    compression_ratio,
447	    original_length,
448	    summary_length,
449	    method_type: "extractive",
450	    rouge_score: 0.5,
451	  }
452	}
   	…

484	pub fn nlp_report_to_string(
485	  task_type : NLPTaskType,
486	  config : NLPConfig,
487	  result_description : String,
488	) -> String {
489	  let mut report = "## 自然语言处理高级报告\n\n"
490	
491	  report = report + "### 任务信息\n\n"
492	  report = report + "| 指标 | 值 |\n|------|------|\n"
493	  report = report +
494	    "| 任务类型 | " +
495	    (match task_type {
496	      NLPTaskType::TextClassification => "文本分类"	<-- UNCOVERED
497	      NLPTaskType::NamedEntityRecognition => "命名实体识别"
498	      NLPTaskType::SentimentAnalysis => "情感分析"
499	      NLPTaskType::TextSummarization => "文本摘要"
500	      NLPTaskType::MachineTranslation => "机器翻译"	<-- UNCOVERED
501	      NLPTaskType::QuestionAnswering => "问答系统"
502	      NLPTaskType::TextGeneration => "文本生成"
503	      _ => "未知"
504	    }) +
505	    " |\n"
506	  report = report +
507	    "| 分类模型 | " +
508	    (match config.classification_model {
509	      TextClassificationModel::NaiveBayes => "朴素贝叶斯"	<-- UNCOVERED
510	      TextClassificationModel::SVM => "SVM"
511	      TextClassificationModel::CNN => "CNN"
512	      TextClassificationModel::RNN => "RNN"
513	      TextClassificationModel::LSTM => "LSTM"
514	      TextClassificationModel::Transformer => "Transformer"
515	      TextClassificationModel::BERT => "BERT"
516	      TextClassificationModel::RoBERTa => "RoBERTa"	<-- UNCOVERED
517	      _ => "未知"
518	    }) +
519	    " |\n"
520	  report = report + "| 最大长度 | " + config.max_length.to_string() + " |\n"
521	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
522	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
523	  report = report + "| 训练轮数 | " + config.num_epochs.to_string() + " |\n"
524	  report = report + "| 预训练模型 | " + config.pretrained_model + " |\n"
525	
526	  report = report + "\n### 结果\n\n"
527	  report = report + result_description + "\n"
528	
529	  report
530	}
   	…

23 uncovered line(s) in src\process\ocr\accuracy_testing.mbt:

 99	pub fn calculate_char_accuracy(expected : String, actual : String) -> Double {
100	  if expected.length() == 0 {
101	    return if actual.length() == 0 { 1.0 } else { 0.0 }
102	  }
103	  let mut correct = 0
104	  let min_len = if expected.length() < actual.length() {
105	    expected.length()	<-- UNCOVERED
106	  } else {
107	    actual.length()
108	  }
109	  for i in 0..<min_len {
110	    if expected[i] == actual[i] {
111	      correct += 1
112	    }
113	  }
114	  correct.to_double() / expected.length().to_double()
115	}
   	…

119	pub fn calculate_word_accuracy(expected : String, actual : String) -> Double {
120	  let expected_words = split_words(expected)
121	  let actual_words = split_words(actual)
122	  if expected_words.length() == 0 {
123	    return if actual_words.length() == 0 { 1.0 } else { 0.0 }	<-- UNCOVERED
124	  }
125	  let mut correct = 0
126	  let min_len = if expected_words.length() < actual_words.length() {
127	    expected_words.length()	<-- UNCOVERED
128	  } else {
129	    actual_words.length()
130	  }
131	  for i in 0..<min_len {
132	    if expected_words[i] == actual_words[i] {
133	      correct += 1
134	    }
135	  }
136	  correct.to_double() / expected_words.length().to_double()
137	}
   	…

171	pub fn calculate_edit_distance(s1 : String, s2 : String) -> Int {
172	  let m = s1.length()
173	  let n = s2.length()
174	  if m == 0 {
175	    return n	<-- UNCOVERED
176	  }
177	  if n == 0 {
178	    return m	<-- UNCOVERED
179	  }
180	  // 使用滚动数组优化空间
181	  let mut prev : Array[Int] = []
182	  let mut curr : Array[Int] = []
183	  for j in 0..<(n + 1) {
184	    prev.push(j)
185	    curr.push(0)
186	  }
187	  for i in 1..<(m + 1) {
188	    curr[0] = i
189	    for j in 1..<(n + 1) {
190	      let cost = if s1[i - 1] == s2[j - 1] { 0 } else { 1 }
191	      let del = prev[j] + 1
192	      let ins = curr[j - 1] + 1
193	      let sub = prev[j - 1] + cost
194	      curr[j] = if del < ins {
195	        if del < sub {
196	          del
197	        } else {
198	          sub
199	        }
200	      } else if ins < sub {
201	        ins
202	      } else {
203	        sub
204	      }
205	    }
206	    let temp = prev
207	    prev = curr
208	    curr = temp
209	  }
210	  prev[n]
211	}
   	…

215	pub fn calculate_accuracy_stats(
216	  expected_lines : Array[String],
217	  actual_lines : Array[String],
218	) -> AccuracyStats raise @types.LoadError {
219	  // 输入验证
220	  if expected_lines.length() == 0 {
221	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
222	      "calculate_accuracy_stats: 期望文本行列表不能为空",
223	    )
224	  }
225	  if actual_lines.length() == 0 {
226	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
227	      "calculate_accuracy_stats: 实际文本行列表不能为空",
228	    )
229	  }
230	  let mut total_chars = 0
231	  let mut correct_chars = 0
232	  let mut total_words = 0
233	  let mut correct_words = 0
234	  let total_lines = expected_lines.length()
235	  let mut correct_lines = 0
236	  let mut false_positives = 0
237	  let mut false_negatives = 0
238	
239	  let min_lines = if expected_lines.length() < actual_lines.length() {
240	    expected_lines.length()	<-- UNCOVERED
241	  } else {
242	    actual_lines.length()
243	  }
244	
245	  for i in 0..<min_lines {
246	    let expected = expected_lines[i]
247	    let actual = actual_lines[i]
248	    total_chars += expected.length()
249	    let mut line_correct = 0
250	    let min_chars = if expected.length() < actual.length() {
251	      expected.length()	<-- UNCOVERED
252	    } else {
253	      actual.length()
254	    }
255	    for j in 0..<min_chars {
256	      if expected[j] == actual[j] {
257	        correct_chars += 1
258	        line_correct += 1
259	      }
260	    }
261	    if actual.length() > expected.length() {
262	      false_positives += actual.length() - expected.length()	<-- UNCOVERED
263	    } else {
264	      false_negatives += expected.length() - actual.length()
265	    }
266	    if line_correct == expected.length() && expected.length() == actual.length() {
267	      correct_lines += 1
268	    }
269	    // 词级准确率
270	    let exp_words = split_words(expected)
271	    let act_words = split_words(actual)
272	    total_words += exp_words.length()
273	    let min_words = if exp_words.length() < act_words.length() {
274	      exp_words.length()	<-- UNCOVERED
275	    } else {
276	      act_words.length()
277	    }
278	    for j in 0..<min_words {
279	      if exp_words[j] == act_words[j] {
280	        correct_words += 1
281	      }
282	    }
283	  }
284	
285	  // 多出的行
286	  if actual_lines.length() > expected_lines.length() {
287	    for i in expected_lines.length()..<actual_lines.length() {	<-- UNCOVERED
288	      false_positives += actual_lines[i].length()
289	    }
290	  } else {
291	    for i in actual_lines.length()..<expected_lines.length() {
292	      false_negatives += expected_lines[i].length()	<-- UNCOVERED
293	    }
294	  }
295	
296	  let char_accuracy = if total_chars > 0 {
297	    correct_chars.to_double() / total_chars.to_double()
298	  } else {
299	    1.0	<-- UNCOVERED
300	  }
301	  let word_accuracy = if total_words > 0 {
302	    correct_words.to_double() / total_words.to_double()
303	  } else {
304	    1.0	<-- UNCOVERED
305	  }
306	  let line_accuracy = if total_lines > 0 {
307	    correct_lines.to_double() / total_lines.to_double()
308	  } else {
309	    1.0	<-- UNCOVERED
310	  }
311	
312	  let precision = if correct_chars + false_positives > 0 {
313	    correct_chars.to_double() / (correct_chars + false_positives).to_double()
314	  } else {
315	    1.0	<-- UNCOVERED
316	  }
317	  let recall = if correct_chars + false_negatives > 0 {
318	    correct_chars.to_double() / (correct_chars + false_negatives).to_double()
319	  } else {
320	    1.0	<-- UNCOVERED
321	  }
322	  let f1_score = if precision + recall > 0 {
323	    2.0 * precision * recall / (precision + recall)
324	  } else {
325	    0.0	<-- UNCOVERED
326	  }
327	
328	  AccuracyStats::{
329	    total_chars,
330	    correct_chars,
331	    char_accuracy,
332	    total_words,
333	    correct_words,
334	    word_accuracy,
335	    total_lines,
336	    correct_lines,
337	    line_accuracy,
338	    false_positives,
339	    false_negatives,
340	    precision,
341	    recall,
342	    f1_score,
343	  }
344	}
   	…

412	pub fn generate_performance_report(
413	  benchmark : PerformanceBenchmark,
414	  dataset_name : String,
415	) -> String {
416	  let mut report = "## OCR 性能基准测试报告\n\n"
417	  report = report + "**测试集**: " + dataset_name + "\n\n"
418	  report = report + "### 处理时间\n\n"
419	  report = report + "| 阶段 | 耗时 (ms) | 占比 |\n"
420	  report = report + "|------|-----------|------|\n"
421	  let total = if benchmark.total_time_ms > 0 {
422	    benchmark.total_time_ms
423	  } else {
424	    1.0	<-- UNCOVERED
425	  }
426	  report = report +
427	    "| 预处理 | " +
428	    benchmark.preprocess_time_ms.to_string() +
429	    " | " +
430	    (benchmark.preprocess_time_ms / total * 100.0).to_string() +
431	    "% |\n"
432	  report = report +
433	    "| 版面分析 | " +
434	    benchmark.layout_time_ms.to_string() +
435	    " | " +
436	    (benchmark.layout_time_ms / total * 100.0).to_string() +
437	    "% |\n"
438	  report = report +
439	    "| 字符识别 | " +
440	    benchmark.recognition_time_ms.to_string() +
441	    " | " +
442	    (benchmark.recognition_time_ms / total * 100.0).to_string() +
443	    "% |\n"
444	  report = report +
445	    "| 后处理 | " +
446	    benchmark.postprocess_time_ms.to_string() +
447	    " | " +
448	    (benchmark.postprocess_time_ms / total * 100.0).to_string() +
449	    "% |\n"
450	  report = report +
451	    "| **总计** | **" +
452	    benchmark.total_time_ms.to_string() +
453	    "** | **100%** |\n\n"
454	  report = report + "### 吞吐量\n\n"
455	  report = report + "| 指标 | 数值 |\n"
456	  report = report + "|------|------|\n"
457	  report = report +
458	    "| 处理速度 (页/秒) | " +
459	    benchmark.pages_per_second.to_string() +
460	    " |\n"
461	  report = report +
462	    "| 识别速度 (字符/秒) | " +
463	    benchmark.chars_per_second.to_string() +
464	    " |\n"
465	  report = report +
466	    "| 内存占用 (MB) | " +
467	    benchmark.memory_usage_mb.to_string() +
468	    " |\n"
469	  report = report +
470	    "| CPU 使用率 (%) | " +
471	    benchmark.cpu_usage_percent.to_string() +
472	    " |\n"
473	  report
474	}
   	…

478	pub fn check_accuracy_threshold(
479	  stats : AccuracyStats,
480	  min_char_accuracy? : Double = 0.95,
481	  min_word_accuracy? : Double = 0.90,
482	  min_f1_score? : Double = 0.92,
483	) -> (Bool, Array[String]) raise @types.LoadError {
484	  // 输入验证
485	  if min_char_accuracy < 0.0 || min_char_accuracy > 1.0 {
486	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
487	      "check_accuracy_threshold: 字符准确率阈值必须在0-1之间",
488	    )
489	  }
490	  if min_word_accuracy < 0.0 || min_word_accuracy > 1.0 {
491	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
492	      "check_accuracy_threshold: 词准确率阈值必须在0-1之间",
493	    )
494	  }
495	  if min_f1_score < 0.0 || min_f1_score > 1.0 {
496	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
497	      "check_accuracy_threshold: F1分数阈值必须在0-1之间",
498	    )
499	  }
500	  let min_char = min_char_accuracy
501	  let min_word = min_word_accuracy
502	  let min_f1 = min_f1_score
503	  let mut passed = true
504	  let issues : Array[String] = []
505	  if stats.char_accuracy < min_char {
506	    passed = false
507	    issues.push(
508	      "字符准确率 " +
509	      (stats.char_accuracy * 100.0).to_string() +
510	      "% 低于阈值 " +
511	      (min_char * 100.0).to_string() +
512	      "%",
513	    )
514	  }
515	  if stats.word_accuracy < min_word {
516	    passed = false
517	    issues.push(
518	      "词准确率 " +
519	      (stats.word_accuracy * 100.0).to_string() +
520	      "% 低于阈值 " +
521	      (min_word * 100.0).to_string() +
522	      "%",
523	    )
524	  }
525	  if stats.f1_score < min_f1 {
526	    passed = false
527	    issues.push(
528	      "F1 分数 " +
529	      (stats.f1_score * 100.0).to_string() +
530	      "% 低于阈值 " +
531	      (min_f1 * 100.0).to_string() +
532	      "%",
533	    )
534	  }
535	  (passed, issues)
536	}
   	…

22 uncovered line(s) in src\process\ocr\advanced_chinese_ocr.mbt:

138	pub fn decompose_chinese_char(
139	  char : String,
140	) -> ChineseCharStructure raise @types.LoadError {
141	  // 输入验证
142	  if char.length() == 0 {
143	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
144	      "decompose_chinese_char: 字符不能为空",
145	    )
146	  }
147	  let seed = if char.length() > 0 { char[0].to_int() } else { 0 }
148	  // 基于码点判断结构类型（简化版）
149	  let structure_type = match seed % 5 {
150	    0 => "single"	<-- UNCOVERED
151	    1 => "left-right"
152	    2 => "top-bottom"
153	    3 => "surround"
154	    _ => "complex"	<-- UNCOVERED
155	  }
156	  // 常见部首列表
157	  let radicals : Array[String] = [
158	    "氵", "扌", "木", "艹", "口", "日", "月", "人", "亻", "彳", "心",
159	    "忄", "火", "灬", "土", "士", "女", "子", "宀", "辶", "纟", "讠",
160	    "钅", "石", "目", "田", "白", "皮", "疒", "立",
161	  ]
162	  let radical = radicals[seed % radicals.length()]
163	  let strokes = 3 + seed % 20
164	  let components : Array[String] = [radical, char]
165	  ChineseCharStructure::{ char, radical, strokes, structure_type, components }
166	}
   	…

170	pub fn recognize_chinese_advanced(
171	  img : @types.Image,
172	  config? : AdvancedChineseOcrConfig,
173	) -> OcrResult raise @types.LoadError {
174	  // 输入验证
175	  if img.width <= 0 || img.height <= 0 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "recognize_chinese_advanced: 图像尺寸必须大于0",
178	    )
179	  }
180	  if img.channels < 1 || img.channels > 4 {
181	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
182	      "recognize_chinese_advanced: 通道数必须在1-4之间",
183	    )
184	  }
185	  let expected_size = img.width * img.height * img.channels
186	  if img.data.length() != expected_size {
187	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
188	      "recognize_chinese_advanced: 数据大小不匹配，期望 " +
189	      expected_size.to_string() +	<-- UNCOVERED
190	      "，实际 " +
191	      img.data.length().to_string(),	<-- UNCOVERED
192	    )
193	  }
194	  let cfg = match config {
195	    Some(c) => c	<-- UNCOVERED
196	    None => advanced_chinese_ocr_default_config()
197	  }
198	  // 1. 预处理
199	  let preprocessed = ocr_preprocess(img)
200	  // 2. 字符分割
201	  let char_regions = segment_advanced_chinese_chars(preprocessed, cfg)
202	  // 3. 生成模板
203	  let templates = generate_3500_chinese_templates(char_set=cfg.char_set)
204	  // 4. 对每个字符进行识别
205	  let char_results : Array[OcrCharResult] = []
206	  let mut text = ""
207	  let mut total_confidence = 0.0
208	  for _tup0 in char_regions {
209	    let (x, y, w, h) = _tup0
210	    // 提取字符图像
211	    let char_img = extract_advanced_char_region(
212	      preprocessed,
213	      x,
214	      y,
215	      w,
216	      h,
217	      cfg.target_size,
218	    )
219	    // 提取特征
220	    let features = extract_advanced_char_features(char_img, 16)
221	    // 模板匹配
222	    let result = match_advanced_template(
223	      features,
224	      templates,
225	      cfg.distance_metric,
226	    )
227	    text += result.0
228	    total_confidence += result.1
229	    char_results.push(OcrCharResult::{
230	      char: result.0,
231	      confidence: result.1,
232	      x,
233	      y,
234	      width: w,
235	      height: h,
236	    })
237	  }
238	  let avg_confidence = if char_results.length() > 0 {
239	    total_confidence / char_results.length().to_double()
240	  } else {
241	    0.0	<-- UNCOVERED
242	  }
243	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
244	}
   	…

325	fn extract_advanced_char_region(
326	  img : @types.Image,
327	  x : Int,
328	  y : Int,
329	  width : Int,
330	  height : Int,
331	  target_size : Int,
332	) -> @types.Image {
333	  let w = img.width
334	  let h = img.height
335	  let crop_w = if x + width > w { w - x } else { width }
336	  let crop_h = if y + height > h { h - y } else { height }
337	  if crop_w <= 0 || crop_h <= 0 {
338	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
339	    return @types.Image::{
340	      width: target_size,
341	      height: target_size,
342	      channels: 1,
343	      data: Bytes::from_array(empty),	<-- UNCOVERED
344	    }
345	  }
346	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
347	  let data = img.data
348	  for py in 0..<target_size {
349	    for px in 0..<target_size {
350	      let src_x = x + px * crop_w / target_size
351	      let src_y = y + py * crop_h / target_size
352	      if src_x < w && src_y < h {
353	        out[py * target_size + px] = data[src_y * w + src_x]
354	      }
355	    }
356	  }
357	  @types.Image::{
358	    width: target_size,
359	    height: target_size,
360	    channels: 1,
361	    data: Bytes::from_array(out),
362	  }
363	}
   	…

367	fn extract_advanced_char_features(
368	  char_img : @types.Image,
369	  grid_size : Int,
370	) -> Array[Double] {
371	  let w = char_img.width
372	  let h = char_img.height
373	  let data = char_img.data
374	  let cell_w = w / grid_size
375	  let cell_h = h / grid_size
376	  let features : Array[Double] = []
377	  for r in 0..<grid_size {
378	    for c in 0..<grid_size {
379	      let mut count = 0
380	      let mut total = 0
381	      for py in 0..<cell_h {
382	        for px in 0..<cell_w {
383	          let sx = c * cell_w + px
384	          let sy = r * cell_h + py
385	          if sx < w && sy < h {
386	            total += 1
387	            if data[sy * w + sx].to_int() > 128 {
388	              count += 1
389	            }
390	          }
391	        }
392	      }
393	      let density = if total > 0 {
394	        count.to_double() / total.to_double()
395	      } else {
396	        0.0	<-- UNCOVERED
397	      }
398	      features.push(density)
399	    }
400	  }
401	  features
402	}
   	…

406	fn match_advanced_template(
407	  features : Array[Double],
408	  templates : Array[(String, Array[Double])],
409	  distance_metric : String,
410	) -> (String, Double) {
411	  let mut best_label = "?"
412	  let mut best_dist = 1000000.0
413	  for _tup1 in templates {
414	    let (label, template) = _tup1
415	    let dist = match distance_metric {
416	      "manhattan" => manhattan_adv(features, template)	<-- UNCOVERED
417	      "cosine" => cosine_adv(features, template)
418	      _ => euclidean_adv(features, template)
419	    }
420	    if dist < best_dist {
421	      best_dist = dist
422	      best_label = label
423	    }
424	  }
425	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
426	  (best_label, confidence)
427	}
   	…

431	fn euclidean_adv(a : Array[Double], b : Array[Double]) -> Double {
432	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
433	  let mut sum_sq = 0.0
434	  for i in 0..<n {
435	    let diff = a[i] - b[i]
436	    sum_sq += diff * diff
437	  }
438	  sum_sq.sqrt()
439	}
   	…

443	fn manhattan_adv(a : Array[Double], b : Array[Double]) -> Double {
444	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
445	  let mut sum = 0.0
446	  for i in 0..<n {
447	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
448	  }
449	  sum
450	}
   	…

454	fn cosine_adv(a : Array[Double], b : Array[Double]) -> Double {
455	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
456	  let mut dot = 0.0
457	  let mut norm_a = 0.0
458	  let mut norm_b = 0.0
459	  for i in 0..<n {
460	    dot += a[i] * b[i]	<-- UNCOVERED
461	    norm_a += a[i] * a[i]
462	    norm_b += b[i] * b[i]
463	  }
464	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
465	  if denom > 0.0 {
466	    1.0 - dot / denom	<-- UNCOVERED
467	  } else {
468	    1.0	<-- UNCOVERED
469	  }
470	}
   	…

62 uncovered line(s) in src\process\ocr\batch_processing.mbt:

107	pub fn create_batch_tasks(
108	  input_files : Array[String],
109	  output_directory : String,
110	  output_format : String,
111	) -> Array[BatchTask] raise @types.LoadError {
112	  // 输入验证
113	  if input_files.length() == 0 {	<-- UNCOVERED
114	    raise @types.LoadError::DecodeFailed(
115	      "create_batch_tasks: 输入文件列表不能为空",
116	    )
117	  }
118	  if output_directory.length() == 0 {	<-- UNCOVERED
119	    raise @types.LoadError::DecodeFailed(
120	      "create_batch_tasks: 输出目录不能为空",
121	    )
122	  }
123	  if output_format.length() == 0 {	<-- UNCOVERED
124	    raise @types.LoadError::DecodeFailed(
125	      "create_batch_tasks: 输出格式不能为空",
126	    )
127	  }
128	  let tasks : Array[BatchTask] = []
129	  for i in 0..<input_files.length() {	<-- UNCOVERED
130	    let input_path = input_files[i]
131	    // 生成输出路径
132	    let extension = match output_format {	<-- UNCOVERED
133	      "text" => ".txt"
134	      "hocr" => ".hocr"
135	      "json" => ".json"
136	      "markdown" => ".md"
137	      _ => ".txt"
138	    }
139	    let output_path = output_directory + "/output_" + i.to_string() + extension	<-- UNCOVERED
140	    let task_id = "task_" + i.to_string()
141	    tasks.push(BatchTask::{
142	      task_id,
143	      input_path,
144	      output_path,
145	      status: BatchTaskStatus::Pending,
146	      progress: 0.0,
147	      error_message: "",
148	      start_time: 0.0,
149	      end_time: 0.0,
150	      page_count: 0,
151	      char_count: 0,
152	      accuracy_estimate: 0.0,
153	    })
154	  }
155	  tasks
156	}
   	…

160	pub fn update_task_progress(
161	  task : BatchTask,
162	  progress : Double,
163	  current_page : Int,
164	  total_pages : Int,
165	) -> BatchTask {
166	  ignore(current_page)	<-- UNCOVERED
167	  let new_progress = if progress > 1.0 {
168	    1.0	<-- UNCOVERED
169	  } else if progress < 0.0 {
170	    0.0
171	  } else {
172	    progress	<-- UNCOVERED
173	  }
174	  BatchTask::{
175	    task_id: task.task_id,
176	    input_path: task.input_path,
177	    output_path: task.output_path,
178	    status: BatchTaskStatus::Processing,
179	    progress: new_progress,
180	    error_message: task.error_message,
181	    start_time: task.start_time,
182	    end_time: task.end_time,
183	    page_count: total_pages,
184	    char_count: task.char_count,
185	    accuracy_estimate: task.accuracy_estimate,
186	  }
187	}
   	…

191	pub fn mark_task_completed(
192	  task : BatchTask,
193	  char_count : Int,
194	  accuracy : Double,
195	  end_time : Double,
196	) -> BatchTask {
197	  BatchTask::{	<-- UNCOVERED
198	    task_id: task.task_id,
199	    input_path: task.input_path,
200	    output_path: task.output_path,
201	    status: BatchTaskStatus::Completed,
202	    progress: 1.0,
203	    error_message: "",
204	    start_time: task.start_time,
205	    end_time,
206	    page_count: task.page_count,
207	    char_count,
208	    accuracy_estimate: accuracy,
209	  }
210	}
   	…

214	pub fn mark_task_failed(
215	  task : BatchTask,
216	  error_message : String,
217	  end_time : Double,
218	) -> BatchTask {
219	  BatchTask::{	<-- UNCOVERED
220	    task_id: task.task_id,
221	    input_path: task.input_path,
222	    output_path: task.output_path,
223	    status: BatchTaskStatus::Failed,
224	    progress: task.progress,
225	    error_message,
226	    start_time: task.start_time,
227	    end_time,
228	    page_count: task.page_count,
229	    char_count: task.char_count,
230	    accuracy_estimate: 0.0,
231	  }
232	}
   	…

236	pub fn calculate_batch_progress(
237	  tasks : Array[BatchTask],
238	  elapsed_time_ms : Double,
239	) -> BatchProgress raise @types.LoadError {
240	  // 输入验证
241	  if tasks.length() == 0 {	<-- UNCOVERED
242	    raise @types.LoadError::DecodeFailed(
243	      "calculate_batch_progress: 任务列表不能为空",
244	    )
245	  }
246	  if elapsed_time_ms < 0.0 {
247	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
248	      "calculate_batch_progress: 已用时间不能为负数",
249	    )
250	  }
251	  let mut completed = 0
252	  let mut failed = 0
253	  let mut skipped = 0
254	  let mut current_index = 0
255	  let mut total_progress = 0.0
256	  let mut current_progress = 0.0
257	  let mut total_pages = 0
258	  let mut total_chars = 0
259	
260	  for i in 0..<tasks.length() {	<-- UNCOVERED
261	    let task = tasks[i]
262	    match task.status {
263	      BatchTaskStatus::Completed => {
264	        completed += 1
265	        total_progress += 1.0
266	        total_pages += task.page_count
267	        total_chars += task.char_count
268	      }
269	      BatchTaskStatus::Failed => {	<-- UNCOVERED
270	        failed += 1
271	        total_progress += task.progress
272	      }
273	      BatchTaskStatus::Skipped => skipped += 1	<-- UNCOVERED
274	      BatchTaskStatus::Processing => {
275	        current_index = i
276	        current_progress = task.progress
277	        total_progress += task.progress
278	      }
279	      BatchTaskStatus::Pending => ()	<-- UNCOVERED
280	    }
281	  }
282	
283	  let overall = if tasks.length() > 0 {	<-- UNCOVERED
284	    total_progress / tasks.length().to_double()
285	  } else {
286	    0.0	<-- UNCOVERED
287	  }
288	  let elapsed_seconds = elapsed_time_ms / 1000.0
289	  let pages_per_second = if elapsed_seconds > 0 {
290	    total_pages.to_double() / elapsed_seconds	<-- UNCOVERED
291	  } else {
292	    0.0	<-- UNCOVERED
293	  }
294	  let chars_per_second = if elapsed_seconds > 0 {
295	    total_chars.to_double() / elapsed_seconds	<-- UNCOVERED
296	  } else {
297	    0.0	<-- UNCOVERED
298	  }
299	
300	  // 估算剩余时间
301	  let estimated_remaining = if completed > 0 {
302	    let avg_time_per_task = elapsed_time_ms / completed.to_double()	<-- UNCOVERED
303	    let remaining_tasks = tasks.length() - completed - failed
304	    avg_time_per_task * remaining_tasks.to_double()
305	  } else {
306	    0.0	<-- UNCOVERED
307	  }
308	
309	  BatchProgress::{
310	    total_tasks: tasks.length(),	<-- UNCOVERED
311	    completed_tasks: completed,
312	    failed_tasks: failed,
313	    skipped_tasks: skipped,
314	    current_task_index: current_index,
315	    overall_progress: overall,
316	    current_task_progress: current_progress,
317	    elapsed_time_ms,
318	    estimated_remaining_ms: estimated_remaining,
319	    pages_per_second,
320	    chars_per_second,
321	  }
322	}
   	…

326	pub fn generate_batch_summary(
327	  tasks : Array[BatchTask],
328	  total_time_ms : Double,
329	) -> BatchResultSummary raise @types.LoadError {
330	  // 输入验证
331	  if tasks.length() == 0 {	<-- UNCOVERED
332	    raise @types.LoadError::DecodeFailed(
333	      "generate_batch_summary: 任务列表不能为空",
334	    )
335	  }
336	  if total_time_ms < 0.0 {
337	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
338	      "generate_batch_summary: 总时间不能为负数",
339	    )
340	  }
341	  let mut successful = 0
342	  let mut failed = 0
343	  let mut skipped = 0
344	  let mut total_pages = 0
345	  let mut total_chars = 0
346	  let mut total_accuracy = 0.0
347	  let failed_ids : Array[String] = []
348	  let output_files : Array[String] = []
349	
350	  for task in tasks {
351	    match task.status {	<-- UNCOVERED
352	      BatchTaskStatus::Completed => {
353	        successful += 1
354	        total_pages += task.page_count
355	        total_chars += task.char_count
356	        total_accuracy += task.accuracy_estimate
357	        output_files.push(task.output_path)	<-- UNCOVERED
358	      }
359	      BatchTaskStatus::Failed => {	<-- UNCOVERED
360	        failed += 1
361	        failed_ids.push(task.task_id)	<-- UNCOVERED
362	      }
363	      BatchTaskStatus::Skipped => skipped += 1	<-- UNCOVERED
364	      _ => ()
365	    }
366	  }
367	
368	  let success_rate = if tasks.length() > 0 {	<-- UNCOVERED
369	    successful.to_double() / tasks.length().to_double()
370	  } else {
371	    0.0	<-- UNCOVERED
372	  }
373	  let avg_accuracy = if successful > 0 {
374	    total_accuracy / successful.to_double()	<-- UNCOVERED
375	  } else {
376	    0.0	<-- UNCOVERED
377	  }
378	  let total_seconds = total_time_ms / 1000.0
379	  let avg_pages_per_second = if total_seconds > 0 {
380	    total_pages.to_double() / total_seconds	<-- UNCOVERED
381	  } else {
382	    0.0	<-- UNCOVERED
383	  }
384	  let avg_chars_per_second = if total_seconds > 0 {
385	    total_chars.to_double() / total_seconds	<-- UNCOVERED
386	  } else {
387	    0.0	<-- UNCOVERED
388	  }
389	
390	  BatchResultSummary::{
391	    total_tasks: tasks.length(),	<-- UNCOVERED
392	    successful_tasks: successful,
393	    failed_tasks: failed,
394	    skipped_tasks: skipped,
395	    success_rate,
396	    total_pages,
397	    total_chars,
398	    total_time_ms,
399	    average_accuracy: avg_accuracy,
400	    average_pages_per_second: avg_pages_per_second,
401	    average_chars_per_second: avg_chars_per_second,
402	    failed_task_ids: failed_ids,
403	    output_files,
404	  }
405	}
   	…

409	pub fn batch_summary_to_string(summary : BatchResultSummary) -> String {
410	  let mut report = "## OCR 批处理结果报告\n\n"	<-- UNCOVERED
411	  report = report + "### 整体统计\n\n"
412	  report = report + "| 指标 | 数值 |\n"
413	  report = report + "|------|------|\n"
414	  report = report +
415	    "| 总任务数 | " +
416	    summary.total_tasks.to_string() +	<-- UNCOVERED
417	    " |\n"
418	  report = report +
419	    "| 成功任务 | " +
420	    summary.successful_tasks.to_string() +	<-- UNCOVERED
421	    " |\n"
422	  report = report +
423	    "| 失败任务 | " +
424	    summary.failed_tasks.to_string() +	<-- UNCOVERED
425	    " |\n"
426	  report = report +
427	    "| 跳过任务 | " +
428	    summary.skipped_tasks.to_string() +	<-- UNCOVERED
429	    " |\n"
430	  report = report +
431	    "| **成功率** | **" +
432	    (summary.success_rate * 100.0).to_string() +	<-- UNCOVERED
433	    "%** |\n"
434	  report = report + "| 总页数 | " + summary.total_pages.to_string() + " |\n"	<-- UNCOVERED
435	  report = report +
436	    "| 总字符数 | " +
437	    summary.total_chars.to_string() +	<-- UNCOVERED
438	    " |\n"
439	  report = report +
440	    "| 总耗时 | " +
441	    (summary.total_time_ms / 1000.0).to_string() +	<-- UNCOVERED
442	    " 秒 |\n"
443	  report = report +
444	    "| 平均准确率 | " +
445	    (summary.average_accuracy * 100.0).to_string() +	<-- UNCOVERED
446	    "% |\n"
447	  report = report +
448	    "| 处理速度 | " +
449	    summary.average_pages_per_second.to_string() +	<-- UNCOVERED
450	    " 页/秒 |\n"
451	  report = report +
452	    "| 识别速度 | " +
453	    summary.average_chars_per_second.to_string() +	<-- UNCOVERED
454	    " 字符/秒 |\n\n"
455	
456	  if summary.failed_tasks > 0 {
457	    report = report + "### 失败任务列表\n\n"	<-- UNCOVERED
458	    for i in 0..<summary.failed_task_ids.length() {
459	      report = report +
460	        (i + 1).to_string() +
461	        ". " +
462	        summary.failed_task_ids[i] +
463	        "\n"
464	    }
465	    report = report + "\n"
466	  }
467	
468	  report = report + "### 输出文件\n\n"
469	  for i in 0..<summary.output_files.length() {	<-- UNCOVERED
470	    report = report +
471	      (i + 1).to_string() +
472	      ". " +
473	      summary.output_files[i] +
474	      "\n"
475	  }
476	
477	  report
478	}
   	…

482	pub fn progress_bar_to_string(progress : Double, width? : Int = 30) -> String {
483	  let bar_width = width	<-- UNCOVERED
484	  let filled = (progress * bar_width.to_double()).to_int()
485	  let mut bar = "["
486	  for i in 0..<bar_width {
487	    if i < filled {	<-- UNCOVERED
488	      bar = bar + "="
489	    } else if i == filled {
490	      bar = bar + ">"
491	    } else {
492	      bar = bar + " "	<-- UNCOVERED
493	    }
494	  }
495	  bar = bar + "] " + (progress * 100.0).to_string() + "%"	<-- UNCOVERED
496	  bar
497	}
   	…

501	pub fn estimate_memory_usage(
502	  image_count : Int,
503	  image_width : Int,
504	  image_height : Int,
505	  channels : Int,
506	) -> MemoryUsageStats {
507	  let per_image_mb = (image_width * image_height * channels).to_double() /	<-- UNCOVERED
508	    (1024.0 * 1024.0)
509	  let current_usage = per_image_mb * 3.0 // 原图+预处理+二值化
510	  let peak_usage = per_image_mb * image_count.to_double() * 1.5	<-- UNCOVERED
511	  MemoryUsageStats::{
512	    current_usage_mb: current_usage,
513	    peak_usage_mb: peak_usage,
514	    average_usage_mb: (current_usage + peak_usage) / 2.0,
515	    total_allocated: image_count * 3,
516	    total_freed: image_count * 2,
517	    gc_count: image_count,
518	  }
519	}
   	…

26 uncovered line(s) in src\process\ocr\binarization.mbt:

 8	fn local_mean_std(
 9	  data : Array[Int],
10	  width : Int,
11	  height : Int,
12	  x : Int,
13	  y : Int,
14	  radius : Int,
15	) -> (Double, Double) {
16	  let mut sum = 0.0
17	  let mut sum_sq = 0.0
18	  let mut count = 0
19	  for dy in -radius..<(radius + 1) {
20	    for dx in -radius..<(radius + 1) {
21	      let nx = x + dx
22	      let ny = y + dy
23	      if nx >= 0 && nx < width && ny >= 0 && ny < height {
24	        let v = data[ny * width + nx].to_double()
25	        sum += v
26	        sum_sq += v * v
27	        count += 1
28	      }
29	    }
30	  }
31	  if count == 0 {
32	    return (0.0, 0.0)	<-- UNCOVERED
33	  }
34	  let mean = sum / count.to_double()
35	  let variance = sum_sq / count.to_double() - mean * mean
36	  let std = if variance < 0.0 { 0.0 } else { variance.sqrt() }
37	  (mean, std)
38	}
  	…

45	pub fn niblack_threshold(
46	  img : @types.Image,
47	  window_size? : Int = 15,
48	  k? : Double = -0.2,
49	) -> @types.Image raise @types.LoadError {
50	  // 输入验证
51	  if img.width <= 0 || img.height <= 0 {
52	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
53	      "niblack_threshold: 图像尺寸必须大于0",
54	    )
55	  }
56	  if img.channels != 1 {
57	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
58	      "niblack_threshold: 仅支持单通道灰度图",
59	    )
60	  }
61	  let expected_size = img.width * img.height
62	  if img.data.length() != expected_size {
63	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
64	      "niblack_threshold: 数据大小不匹配，期望 " +
65	      expected_size.to_string() +	<-- UNCOVERED
66	      "，实际 " +
67	      img.data.length().to_string(),	<-- UNCOVERED
68	    )
69	  }
70	  if window_size <= 0 || window_size % 2 == 0 {
71	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
72	      "niblack_threshold: window_size必须为正奇数",
73	    )
74	  }
75	
76	  let w = img.width
77	  let h = img.height
78	  let radius = window_size / 2
79	  // 转换为 Int 数组
80	  let data : Array[Int] = Array::make(w * h, 0)
81	  for i in 0..<(w * h) {
82	    data[i] = img.data[i].to_int()
83	  }
84	  let out : Array[Byte] = Array::make(w * h, b'\x00')
85	  for y in 0..<h {
86	    for x in 0..<w {
87	      let (mean, std) = local_mean_std(data, w, h, x, y, radius)
88	      let threshold = mean + k * std
89	      let v = data[y * w + x].to_double()
90	      out[y * w + x] = if v > threshold { b'\xFF' } else { b'\x00' }
91	    }
92	  }
93	  @types.Image::{
94	    width: w,
95	    height: h,
96	    channels: 1,
97	    data: Bytes::from_array(out),
98	  }
99	}
  	…

107	pub fn sauvola_threshold(
108	  img : @types.Image,
109	  window_size? : Int = 15,
110	  k? : Double = 0.5,
111	  r? : Double = 128.0,
112	) -> @types.Image raise @types.LoadError {
113	  // 输入验证
114	  if img.width <= 0 || img.height <= 0 {
115	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
116	      "sauvola_threshold: 图像尺寸必须大于0",
117	    )
118	  }
119	  if img.channels != 1 {
120	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
121	      "sauvola_threshold: 仅支持单通道灰度图",
122	    )
123	  }
124	  let expected_size = img.width * img.height
125	  if img.data.length() != expected_size {
126	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
127	      "sauvola_threshold: 数据大小不匹配，期望 " +
128	      expected_size.to_string() +	<-- UNCOVERED
129	      "，实际 " +
130	      img.data.length().to_string(),	<-- UNCOVERED
131	    )
132	  }
133	  if window_size <= 0 || window_size % 2 == 0 {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "sauvola_threshold: window_size必须为正奇数",
136	    )
137	  }
138	  if r <= 0.0 {
139	    raise @types.LoadError::DecodeFailed("sauvola_threshold: r必须大于0")	<-- UNCOVERED
140	  }
141	
142	  let w = img.width
143	  let h = img.height
144	  let radius = window_size / 2
145	  // 转换为 Int 数组
146	  let data : Array[Int] = Array::make(w * h, 0)
147	  for i in 0..<(w * h) {
148	    data[i] = img.data[i].to_int()
149	  }
150	  let out : Array[Byte] = Array::make(w * h, b'\x00')
151	  for y in 0..<h {
152	    for x in 0..<w {
153	      let (mean, std) = local_mean_std(data, w, h, x, y, radius)
154	      let threshold = mean * (1.0 + k * (std / r - 1.0))
155	      let v = data[y * w + x].to_double()
156	      out[y * w + x] = if v > threshold { b'\xFF' } else { b'\x00' }
157	    }
158	  }
159	  @types.Image::{
160	    width: w,
161	    height: h,
162	    channels: 1,
163	    data: Bytes::from_array(out),
164	  }
165	}
   	…

170	pub fn adaptive_threshold_ocr(
171	  img : @types.Image,
172	  block_size? : Int = 15,
173	  c? : Int = 10,
174	) -> @types.Image raise @types.LoadError {
175	  // 输入验证
176	  if img.width <= 0 || img.height <= 0 {
177	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
178	      "adaptive_threshold_ocr: 图像尺寸必须大于0",
179	    )
180	  }
181	  if img.channels != 1 {
182	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
183	      "adaptive_threshold_ocr: 仅支持单通道灰度图",
184	    )
185	  }
186	  let expected_size = img.width * img.height
187	  if img.data.length() != expected_size {
188	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
189	      "adaptive_threshold_ocr: 数据大小不匹配，期望 " +
190	      expected_size.to_string() +	<-- UNCOVERED
191	      "，实际 " +
192	      img.data.length().to_string(),	<-- UNCOVERED
193	    )
194	  }
195	  if block_size <= 0 || block_size % 2 == 0 {
196	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
197	      "adaptive_threshold_ocr: block_size必须为正奇数",
198	    )
199	  }
200	
201	  let w = img.width
202	  let h = img.height
203	  let radius = block_size / 2
204	  // 转换为 Int 数组
205	  let data : Array[Int] = Array::make(w * h, 0)
206	  for i in 0..<(w * h) {
207	    data[i] = img.data[i].to_int()
208	  }
209	  let out : Array[Byte] = Array::make(w * h, b'\x00')
210	  for y in 0..<h {
211	    for x in 0..<w {
212	      // 计算局部均值
213	      let (mean, _) = local_mean_std(data, w, h, x, y, radius)
214	      let threshold = mean - c.to_double()
215	      let v = data[y * w + x].to_double()
216	      out[y * w + x] = if v > threshold { b'\xFF' } else { b'\x00' }
217	    }
218	  }
219	  @types.Image::{
220	    width: w,
221	    height: h,
222	    channels: 1,
223	    data: Bytes::from_array(out),
224	  }
225	}
   	…

229	pub fn otsu_threshold(
230	  img : @types.Image,
231	) -> @types.Image raise @types.LoadError {
232	  // 输入验证
233	  if img.width <= 0 || img.height <= 0 {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "otsu_threshold: 图像尺寸必须大于0",
236	    )
237	  }
238	  if img.channels != 1 {
239	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
240	      "otsu_threshold: 仅支持单通道灰度图",
241	    )
242	  }
243	  let expected_size = img.width * img.height
244	  if img.data.length() != expected_size {
245	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
246	      "otsu_threshold: 数据大小不匹配，期望 " +
247	      expected_size.to_string() +	<-- UNCOVERED
248	      "，实际 " +
249	      img.data.length().to_string(),	<-- UNCOVERED
250	    )
251	  }
252	
253	  let w = img.width
254	  let h = img.height
255	  let n = w * h
256	  // 计算直方图
257	  let histogram : Array[Int] = Array::make(256, 0)
258	  for i in 0..<n {
259	    histogram[img.data[i].to_int()] += 1
260	  }
261	  // 计算 Otsu 最佳阈值
262	  let mut sum = 0
263	  for t in 0..<256 {
264	    sum += t * histogram[t]
265	  }
266	  let mut sum_b = 0
267	  let mut w_b = 0
268	  let mut max_variance = 0.0
269	  let mut threshold = 128
270	  for t in 0..<256 {
271	    w_b += histogram[t]
272	    if w_b == 0 {
273	      continue	<-- UNCOVERED
274	    }
275	    let w_f = n - w_b
276	    if w_f == 0 {
277	      break
278	    }
279	    sum_b += t * histogram[t]
280	    let m_b = sum_b.to_double() / w_b.to_double()
281	    let m_f = (sum - sum_b).to_double() / w_f.to_double()
282	    let variance = w_b.to_double() * w_f.to_double() * (m_b - m_f) * (m_b - m_f)
283	    if variance > max_variance {
284	      max_variance = variance
285	      threshold = t
286	    }
287	  }
288	  // 应用阈值
289	  let out : Array[Byte] = Array::make(n, b'\x00')
290	  for i in 0..<n {
291	    out[i] = if img.data[i].to_int() > threshold { b'\xFF' } else { b'\x00' }
292	  }
293	  @types.Image::{
294	    width: w,
295	    height: h,
296	    channels: 1,
297	    data: Bytes::from_array(out),
298	  }
299	}
   	…

54 uncovered line(s) in src\process\ocr\char_segmentation.mbt:

 24	pub fn drop_fall_segmentation(
 25	  img : @types.Image,
 26	  start_x? : Int = -1,
 27	) -> DropFallResult raise @types.LoadError {
 28	  // 输入验证
 29	  if img.width <= 0 || img.height <= 0 {
 30	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 31	      "drop_fall_segmentation: 图像尺寸必须大于0",
 32	    )
 33	  }
 34	  if img.channels != 1 {
 35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 36	      "drop_fall_segmentation: 仅支持单通道二值图",
 37	    )
 38	  }
 39	  let expected_size = img.width * img.height
 40	  if img.data.length() != expected_size {
 41	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 42	      "drop_fall_segmentation: 数据大小不匹配，期望 " +
 43	      expected_size.to_string() +	<-- UNCOVERED
 44	      "，实际 " +
 45	      img.data.length().to_string(),	<-- UNCOVERED
 46	    )
 47	  }
 48	
 49	  let w = img.width
 50	  let h = img.height
 51	  let data = img.data
 52	  // 如果未指定起始x，使用中间位置
 53	  let sx = if start_x < 0 { w / 2 } else { start_x }
 54	  // 滴水路径
 55	  let path : Array[(Int, Int)] = []
 56	  let mut x = sx
 57	  let mut y = 0
 58	  let stuck = false
 59	  let mut iterations = 0
 60	  let max_iterations = w * h
 61	  while y < h - 1 && !stuck && iterations < max_iterations {
 62	    iterations += 1
 63	    path.push((x, y))
 64	    // 检查下方三个方向：左下、正下、右下
 65	    let left = if x > 0 { data[(y + 1) * w + (x - 1)].to_int() } else { 255 }
 66	    let center = data[(y + 1) * w + x].to_int()
 67	    let right = if x < w - 1 {
 68	      data[(y + 1) * w + (x + 1)].to_int()
 69	    } else {
 70	      255	<-- UNCOVERED
 71	    }
 72	    // 水滴优先向黑色像素（前景）移动
 73	    if center < 128 {
 74	      // 正下是黑色，继续向下
 75	      y += 1
 76	    } else if left < 128 && right < 128 {
 77	      // 左右都是黑色，随机选择（这里选择左）
 78	      x -= 1	<-- UNCOVERED
 79	      y += 1
 80	    } else if left < 128 {
 81	      // 左下是黑色
 82	      x -= 1	<-- UNCOVERED
 83	      y += 1
 84	    } else if right < 128 {
 85	      // 右下是黑色
 86	      x += 1
 87	      y += 1
 88	    } else {
 89	      // 下方都是白色，水滴在白色区域
 90	      // 检查是否到达底部或卡住
 91	      if y >= h - 1 {
 92	        break	<-- UNCOVERED
 93	      }
 94	      // 在白色区域，继续向下（模拟水滴穿透白色背景）
 95	      y += 1
 96	    }
 97	    // 边界检查
 98	    if x < 0 {
 99	      x = 0	<-- UNCOVERED
100	    }
101	    if x >= w {
102	      x = w - 1	<-- UNCOVERED
103	    }
104	  }
105	  path.push((x, y))
106	  // 从路径中提取分割点
107	  let split_points : Array[SplitPoint] = []
108	  if path.length() > 0 {
109	    let (final_x, _) = path[path.length() - 1]
110	    split_points.push(SplitPoint::{
111	      x: final_x,
112	      y_top: 0,
113	      y_bottom: h - 1,
114	      confidence: 0.8,
115	    })
116	  }
117	  // 计算字符区域
118	  let char_regions : Array[(Int, Int)] = []
119	  if split_points.length() > 0 {
120	    let mut prev_x = 0
121	    for sp in split_points {
122	      char_regions.push((prev_x, sp.x))
123	      prev_x = sp.x
124	    }
125	    char_regions.push((prev_x, w - 1))
126	  } else {
127	    char_regions.push((0, w - 1))	<-- UNCOVERED
128	  }
129	  DropFallResult::{
130	    split_points,
131	    char_regions,
132	    success: split_points.length() > 0,
133	  }
134	}
   	…

139	pub fn detect_touching_chars(
140	  img : @types.Image,
141	  min_char_width? : Int = 8,
142	  max_char_width? : Int = 50,
143	) -> Array[Int] raise @types.LoadError {
144	  // 输入验证
145	  if img.width <= 0 || img.height <= 0 {
146	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
147	      "detect_touching_chars: 图像尺寸必须大于0",
148	    )
149	  }
150	  if img.channels != 1 {
151	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
152	      "detect_touching_chars: 仅支持单通道二值图",
153	    )
154	  }
155	  let expected_size = img.width * img.height
156	  if img.data.length() != expected_size {
157	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
158	      "detect_touching_chars: 数据大小不匹配，期望 " +
159	      expected_size.to_string() +	<-- UNCOVERED
160	      "，实际 " +
161	      img.data.length().to_string(),	<-- UNCOVERED
162	    )
163	  }
164	  if min_char_width <= 0 {
165	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
166	      "detect_touching_chars: min_char_width必须大于0",
167	    )
168	  }
169	  if max_char_width < min_char_width {
170	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
171	      "detect_touching_chars: max_char_width不能小于min_char_width",
172	    )
173	  }
174	
175	  let w = img.width
176	  let h = img.height
177	  let data = img.data
178	  // 垂直投影
179	  let projection : Array[Int] = Array::make(w, 0)
180	  for y in 0..<h {
181	    for x in 0..<w {
182	      if data[y * w + x].to_int() > 128 {
183	        projection[x] += 1
184	      }
185	    }
186	  }
187	  // 找局部最小值（可能的分割点）
188	  let minima : Array[Int] = []
189	  for x in 2..<(w - 2) {
190	    if projection[x] < projection[x - 1] &&
191	      projection[x] <= projection[x + 1] &&
192	      projection[x] < projection[x - 2] &&
193	      projection[x] <= projection[x + 2] {
194	      minima.push(x)	<-- UNCOVERED
195	    }
196	  }
197	  // 过滤：只保留在合理字符宽度范围内的分割点
198	  let touching_points : Array[Int] = []
199	  let mut last_split = 0
200	  for x in minima {
201	    let width = x - last_split	<-- UNCOVERED
202	    if width >= min_char_width && width <= max_char_width * 2 {
203	      // 检查这个位置是否真的是粘连点（投影值较低但不为0）
204	      if projection[x] > 0 && projection[x] < h / 4 {	<-- UNCOVERED
205	        touching_points.push(x)
206	        last_split = x
207	      }
208	    }
209	  }
210	  touching_points
211	}
   	…

216	pub fn segment_by_connected_components(
217	  img : @types.Image,
218	  min_area? : Int = 5,
219	  max_area? : Int = 10000,
220	) -> Array[(Int, Int, Int, Int)] raise @types.LoadError {
221	  // 输入验证
222	  if img.width <= 0 || img.height <= 0 {
223	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
224	      "segment_by_connected_components: 图像尺寸必须大于0",
225	    )
226	  }
227	  if img.channels != 1 {
228	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
229	      "segment_by_connected_components: 仅支持单通道二值图",
230	    )
231	  }
232	  let expected_size = img.width * img.height
233	  if img.data.length() != expected_size {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "segment_by_connected_components: 数据大小不匹配，期望 " +
236	      expected_size.to_string() +	<-- UNCOVERED
237	      "，实际 " +
238	      img.data.length().to_string(),	<-- UNCOVERED
239	    )
240	  }
241	  if min_area < 0 {
242	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
243	      "segment_by_connected_components: min_area不能为负数",
244	    )
245	  }
246	  if max_area < min_area {
247	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
248	      "segment_by_connected_components: max_area不能小于min_area",
249	    )
250	  }
251	
252	  let w = img.width
253	  let h = img.height
254	  let data = img.data
255	  // 简单连通域标记（两遍扫描的简化版）
256	  let labels : Array[Int] = Array::make(w * h, 0)
257	  let mut next_label = 1
258	  // 第一遍
259	  for y in 0..<h {
260	    for x in 0..<w {
261	      if data[y * w + x].to_int() > 128 {
262	        let mut label = 0
263	        // 检查左和上
264	        if x > 0 && labels[y * w + x - 1] > 0 {
265	          label = labels[y * w + x - 1]
266	        }
267	        if y > 0 && labels[(y - 1) * w + x] > 0 {
268	          if label == 0 {
269	            label = labels[(y - 1) * w + x]
270	          }
271	        }
272	        if label == 0 {
273	          label = next_label
274	          next_label += 1
275	        }
276	        labels[y * w + x] = label
277	      }
278	    }
279	  }
280	  // 第二遍：合并等价标签（简化版，只处理4邻域）
281	  // 计算每个标签的边界框
282	  let min_x : Array[Int] = Array::make(next_label, w)
283	  let max_x : Array[Int] = Array::make(next_label, 0)
284	  let min_y : Array[Int] = Array::make(next_label, h)
285	  let max_y : Array[Int] = Array::make(next_label, 0)
286	  let area : Array[Int] = Array::make(next_label, 0)
287	  for y in 0..<h {
288	    for x in 0..<w {
289	      let label = labels[y * w + x]
290	      if label > 0 {
291	        area[label] += 1
292	        if x < min_x[label] {
293	          min_x[label] = x
294	        }
295	        if x > max_x[label] {
296	          max_x[label] = x
297	        }
298	        if y < min_y[label] {
299	          min_y[label] = y
300	        }
301	        if y > max_y[label] {
302	          max_y[label] = y
303	        }
304	      }
305	    }
306	  }
307	  // 过滤并返回
308	  let regions : Array[(Int, Int, Int, Int)] = []
309	  for label in 1..<next_label {
310	    if area[label] >= min_area && area[label] <= max_area {
311	      let rw = max_x[label] - min_x[label] + 1
312	      let rh = max_y[label] - min_y[label] + 1
313	      if rw > 0 && rh > 0 {
314	        regions.push((min_x[label], min_y[label], rw, rh))
315	      }
316	    }
317	  }
318	  regions
319	}
   	…

323	pub fn merge_small_segments(
324	  segments : Array[(Int, Int, Int, Int)],
325	  min_width : Int,
326	  min_height : Int,
327	) -> Array[(Int, Int, Int, Int)] raise @types.LoadError {
328	  // 输入验证
329	  if segments.length() == 0 {
330	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
331	      "merge_small_segments: 分割区域列表不能为空",
332	    )
333	  }
334	  if min_width <= 0 {
335	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
336	      "merge_small_segments: min_width必须大于0",
337	    )
338	  }
339	  if min_height <= 0 {
340	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
341	      "merge_small_segments: min_height必须大于0",
342	    )
343	  }
344	
345	  let merged : Array[(Int, Int, Int, Int)] = []
346	  let mut current = segments[0]
347	  for i in 1..<segments.length() {
348	    let next = segments[i]
349	    let (cx, cy, cw, ch) = current
350	    let (nx, ny, nw, nh) = next
351	    // 检查当前区域是否过小
352	    if cw < min_width || ch < min_height {
353	      // 合并到下一个区域
354	      let new_x = min(cx, nx)
355	      let new_y = min(cy, ny)
356	      let new_max_x = max(cx + cw, nx + nw)
357	      let new_max_y = max(cy + ch, ny + nh)
358	      current = (new_x, new_y, new_max_x - new_x, new_max_y - new_y)
359	    } else {
360	      merged.push(current)
361	      current = next
362	    }
363	  }
364	  // 处理最后一个区域
365	  let (cx, cy, cw, ch) = current
366	  if cw >= min_width && ch >= min_height {
367	    merged.push(current)	<-- UNCOVERED
368	  } else if merged.length() > 0 {
369	    // 合并到前一个区域
370	    let (px, py, pw, ph) = merged[merged.length() - 1]
371	    let new_x = min(px, cx)
372	    let new_y = min(py, cy)
373	    let new_max_x = max(px + pw, cx + cw)
374	    let new_max_y = max(py + ph, cy + ch)
375	    merged[merged.length() - 1] = (
376	      new_x,
377	      new_y,
378	      new_max_x - new_x,
379	      new_max_y - new_y,
380	    )
381	  }
382	  merged
383	}
   	…

387	pub fn filter_noise_segments(
388	  segments : Array[(Int, Int, Int, Int)],
389	  img_width : Int,
390	  img_height : Int,
391	  min_area_ratio? : Double = 0.001,
392	  max_area_ratio? : Double = 0.5,
393	) -> Array[(Int, Int, Int, Int)] raise @types.LoadError {
394	  // 输入验证
395	  if segments.length() == 0 {
396	    raise @types.LoadError::DecodeFailed(
397	      "filter_noise_segments: 分割区域列表不能为空",
398	    )
399	  }
400	  if img_width <= 0 || img_height <= 0 {
401	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
402	      "filter_noise_segments: 图像尺寸必须大于0",
403	    )
404	  }
405	  if min_area_ratio < 0.0 || min_area_ratio > 1.0 {
406	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
407	      "filter_noise_segments: min_area_ratio必须在0-1之间",
408	    )
409	  }
410	  if max_area_ratio < 0.0 || max_area_ratio > 1.0 {
411	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
412	      "filter_noise_segments: max_area_ratio必须在0-1之间",
413	    )
414	  }
415	  if max_area_ratio < min_area_ratio {
416	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
417	      "filter_noise_segments: max_area_ratio不能小于min_area_ratio",
418	    )
419	  }
420	
421	  let img_area = img_width * img_height
422	  let min_area = (img_area.to_double() * min_area_ratio).to_int()
423	  let max_area = (img_area.to_double() * max_area_ratio).to_int()
424	  let filtered : Array[(Int, Int, Int, Int)] = []
425	  for _tup0 in segments {
426	    let (x, y, w, h) = _tup0
427	    let area = w * h
428	    if area >= min_area && area <= max_area {
429	      filtered.push((x, y, w, h))
430	    }
431	  }
432	  filtered
433	}
   	…

441	pub fn complete_char_segmentation(
442	  img : @types.Image,
443	  min_char_width? : Int = 5,
444	  max_char_width? : Int = 60,
445	) -> Array[(Int, Int, Int, Int)] raise @types.LoadError {
446	  // 输入验证
447	  if img.width <= 0 || img.height <= 0 {
448	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
449	      "complete_char_segmentation: 图像尺寸必须大于0",
450	    )
451	  }
452	  if img.channels != 1 {
453	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
454	      "complete_char_segmentation: 仅支持单通道二值图",
455	    )
456	  }
457	  let expected_size = img.width * img.height
458	  if img.data.length() != expected_size {
459	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
460	      "complete_char_segmentation: 数据大小不匹配，期望 " +
461	      expected_size.to_string() +	<-- UNCOVERED
462	      "，实际 " +
463	      img.data.length().to_string(),	<-- UNCOVERED
464	    )
465	  }
466	  if min_char_width <= 0 {
467	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
468	      "complete_char_segmentation: min_char_width必须大于0",
469	    )
470	  }
471	  if max_char_width < min_char_width {
472	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
473	      "complete_char_segmentation: max_char_width不能小于min_char_width",
474	    )
475	  }
476	
477	  let w = img.width
478	  let h = img.height
479	  // 1. 连通域初步分割
480	  let segments = segment_by_connected_components(
481	    img,
482	    min_area=5,
483	    max_area=100000,
484	  )
485	  // 2. 检测粘连字符（宽度过大的区域）
486	  let final_segments : Array[(Int, Int, Int, Int)] = []
487	  for _tup1 in segments {
488	    let (x, y, sw, sh) = _tup1
489	    if sw > max_char_width && sh > min_char_width {
490	      // 可能是粘连字符，尝试滴水算法分割
491	      // 裁剪这个区域
492	      let sub_img = crop_sub_image(img, x, y, sw, sh)	<-- UNCOVERED
493	      match sub_img {
494	        Some(si) => {
495	          let drop_result = drop_fall_segmentation(si)
496	          if drop_result.success && drop_result.char_regions.length() > 1 {
497	            // 分割成功，添加子区域
498	            for _tup2 in drop_result.char_regions {	<-- UNCOVERED
499	              let (rx1, rx2) = _tup2
500	              let rw = rx2 - rx1 + 1
501	              if rw >= min_char_width {
502	                final_segments.push((x + rx1, y, rw, sh))	<-- UNCOVERED
503	              }
504	            }
505	          } else {
506	            // 分割失败，保留原区域
507	            final_segments.push((x, y, sw, sh))	<-- UNCOVERED
508	          }
509	        }
510	        None => final_segments.push((x, y, sw, sh))	<-- UNCOVERED
511	      }
512	    } else {
513	      final_segments.push((x, y, sw, sh))
514	    }
515	  }
516	  // 3. 后处理：合并过小的区域
517	  let merged = merge_small_segments(
518	    final_segments, min_char_width, min_char_width,
519	  )
520	  // 4. 过滤噪声
521	  let filtered = filter_noise_segments(
522	    merged,
523	    w,
524	    h,
525	    min_area_ratio=0.0005,
526	    max_area_ratio=0.8,
527	  )
528	  filtered
529	}
   	…

533	fn crop_sub_image(
534	  img : @types.Image,
535	  x : Int,
536	  y : Int,
537	  width : Int,
538	  height : Int,
539	) -> @types.Image? {
540	  let w = img.width	<-- UNCOVERED
541	  let h = img.height
542	  let ch = img.channels
543	  if x < 0 || y < 0 || x + width > w || y + height > h {
544	    return None	<-- UNCOVERED
545	  }
546	  let out : Array[Byte] = Array::make(width * height * ch, b'\x00')	<-- UNCOVERED
547	  let data = img.data
548	  for yy in 0..<height {
549	    for xx in 0..<width {	<-- UNCOVERED
550	      for c in 0..<ch {
551	        out[(yy * width + xx) * ch + c] = data[((y + yy) * w + (x + xx)) * ch +
552	          c]
553	      }
554	    }
555	  }
556	  Some(@types.Image::{
557	    width,
558	    height,
559	    channels: ch,
560	    data: Bytes::from_array(out),	<-- UNCOVERED
561	  })
562	}
   	…

53 uncovered line(s) in src\process\ocr\chinese_dictionary.mbt:

484	pub fn create_default_chinese_dictionary() -> DictChineseDict {
485	  let chars = get_dict_common_chinese_chars()
486	  let mut level1 = 0
487	  let mut level2 = 0
488	  let mut level3 = 0
489	  for char in chars {
490	    match char.level {
491	      "一级" => level1 += 1
492	      "二级" => level2 += 1	<-- UNCOVERED
493	      _ => level3 += 1
494	    }
495	  }
496	  let radical_index : Array[(String, Array[Int])] = []
497	  let pinyin_index : Array[(String, Array[Int])] = []
498	  DictChineseDict::{
499	    chars,
500	    total_chars: chars.length(),
501	    level1_count: level1,
502	    level2_count: level2,
503	    level3_count: level3,
504	    radical_index,
505	    pinyin_index,
506	  }
507	}
   	…

511	pub fn lookup_by_radical(
512	  dict : DictChineseDict,
513	  radical : String,
514	) -> Array[DictCharEntry] raise @types.LoadError {
515	  // 输入验证
516	  if radical.length() == 0 {	<-- UNCOVERED
517	    raise @types.LoadError::DecodeFailed(
518	      "lookup_by_radical: 部首不能为空",
519	    )
520	  }
521	  let results : Array[DictCharEntry] = []
522	  for char in dict.chars {
523	    if char.radical == radical {	<-- UNCOVERED
524	      results.push(char)
525	    }
526	  }
527	  results
528	}
   	…

532	pub fn lookup_by_pinyin(
533	  dict : DictChineseDict,
534	  pinyin : String,
535	) -> Array[DictCharEntry] raise @types.LoadError {
536	  // 输入验证
537	  if pinyin.length() == 0 {	<-- UNCOVERED
538	    raise @types.LoadError::DecodeFailed("lookup_by_pinyin: 拼音不能为空")
539	  }
540	  let results : Array[DictCharEntry] = []
541	  for char in dict.chars {
542	    for py in char.pinyin {	<-- UNCOVERED
543	      if py == pinyin {
544	        results.push(char)
545	        break
546	      }
547	    }
548	  }
549	  results
550	}
   	…

554	pub fn lookup_by_structure(
555	  dict : DictChineseDict,
556	  structure : DictCharStructure,
557	) -> Array[DictCharEntry] {
558	  let results : Array[DictCharEntry] = []	<-- UNCOVERED
559	  for char in dict.chars {
560	    if char.structure == structure {	<-- UNCOVERED
561	      results.push(char)
562	    }
563	  }
564	  results
565	}
   	…

569	pub fn calculate_chinese_similarity(
570	  char1 : DictCharEntry,
571	  char2 : DictCharEntry,
572	) -> Double {
573	  let mut score = 0.0	<-- UNCOVERED
574	  // 结构匹配（30%）
575	  if char1.structure == char2.structure {
576	    score += 0.3	<-- UNCOVERED
577	  }
578	  // 部首匹配（20%）
579	  if char1.radical == char2.radical {
580	    score += 0.2	<-- UNCOVERED
581	  }
582	  // 笔画数接近（20%）
583	  let stroke_diff = (char1.stroke_count - char2.stroke_count).abs()	<-- UNCOVERED
584	  if stroke_diff == 0 {
585	    score += 0.2	<-- UNCOVERED
586	  } else if stroke_diff <= 2 {
587	    score += 0.15
588	  } else if stroke_diff <= 4 {
589	    score += 0.1
590	  }
591	  // 部件匹配（30%）
592	  let mut component_match = 0
593	  for c1 in char1.components {
594	    for c2 in char2.components {	<-- UNCOVERED
595	      if c1.name == c2.name {
596	        component_match += 1
597	        break
598	      }
599	    }
600	  }
601	  let total_components = if char1.components.length() >	<-- UNCOVERED
602	    char2.components.length() {
603	    char1.components.length()
604	  } else {
605	    char2.components.length()	<-- UNCOVERED
606	  }
607	  if total_components > 0 {
608	    score += component_match.to_double() / total_components.to_double() * 0.3	<-- UNCOVERED
609	  } else {
610	    score += 0.15	<-- UNCOVERED
611	  }
612	  // 都是独体字
613	  score
614	}
   	…

618	pub fn find_similar_chinese_chars(
619	  dict : DictChineseDict,
620	  target_char : String,
621	  top_n? : Int = 5,
622	) -> Array[DictCharMatch] raise @types.LoadError {
623	  // 输入验证
624	  if target_char.length() == 0 {	<-- UNCOVERED
625	    raise @types.LoadError::DecodeFailed(
626	      "find_similar_chinese_chars: 目标字符不能为空",
627	    )
628	  }
629	  if top_n <= 0 {
630	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
631	      "find_similar_chinese_chars: 返回数量必须大于0",
632	    )
633	  }
634	  let n = top_n
635	  let target_entry = lookup_char(dict, target_char)	<-- UNCOVERED
636	  let matches : Array[DictCharMatch] = []
637	  match target_entry {	<-- UNCOVERED
638	    Some(target) => {
639	      for char in dict.chars {
640	        if char.char != target_char {	<-- UNCOVERED
641	          let similarity = calculate_chinese_similarity(target, char)
642	          if similarity > 0.3 {
643	            let mut component_match = 0	<-- UNCOVERED
644	            for c1 in target.components {
645	              for c2 in char.components {	<-- UNCOVERED
646	                if c1.name == c2.name {
647	                  component_match += 1
648	                  break
649	                }
650	              }
651	            }
652	            matches.push(DictCharMatch::{	<-- UNCOVERED
653	              target_char,
654	              matched_char: char.char,
655	              similarity,
656	              structure_match: target.structure == char.structure,
657	              component_match_count: component_match,
658	              total_components: if target.components.length() >	<-- UNCOVERED
659	                char.components.length() {
660	                target.components.length()
661	              } else {
662	                char.components.length()	<-- UNCOVERED
663	              },
664	              stroke_diff: (target.stroke_count - char.stroke_count).abs(),	<-- UNCOVERED
665	            })
666	          }
667	        }
668	      }
669	      // 按相似度排序
670	      for i in 0..<matches.length() {	<-- UNCOVERED
671	        for j in 0..<(matches.length() - i - 1) {
672	          if matches[j].similarity < matches[j + 1].similarity {
673	            let temp = matches[j]
674	            matches[j] = matches[j + 1]
675	            matches[j + 1] = temp
676	          }
677	        }
678	      }
679	    }
680	    None => ()	<-- UNCOVERED
681	  }
682	  // 返回前 n 个
683	  let results : Array[DictCharMatch] = []
684	  let limit = if n < matches.length() { n } else { matches.length() }	<-- UNCOVERED
685	  for i in 0..<limit {
686	    results.push(matches[i])	<-- UNCOVERED
687	  }
688	  results
689	}
   	…

693	pub fn lookup_char(
694	  dict : DictChineseDict,
695	  char : String,
696	) -> DictCharEntry? raise @types.LoadError {
697	  // 输入验证
698	  if char.length() == 0 {	<-- UNCOVERED
699	    raise @types.LoadError::DecodeFailed("lookup_char: 字符不能为空")
700	  }
701	  for entry in dict.chars {
702	    if entry.char == char {	<-- UNCOVERED
703	      return Some(entry)
704	    }
705	  }
706	  None
707	}
   	…

711	pub fn recognize_by_components(
712	  dict : DictChineseDict,
713	  detected_structure : DictCharStructure,
714	  detected_components : Array[String],
715	  stroke_count_hint? : Int = 0,
716	) -> Array[(String, Double)] {
717	  let stroke_hint = stroke_count_hint	<-- UNCOVERED
718	  let candidates : Array[(String, Double)] = []
719	  for char in dict.chars {
720	    let mut score = 0.0	<-- UNCOVERED
721	    // 结构匹配
722	    if char.structure == detected_structure {
723	      score += 0.4	<-- UNCOVERED
724	    }
725	    // 部件匹配
726	    let mut component_match = 0
727	    for detected in detected_components {
728	      for comp in char.components {	<-- UNCOVERED
729	        if comp.name == detected {
730	          component_match += 1
731	          break
732	        }
733	      }
734	    }
735	    let total_detected = if detected_components.length() > 0 {	<-- UNCOVERED
736	      detected_components.length()
737	    } else {
738	      1	<-- UNCOVERED
739	    }
740	    score += component_match.to_double() / total_detected.to_double() * 0.4	<-- UNCOVERED
741	    // 笔画数匹配
742	    if stroke_hint > 0 {
743	      let stroke_diff = (char.stroke_count - stroke_hint).abs()	<-- UNCOVERED
744	      if stroke_diff == 0 {
745	        score += 0.2	<-- UNCOVERED
746	      } else if stroke_diff <= 2 {
747	        score += 0.15
748	      } else if stroke_diff <= 4 {
749	        score += 0.1
750	      }
751	    } else {
752	      score += 0.1	<-- UNCOVERED
753	    }
754	    if score > 0.3 {
755	      candidates.push((char.char, score))	<-- UNCOVERED
756	    }
757	  }
758	  // 排序
759	  for i in 0..<candidates.length() {	<-- UNCOVERED
760	    for j in 0..<(candidates.length() - i - 1) {
761	      if candidates[j].1 < candidates[j + 1].1 {
762	        let temp = candidates[j]
763	        candidates[j] = candidates[j + 1]
764	        candidates[j + 1] = temp
765	      }
766	    }
767	  }
768	  candidates
769	}
   	…

773	pub fn generate_chinese_dictionary_report(dict : DictChineseDict) -> String {
774	  let mut report = "## 中文字库统计报告\n\n"	<-- UNCOVERED
775	  report = report + "### 整体统计\n\n"
776	  report = report + "| 指标 | 数值 |\n"
777	  report = report + "|------|------|\n"
778	  report = report + "| 总字数 | " + dict.total_chars.to_string() + " |\n"	<-- UNCOVERED
779	  report = report + "| 一级字 | " + dict.level1_count.to_string() + " |\n"
780	  report = report + "| 二级字 | " + dict.level2_count.to_string() + " |\n"
781	  report = report + "| 三级字 | " + dict.level3_count.to_string() + " |\n\n"
782	  report = report + "### 高频汉字 TOP 10\n\n"
783	  report = report +
784	    "| 排名 | 汉字 | 拼音 | 笔画 | 结构 | 部首 | 频率 |\n"
785	  report = report + "|------|------|------|------|------|------|------|\n"
786	  // 按频率排序（简化：直接取前10个）
787	  let limit = if dict.chars.length() < 10 { dict.chars.length() } else { 10 }	<-- UNCOVERED
788	  for i in 0..<limit {
789	    let char = dict.chars[i]	<-- UNCOVERED
790	    let pinyin_str = if char.pinyin.length() > 0 { char.pinyin[0] } else { "" }
791	    let structure_str = match char.structure {
792	      DictCharStructure::LeftRight => "左右"
793	      DictCharStructure::UpDown => "上下"
794	      DictCharStructure::LeftMiddleRight => "左中右"
795	      DictCharStructure::UpMiddleDown => "上中下"
796	      DictCharStructure::Surround => "包围"
797	      DictCharStructure::HalfSurround => "半包围"
798	      DictCharStructure::Independent => "独体"
799	      DictCharStructure::Complex => "复杂"
800	    }
801	    report = report +
802	      "| " +
803	      (i + 1).to_string() +	<-- UNCOVERED
804	      " | " +
805	      char.char +
806	      " | " +
807	      pinyin_str +
808	      " | " +
809	      char.stroke_count.to_string() +	<-- UNCOVERED
810	      " | " +
811	      structure_str +
812	      " | " +
813	      char.radical +
814	      " | " +
815	      char.frequency.to_string() +	<-- UNCOVERED
816	      " |\n"
817	  }
818	  report
819	}
   	…

23 uncovered line(s) in src\process\ocr\chinese_ocr.mbt:

28	pub fn generate_chinese_templates(
29	  char_set? : String = "common",
30	) -> Array[(String, Array[Double])] {
31	  let templates : Array[(String, Array[Double])] = []
32	  // 常用汉字列表（100个最常用汉字）
33	  let common_chars : Array[String] = [
34	    "的", "一", "是", "在", "不", "了", "有", "和", "人", "这", "中",
35	    "大", "为", "上", "个", "国", "我", "以", "要", "他", "时", "来",
36	    "用", "们", "生", "到", "作", "地", "于", "出", "就", "分", "对",
37	    "成", "会", "可", "主", "发", "年", "动", "同", "工", "也", "能",
38	    "下", "过", "子", "说", "产", "种", "面", "而", "方", "后", "多",
39	    "定", "行", "学", "法", "所", "民", "得", "经", "十", "三", "之",
40	    "进", "着", "等", "部", "度", "家", "电", "力", "里", "如", "水",
41	    "化", "高", "自", "二", "理", "起", "小", "物", "现", "实", "加",
42	    "量", "都", "两", "体", "重", "机", "当", "使", "点", "从", "业",
43	    "本",
44	  ]
45	  let chars = if char_set == "full" {
46	    // 完整字符集（这里简化，仍然用常用汉字）
47	    common_chars	<-- UNCOVERED
48	  } else {
49	    common_chars
50	  }
51	  for char in chars {
52	    let template = generate_chinese_char_template(char)
53	    templates.push((char, template))
54	  }
55	  templates
56	}
  	…

 85	pub fn recognize_chinese(
 86	  img : @types.Image,
 87	  config? : ChineseOcrConfig,
 88	) -> OcrResult raise @types.LoadError {
 89	  // 输入验证
 90	  if img.width <= 0 || img.height <= 0 {
 91	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 92	      "recognize_chinese: 图像尺寸必须大于0",
 93	    )
 94	  }
 95	  if img.channels != 1 {
 96	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 97	      "recognize_chinese: 仅支持单通道灰度图",
 98	    )
 99	  }
100	  let expected_size = img.width * img.height
101	  if img.data.length() != expected_size {
102	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
103	      "recognize_chinese: 数据大小不匹配，期望 " +
104	      expected_size.to_string() +	<-- UNCOVERED
105	      "，实际 " +
106	      img.data.length().to_string(),	<-- UNCOVERED
107	    )
108	  }
109	
110	  let cfg = match config {
111	    Some(c) => c	<-- UNCOVERED
112	    None => chinese_ocr_default_config()
113	  }
114	  // 配置验证
115	  if cfg.min_char_width <= 0 {
116	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
117	      "recognize_chinese: min_char_width必须大于0",
118	    )
119	  }
120	  if cfg.max_char_width < cfg.min_char_width {
121	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
122	      "recognize_chinese: max_char_width不能小于min_char_width",
123	    )
124	  }
125	  if cfg.target_size <= 0 {
126	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
127	      "recognize_chinese: target_size必须大于0",
128	    )
129	  }
130	  // 1. 预处理
131	  let preprocessed = ocr_preprocess(img)
132	  // 2. 字符分割（中文字符通常是方块形，使用连通域分割）
133	  let char_regions = segment_chinese_chars(preprocessed, cfg)
134	  // 3. 生成模板
135	  let templates = generate_chinese_templates(char_set=cfg.char_set)
136	  // 4. 对每个字符进行识别
137	  let char_results : Array[OcrCharResult] = []
138	  let mut text = ""
139	  let mut total_confidence = 0.0
140	  for _tup0 in char_regions {
141	    let (x, y, w, h) = _tup0
142	    // 提取字符图像
143	    let char_img = extract_chinese_char_region(
144	      preprocessed,
145	      x,
146	      y,
147	      w,
148	      h,
149	      cfg.target_size,
150	    )
151	    // 提取特征
152	    let features = extract_chinese_char_features(char_img, 16)
153	    // 模板匹配
154	    let result = match_chinese_template(
155	      features,
156	      templates,
157	      cfg.distance_metric,
158	    )
159	    text += result.0
160	    total_confidence += result.1
161	    char_results.push(OcrCharResult::{
162	      char: result.0,
163	      confidence: result.1,
164	      x,
165	      y,
166	      width: w,
167	      height: h,
168	    })
169	  }
170	  let avg_confidence = if char_results.length() > 0 {
171	    total_confidence / char_results.length().to_double()
172	  } else {
173	    0.0	<-- UNCOVERED
174	  }
175	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
176	}
   	…

258	fn extract_chinese_char_region(
259	  img : @types.Image,
260	  x : Int,
261	  y : Int,
262	  width : Int,
263	  height : Int,
264	  target_size : Int,
265	) -> @types.Image {
266	  let w = img.width
267	  let h = img.height
268	  let crop_w = if x + width > w { w - x } else { width }
269	  let crop_h = if y + height > h { h - y } else { height }
270	  if crop_w <= 0 || crop_h <= 0 {
271	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
272	    return @types.Image::{
273	      width: target_size,
274	      height: target_size,
275	      channels: 1,
276	      data: Bytes::from_array(empty),	<-- UNCOVERED
277	    }
278	  }
279	  // 缩放到目标尺寸（中文字符保持正方形）
280	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
281	  let data = img.data
282	  for py in 0..<target_size {
283	    for px in 0..<target_size {
284	      let src_x = x + px * crop_w / target_size
285	      let src_y = y + py * crop_h / target_size
286	      if src_x < w && src_y < h {
287	        out[py * target_size + px] = data[src_y * w + src_x]
288	      }
289	    }
290	  }
291	  @types.Image::{
292	    width: target_size,
293	    height: target_size,
294	    channels: 1,
295	    data: Bytes::from_array(out),
296	  }
297	}
   	…

301	fn extract_chinese_char_features(
302	  char_img : @types.Image,
303	  grid_size : Int,
304	) -> Array[Double] {
305	  let w = char_img.width
306	  let h = char_img.height
307	  let data = char_img.data
308	  let cell_w = w / grid_size
309	  let cell_h = h / grid_size
310	  let features : Array[Double] = []
311	  for r in 0..<grid_size {
312	    for c in 0..<grid_size {
313	      let mut count = 0
314	      let mut total = 0
315	      for py in 0..<cell_h {
316	        for px in 0..<cell_w {
317	          let sx = c * cell_w + px
318	          let sy = r * cell_h + py
319	          if sx < w && sy < h {
320	            total += 1
321	            if data[sy * w + sx].to_int() > 128 {
322	              count += 1
323	            }
324	          }
325	        }
326	      }
327	      let density = if total > 0 {
328	        count.to_double() / total.to_double()
329	      } else {
330	        0.0	<-- UNCOVERED
331	      }
332	      features.push(density)
333	    }
334	  }
335	  features
336	}
   	…

340	fn match_chinese_template(
341	  features : Array[Double],
342	  templates : Array[(String, Array[Double])],
343	  distance_metric : String,
344	) -> (String, Double) {
345	  let mut best_label = "?"
346	  let mut best_dist = 1000000.0
347	  for _tup1 in templates {
348	    let (label, template) = _tup1
349	    let dist = match distance_metric {
350	      "manhattan" => manhattan_distance_chi(features, template)	<-- UNCOVERED
351	      "cosine" => cosine_distance_chi(features, template)
352	      _ => euclidean_distance_chi(features, template)
353	    }
354	    if dist < best_dist {
355	      best_dist = dist
356	      best_label = label
357	    }
358	  }
359	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
360	  (best_label, confidence)
361	}
   	…

365	fn euclidean_distance_chi(a : Array[Double], b : Array[Double]) -> Double {
366	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
367	  let mut sum_sq = 0.0
368	  for i in 0..<n {
369	    let diff = a[i] - b[i]
370	    sum_sq += diff * diff
371	  }
372	  sum_sq.sqrt()
373	}
   	…

377	fn manhattan_distance_chi(a : Array[Double], b : Array[Double]) -> Double {
378	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
379	  let mut sum = 0.0
380	  for i in 0..<n {
381	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
382	  }
383	  sum
384	}
   	…

388	fn cosine_distance_chi(a : Array[Double], b : Array[Double]) -> Double {
389	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
390	  let mut dot = 0.0
391	  let mut norm_a = 0.0
392	  let mut norm_b = 0.0
393	  for i in 0..<n {
394	    dot += a[i] * b[i]	<-- UNCOVERED
395	    norm_a += a[i] * a[i]
396	    norm_b += b[i] * b[i]
397	  }
398	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
399	  if denom > 0.0 {
400	    1.0 - dot / denom	<-- UNCOVERED
401	  } else {
402	    1.0	<-- UNCOVERED
403	  }
404	}
   	…

31 uncovered line(s) in src\process\ocr\chinese_ocr_enhanced.mbt:

357	pub fn structure_based_filter(
358	  char_img : @types.Image,
359	  char_db : Array[ChineseCharInfo],
360	  max_candidates? : Int = 50,
361	) -> Array[ChineseCharInfo] {
362	  // 分析输入字符的结构特征
363	  let w = char_img.width
364	  let h = char_img.height
365	  let ratio = if h > 0 { w.to_double() / h.to_double() } else { 1.0 }
366	  // 估计笔画密度
367	  let data = char_img.data
368	  let mut ink_pixels = 0
369	  let total = w * h
370	  for i in 0..<total {
371	    if data[i].to_int() > 128 {
372	      ink_pixels += 1
373	    }
374	  }
375	  let density = if total > 0 {
376	    ink_pixels.to_double() / total.to_double()
377	  } else {
378	    0.0	<-- UNCOVERED
379	  }
380	  // 估计复杂度（基于密度和纵横比）
381	  let estimated_complexity = if density > 0.4 {
382	    5
383	  } else if density > 0.3 {	<-- UNCOVERED
384	    4
385	  } else if density > 0.2 {
386	    3
387	  } else if density > 0.1 {
388	    2
389	  } else {
390	    1	<-- UNCOVERED
391	  }
392	  // 估计结构类型
393	  let estimated_structure = if ratio > 1.2 {
394	    "left-right"	<-- UNCOVERED
395	  } else if ratio < 0.8 {
396	    "top-bottom"	<-- UNCOVERED
397	  } else {
398	    "single"
399	  }
400	  // 筛选候选字
401	  let candidates : Array[ChineseCharInfo] = []
402	  for info in char_db {
403	    let mut _score = 0
404	    // 结构类型匹配
405	    if info.structure_type == estimated_structure {
406	      _score += 3
407	    }
408	    // 复杂度匹配
409	    if info.complexity == estimated_complexity {
410	      _score += 2	<-- UNCOVERED
411	    } else if (info.complexity - estimated_complexity).abs() <= 1 {
412	      _score += 1
413	    }
414	    // 常用等级（优先常用字）
415	    _score += (6 - info.common_grade).max(0)
416	    candidates.push(info)
417	    // 按分数排序（简单冒泡）
418	    let mut i = candidates.length() - 1
419	    while i > 0 {
420	      if candidates[i].common_grade < candidates[i - 1].common_grade {
421	        let temp = candidates[i]	<-- UNCOVERED
422	        candidates[i] = candidates[i - 1]
423	        candidates[i - 1] = temp
424	      }
425	      i -= 1
426	    }
427	  }
428	  // 限制候选数量
429	  let result : Array[ChineseCharInfo] = []
430	  let n = if max_candidates < candidates.length() {
431	    max_candidates
432	  } else {
433	    candidates.length()	<-- UNCOVERED
434	  }
435	  for i in 0..<n {
436	    result.push(candidates[i])
437	  }
438	  result
439	}
   	…

443	pub fn extract_stroke_features(
444	  char_img : @types.Image,
445	) -> Array[Double] raise @types.LoadError {
446	  // 输入验证
447	  if char_img.width <= 0 || char_img.height <= 0 {
448	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
449	      "extract_stroke_features: 图像尺寸必须大于0",
450	    )
451	  }
452	  if char_img.channels != 1 {
453	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
454	      "extract_stroke_features: 仅支持单通道灰度图",
455	    )
456	  }
457	  let expected_size = char_img.width * char_img.height
458	  if char_img.data.length() != expected_size {
459	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
460	      "extract_stroke_features: 数据大小不匹配，期望 " +
461	      expected_size.to_string() +	<-- UNCOVERED
462	      "，实际 " +
463	      char_img.data.length().to_string(),	<-- UNCOVERED
464	    )
465	  }
466	  let w = char_img.width
467	  let h = char_img.height
468	  let data = char_img.data
469	  let features : Array[Double] = []
470	  // 特征1：水平投影（上中下三部分的密度）
471	  let mut top_density = 0.0
472	  let mut mid_density = 0.0
473	  let mut bot_density = 0.0
474	  for y in 0..<h {
475	    let mut count = 0
476	    for x in 0..<w {
477	      if data[y * w + x].to_int() > 128 {
478	        count += 1
479	      }
480	    }
481	    let density = count.to_double() / w.to_double()
482	    if y < h / 3 {
483	      top_density += density
484	    } else if y < 2 * h / 3 {
485	      mid_density += density
486	    } else {
487	      bot_density += density
488	    }
489	  }
490	  features.push(top_density / (h / 3).to_double())
491	  features.push(mid_density / (h / 3).to_double())
492	  features.push(bot_density / (h / 3).to_double())
493	  // 特征2：垂直投影（左中右三部分的密度）
494	  let mut left_density = 0.0
495	  let mut center_density = 0.0
496	  let mut right_density = 0.0
497	  for x in 0..<w {
498	    let mut count = 0
499	    for y in 0..<h {
500	      if data[y * w + x].to_int() > 128 {
501	        count += 1
502	      }
503	    }
504	    let density = count.to_double() / h.to_double()
505	    if x < w / 3 {
506	      left_density += density
507	    } else if x < 2 * w / 3 {
508	      center_density += density
509	    } else {
510	      right_density += density
511	    }
512	  }
513	  features.push(left_density / (w / 3).to_double())
514	  features.push(center_density / (w / 3).to_double())
515	  features.push(right_density / (w / 3).to_double())
516	  // 特征3：总密度
517	  let mut total_ink = 0
518	  for i in 0..<(w * h) {
519	    if data[i].to_int() > 128 {
520	      total_ink += 1
521	    }
522	  }
523	  features.push(total_ink.to_double() / (w * h).to_double())
524	  // 特征4：重心位置
525	  let mut sum_x = 0.0
526	  let mut sum_y = 0.0
527	  for y in 0..<h {
528	    for x in 0..<w {
529	      if data[y * w + x].to_int() > 128 {
530	        sum_x += x.to_double()
531	        sum_y += y.to_double()
532	      }
533	    }
534	  }
535	  if total_ink > 0 {
536	    features.push(sum_x / total_ink.to_double() / w.to_double())
537	    features.push(sum_y / total_ink.to_double() / h.to_double())
538	  } else {
539	    features.push(0.5)	<-- UNCOVERED
540	    features.push(0.5)
541	  }
542	  features
543	}
   	…

547	pub fn recognize_enhanced_chinese(
548	  img : @types.Image,
549	  config? : EnhancedChineseOcrConfig,
550	) -> OcrResult raise @types.LoadError {
551	  // 输入验证
552	  if img.width <= 0 || img.height <= 0 {
553	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
554	      "recognize_enhanced_chinese: 图像尺寸必须大于0",
555	    )
556	  }
557	  if img.channels < 1 || img.channels > 4 {
558	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
559	      "recognize_enhanced_chinese: 通道数必须在1-4之间",
560	    )
561	  }
562	  let expected_size = img.width * img.height * img.channels
563	  if img.data.length() != expected_size {
564	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
565	      "recognize_enhanced_chinese: 数据大小不匹配，期望 " +
566	      expected_size.to_string() +	<-- UNCOVERED
567	      "，实际 " +
568	      img.data.length().to_string(),	<-- UNCOVERED
569	    )
570	  }
571	  let cfg = match config {
572	    Some(c) => c	<-- UNCOVERED
573	    None => enhanced_chinese_ocr_default_config()
574	  }
575	  // 1. 预处理
576	  let preprocessed = ocr_preprocess(img)
577	  // 2. 字符分割
578	  let char_regions = segment_enhanced_chars(preprocessed, cfg)
579	  // 3. 生成模板
580	  let templates = generate_enhanced_chinese_templates(
581	    char_set_size=cfg.char_set_size,
582	  )
583	  // 4. 生成结构信息数据库
584	  let char_db = generate_chinese_char_info_db()
585	  // 5. 对每个字符进行识别
586	  let char_results : Array[OcrCharResult] = []
587	  let mut text = ""
588	  let mut total_confidence = 0.0
589	  for _tup1 in char_regions {
590	    let (x, y, w, h) = _tup1
591	    // 提取字符图像
592	    let char_img = extract_enhanced_char_region(
593	      preprocessed,
594	      x,
595	      y,
596	      w,
597	      h,
598	      cfg.target_size,
599	    )
600	    // 结构预筛选（如果启用）
601	    let candidates = if cfg.use_structure_filter {
602	      structure_based_filter(char_img, char_db, max_candidates=50)
603	    } else {
604	      char_db	<-- UNCOVERED
605	    }
606	    // 提取笔画特征（如果启用）
607	    let stroke_features = if cfg.use_stroke_features {
608	      extract_stroke_features(char_img)
609	    } else {
610	      []	<-- UNCOVERED
611	    }
612	    // 模板匹配（在候选范围内）
613	    let result = match_enhanced_template(
614	      char_img, templates, candidates, stroke_features, cfg,
615	    )
616	    text += result.0
617	    total_confidence += result.1
618	    char_results.push(OcrCharResult::{
619	      char: result.0,
620	      confidence: result.1,
621	      x,
622	      y,
623	      width: w,
624	      height: h,
625	    })
626	  }
627	  let avg_confidence = if char_results.length() > 0 {
628	    total_confidence / char_results.length().to_double()
629	  } else {
630	    0.0	<-- UNCOVERED
631	  }
632	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
633	}
   	…

711	fn extract_enhanced_char_region(
712	  img : @types.Image,
713	  x : Int,
714	  y : Int,
715	  width : Int,
716	  height : Int,
717	  target_size : Int,
718	) -> @types.Image {
719	  let w = img.width
720	  let h = img.height
721	  let crop_w = if x + width > w { w - x } else { width }
722	  let crop_h = if y + height > h { h - y } else { height }
723	  if crop_w <= 0 || crop_h <= 0 {
724	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
725	    return @types.Image::{
726	      width: target_size,
727	      height: target_size,
728	      channels: 1,
729	      data: Bytes::from_array(empty),	<-- UNCOVERED
730	    }
731	  }
732	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
733	  let data = img.data
734	  for py in 0..<target_size {
735	    for px in 0..<target_size {
736	      let src_x = x + px * crop_w / target_size
737	      let src_y = y + py * crop_h / target_size
738	      if src_x < w && src_y < h {
739	        out[py * target_size + px] = data[src_y * w + src_x]
740	      }
741	    }
742	  }
743	  @types.Image::{
744	    width: target_size,
745	    height: target_size,
746	    channels: 1,
747	    data: Bytes::from_array(out),
748	  }
749	}
   	…

753	fn match_enhanced_template(
754	  char_img : @types.Image,
755	  templates : Array[(String, Array[Double])],
756	  candidates : Array[ChineseCharInfo],
757	  _stroke_features : Array[Double],
758	  cfg : EnhancedChineseOcrConfig,
759	) -> (String, Double) {
760	  let w = char_img.width
761	  let h = char_img.height
762	  let data = char_img.data
763	  // 提取网格特征
764	  let grid_size = 16
765	  let cell_w = w / grid_size
766	  let cell_h = h / grid_size
767	  let features : Array[Double] = []
768	  for r in 0..<grid_size {
769	    for c in 0..<grid_size {
770	      let mut count = 0
771	      let mut total = 0
772	      for py in 0..<cell_h {
773	        for px in 0..<cell_w {
774	          let sx = c * cell_w + px
775	          let sy = r * cell_h + py
776	          if sx < w && sy < h {
777	            total += 1
778	            if data[sy * w + sx].to_int() > 128 {
779	              count += 1
780	            }
781	          }
782	        }
783	      }
784	      let density = if total > 0 {
785	        count.to_double() / total.to_double()
786	      } else {
787	        0.0	<-- UNCOVERED
788	      }
789	      features.push(density)
790	    }
791	  }
792	  // 构建候选字符集合
793	  let candidate_chars : Array[String] = []
794	  for info in candidates {
795	    candidate_chars.push(info.char)
796	  }
797	  // 在候选范围内匹配
798	  let mut best_label = "?"
799	  let mut best_dist = 1000000.0
800	  for _tup2 in templates {
801	    let (label, template) = _tup2
802	    // 检查是否在候选范围内（如果候选列表非空）
803	    let mut in_candidates = candidate_chars.length() == 0
804	    for c in candidate_chars {
805	      if c == label {
806	        in_candidates = true
807	      }
808	    }
809	    if !in_candidates {
810	      continue
811	    }
812	    // 计算距离
813	    let n = if features.length() < template.length() {
814	      features.length()
815	    } else {
816	      template.length()	<-- UNCOVERED
817	    }
818	    let mut dist = 0.0
819	    match cfg.distance_metric {
820	      "manhattan" =>
821	        for i in 0..<n {	<-- UNCOVERED
822	          dist += (features[i] - template[i]).abs()
823	        }
824	      "cosine" => {	<-- UNCOVERED
825	        let mut dot = 0.0
826	        let mut norm_a = 0.0
827	        let mut norm_b = 0.0
828	        for i in 0..<n {
829	          dot += features[i] * template[i]	<-- UNCOVERED
830	          norm_a += features[i] * features[i]
831	          norm_b += template[i] * template[i]
832	        }
833	        let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
834	        dist = if denom > 0.0 { 1.0 - dot / denom } else { 1.0 }
835	      }
836	      _ => {
837	        for i in 0..<n {
838	          let diff = features[i] - template[i]
839	          dist += diff * diff
840	        }
841	        dist = dist.sqrt()
842	      }
843	    }
844	    if dist < best_dist {
845	      best_dist = dist
846	      best_label = label
847	    }
848	  }
849	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
850	  (best_label, confidence)
851	}
   	…

36 uncovered line(s) in src\process\ocr\classifier.mbt:

45	pub fn euclidean_distance(
46	  a : Array[Double],
47	  b : Array[Double],
48	) -> Double raise @types.LoadError {
49	  // 输入验证
50	  if a.length() == 0 {
51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
52	      "euclidean_distance: 向量a不能为空",
53	    )
54	  }
55	  if b.length() == 0 {
56	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
57	      "euclidean_distance: 向量b不能为空",
58	    )
59	  }
60	
61	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
62	  let mut sum_sq = 0.0
63	  for i in 0..<n {
64	    let diff = a[i] - b[i]
65	    sum_sq += diff * diff
66	  }
67	  sum_sq.sqrt()
68	}
  	…

72	pub fn manhattan_distance(
73	  a : Array[Double],
74	  b : Array[Double],
75	) -> Double raise @types.LoadError {
76	  // 输入验证
77	  if a.length() == 0 {
78	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
79	      "manhattan_distance: 向量a不能为空",
80	    )
81	  }
82	  if b.length() == 0 {
83	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
84	      "manhattan_distance: 向量b不能为空",
85	    )
86	  }
87	
88	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
89	  let mut sum = 0.0
90	  for i in 0..<n {
91	    sum += (a[i] - b[i]).abs()
92	  }
93	  sum
94	}
  	…

 98	pub fn cosine_distance(
 99	  a : Array[Double],
100	  b : Array[Double],
101	) -> Double raise @types.LoadError {
102	  // 输入验证
103	  if a.length() == 0 {
104	    raise @types.LoadError::DecodeFailed("cosine_distance: 向量a不能为空")	<-- UNCOVERED
105	  }
106	  if b.length() == 0 {
107	    raise @types.LoadError::DecodeFailed("cosine_distance: 向量b不能为空")	<-- UNCOVERED
108	  }
109	
110	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
111	  let mut dot = 0.0
112	  let mut norm_a = 0.0
113	  let mut norm_b = 0.0
114	  for i in 0..<n {
115	    dot += a[i] * b[i]
116	    norm_a += a[i] * a[i]
117	    norm_b += b[i] * b[i]
118	  }
119	  let denom = norm_a.sqrt() * norm_b.sqrt()
120	  if denom > 0.0 {
121	    1.0 - dot / denom
122	  } else {
123	    1.0	<-- UNCOVERED
124	  }
125	}
   	…

129	pub fn chebyshev_distance(
130	  a : Array[Double],
131	  b : Array[Double],
132	) -> Double raise @types.LoadError {
133	  // 输入验证
134	  if a.length() == 0 {
135	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
136	      "chebyshev_distance: 向量a不能为空",
137	    )
138	  }
139	  if b.length() == 0 {
140	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
141	      "chebyshev_distance: 向量b不能为空",
142	    )
143	  }
144	
145	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
146	  let mut max_diff = 0.0
147	  for i in 0..<n {
148	    let diff = (a[i] - b[i]).abs()
149	    if diff > max_diff {
150	      max_diff = diff
151	    }
152	  }
153	  max_diff
154	}
   	…

158	pub fn compute_distance(
159	  a : Array[Double],
160	  b : Array[Double],
161	  metric : DistanceMetric,
162	) -> Double raise @types.LoadError {
163	  // 输入验证
164	  if a.length() == 0 {
165	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
166	      "compute_distance: 向量a不能为空",
167	    )
168	  }
169	  if b.length() == 0 {
170	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
171	      "compute_distance: 向量b不能为空",
172	    )
173	  }
174	
175	  match metric {
176	    DistanceMetric::Euclidean => euclidean_distance(a, b)
177	    DistanceMetric::Manhattan => manhattan_distance(a, b)
178	    DistanceMetric::Cosine => cosine_distance(a, b)
179	    DistanceMetric::Chebyshev => chebyshev_distance(a, b)
180	  }
181	}
   	…

185	pub fn knn_create(
186	  k? : Int = 3,
187	  distance_metric? : String = "euclidean",
188	) -> KnnClassifier raise @types.LoadError {
189	  // 输入验证
190	  if k <= 0 {
191	    raise @types.LoadError::DecodeFailed("knn_create: k必须大于0")	<-- UNCOVERED
192	  }
193	
194	  KnnClassifier::{ samples: [], k, distance_metric }
195	}
   	…

199	pub fn knn_add_sample(
200	  classifier : KnnClassifier,
201	  features : Array[Double],
202	  label : String,
203	) -> KnnClassifier raise @types.LoadError {
204	  // 输入验证
205	  if features.length() == 0 {
206	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
207	      "knn_add_sample: 特征向量不能为空",
208	    )
209	  }
210	  if label.length() == 0 {
211	    raise @types.LoadError::DecodeFailed("knn_add_sample: 标签不能为空")	<-- UNCOVERED
212	  }
213	
214	  let new_samples = classifier.samples
215	  new_samples.push(TrainingSample::{ features, label })
216	  KnnClassifier::{
217	    samples: new_samples,
218	    k: classifier.k,
219	    distance_metric: classifier.distance_metric,
220	  }
221	}
   	…

225	pub fn knn_add_samples(
226	  classifier : KnnClassifier,
227	  samples : Array[(Array[Double], String)],
228	) -> KnnClassifier raise @types.LoadError {
229	  // 输入验证
230	  if samples.length() == 0 {
231	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
232	      "knn_add_samples: 样本列表不能为空",
233	    )
234	  }
235	
236	  let new_samples = classifier.samples
237	  for _tup0 in samples {
238	    let (features, label) = _tup0
239	    new_samples.push(TrainingSample::{ features, label })
240	  }
241	  KnnClassifier::{
242	    samples: new_samples,
243	    k: classifier.k,
244	    distance_metric: classifier.distance_metric,
245	  }
246	}
   	…

250	pub fn knn_predict(
251	  classifier : KnnClassifier,
252	  features : Array[Double],
253	) -> ClassificationResult raise @types.LoadError {
254	  // 输入验证
255	  if features.length() == 0 {
256	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
257	      "knn_predict: 特征向量不能为空",
258	    )
259	  }
260	  if classifier.samples.length() == 0 {
261	    raise @types.LoadError::DecodeFailed(
262	      "knn_predict: 分类器没有训练样本",
263	    )
264	  }
265	
266	  let metric = match classifier.distance_metric {
267	    "manhattan" => DistanceMetric::Manhattan	<-- UNCOVERED
268	    "cosine" => DistanceMetric::Cosine
269	    "chebyshev" => DistanceMetric::Chebyshev
270	    _ => DistanceMetric::Euclidean
271	  }
272	  // 计算所有样本的距离
273	  let distances : Array[(Double, String)] = []
274	  for sample in classifier.samples {
275	    let dist = compute_distance(features, sample.features, metric)
276	    distances.push((dist, sample.label))
277	  }
278	  // 按距离排序（冒泡排序，简单实现）
279	  for _ in 0..<distances.length() {
280	    for j in 0..<(distances.length() - 1) {
281	      if distances[j].0 > distances[j + 1].0 {
282	        let temp = distances[j]	<-- UNCOVERED
283	        distances[j] = distances[j + 1]
284	        distances[j + 1] = temp
285	      }
286	    }
287	  }
288	  // 取前 k 个最近邻
289	  let k = if classifier.k < distances.length() {
290	    classifier.k	<-- UNCOVERED
291	  } else {
292	    distances.length()
293	  }
294	  let top_k : Array[(String, Double)] = []
295	  let label_votes : Array[(String, Int)] = []
296	  for i in 0..<k {
297	    let (dist, label) = distances[i]
298	    let confidence = if dist > 0.0 { 1.0 / (1.0 + dist) } else { 1.0 }
299	    top_k.push((label, confidence))
300	    // 投票
301	    let mut found = false
302	    for j in 0..<label_votes.length() {
303	      if label_votes[j].0 == label {
304	        label_votes[j] = (label, label_votes[j].1 + 1)
305	        found = true
306	      }
307	    }
308	    if !found {
309	      label_votes.push((label, 1))
310	    }
311	  }
312	  // 找得票最多的标签
313	  let mut best_label = ""
314	  let mut best_votes = 0
315	  for _tup1 in label_votes {
316	    let (label, votes) = _tup1
317	    if votes > best_votes {
318	      best_votes = votes
319	      best_label = label
320	    }
321	  }
322	  let confidence = if k > 0 {
323	    best_votes.to_double() / k.to_double()
324	  } else {
325	    0.0	<-- UNCOVERED
326	  }
327	  ClassificationResult::{ label: best_label, confidence, top_k }
328	}
   	…

341	pub fn template_matcher_add(
342	  matcher : TemplateMatcher,
343	  label : String,
344	  template : Array[Double],
345	) -> TemplateMatcher raise @types.LoadError {
346	  // 输入验证
347	  if template.length() == 0 {
348	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
349	      "template_matcher_add: 模板特征向量不能为空",
350	    )
351	  }
352	  if label.length() == 0 {
353	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
354	      "template_matcher_add: 标签不能为空",
355	    )
356	  }
357	
358	  let new_templates = matcher.templates
359	  new_templates.push((label, template))
360	  TemplateMatcher::{
361	    templates: new_templates,
362	    distance_metric: matcher.distance_metric,
363	  }
364	}
   	…

368	pub fn template_matcher_predict(
369	  matcher : TemplateMatcher,
370	  features : Array[Double],
371	) -> ClassificationResult raise @types.LoadError {
372	  // 输入验证
373	  if features.length() == 0 {
374	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
375	      "template_matcher_predict: 特征向量不能为空",
376	    )
377	  }
378	  if matcher.templates.length() == 0 {
379	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
380	      "template_matcher_predict: 模板匹配器没有模板",
381	    )
382	  }
383	
384	  let metric = match matcher.distance_metric {
385	    "manhattan" => DistanceMetric::Manhattan	<-- UNCOVERED
386	    "cosine" => DistanceMetric::Cosine
387	    "chebyshev" => DistanceMetric::Chebyshev
388	    _ => DistanceMetric::Euclidean
389	  }
390	  // 计算与所有模板的距离
391	  let distances : Array[(Double, String)] = []
392	  for _tup2 in matcher.templates {
393	    let (label, template) = _tup2
394	    let dist = compute_distance(features, template, metric)
395	    distances.push((dist, label))
396	  }
397	  // 按距离排序
398	  for _ in 0..<distances.length() {
399	    for j in 0..<(distances.length() - 1) {
400	      if distances[j].0 > distances[j + 1].0 {
401	        let temp = distances[j]	<-- UNCOVERED
402	        distances[j] = distances[j + 1]
403	        distances[j + 1] = temp
404	      }
405	    }
406	  }
407	  // 返回最佳匹配
408	  let top_k : Array[(String, Double)] = []
409	  let n = if 5 < distances.length() { 5 } else { distances.length() }
410	  for i in 0..<n {
411	    let (dist, label) = distances[i]
412	    let confidence = if dist > 0.0 { 1.0 / (1.0 + dist) } else { 1.0 }
413	    top_k.push((label, confidence))
414	  }
415	  if distances.length() > 0 {
416	    let (best_dist, best_label) = distances[0]
417	    let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
418	    ClassificationResult::{ label: best_label, confidence, top_k }
419	  } else {
420	    ClassificationResult::{ label: "", confidence: 0.0, top_k: [] }	<-- UNCOVERED
421	  }
422	}
   	…

426	pub fn normalize_features(
427	  features : Array[Double],
428	) -> Array[Double] raise @types.LoadError {
429	  // 输入验证
430	  if features.length() == 0 {
431	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
432	      "normalize_features: 特征向量不能为空",
433	    )
434	  }
435	  let mut min_val = features[0]
436	  let mut max_val = features[0]
437	  for v in features {
438	    if v < min_val {
439	      min_val = v	<-- UNCOVERED
440	    }
441	    if v > max_val {
442	      max_val = v
443	    }
444	  }
445	  let range = max_val - min_val
446	  let normalized : Array[Double] = []
447	  if range > 0.0 {
448	    for v in features {
449	      normalized.push((v - min_val) / range)
450	    }
451	  } else {
452	    for _ in features {	<-- UNCOVERED
453	      normalized.push(0.0)
454	    }
455	  }
456	  normalized
457	}
   	…

461	pub fn standardize_features(
462	  features : Array[Double],
463	) -> Array[Double] raise @types.LoadError {
464	  // 输入验证
465	  if features.length() == 0 {
466	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
467	      "standardize_features: 特征向量不能为空",
468	    )
469	  }
470	  // 计算均值
471	  let mut sum = 0.0
472	  for v in features {
473	    sum += v
474	  }
475	  let mean = sum / features.length().to_double()
476	  // 计算标准差
477	  let mut sum_sq = 0.0
478	  for v in features {
479	    let diff = v - mean
480	    sum_sq += diff * diff
481	  }
482	  let std = if sum_sq > 0.0 {
483	    (sum_sq / features.length().to_double()).sqrt()
484	  } else {
485	    1.0	<-- UNCOVERED
486	  }
487	  // 标准化
488	  let standardized : Array[Double] = []
489	  for v in features {
490	    standardized.push((v - mean) / std)
491	  }
492	  standardized
493	}
   	…

20 uncovered line(s) in src\process\ocr\connected_components.mbt:

 29	pub fn connected_components_analysis(
 30	  img : @types.Image,
 31	  connectivity? : Int = 8,
 32	) -> OcrConnectedComponentsResult raise @types.LoadError {
 33	  // 输入验证
 34	  if img.width <= 0 || img.height <= 0 {
 35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 36	      "connected_components_analysis: 图像尺寸必须大于0",
 37	    )
 38	  }
 39	  if img.channels != 1 {
 40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 41	      "connected_components_analysis: 仅支持单通道二值图",
 42	    )
 43	  }
 44	  let expected_size = img.width * img.height
 45	  if img.data.length() != expected_size {
 46	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 47	      "connected_components_analysis: 数据大小不匹配，期望 " +
 48	      expected_size.to_string() +	<-- UNCOVERED
 49	      "，实际 " +
 50	      img.data.length().to_string(),	<-- UNCOVERED
 51	    )
 52	  }
 53	  if connectivity != 4 && connectivity != 8 {
 54	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 55	      "connected_components_analysis: connectivity必须为4或8",
 56	    )
 57	  }
 58	
 59	  let w = img.width
 60	  let h = img.height
 61	  let n = w * h
 62	  let data = img.data
 63	  // 第一遍：临时标记
 64	  let labels : Array[Int] = Array::make(n, 0)
 65	  let mut next_label = 1
 66	  // 并查集（用于等价标签合并）
 67	  let parent : Array[Int] = Array::make(n + 1, 0)
 68	  for i in 0..<(n + 1) {
 69	    parent[i] = i
 70	  }
 71	  // find 函数（路径压缩）
 72	  fn find(x : Int, parent : Array[Int]) -> Int {
 73	    let mut root = x
 74	    while parent[root] != root {
 75	      root = parent[root]
 76	    }
 77	    // 路径压缩
 78	    let mut curr = x
 79	    while parent[curr] != curr {
 80	      let next = parent[curr]
 81	      parent[curr] = root
 82	      curr = next
 83	    }
 84	    root
 85	  }
 86	  // union 函数
 87	  fn union(x : Int, y : Int, parent : Array[Int]) -> Unit {
 88	    let rx = find(x, parent)
 89	    let ry = find(y, parent)
 90	    if rx != ry {
 91	      parent[rx] = ry
 92	    }
 93	  }
 94	  // 第一遍扫描
 95	  for y in 0..<h {
 96	    for x in 0..<w {
 97	      let idx = y * w + x
 98	      if data[idx].to_int() > 128 {
 99	        // 前景像素
100	        let neighbors : Array[Int] = []
101	        // 左邻域
102	        if x > 0 && labels[idx - 1] > 0 {
103	          neighbors.push(labels[idx - 1])
104	        }
105	        // 上邻域
106	        if y > 0 && labels[idx - w] > 0 {
107	          neighbors.push(labels[idx - w])
108	        }
109	        if connectivity == 8 {
110	          // 左上
111	          if x > 0 && y > 0 && labels[idx - w - 1] > 0 {
112	            neighbors.push(labels[idx - w - 1])
113	          }
114	          // 右上
115	          if x < w - 1 && y > 0 && labels[idx - w + 1] > 0 {
116	            neighbors.push(labels[idx - w + 1])
117	          }
118	        }
119	        if neighbors.length() == 0 {
120	          // 新标签
121	          labels[idx] = next_label
122	          next_label += 1
123	        } else {
124	          // 使用最小邻居标签
125	          let mut min_label = neighbors[0]
126	          for i in 1..<neighbors.length() {
127	            if neighbors[i] < min_label {
128	              min_label = neighbors[i]
129	            }
130	          }
131	          labels[idx] = min_label
132	          // 合并等价标签
133	          for label in neighbors {
134	            union(min_label, label, parent)
135	          }
136	        }
137	      }
138	    }
139	  }
140	  // 第二遍：应用并查集
141	  let final_labels : Array[Int] = Array::make(n, 0)
142	  let label_map : Array[Int] = Array::make(next_label, 0)
143	  let mut num_components = 0
144	  for i in 0..<n {
145	    if labels[i] > 0 {
146	      let root = find(labels[i], parent)
147	      if label_map[root] == 0 {
148	        num_components += 1
149	        label_map[root] = num_components
150	      }
151	      final_labels[i] = label_map[root]
152	    }
153	  }
154	  // 计算每个连通域的属性
155	  let components : Array[OcrConnectedComponent] = []
156	  for c in 1..<(num_components + 1) {
157	    let mut min_x = w
158	    let mut max_x = 0
159	    let mut min_y = h
160	    let mut max_y = 0
161	    let mut pixel_count = 0
162	    let mut sum_x = 0
163	    let mut sum_y = 0
164	    for y in 0..<h {
165	      for x in 0..<w {
166	        if final_labels[y * w + x] == c {
167	          pixel_count += 1
168	          sum_x += x
169	          sum_y += y
170	          if x < min_x {
171	            min_x = x
172	          }
173	          if x > max_x {
174	            max_x = x
175	          }
176	          if y < min_y {
177	            min_y = y
178	          }
179	          if y > max_y {
180	            max_y = y
181	          }
182	        }
183	      }
184	    }
185	    if pixel_count > 0 {
186	      let comp = OcrConnectedComponent::{
187	        label: c,
188	        x: min_x,
189	        y: min_y,
190	        width: max_x - min_x + 1,
191	        height: max_y - min_y + 1,
192	        area: (max_x - min_x + 1) * (max_y - min_y + 1),
193	        pixel_count,
194	        bounding_box: (min_x, min_y, max_x - min_x + 1, max_y - min_y + 1),
195	        centroid: (
196	          sum_x.to_double() / pixel_count.to_double(),
197	          sum_y.to_double() / pixel_count.to_double(),
198	        ),
199	      }
200	      components.push(comp)
201	    }
202	  }
203	  OcrConnectedComponentsResult::{
204	    labels: final_labels,
205	    num_components,
206	    components,
207	  }
208	}
   	…

212	pub fn filter_components_by_area(
213	  result : OcrConnectedComponentsResult,
214	  min_area : Int,
215	  max_area : Int,
216	) -> Array[OcrConnectedComponent] raise @types.LoadError {
217	  // 输入验证
218	  if result.components.length() == 0 {
219	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
220	      "filter_components_by_area: 连通域列表不能为空",
221	    )
222	  }
223	  if min_area < 0 {
224	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
225	      "filter_components_by_area: min_area不能为负数",
226	    )
227	  }
228	  if max_area < min_area {
229	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
230	      "filter_components_by_area: max_area不能小于min_area",
231	    )
232	  }
233	
234	  let filtered : Array[OcrConnectedComponent] = []
235	  for comp in result.components {
236	    if comp.pixel_count >= min_area && comp.pixel_count <= max_area {
237	      filtered.push(comp)
238	    }
239	  }
240	  filtered
241	}
   	…

245	pub fn filter_components_by_aspect_ratio(
246	  result : OcrConnectedComponentsResult,
247	  min_ratio : Double,
248	  max_ratio : Double,
249	) -> Array[OcrConnectedComponent] raise @types.LoadError {
250	  // 输入验证
251	  if result.components.length() == 0 {
252	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
253	      "filter_components_by_aspect_ratio: 连通域列表不能为空",
254	    )
255	  }
256	  if min_ratio < 0.0 {
257	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
258	      "filter_components_by_aspect_ratio: min_ratio不能为负数",
259	    )
260	  }
261	  if max_ratio < min_ratio {
262	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
263	      "filter_components_by_aspect_ratio: max_ratio不能小于min_ratio",
264	    )
265	  }
266	
267	  let filtered : Array[OcrConnectedComponent] = []
268	  for comp in result.components {
269	    if comp.height > 0 {
270	      let ratio = comp.width.to_double() / comp.height.to_double()
271	      if ratio >= min_ratio && ratio <= max_ratio {
272	        filtered.push(comp)
273	      }
274	    }
275	  }
276	  filtered
277	}
   	…

281	pub fn filter_components_by_position(
282	  result : OcrConnectedComponentsResult,
283	  x : Int,
284	  y : Int,
285	  width : Int,
286	  height : Int,
287	) -> Array[OcrConnectedComponent] raise @types.LoadError {
288	  // 输入验证
289	  if result.components.length() == 0 {	<-- UNCOVERED
290	    raise @types.LoadError::DecodeFailed(
291	      "filter_components_by_position: 连通域列表不能为空",
292	    )
293	  }
294	  if width <= 0 || height <= 0 {
295	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
296	      "filter_components_by_position: 矩形尺寸必须大于0",
297	    )
298	  }
299	
300	  let filtered : Array[OcrConnectedComponent] = []
301	  for comp in result.components {
302	    let cx = comp.x + comp.width / 2	<-- UNCOVERED
303	    let cy = comp.y + comp.height / 2
304	    if cx >= x && cx <= x + width && cy >= y && cy <= y + height {
305	      filtered.push(comp)	<-- UNCOVERED
306	    }
307	  }
308	  filtered
309	}
   	…

313	pub fn merge_adjacent_components(
314	  components : Array[OcrConnectedComponent],
315	  max_gap : Int,
316	) -> Array[OcrConnectedComponent] raise @types.LoadError {
317	  // 输入验证
318	  if components.length() == 0 {
319	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
320	      "merge_adjacent_components: 连通域列表不能为空",
321	    )
322	  }
323	  if max_gap < 0 {
324	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
325	      "merge_adjacent_components: max_gap不能为负数",
326	    )
327	  }
328	
329	  let merged : Array[OcrConnectedComponent] = []
330	  let mut current = components[0]
331	  for i in 1..<components.length() {
332	    let next = components[i]
333	    // 检查是否相邻（水平方向）
334	    let gap = next.x - (current.x + current.width)
335	    let vertical_overlap = min(current.y + current.height, next.y + next.height) -
336	      max(current.y, next.y)
337	    if gap <= max_gap && vertical_overlap > 0 {
338	      // 合并
339	      let new_x = min(current.x, next.x)
340	      let new_y = min(current.y, next.y)
341	      let new_max_x = max(current.x + current.width, next.x + next.width)
342	      let new_max_y = max(current.y + current.height, next.y + next.height)
343	      current = OcrConnectedComponent::{
344	        label: current.label,
345	        x: new_x,
346	        y: new_y,
347	        width: new_max_x - new_x,
348	        height: new_max_y - new_y,
349	        area: (new_max_x - new_x) * (new_max_y - new_y),
350	        pixel_count: current.pixel_count + next.pixel_count,
351	        bounding_box: (new_x, new_y, new_max_x - new_x, new_max_y - new_y),
352	        centroid: (
353	          (current.centroid.0 + next.centroid.0) / 2.0,
354	          (current.centroid.1 + next.centroid.1) / 2.0,
355	        ),
356	      }
357	    } else {
358	      merged.push(current)	<-- UNCOVERED
359	      current = next
360	    }
361	  }
362	  merged.push(current)
363	  merged
364	}
   	…

378	fn max(a : Int, b : Int) -> Int {
379	  if a > b {
380	    a	<-- UNCOVERED
381	  } else {
382	    b
383	  }
384	}
   	…

75 uncovered line(s) in src\process\ocr\deep_learning.mbt:

167	pub fn create_neural_layer(
168	  layer_type : DLLayerType,
169	  name : String,
170	  input_shape : Array[Int],
171	) -> DLNeuralLayer {
172	  let output_shape = match layer_type {	<-- UNCOVERED
173	    DLLayerType::Conv2D => [input_shape[0], input_shape[1], input_shape[2]]
174	    DLLayerType::MaxPool2D =>
175	      [input_shape[0], input_shape[1] / 2, input_shape[2] / 2]	<-- UNCOVERED
176	    DLLayerType::AvgPool2D =>
177	      [input_shape[0], input_shape[1] / 2, input_shape[2] / 2]	<-- UNCOVERED
178	    DLLayerType::Flatten => [input_shape[0] * input_shape[1] * input_shape[2]]
179	    DLLayerType::Dense => [input_shape[0]]
180	    _ => input_shape
181	  }
182	  DLNeuralLayer::{
183	    layer_type,
184	    name,
185	    config: "{}",
186	    weights: [],
187	    biases: [],
188	    input_shape,
189	    output_shape,
190	  }
191	}
   	…

195	pub fn normalize_image(
196	  image_data : Array[Double],
197	  mean : Double,
198	  std : Double,
199	) -> Array[Double] raise @types.LoadError {
200	  // 输入验证
201	  if image_data.length() == 0 {	<-- UNCOVERED
202	    raise @types.LoadError::DecodeFailed(
203	      "normalize_image: 图像数据不能为空",
204	    )
205	  }
206	  if std <= 0.0 {
207	    raise @types.LoadError::DecodeFailed("normalize_image: std必须大于0")	<-- UNCOVERED
208	  }
209	  let normalized : Array[Double] = []
210	  for pixel in image_data {
211	    normalized.push((pixel - mean) / std)	<-- UNCOVERED
212	  }
213	  normalized
214	}
   	…

218	pub fn resize_image_for_cnn(
219	  img : @types.Image,
220	  target_width : Int,
221	  target_height : Int,
222	) -> Array[Double] raise @types.LoadError {
223	  // 输入验证
224	  if img.width <= 0 || img.height <= 0 {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "resize_image_for_cnn: 图像尺寸必须大于0",
227	    )
228	  }
229	  if img.channels < 1 || img.channels > 4 {
230	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
231	      "resize_image_for_cnn: 通道数必须在1-4之间",
232	    )
233	  }
234	  let expected_size = img.width * img.height * img.channels
235	  if img.data.length() != expected_size {
236	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
237	      "resize_image_for_cnn: 数据大小不匹配，期望 " +
238	      expected_size.to_string() +	<-- UNCOVERED
239	      "，实际 " +
240	      img.data.length().to_string(),	<-- UNCOVERED
241	    )
242	  }
243	  if target_width <= 0 || target_height <= 0 {
244	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
245	      "resize_image_for_cnn: 目标尺寸必须大于0",
246	    )
247	  }
248	  let result : Array[Double] = []
249	  let src_w = img.width
250	  let src_h = img.height
251	  let x_scale = src_w.to_double() / target_width.to_double()
252	  let y_scale = src_h.to_double() / target_height.to_double()
253	
254	  for y in 0..<target_height {
255	    for x in 0..<target_width {
256	      let src_x = (x.to_double() * x_scale).to_int()
257	      let src_y = (y.to_double() * y_scale).to_int()
258	      let idx = src_y * src_w + src_x
259	      if idx < img.data.length() {
260	        // 归一化到 0-1
261	        result.push(img.data[idx].to_double() / 255.0)
262	      } else {
263	        result.push(0.0)	<-- UNCOVERED
264	      }
265	    }
266	  }
267	  result
268	}
   	…

272	pub fn relu(x : Double) -> Double {
273	  if x > 0.0 {
274	    x
275	  } else {
276	    0.0	<-- UNCOVERED
277	  }
278	}
   	…

282	fn approx_exp(x : Double) -> Double {
283	  // 范围限制
284	  let t = if x > 20.0 { 20.0 } else if x < -20.0 { -20.0 } else { x }	<-- UNCOVERED
285	  let mut result = 1.0
286	  let mut term = 1.0
287	  for i in 1..<20 {
288	    term = term * t / i.to_double()
289	    result = result + term
290	  }
291	  result
292	}
   	…

296	fn approx_tanh(x : Double) -> Double {
297	  let e2x = approx_exp(2.0 * x)	<-- UNCOVERED
298	  (e2x - 1.0) / (e2x + 1.0)
299	}
   	…

303	fn approx_log(x : Double) -> Double {
304	  if x <= 0.0 {
305	    -100.0	<-- UNCOVERED
306	  } else {
307	    // 简单近似
308	    let mut result = 0.0
309	    let mut t = (x - 1.0) / (x + 1.0)
310	    let t2 = t * t
311	    for i in 0..<20 {
312	      result = result + t / (2.0 * i.to_double() + 1.0)
313	      t = t * t2
314	    }
315	    2.0 * result
316	  }
317	}
   	…

321	pub fn sigmoid(x : Double) -> Double {
322	  1.0 / (1.0 + approx_exp(-x))	<-- UNCOVERED
323	}
   	…

350	pub fn conv2d(
351	  input : Array[Double],
352	  in_h : Int,
353	  in_w : Int,
354	  in_c : Int,
355	  kernel : Array[Double],
356	  out_c : Int,
357	  k_size : Int,
358	  stride : Int,
359	  padding : Int,
360	) -> Array[Double] raise @types.LoadError {
361	  // 输入验证
362	  if input.length() == 0 {
363	    raise @types.LoadError::DecodeFailed("conv2d: 输入数据不能为空")	<-- UNCOVERED
364	  }
365	  if kernel.length() == 0 {
366	    raise @types.LoadError::DecodeFailed("conv2d: 卷积核不能为空")	<-- UNCOVERED
367	  }
368	  if in_h <= 0 || in_w <= 0 {
369	    raise @types.LoadError::DecodeFailed("conv2d: 输入尺寸必须大于0")	<-- UNCOVERED
370	  }
371	  if in_c <= 0 {
372	    raise @types.LoadError::DecodeFailed("conv2d: 输入通道数必须大于0")	<-- UNCOVERED
373	  }
374	  if out_c <= 0 {
375	    raise @types.LoadError::DecodeFailed("conv2d: 输出通道数必须大于0")	<-- UNCOVERED
376	  }
377	  if k_size <= 0 {
378	    raise @types.LoadError::DecodeFailed("conv2d: 卷积核尺寸必须大于0")	<-- UNCOVERED
379	  }
380	  if stride <= 0 {
381	    raise @types.LoadError::DecodeFailed("conv2d: 步长必须大于0")	<-- UNCOVERED
382	  }
383	  if padding < 0 {
384	    raise @types.LoadError::DecodeFailed("conv2d: 填充不能为负数")	<-- UNCOVERED
385	  }
386	  let expected_input_size = in_h * in_w * in_c
387	  if input.length() != expected_input_size {
388	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
389	      "conv2d: 输入数据大小不匹配，期望 " +
390	      expected_input_size.to_string() +	<-- UNCOVERED
391	      "，实际 " +
392	      input.length().to_string(),	<-- UNCOVERED
393	    )
394	  }
395	  let out_h = (in_h + 2 * padding - k_size) / stride + 1
396	  let out_w = (in_w + 2 * padding - k_size) / stride + 1
397	  let output : Array[Double] = []
398	
399	  for oc in 0..<out_c {
400	    for oh in 0..<out_h {
401	      for ow in 0..<out_w {
402	        let mut sum = 0.0
403	        for ic in 0..<in_c {
404	          for kh in 0..<k_size {
405	            for kw in 0..<k_size {
406	              let ih = oh * stride - padding + kh
407	              let iw = ow * stride - padding + kw
408	              if ih >= 0 && ih < in_h && iw >= 0 && iw < in_w {
409	                let input_idx = ic * in_h * in_w + ih * in_w + iw
410	                let kernel_idx = oc * in_c * k_size * k_size +
411	                  ic * k_size * k_size +
412	                  kh * k_size +
413	                  kw
414	                if input_idx < input.length() && kernel_idx < kernel.length() {
415	                  sum += input[input_idx] * kernel[kernel_idx]
416	                }
417	              }
418	            }
419	          }
420	        }
421	        output.push(relu(sum))
422	      }
423	    }
424	  }
425	  output
426	}
   	…

430	pub fn max_pool2d(
431	  input : Array[Double],
432	  in_h : Int,
433	  in_w : Int,
434	  channels : Int,
435	  pool_size : Int,
436	) -> Array[Double] raise @types.LoadError {
437	  // 输入验证
438	  if input.length() == 0 {
439	    raise @types.LoadError::DecodeFailed("max_pool2d: 输入数据不能为空")	<-- UNCOVERED
440	  }
441	  if in_h <= 0 || in_w <= 0 {
442	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
443	      "max_pool2d: 输入尺寸必须大于0",
444	    )
445	  }
446	  if channels <= 0 {
447	    raise @types.LoadError::DecodeFailed("max_pool2d: 通道数必须大于0")	<-- UNCOVERED
448	  }
449	  if pool_size <= 0 {
450	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
451	      "max_pool2d: 池化尺寸必须大于0",
452	    )
453	  }
454	  let expected_input_size = in_h * in_w * channels
455	  if input.length() != expected_input_size {
456	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
457	      "max_pool2d: 输入数据大小不匹配，期望 " +
458	      expected_input_size.to_string() +	<-- UNCOVERED
459	      "，实际 " +
460	      input.length().to_string(),	<-- UNCOVERED
461	    )
462	  }
463	  let out_h = in_h / pool_size
464	  let out_w = in_w / pool_size
465	  let output : Array[Double] = []
466	
467	  for c in 0..<channels {
468	    for oh in 0..<out_h {
469	      for ow in 0..<out_w {
470	        let mut max_val = -1.0e10
471	        for ph in 0..<pool_size {
472	          for pw in 0..<pool_size {
473	            let ih = oh * pool_size + ph
474	            let iw = ow * pool_size + pw
475	            let idx = c * in_h * in_w + ih * in_w + iw
476	            if idx < input.length() && input[idx] > max_val {
477	              max_val = input[idx]
478	            }
479	          }
480	        }
481	        output.push(max_val)
482	      }
483	    }
484	  }
485	  output
486	}
   	…

490	pub fn dense_forward(
491	  input : Array[Double],
492	  weights : Array[Double],
493	  biases : Array[Double],
494	  in_features : Int,
495	  out_features : Int,
496	  activation : String,
497	) -> Array[Double] {
498	  let output : Array[Double] = []
499	  for o in 0..<out_features {
500	    let mut sum = biases[o]
501	    for i in 0..<in_features {
502	      let w_idx = o * in_features + i
503	      if w_idx < weights.length() && i < input.length() {
504	        sum += input[i] * weights[w_idx]
505	      }
506	    }
507	    let activated = match activation {
508	      "relu" => relu(sum)	<-- UNCOVERED
509	      "sigmoid" => sigmoid(sum)
510	      "tanh" => approx_tanh(sum)
511	      _ => sum
512	    }
513	    output.push(activated)
514	  }
515	  output
516	}
   	…

520	pub fn cnn_infer(
521	  image_data : Array[Double],
522	  config : DLCnnModelConfig,
523	  labels : Array[String],
524	) -> DLInferenceResult {
525	  let start_time = 0.0
526	
527	  // 简化版推理：使用模板匹配代替真实 CNN
528	  // 实际部署时应加载预训练权重
529	  let mut best_class = 0
530	  let mut best_score = -1.0
531	  let scores : Array[(Int, Double)] = []
532	
533	  for c in 0..<config.num_classes {
534	    // 简化：计算图像与模板的相似度
535	    let mut score = 0.0
536	    for i in 0..<image_data.length() {
537	      score += image_data[i]
538	    }
539	    score = score / image_data.length().to_double()
540	    // 添加类别相关的伪分数
541	    score = score + c.to_double() * 0.001
542	    scores.push((c, score))
543	    if score > best_score {
544	      best_score = score
545	      best_class = c
546	    }
547	  }
548	
549	  // 计算置信度（softmax 归一化）
550	  let logits : Array[Double] = []
551	  for _tup0 in scores {
552	    let (_, s) = _tup0
553	    logits.push(s)
554	  }
555	  let probs = softmax(logits)
556	  let confidence = probs[best_class]
557	
558	  // Top-K 预测
559	  let top_k : Array[(Int, Double)] = []
560	  let sorted_scores = scores
561	  // 简单排序（冒泡）
562	  for i in 0..<sorted_scores.length() {
563	    for j in 0..<(sorted_scores.length() - i - 1) {
564	      if sorted_scores[j].1 < sorted_scores[j + 1].1 {
565	        let temp = sorted_scores[j]
566	        sorted_scores[j] = sorted_scores[j + 1]
567	        sorted_scores[j + 1] = temp
568	      }
569	    }
570	  }
571	  for
572	    i in 0..<(if 5 < sorted_scores.length() {
573	      5
574	    } else {
575	      sorted_scores.length()
576	    }) {
577	    top_k.push(sorted_scores[i])
578	  }
579	
580	  let end_time = 0.0
581	
582	  DLInferenceResult::{
583	    predicted_class: best_class,
584	    predicted_label: if best_class < labels.length() {
585	      labels[best_class]
586	    } else {
587	      "unknown"	<-- UNCOVERED
588	    },
589	    confidence,
590	    top_k_predictions: top_k,
591	    inference_time_ms: end_time - start_time,
592	  }
593	}
   	…

597	pub fn create_training_sample(
598	  img : @types.Image,
599	  label : Int,
600	  label_text : String,
601	) -> DLTrainingSample raise @types.LoadError {
602	  // 输入验证
603	  if img.width <= 0 || img.height <= 0 {
604	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
605	      "create_training_sample: 图像尺寸必须大于0",
606	    )
607	  }
608	  if label < 0 {
609	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
610	      "create_training_sample: 标签不能为负数",
611	    )
612	  }
613	  let normalized = resize_image_for_cnn(img, 32, 32)
614	  DLTrainingSample::{
615	    image_data: normalized,
616	    label,
617	    label_text,
618	    width: 32,
619	    height: 32,
620	  }
621	}
   	…

633	pub fn create_training_history(
634	  epoch : Int,
635	  train_loss : Double,
636	  train_accuracy : Double,
637	  val_loss : Double,
638	  val_accuracy : Double,
639	  learning_rate : Double,
640	  time_ms : Double,
641	) -> DLTrainingHistory {
642	  DLTrainingHistory::{	<-- UNCOVERED
643	    epoch,
644	    train_loss,
645	    train_accuracy,
646	    val_loss,
647	    val_accuracy,
648	    learning_rate,
649	    time_ms,
650	  }
651	}
   	…

655	pub fn generate_training_report(
656	  history : Array[DLTrainingHistory],
657	  config : DLCnnModelConfig,
658	) -> String {
659	  let mut report = "## CNN 模型训练报告\n\n"	<-- UNCOVERED
660	  report = report + "### 模型配置\n\n"
661	  report = report + "| 参数 | 值 |\n"
662	  report = report + "|------|------|\n"
663	  report = report + "| 模型名称 | " + config.model_name + " |\n"
664	  report = report +
665	    "| 输入尺寸 | " +
666	    config.input_width.to_string() +	<-- UNCOVERED
667	    "x" +
668	    config.input_height.to_string() +	<-- UNCOVERED
669	    " |\n"
670	  report = report + "| 类别数 | " + config.num_classes.to_string() + " |\n"	<-- UNCOVERED
671	  report = report +
672	    "| 卷积层数 | " +
673	    config.conv_layers.length().to_string() +	<-- UNCOVERED
674	    " |\n"
675	  report = report +
676	    "| 全连接层数 | " +
677	    config.dense_layers.length().to_string() +	<-- UNCOVERED
678	    " |\n"
679	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"	<-- UNCOVERED
680	  report = report + "| 批大小 | " + config.batch_size.to_string() + " |\n"
681	  report = report + "| 训练轮数 | " + config.epochs.to_string() + " |\n\n"
682	
683	  report = report + "### 训练历史\n\n"
684	  report = report +
685	    "| Epoch | Train Loss | Train Acc | Val Loss | Val Acc | Time |\n"
686	  report = report +
687	    "|-------|------------|-----------|----------|---------|------|\n"
688	  for h in history {
689	    report = report + "| " + h.epoch.to_string() + " | "	<-- UNCOVERED
690	    report = report + h.train_loss.to_string() + " | "
691	    report = report + (h.train_accuracy * 100.0).to_string() + "% | "
692	    report = report + h.val_loss.to_string() + " | "
693	    report = report + (h.val_accuracy * 100.0).to_string() + "% | "
694	    report = report + h.time_ms.to_string() + "ms |\n"
695	  }
696	
697	  if history.length() > 0 {	<-- UNCOVERED
698	    let last = history[history.length() - 1]
699	    report = report + "\n### 最终结果\n\n"
700	    report = report +
701	      "- 训练准确率: " +
702	      (last.train_accuracy * 100.0).to_string() +	<-- UNCOVERED
703	      "%\n"
704	    report = report +
705	      "- 验证准确率: " +
706	      (last.val_accuracy * 100.0).to_string() +	<-- UNCOVERED
707	      "%\n"
708	    report = report + "- 训练损失: " + last.train_loss.to_string() + "\n"	<-- UNCOVERED
709	    report = report + "- 验证损失: " + last.val_loss.to_string() + "\n"
710	  }
711	
712	  report
713	}
   	…

717	pub fn save_model(
718	  config : DLCnnModelConfig,
719	  weights : Array[Double],
720	  path : String,
721	) -> Bool {
722	  ignore(config)	<-- UNCOVERED
723	  ignore(path)
724	  ignore(weights)
725	  // 简化版：实际应序列化权重
726	  true
727	}
   	…

731	pub fn load_model(path : String) -> DLCnnModelConfig? {
732	  ignore(path)	<-- UNCOVERED
733	  // 简化版：实际应反序列化权重
734	  None
735	}
   	…

739	pub fn create_feature_extractor(
740	  model_name : String,
741	  feature_dim : Int,
742	) -> DLFeatureExtractor {
743	  DLFeatureExtractor::{	<-- UNCOVERED
744	    model_name,
745	    feature_dim,
746	    layer_name: "conv3",
747	    weights: [],
748	  }
749	}
   	…

753	pub fn extract_features(
754	  extractor : DLFeatureExtractor,
755	  img : @types.Image,
756	) -> Array[Double] {
757	  // 简化版：使用图像统计特征代替 CNN 特征
758	  let features : Array[Double] = []	<-- UNCOVERED
759	  let w = img.width
760	  let h = img.height
761	
762	  // 1. 均值
763	  let mut sum = 0.0
764	  for pixel in img.data {
765	    sum += pixel.to_double()	<-- UNCOVERED
766	  }
767	  features.push(sum / img.data.length().to_double() / 255.0)	<-- UNCOVERED
768	
769	  // 2. 标准差
770	  let mean = sum / img.data.length().to_double()	<-- UNCOVERED
771	  let mut var_sum = 0.0
772	  for pixel in img.data {
773	    let diff = pixel.to_double() - mean	<-- UNCOVERED
774	    var_sum += diff * diff
775	  }
776	  features.push((var_sum / img.data.length().to_double()).sqrt() / 255.0)	<-- UNCOVERED
777	
778	  // 3. 水平投影
779	  for y in 0..<(if 8 < h { 8 } else { h }) {	<-- UNCOVERED
780	    let mut row_sum = 0.0
781	    for x in 0..<w {
782	      row_sum += img.data[y * w + x].to_double()	<-- UNCOVERED
783	    }
784	    features.push(row_sum / w.to_double() / 255.0)	<-- UNCOVERED
785	  }
786	
787	  // 4. 垂直投影
788	  for x in 0..<(if 8 < w { 8 } else { w }) {	<-- UNCOVERED
789	    let mut col_sum = 0.0
790	    for y in 0..<h {
791	      col_sum += img.data[y * w + x].to_double()	<-- UNCOVERED
792	    }
793	    features.push(col_sum / h.to_double() / 255.0)	<-- UNCOVERED
794	  }
795	
796	  // 填充到 feature_dim
797	  while features.length() < extractor.feature_dim {	<-- UNCOVERED
798	    features.push(0.0)
799	  }
800	
801	  features
802	}
   	…

806	pub fn cosine_similarity(
807	  feat1 : Array[Double],
808	  feat2 : Array[Double],
809	) -> Double {
810	  let mut dot = 0.0	<-- UNCOVERED
811	  let mut norm1 = 0.0
812	  let mut norm2 = 0.0
813	  let len = if feat1.length() < feat2.length() {	<-- UNCOVERED
814	    feat1.length()
815	  } else {
816	    feat2.length()	<-- UNCOVERED
817	  }
818	  for i in 0..<len {
819	    dot += feat1[i] * feat2[i]	<-- UNCOVERED
820	    norm1 += feat1[i] * feat1[i]
821	    norm2 += feat2[i] * feat2[i]
822	  }
823	  if norm1 > 0.0 && norm2 > 0.0 {
824	    dot / (norm1.sqrt() * norm2.sqrt())	<-- UNCOVERED
825	  } else {
826	    0.0	<-- UNCOVERED
827	  }
828	}
   	…

7 uncovered line(s) in src\process\ocr\deep_learning_v13.mbt:

149	pub fn cnn_train_step(
150	  input : Array[Double],
151	  target : Int,
152	  conv_weights : Array[Double],
153	  conv_bias : Array[Double],
154	  dense_weights : Array[Double],
155	  dense_bias : Array[Double],
156	  in_h : Int,
157	  in_w : Int,
158	  in_c : Int,
159	  conv_out_c : Int,
160	  k_size : Int,
161	  num_classes : Int,
162	  learning_rate : Double,
163	) -> (Array[Double], Array[Double], Array[Double], Array[Double], Double) raise @types.LoadError {
164	  if input.length() == 0 {
165	    raise @types.LoadError::DecodeFailed("cnn_train_step: 输入不能为空")
166	  }
167	  if target < 0 || target >= num_classes {
168	    raise @types.LoadError::DecodeFailed("cnn_train_step: 目标标签越界")	<-- UNCOVERED
169	  }
170	  // 前向传播
171	  let conv_out = conv2d(
172	    input, in_h, in_w, in_c, conv_weights, conv_out_c, k_size, 1, 0,
173	  )
174	  let conv_out_h = in_h - k_size + 1
175	  let conv_out_w = in_w - k_size + 1
176	  let relu_out : Array[Double] = []
177	  for c in 0..<conv_out_c {
178	    for i in 0..<(conv_out_h * conv_out_w) {
179	      let idx = c * conv_out_h * conv_out_w + i
180	      let val = conv_out[idx] + conv_bias[c]
181	      relu_out.push(if val > 0.0 { val } else { 0.0 })
182	    }
183	  }
184	  let pool_out = max_pool2d(relu_out, conv_out_h, conv_out_w, conv_out_c, 2)
185	  let pool_h = conv_out_h / 2
186	  let pool_w = conv_out_w / 2
187	  let flat_size = pool_h * pool_w * conv_out_c
188	  let logits = dense_forward(
189	    pool_out, dense_weights, dense_bias, flat_size, num_classes, "none",
190	  )
191	  let probs = softmax(logits)
192	  let loss = cross_entropy_loss(probs, target)
193	  // 反向传播
194	  let grad_logits = softmax_cross_entropy_backward(probs, target)
195	  let (grad_pool, _, grad_dense_bias) = dense_backward(
196	    grad_logits, dense_weights, flat_size, num_classes,
197	  )
198	  let grad_relu = max_pool2d_backward(
199	    grad_pool, relu_out, conv_out_h, conv_out_w, conv_out_c, 2,
200	  )
201	  let grad_conv = relu_backward(grad_relu, relu_out)
202	  // 更新偏置
203	  let new_dense_bias = update_weights_sgd(
204	    dense_bias, grad_dense_bias, learning_rate,
205	  )
206	  let new_conv_bias : Array[Double] = []
207	  for c in 0..<conv_out_c {
208	    let mut grad_sum = 0.0
209	    for i in 0..<(conv_out_h * conv_out_w) {
210	      let idx = c * conv_out_h * conv_out_w + i
211	      grad_sum += grad_conv[idx]
212	    }
213	    new_conv_bias.push(conv_bias[c] - learning_rate * grad_sum)
214	  }
215	  // 卷积权重更新（简化）
216	  let new_conv_weights : Array[Double] = []
217	  for i in 0..<conv_weights.length() {
218	    let mut grad_w = 0.0
219	    for j in 0..<input.length() {
220	      if j < grad_conv.length() {
221	        grad_w += input[j] * grad_conv[j]
222	      }
223	    }
224	    grad_w = grad_w / input.length().to_double()
225	    new_conv_weights.push(conv_weights[i] - learning_rate * grad_w)
226	  }
227	  // Dense 权重更新
228	  let new_dense_weights : Array[Double] = []
229	  for i in 0..<dense_weights.length() {
230	    let row = i / num_classes
231	    let col = i % num_classes
232	    let grad_w = pool_out[row] * grad_logits[col]
233	    new_dense_weights.push(dense_weights[i] - learning_rate * grad_w)
234	  }
235	  (new_conv_weights, new_conv_bias, new_dense_weights, new_dense_bias, loss)
236	}
   	…

240	pub fn cnn_train(
241	  samples : Array[DLTrainingSample],
242	  conv_weights : Array[Double],
243	  conv_bias : Array[Double],
244	  dense_weights : Array[Double],
245	  dense_bias : Array[Double],
246	  in_h : Int,
247	  in_w : Int,
248	  in_c : Int,
249	  conv_out_c : Int,
250	  k_size : Int,
251	  num_classes : Int,
252	  learning_rate : Double,
253	  epochs : Int,
254	) -> (
255	  Array[Double],
256	  Array[Double],
257	  Array[Double],
258	  Array[Double],
259	  Array[DLTrainingHistory],
260	) raise @types.LoadError {
261	  if samples.length() == 0 {
262	    raise @types.LoadError::DecodeFailed("cnn_train: 训练样本不能为空")	<-- UNCOVERED
263	  }
264	  if epochs <= 0 {
265	    raise @types.LoadError::DecodeFailed("cnn_train: epoch 数量必须大于0")	<-- UNCOVERED
266	  }
267	  let mut cw = conv_weights
268	  let mut cb = conv_bias
269	  let mut dw = dense_weights
270	  let mut db = dense_bias
271	  let history : Array[DLTrainingHistory] = []
272	  for epoch in 0..<epochs {
273	    let mut total_loss = 0.0
274	    let mut correct = 0
275	    for s in 0..<samples.length() {
276	      let sample = samples[s]
277	      let result = cnn_train_step(
278	        sample.image_data,
279	        sample.label,
280	        cw,
281	        cb,
282	        dw,
283	        db,
284	        in_h,
285	        in_w,
286	        in_c,
287	        conv_out_c,
288	        k_size,
289	        num_classes,
290	        learning_rate,
291	      )
292	      cw = result.0
293	      cb = result.1
294	      dw = result.2
295	      db = result.3
296	      total_loss += result.4
297	      if result.4 < 1.0 {
298	        correct += 1
299	      }
300	    }
301	    let avg_loss = total_loss / samples.length().to_double()
302	    let accuracy = correct.to_double() / samples.length().to_double()
303	    history.push(DLTrainingHistory::{
304	      epoch: epoch + 1,
305	      train_loss: avg_loss,
306	      train_accuracy: accuracy,
307	      val_loss: avg_loss,
308	      val_accuracy: accuracy,
309	      learning_rate,
310	      time_ms: 0.0,
311	    })
312	  }
313	  (cw, cb, dw, db, history)
314	}
   	…

318	pub fn cnn_recognize_char(
319	  img : @types.Image,
320	  config : DLCnnModelConfig,
321	  labels : Array[String],
322	) -> DLInferenceResult raise @types.LoadError {
323	  if img.width <= 0 || img.height <= 0 {
324	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
325	      "cnn_recognize_char: 图像尺寸必须大于0",
326	    )
327	  }
328	  if img.channels < 1 || img.channels > 4 {
329	    raise @types.LoadError::DecodeFailed("cnn_recognize_char: 通道数非法")	<-- UNCOVERED
330	  }
331	  if labels.length() != config.num_classes {
332	    raise @types.LoadError::DecodeFailed(
333	      "cnn_recognize_char: 标签数量不匹配",
334	    )
335	  }
336	  let image_data = resize_image_for_cnn(
337	    img,
338	    config.input_width,
339	    config.input_height,
340	  )
341	  cnn_infer(image_data, config, labels)
342	}
   	…

369	pub fn evaluate_cnn_model(
370	  test_samples : Array[DLTrainingSample],
371	  config : DLCnnModelConfig,
372	  labels : Array[String],
373	) -> (Double, Double) raise @types.LoadError {
374	  if test_samples.length() == 0 {
375	    raise @types.LoadError::DecodeFailed(
376	      "evaluate_cnn_model: 测试样本不能为空",
377	    )
378	  }
379	  if labels.length() != config.num_classes {
380	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
381	      "evaluate_cnn_model: 标签数量不匹配",
382	    )
383	  }
384	  let mut correct = 0
385	  let mut total_conf = 0.0
386	  for i in 0..<test_samples.length() {
387	    let sample = test_samples[i]
388	    let img = @types.Image::{
389	      width: sample.width,
390	      height: sample.height,
391	      channels: 1,
392	      data: Bytes::from_array(Array::make(sample.width * sample.height, 128)),
393	    }
394	    let result = cnn_recognize_char(img, config, labels)
395	    total_conf += result.confidence
396	    if result.predicted_class == sample.label {
397	      correct += 1
398	    }
399	  }
400	  let accuracy = correct.to_double() / test_samples.length().to_double()
401	  let avg_conf = total_conf / test_samples.length().to_double()
402	  (accuracy, avg_conf)
403	}
   	…

407	pub fn cnn_confusion_matrix(
408	  test_samples : Array[DLTrainingSample],
409	  config : DLCnnModelConfig,
410	  labels : Array[String],
411	) -> Array[Array[Int]] raise @types.LoadError {
412	  if test_samples.length() == 0 {
413	    raise @types.LoadError::DecodeFailed(
414	      "cnn_confusion_matrix: 测试样本不能为空",
415	    )
416	  }
417	  if labels.length() != config.num_classes {
418	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
419	      "cnn_confusion_matrix: 标签数量不匹配",
420	    )
421	  }
422	  let n = config.num_classes
423	  let matrix : Array[Array[Int]] = []
424	  for _ in 0..<n {
425	    let row : Array[Int] = Array::make(n, 0)
426	    matrix.push(row)
427	  }
428	  for i in 0..<test_samples.length() {
429	    let sample = test_samples[i]
430	    let img = @types.Image::{
431	      width: sample.width,
432	      height: sample.height,
433	      channels: 1,
434	      data: Bytes::from_array(Array::make(sample.width * sample.height, 128)),
435	    }
436	    let result = cnn_recognize_char(img, config, labels)
437	    if sample.label < n && result.predicted_class < n {
438	      matrix[sample.label][result.predicted_class] += 1
439	    }
440	  }
441	  matrix
442	}
   	…

16 uncovered line(s) in src\process\ocr\deskew.mbt:

19	pub fn detect_skew_angle(img : @types.Image) -> Double raise @types.LoadError {
20	  // 输入验证
21	  if img.width <= 0 || img.height <= 0 {
22	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
23	      "detect_skew_angle: 图像尺寸必须大于0",
24	    )
25	  }
26	  if img.channels != 1 {
27	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
28	      "detect_skew_angle: 仅支持单通道灰度图",
29	    )
30	  }
31	  let expected_size = img.width * img.height
32	  if img.data.length() != expected_size {
33	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
34	      "detect_skew_angle: 数据大小不匹配，期望 " +
35	      expected_size.to_string() +	<-- UNCOVERED
36	      "，实际 " +
37	      img.data.length().to_string(),	<-- UNCOVERED
38	    )
39	  }
40	
41	  let w = img.width
42	  let h = img.height
43	  let data = img.data
44	  // 在 -15 到 +15 度范围内搜索最佳倾斜角度
45	  let mut best_angle = 0.0
46	  let mut best_variance = 0.0
47	  let step = 0.5 // 0.5 度步长
48	  let mut angle = -15.0
49	  while angle <= 15.0 {
50	    let variance = projection_variance_at_angle(data, w, h, angle)
51	    if variance > best_variance {
52	      best_variance = variance
53	      best_angle = angle
54	    }
55	    angle += step
56	  }
57	  best_angle
58	}
  	…

 62	fn projection_variance_at_angle(
 63	  data : Bytes,
 64	  width : Int,
 65	  height : Int,
 66	  angle_deg : Double,
 67	) -> Double {
 68	  let angle_rad = angle_deg * 3.141592653589793 / 180.0
 69	  let cos_a = @math.cos(angle_rad)
 70	  let sin_a = @math.sin(angle_rad)
 71	  // 计算旋转后的图像尺寸
 72	  let new_h = (height.to_double() * cos_a.abs() +
 73	    width.to_double() * sin_a.abs()).to_int() +
 74	    1
 75	  // 计算水平投影
 76	  let projection : Array[Int] = Array::make(new_h, 0)
 77	  for y in 0..<height {
 78	    for x in 0..<width {
 79	      if data[y * width + x].to_int() < 128 {
 80	        // 黑色像素（文字）
 81	        let new_y = ((x.to_double() - width.to_double() / 2.0) * sin_a +
 82	        (y.to_double() - height.to_double() / 2.0) * cos_a +
 83	        new_h.to_double() / 2.0).to_int()
 84	        if new_y >= 0 && new_y < new_h {
 85	          projection[new_y] += 1
 86	        }
 87	      }
 88	    }
 89	  }
 90	  // 计算投影的方差
 91	  let mut sum = 0
 92	  let mut sum_sq = 0
 93	  let mut count = 0
 94	  for i in 0..<new_h {
 95	    sum += projection[i]
 96	    sum_sq += projection[i] * projection[i]
 97	    count += 1
 98	  }
 99	  if count == 0 {
100	    return 0.0	<-- UNCOVERED
101	  }
102	  let mean = sum.to_double() / count.to_double()
103	  let variance = sum_sq.to_double() / count.to_double() - mean * mean
104	  variance
105	}
   	…

109	pub fn deskew(img : @types.Image) -> @types.Image raise @types.LoadError {
110	  // 输入验证
111	  if img.width <= 0 || img.height <= 0 {
112	    raise @types.LoadError::DecodeFailed("deskew: 图像尺寸必须大于0")	<-- UNCOVERED
113	  }
114	  if img.channels != 1 {
115	    raise @types.LoadError::DecodeFailed("deskew: 仅支持单通道灰度图")	<-- UNCOVERED
116	  }
117	  let expected_size = img.width * img.height
118	  if img.data.length() != expected_size {
119	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
120	      "deskew: 数据大小不匹配，期望 " +
121	      expected_size.to_string() +	<-- UNCOVERED
122	      "，实际 " +
123	      img.data.length().to_string(),	<-- UNCOVERED
124	    )
125	  }
126	
127	  let angle = detect_skew_angle(img)
128	  rotate_document(img, -angle)
129	}
   	…

134	pub fn rotate_document(
135	  img : @types.Image,
136	  angle : Double,
137	) -> @types.Image raise @types.LoadError {
138	  // 输入验证
139	  if img.width <= 0 || img.height <= 0 {
140	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
141	      "rotate_document: 图像尺寸必须大于0",
142	    )
143	  }
144	  if img.channels < 1 || img.channels > 4 {
145	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
146	      "rotate_document: 通道数必须在1-4之间",
147	    )
148	  }
149	  let expected_size = img.width * img.height * img.channels
150	  if img.data.length() != expected_size {
151	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
152	      "rotate_document: 数据大小不匹配，期望 " +
153	      expected_size.to_string() +	<-- UNCOVERED
154	      "，实际 " +
155	      img.data.length().to_string(),	<-- UNCOVERED
156	    )
157	  }
158	
159	  let w = img.width
160	  let h = img.height
161	  let ch = img.channels
162	  let angle_rad = angle * 3.141592653589793 / 180.0
163	  let cos_a = @math.cos(angle_rad)
164	  let sin_a = @math.sin(angle_rad)
165	  // 计算新图像尺寸
166	  let new_w = (w.to_double() * cos_a.abs() + h.to_double() * sin_a.abs()).to_int() +
167	    1
168	  let new_h = (w.to_double() * sin_a.abs() + h.to_double() * cos_a.abs()).to_int() +
169	    1
170	  // 创建白色背景的新图像
171	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\xFF')
172	  let data = img.data
173	  // 反向映射：对新图像的每个像素，找到原图像对应的像素
174	  for y in 0..<new_h {
175	    for x in 0..<new_w {
176	      // 相对于中心的坐标
177	      let cx = x.to_double() - new_w.to_double() / 2.0
178	      let cy = y.to_double() - new_h.to_double() / 2.0
179	      // 反向旋转
180	      let src_x = cx * cos_a + cy * sin_a + w.to_double() / 2.0
181	      let src_y = -cx * sin_a + cy * cos_a + h.to_double() / 2.0
182	      // 双线性插值
183	      let x0 = src_x.to_int()
184	      let y0 = src_y.to_int()
185	      let x1 = x0 + 1
186	      let y1 = y0 + 1
187	      let fx = src_x - x0.to_double()
188	      let fy = src_y - y0.to_double()
189	      if x0 >= 0 && x1 < w && y0 >= 0 && y1 < h {
190	        for c in 0..<ch {
191	          let v00 = data[(y0 * w + x0) * ch + c].to_int().to_double()
192	          let v01 = data[(y0 * w + x1) * ch + c].to_int().to_double()
193	          let v10 = data[(y1 * w + x0) * ch + c].to_int().to_double()
194	          let v11 = data[(y1 * w + x1) * ch + c].to_int().to_double()
195	          // 双线性插值
196	          let v0 = v00 * (1.0 - fx) + v01 * fx
197	          let v1 = v10 * (1.0 - fx) + v11 * fx
198	          let v = v0 * (1.0 - fy) + v1 * fy
199	          out[(y * new_w + x) * ch + c] = clamp_byte_deskew(v.to_int())
200	        }
201	      }
202	    }
203	  }
204	  @types.Image::{
205	    width: new_w,
206	    height: new_h,
207	    channels: ch,
208	    data: Bytes::from_array(out),
209	  }
210	}
   	…

24 uncovered line(s) in src\process\ocr\digit_ocr.mbt:

 62	fn generate_single_digit_template(digit : Int) -> Array[Double] {
 63	  // 使用 8x8 网格特征作为模板
 64	  let size = 8
 65	  let features : Array[Double] = Array::make(size * size, 0.0)
 66	  // 根据数字设置特征模式（简化的7段显示模式）
 67	  match digit {
 68	    0 => {
 69	      // 0: 上、上左、上右、下左、下右、下（缺中）
 70	      set_segment(features, size, 0, 1, 6, 1) // 上
 71	      set_segment(features, size, 1, 0, 1, 3) // 上左
 72	      set_segment(features, size, 1, 6, 1, 3) // 上右
 73	      set_segment(features, size, 4, 0, 1, 3) // 下左
 74	      set_segment(features, size, 4, 6, 1, 3) // 下右
 75	      set_segment(features, size, 6, 1, 6, 1) // 下
 76	    }
 77	    1 =>
 78	      // 1: 上右、下右
 79	      set_segment(features, size, 0, 6, 1, 7) // 右
 80	    2 => {
 81	      // 2: 上、上右、中、下左、下
 82	      set_segment(features, size, 0, 1, 6, 1) // 上
 83	      set_segment(features, size, 1, 6, 1, 3) // 上右
 84	      set_segment(features, size, 3, 1, 6, 1) // 中
 85	      set_segment(features, size, 4, 0, 1, 3) // 下左
 86	      set_segment(features, size, 6, 1, 6, 1) // 下
 87	    }
 88	    3 => {
 89	      // 3: 上、上右、中、下右、下
 90	      set_segment(features, size, 0, 1, 6, 1) // 上
 91	      set_segment(features, size, 1, 6, 1, 3) // 上右
 92	      set_segment(features, size, 3, 1, 6, 1) // 中
 93	      set_segment(features, size, 4, 6, 1, 3) // 下右
 94	      set_segment(features, size, 6, 1, 6, 1) // 下
 95	    }
 96	    4 => {
 97	      // 4: 上左、上右、中、下右
 98	      set_segment(features, size, 1, 0, 1, 3) // 上左
 99	      set_segment(features, size, 1, 6, 1, 3) // 上右
100	      set_segment(features, size, 3, 1, 6, 1) // 中
101	      set_segment(features, size, 4, 6, 1, 3) // 下右
102	    }
103	    5 => {
104	      // 5: 上、上左、中、下右、下
105	      set_segment(features, size, 0, 1, 6, 1) // 上
106	      set_segment(features, size, 1, 0, 1, 3) // 上左
107	      set_segment(features, size, 3, 1, 6, 1) // 中
108	      set_segment(features, size, 4, 6, 1, 3) // 下右
109	      set_segment(features, size, 6, 1, 6, 1) // 下
110	    }
111	    6 => {
112	      // 6: 上、上左、中、下左、下右、下
113	      set_segment(features, size, 0, 1, 6, 1) // 上
114	      set_segment(features, size, 1, 0, 1, 3) // 上左
115	      set_segment(features, size, 3, 1, 6, 1) // 中
116	      set_segment(features, size, 4, 0, 1, 3) // 下左
117	      set_segment(features, size, 4, 6, 1, 3) // 下右
118	      set_segment(features, size, 6, 1, 6, 1) // 下
119	    }
120	    7 => {
121	      // 7: 上、上右、下右
122	      set_segment(features, size, 0, 1, 6, 1) // 上
123	      set_segment(features, size, 1, 6, 1, 6) // 右
124	    }
125	    8 => {
126	      // 8: 全部段
127	      set_segment(features, size, 0, 1, 6, 1) // 上
128	      set_segment(features, size, 1, 0, 1, 3) // 上左
129	      set_segment(features, size, 1, 6, 1, 3) // 上右
130	      set_segment(features, size, 3, 1, 6, 1) // 中
131	      set_segment(features, size, 4, 0, 1, 3) // 下左
132	      set_segment(features, size, 4, 6, 1, 3) // 下右
133	      set_segment(features, size, 6, 1, 6, 1) // 下
134	    }
135	    9 => {
136	      // 9: 上、上左、上右、中、下右、下
137	      set_segment(features, size, 0, 1, 6, 1) // 上
138	      set_segment(features, size, 1, 0, 1, 3) // 上左
139	      set_segment(features, size, 1, 6, 1, 3) // 上右
140	      set_segment(features, size, 3, 1, 6, 1) // 中
141	      set_segment(features, size, 4, 6, 1, 3) // 下右
142	      set_segment(features, size, 6, 1, 6, 1) // 下
143	    }
144	    _ => ignore(())	<-- UNCOVERED
145	  }
146	  features
147	}
   	…

173	pub fn recognize_digits(
174	  img : @types.Image,
175	  config? : DigitOcrConfig,
176	) -> OcrResult raise @types.LoadError {
177	  // 输入验证
178	  if img.width <= 0 || img.height <= 0 {
179	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
180	      "recognize_digits: 图像尺寸必须大于0",
181	    )
182	  }
183	  if img.channels != 1 {
184	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
185	      "recognize_digits: 仅支持单通道灰度图",
186	    )
187	  }
188	  let expected_size = img.width * img.height
189	  if img.data.length() != expected_size {
190	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
191	      "recognize_digits: 数据大小不匹配，期望 " +
192	      expected_size.to_string() +	<-- UNCOVERED
193	      "，实际 " +
194	      img.data.length().to_string(),	<-- UNCOVERED
195	    )
196	  }
197	
198	  let cfg = match config {
199	    Some(c) => c	<-- UNCOVERED
200	    None => digit_ocr_default_config()
201	  }
202	  // 配置验证
203	  if cfg.min_char_width <= 0 {
204	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
205	      "recognize_digits: min_char_width必须大于0",
206	    )
207	  }
208	  if cfg.max_char_width < cfg.min_char_width {
209	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
210	      "recognize_digits: max_char_width不能小于min_char_width",
211	    )
212	  }
213	  if cfg.target_size <= 0 {
214	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
215	      "recognize_digits: target_size必须大于0",
216	    )
217	  }
218	  if cfg.k <= 0 {
219	    raise @types.LoadError::DecodeFailed("recognize_digits: k必须大于0")	<-- UNCOVERED
220	  }
221	  // 1. 预处理
222	  let preprocessed = ocr_preprocess(img)
223	  // 2. 字符分割
224	  let char_regions = complete_char_segmentation(
225	    preprocessed,
226	    min_char_width=cfg.min_char_width,
227	    max_char_width=cfg.max_char_width,
228	  )
229	  // 3. 生成模板
230	  let templates = generate_digit_templates()
231	  // 4. 对每个字符进行识别
232	  let char_results : Array[OcrCharResult] = []
233	  let mut text = ""
234	  let mut total_confidence = 0.0
235	  for _tup0 in char_regions {
236	    let (x, y, w, h) = _tup0
237	    // 提取字符图像
238	    let char_img = extract_char_region(
239	      preprocessed,
240	      x,
241	      y,
242	      w,
243	      h,
244	      cfg.target_size,
245	    )
246	    // 提取特征（网格特征）
247	    let features = extract_char_features(char_img, 8)
248	    // 模板匹配
249	    let result = match_template(features, templates, cfg.distance_metric)
250	    text += result.0
251	    total_confidence += result.1
252	    char_results.push(OcrCharResult::{
253	      char: result.0,
254	      confidence: result.1,
255	      x,
256	      y,
257	      width: w,
258	      height: h,
259	    })
260	  }
261	  let avg_confidence = if char_results.length() > 0 {
262	    total_confidence / char_results.length().to_double()
263	  } else {
264	    0.0	<-- UNCOVERED
265	  }
266	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
267	}
   	…

271	fn extract_char_region(
272	  img : @types.Image,
273	  x : Int,
274	  y : Int,
275	  width : Int,
276	  height : Int,
277	  target_size : Int,
278	) -> @types.Image {
279	  let w = img.width
280	  let h = img.height
281	  // 裁剪
282	  let crop_w = if x + width > w { w - x } else { width }
283	  let crop_h = if y + height > h { h - y } else { height }
284	  if crop_w <= 0 || crop_h <= 0 {
285	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
286	    return @types.Image::{
287	      width: target_size,
288	      height: target_size,
289	      channels: 1,
290	      data: Bytes::from_array(empty),	<-- UNCOVERED
291	    }
292	  }
293	  // 缩放到目标尺寸（保持宽高比，居中）
294	  let scale = if crop_w > crop_h {
295	    target_size.to_double() / crop_w.to_double()
296	  } else {
297	    target_size.to_double() / crop_h.to_double()
298	  }
299	  let new_w = max(1, (crop_w.to_double() * scale).to_int())
300	  let new_h = max(1, (crop_h.to_double() * scale).to_int())
301	  let offset_x = (target_size - new_w) / 2
302	  let offset_y = (target_size - new_h) / 2
303	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
304	  let data = img.data
305	  // 最近邻缩放
306	  for py in 0..<new_h {
307	    for px in 0..<new_w {
308	      let src_x = x + px * crop_w / new_w
309	      let src_y = y + py * crop_h / new_h
310	      if src_x < w && src_y < h {
311	        out[(offset_y + py) * target_size + (offset_x + px)] = data[src_y * w +
312	          src_x]
313	      }
314	    }
315	  }
316	  @types.Image::{
317	    width: target_size,
318	    height: target_size,
319	    channels: 1,
320	    data: Bytes::from_array(out),
321	  }
322	}
   	…

326	fn extract_char_features(
327	  char_img : @types.Image,
328	  grid_size : Int,
329	) -> Array[Double] {
330	  let w = char_img.width
331	  let h = char_img.height
332	  let data = char_img.data
333	  let cell_w = w / grid_size
334	  let cell_h = h / grid_size
335	  let features : Array[Double] = []
336	  for r in 0..<grid_size {
337	    for c in 0..<grid_size {
338	      let mut count = 0
339	      let mut total = 0
340	      for py in 0..<cell_h {
341	        for px in 0..<cell_w {
342	          let sx = c * cell_w + px
343	          let sy = r * cell_h + py
344	          if sx < w && sy < h {
345	            total += 1
346	            if data[sy * w + sx].to_int() > 128 {
347	              count += 1
348	            }
349	          }
350	        }
351	      }
352	      let density = if total > 0 {
353	        count.to_double() / total.to_double()
354	      } else {
355	        0.0	<-- UNCOVERED
356	      }
357	      features.push(density)
358	    }
359	  }
360	  features
361	}
   	…

365	fn match_template(
366	  features : Array[Double],
367	  templates : Array[(String, Array[Double])],
368	  distance_metric : String,
369	) -> (String, Double) {
370	  let mut best_label = "?"
371	  let mut best_dist = 1000000.0
372	  for _tup1 in templates {
373	    let (label, template) = _tup1
374	    let dist = match distance_metric {
375	      "manhattan" => manhattan_distance_local(features, template)	<-- UNCOVERED
376	      "cosine" => cosine_distance_local(features, template)
377	      _ => euclidean_distance_local(features, template)
378	    }
379	    if dist < best_dist {
380	      best_dist = dist
381	      best_label = label
382	    }
383	  }
384	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
385	  (best_label, confidence)
386	}
   	…

390	fn euclidean_distance_local(a : Array[Double], b : Array[Double]) -> Double {
391	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
392	  let mut sum_sq = 0.0
393	  for i in 0..<n {
394	    let diff = a[i] - b[i]
395	    sum_sq += diff * diff
396	  }
397	  sum_sq.sqrt()
398	}
   	…

402	fn manhattan_distance_local(a : Array[Double], b : Array[Double]) -> Double {
403	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
404	  let mut sum = 0.0
405	  for i in 0..<n {
406	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
407	  }
408	  sum
409	}
   	…

413	fn cosine_distance_local(a : Array[Double], b : Array[Double]) -> Double {
414	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
415	  let mut dot = 0.0
416	  let mut norm_a = 0.0
417	  let mut norm_b = 0.0
418	  for i in 0..<n {
419	    dot += a[i] * b[i]	<-- UNCOVERED
420	    norm_a += a[i] * a[i]
421	    norm_b += b[i] * b[i]
422	  }
423	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
424	  if denom > 0.0 {
425	    1.0 - dot / denom	<-- UNCOVERED
426	  } else {
427	    1.0	<-- UNCOVERED
428	  }
429	}
   	…

23 uncovered line(s) in src\process\ocr\document_enhance.mbt:

19	pub fn document_enhance(
20	  img : @types.Image,
21	) -> @types.Image raise @types.LoadError {
22	  // 输入验证
23	  if img.width <= 0 || img.height <= 0 {
24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
25	      "document_enhance: 图像尺寸必须大于0",
26	    )
27	  }
28	  if img.channels < 1 || img.channels > 4 {
29	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
30	      "document_enhance: 通道数必须在1-4之间",
31	    )
32	  }
33	  let expected_size = img.width * img.height * img.channels
34	  if img.data.length() != expected_size {
35	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
36	      "document_enhance: 数据大小不匹配，期望 " +
37	      expected_size.to_string() +	<-- UNCOVERED
38	      "，实际 " +
39	      img.data.length().to_string(),	<-- UNCOVERED
40	    )
41	  }
42	  let mut result = img
43	  // 1. 转灰度（如果是彩色图像）
44	  if result.channels != 1 {
45	    result = to_grayscale_doc(result)	<-- UNCOVERED
46	  }
47	  // 2. 对比度增强（拉伸直方图）
48	  result = contrast_stretch_doc(result)
49	  // 3. 轻度去噪（中值滤波）
50	  result = median_denoise_doc(result, 3)
51	  // 4. 锐化（增强文字边缘）
52	  result = sharpen_doc(result, 0.5)
53	  result
54	}
  	…

58	fn to_grayscale_doc(img : @types.Image) -> @types.Image {
59	  if img.channels == 1 {	<-- UNCOVERED
60	    return img
61	  }
62	  let n = img.width * img.height
63	  let ch = img.channels
64	  let out : Array[Byte] = Array::make(n, b'\x00')	<-- UNCOVERED
65	  let data = img.data
66	  for i in 0..<n {
67	    let base = i * ch	<-- UNCOVERED
68	    let r = data[base].to_int()
69	    let g = data[base + 1].to_int()
70	    let b = data[base + 2].to_int()
71	    let gray = (r * 299 + g * 587 + b * 114) / 1000
72	    out[i] = gray.to_byte()	<-- UNCOVERED
73	  }
74	  @types.Image::{
75	    width: img.width,
76	    height: img.height,
77	    channels: 1,
78	    data: Bytes::from_array(out),	<-- UNCOVERED
79	  }
80	}
  	…

209	pub fn shadow_removal(
210	  img : @types.Image,
211	  kernel_size? : Int = 51,
212	) -> @types.Image raise @types.LoadError {
213	  // 输入验证
214	  if img.width <= 0 || img.height <= 0 {
215	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
216	      "shadow_removal: 图像尺寸必须大于0",
217	    )
218	  }
219	  if img.channels != 1 {
220	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
221	      "shadow_removal: 仅支持单通道灰度图",
222	    )
223	  }
224	  let expected_size = img.width * img.height
225	  if img.data.length() != expected_size {
226	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
227	      "shadow_removal: 数据大小不匹配，期望 " +
228	      expected_size.to_string() +	<-- UNCOVERED
229	      "，实际 " +
230	      img.data.length().to_string(),	<-- UNCOVERED
231	    )
232	  }
233	  if kernel_size <= 0 || kernel_size % 2 == 0 {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "shadow_removal: 核大小必须为正奇数",
236	    )
237	  }
238	  let w = img.width
239	  let h = img.height
240	  let data = img.data
241	  // 估计背景（大核均值模糊）
242	  let radius = kernel_size / 2
243	  let background : Array[Int] = Array::make(w * h, 0)
244	  for y in 0..<h {
245	    for x in 0..<w {
246	      let mut sum = 0
247	      let mut count = 0
248	      for dy in -radius..<(radius + 1) {
249	        for dx in -radius..<(radius + 1) {
250	          let nx = x + dx
251	          let ny = y + dy
252	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
253	            sum += data[ny * w + nx].to_int()
254	            count += 1
255	          }
256	        }
257	      }
258	      background[y * w + x] = sum / count
259	    }
260	  }
261	  // 去除阴影：result = (original / background) * 255
262	  let out : Array[Byte] = Array::make(w * h, b'\x00')
263	  for i in 0..<(w * h) {
264	    let orig = data[i].to_int()
265	    let bg = background[i]
266	    let result = if bg > 0 { orig * 255 / bg } else { 255 }
267	    out[i] = clamp_byte_doc(result)
268	  }
269	  @types.Image::{
270	    width: w,
271	    height: h,
272	    channels: 1,
273	    data: Bytes::from_array(out),
274	  }
275	}
   	…

280	pub fn background_normalization(
281	  img : @types.Image,
282	  kernel_size? : Int = 31,
283	) -> @types.Image raise @types.LoadError {
284	  // 输入验证
285	  if img.width <= 0 || img.height <= 0 {
286	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
287	      "background_normalization: 图像尺寸必须大于0",
288	    )
289	  }
290	  if img.channels != 1 {
291	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
292	      "background_normalization: 仅支持单通道灰度图",
293	    )
294	  }
295	  let expected_size = img.width * img.height
296	  if img.data.length() != expected_size {
297	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
298	      "background_normalization: 数据大小不匹配，期望 " +
299	      expected_size.to_string() +	<-- UNCOVERED
300	      "，实际 " +
301	      img.data.length().to_string(),	<-- UNCOVERED
302	    )
303	  }
304	  if kernel_size <= 0 || kernel_size % 2 == 0 {
305	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
306	      "background_normalization: 核大小必须为正奇数",
307	    )
308	  }
309	  let w = img.width
310	  let h = img.height
311	  let data = img.data
312	  // 估计背景（大核中值模糊）
313	  let radius = kernel_size / 2
314	  let background : Array[Int] = Array::make(w * h, 0)
315	  for y in 0..<h {
316	    for x in 0..<w {
317	      let values : Array[Int] = []
318	      for dy in -radius..<(radius + 1) {
319	        for dx in -radius..<(radius + 1) {
320	          let nx = x + dx
321	          let ny = y + dy
322	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
323	            values.push(data[ny * w + nx].to_int())
324	          }
325	        }
326	      }
327	      // 取最大值作为背景估计（文字是暗的，背景是亮的）
328	      let mut max_val = 0
329	      for v in values {
330	        if v > max_val {
331	          max_val = v
332	        }
333	      }
334	      background[y * w + x] = max_val
335	    }
336	  }
337	  // 归一化：result = original * (255 / background)
338	  let out : Array[Byte] = Array::make(w * h, b'\x00')
339	  for i in 0..<(w * h) {
340	    let orig = data[i].to_int()
341	    let bg = background[i]
342	    let result = if bg > 0 { orig * 255 / bg } else { 255 }
343	    out[i] = clamp_byte_doc(result)
344	  }
345	  @types.Image::{
346	    width: w,
347	    height: h,
348	    channels: 1,
349	    data: Bytes::from_array(out),
350	  }
351	}
   	…

50 uncovered line(s) in src\process\ocr\document_reconstruction.mbt:

154	pub fn detect_image_regions_markdown(
155	  binary_img : @types.Image,
156	  min_area? : Int = 500,
157	) -> Array[MarkdownImage] raise @types.LoadError {
158	  // 输入验证
159	  if binary_img.width <= 0 || binary_img.height <= 0 {
160	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
161	      "detect_image_regions_markdown: 图像尺寸必须大于0",
162	    )
163	  }
164	  if binary_img.channels != 1 {
165	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
166	      "detect_image_regions_markdown: 仅支持单通道二值图",
167	    )
168	  }
169	  let expected_size = binary_img.width * binary_img.height
170	  if binary_img.data.length() != expected_size {
171	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
172	      "detect_image_regions_markdown: 数据大小不匹配，期望 " +
173	      expected_size.to_string() +	<-- UNCOVERED
174	      "，实际 " +
175	      binary_img.data.length().to_string(),	<-- UNCOVERED
176	    )
177	  }
178	  if min_area <= 0 {
179	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
180	      "detect_image_regions_markdown: 最小区域面积必须大于0",
181	    )
182	  }
183	  let min = min_area
184	  let w = binary_img.width
185	  let h = binary_img.height
186	  let regions : Array[MarkdownImage] = []
187	  let visited : Array[Bool] = []
188	  for _ in 0..<(w * h) {
189	    visited.push(false)
190	  }
191	  for y in 0..<h {
192	    for x in 0..<w {
193	      let idx = y * w + x
194	      if !visited[idx] && binary_img.data[idx].to_int() < 128 {
195	        let stack_x : Array[Int] = []	<-- UNCOVERED
196	        let stack_y : Array[Int] = []
197	        stack_x.push(x)	<-- UNCOVERED
198	        stack_y.push(y)
199	        visited[idx] = true
200	        let mut min_x = x
201	        let mut max_x = x
202	        let mut min_y = y
203	        let mut max_y = y
204	        let mut area = 0
205	        while stack_x.length() > 0 {	<-- UNCOVERED
206	          let cx_opt = stack_x.pop()
207	          let cy_opt = stack_y.pop()
208	          match cx_opt {
209	            Some(cx) =>
210	              match cy_opt {	<-- UNCOVERED
211	                Some(cy) => {
212	                  area += 1
213	                  if cx < min_x {
214	                    min_x = cx	<-- UNCOVERED
215	                  }
216	                  if cx > max_x {
217	                    max_x = cx	<-- UNCOVERED
218	                  }
219	                  if cy < min_y {
220	                    min_y = cy	<-- UNCOVERED
221	                  }
222	                  if cy > max_y {
223	                    max_y = cy	<-- UNCOVERED
224	                  }
225	                  let neighbors = [
226	                    (cx - 1, cy),
227	                    (cx + 1, cy),
228	                    (cx, cy - 1),
229	                    (cx, cy + 1),
230	                  ]
231	                  for _tup1 in neighbors {
232	                    let (nx, ny) = _tup1	<-- UNCOVERED
233	                    if nx >= 0 && nx < w && ny >= 0 && ny < h {
234	                      let nidx = ny * w + nx	<-- UNCOVERED
235	                      if !visited[nidx] && binary_img.data[nidx].to_int() < 128 {
236	                        visited[nidx] = true
237	                        stack_x.push(nx)
238	                        stack_y.push(ny)
239	                      }
240	                    }
241	                  }
242	                }
243	                None => ()	<-- UNCOVERED
244	              }
245	            None => ()	<-- UNCOVERED
246	          }
247	        }
248	        let region_width = max_x - min_x + 1
249	        let region_height = max_y - min_y + 1
250	        if area >= min && region_width > 20 && region_height > 20 {
251	          let fill_ratio = area.to_double() /	<-- UNCOVERED
252	            (region_width * region_height).to_double()
253	          let image_type = if fill_ratio > 0.8 {
254	            "diagram"	<-- UNCOVERED
255	          } else if fill_ratio > 0.5 {
256	            "chart"
257	          } else {
258	            "photo"	<-- UNCOVERED
259	          }
260	          regions.push(MarkdownImage::{	<-- UNCOVERED
261	            x: min_x,
262	            y: min_y,
263	            width: region_width,
264	            height: region_height,
265	            image_type,
266	            confidence: 0.7,
267	          })
268	        }
269	      }
270	    }
271	  }
272	  regions
273	}
   	…

277	pub fn classify_markdown_element(
278	  block : TextBlock,
279	  avg_font_size? : Double = 16.0,
280	) -> MarkdownElementType {
281	  let avg_size = avg_font_size	<-- UNCOVERED
282	  let height_ratio = block.height.to_double() / avg_size
283	  if height_ratio > 3.0 && block.y < 100 {
284	    MarkdownElementType::Title	<-- UNCOVERED
285	  } else if height_ratio > 2.0 {
286	    MarkdownElementType::Heading1
287	  } else if height_ratio > 1.5 {
288	    MarkdownElementType::Heading2
289	  } else if height_ratio > 1.2 {
290	    MarkdownElementType::Heading3
291	  } else if block.height < 5 && block.width > 100 {
292	    MarkdownElementType::HorizontalRule
293	  } else {
294	    MarkdownElementType::Paragraph	<-- UNCOVERED
295	  }
296	}
   	…

300	pub fn markdown_table_to_string(table : MarkdownTable) -> String {
301	  let mut md = ""
302	  if table.has_header && table.rows > 0 {
303	    md = md + "| "
304	    for col in 0..<table.cols {
305	      let cell_text = if col < table.cells[0].length() {
306	        table.cells[0][col]
307	      } else {
308	        ""	<-- UNCOVERED
309	      }
310	      md = md + cell_text + " | "
311	    }
312	    md = md + "\n| "
313	    for _ in 0..<table.cols {
314	      md = md + "--- | "
315	    }
316	    md = md + "\n"
317	    for row in 1..<table.rows {
318	      md = md + "| "
319	      for col in 0..<table.cols {
320	        let cell_text = if row < table.cells.length() &&
321	          col < table.cells[row].length() {
322	          table.cells[row][col]
323	        } else {
324	          ""	<-- UNCOVERED
325	        }
326	        md = md + cell_text + " | "
327	      }
328	      md = md + "\n"
329	    }
330	  } else {
331	    for row in 0..<table.rows {	<-- UNCOVERED
332	      md = md + "| "
333	      for col in 0..<table.cols {
334	        let cell_text = if row < table.cells.length() &&	<-- UNCOVERED
335	          col < table.cells[row].length() {
336	          table.cells[row][col]
337	        } else {
338	          ""	<-- UNCOVERED
339	        }
340	        md = md + cell_text + " | "
341	      }
342	      md = md + "\n"
343	    }
344	  }
345	  md
346	}
   	…

350	pub fn determine_reading_order_markdown(
351	  elements : Array[MarkdownElement],
352	) -> Array[Int] {
353	  let indices : Array[Int] = []
354	  for i in 0..<elements.length() {
355	    indices.push(i)	<-- UNCOVERED
356	  }
357	  for i in 0..<indices.length() {
358	    for j in 0..<(indices.length() - i - 1) {	<-- UNCOVERED
359	      let a = indices[j]
360	      let b = indices[j + 1]
361	      let should_swap = if (elements[a].y - elements[b].y).abs() < 20 {	<-- UNCOVERED
362	        elements[a].x > elements[b].x
363	      } else {
364	        elements[a].y > elements[b].y	<-- UNCOVERED
365	      }
366	      if should_swap {
367	        let temp = indices[j]	<-- UNCOVERED
368	        indices[j] = indices[j + 1]
369	        indices[j + 1] = temp
370	      }
371	    }
372	  }
373	  indices
374	}
   	…

378	fn markdown_element_to_string(element : MarkdownElement) -> String {
379	  match element.element_type {	<-- UNCOVERED
380	    MarkdownElementType::Title => "# " + element.content + "\n\n"
381	    MarkdownElementType::Heading1 => "## " + element.content + "\n\n"
382	    MarkdownElementType::Heading2 => "### " + element.content + "\n\n"
383	    MarkdownElementType::Heading3 => "#### " + element.content + "\n\n"
384	    MarkdownElementType::Paragraph => element.content + "\n\n"
385	    MarkdownElementType::ListItem => "- " + element.content + "\n"
386	    MarkdownElementType::Table => element.content + "\n"
387	    MarkdownElementType::Image => "![图片](" + element.content + ")\n\n"
388	    MarkdownElementType::Quote => "> " + element.content + "\n\n"
389	    MarkdownElementType::CodeBlock => "```\n" + element.content + "\n```\n\n"
390	    MarkdownElementType::HorizontalRule => "---\n\n"
391	    MarkdownElementType::PageBreak => "\n\n---\n\n"
392	    MarkdownElementType::Unknown => element.content + "\n\n"
393	  }
394	}
   	…

398	pub fn markdown_document_to_string(doc : MarkdownDocument) -> String {
399	  let mut md = ""
400	  if doc.title.length() > 0 {
401	    md = md + "# " + doc.title + "\n\n"
402	  }
403	  for idx in doc.reading_order {
404	    if idx < doc.elements.length() {	<-- UNCOVERED
405	      md = md + markdown_element_to_string(doc.elements[idx])
406	    }
407	  }
408	  md
409	}
   	…

413	pub fn build_markdown_document(
414	  ocr_result : OCRPageResult,
415	  title? : String,
416	) -> MarkdownDocument raise @types.LoadError {
417	  // 输入验证
418	  if ocr_result.blocks.length() == 0 {	<-- UNCOVERED
419	    raise @types.LoadError::DecodeFailed(
420	      "build_markdown_document: OCR结果的文本块列表不能为空",
421	    )
422	  }
423	  let doc_title = match title {	<-- UNCOVERED
424	    Some(v) => v
425	    None => ""
426	  }
427	  let elements : Array[MarkdownElement] = []
428	  let tables : Array[MarkdownTable] = []
429	  let images : Array[MarkdownImage] = []
430	  let symbols : Array[MarkdownSymbol] = []
431	  for block in ocr_result.blocks {
432	    let element_type = classify_markdown_element(block, avg_font_size=16.0)	<-- UNCOVERED
433	    let element = MarkdownElement::{
434	      element_type,
435	      x: block.x,
436	      y: block.y,
437	      width: block.width,
438	      height: block.height,
439	      content: block.text,
440	      confidence: block.confidence,
441	    }
442	    elements.push(element)	<-- UNCOVERED
443	    let block_symbols = recognize_symbols_markdown(block.text)
444	    for s in block_symbols {
445	      symbols.push(s)	<-- UNCOVERED
446	    }
447	  }
448	  let reading_order = determine_reading_order_markdown(elements)	<-- UNCOVERED
449	  MarkdownDocument::{
450	    title: doc_title,
451	    elements,
452	    tables,
453	    images,
454	    symbols,
455	    reading_order,
456	  }
457	}
   	…

461	pub fn generate_markdown_image_placeholder(
462	  image : MarkdownImage,
463	  index : Int,
464	) -> String {
465	  "![图片" +	<-- UNCOVERED
466	  index.to_string() +
467	  "（" +
468	  image.image_type +
469	  "，位置：" +
470	  image.x.to_string() +	<-- UNCOVERED
471	  "," +
472	  image.y.to_string() +	<-- UNCOVERED
473	  "，尺寸：" +
474	  image.width.to_string() +	<-- UNCOVERED
475	  "x" +
476	  image.height.to_string() +	<-- UNCOVERED
477	  "）](image_" +
478	  index.to_string() +	<-- UNCOVERED
479	  ".png)"
480	}
   	…

484	pub fn analyze_markdown_element_distribution(
485	  doc : MarkdownDocument,
486	) -> Array[(String, Int)] {
487	  let mut title_count = 0
488	  let mut heading_count = 0
489	  let mut paragraph_count = 0
490	  let mut list_count = 0
491	  let mut table_count = 0
492	  let mut image_count = 0
493	  let mut quote_count = 0
494	  let mut code_count = 0
495	  for element in doc.elements {
496	    match element.element_type {	<-- UNCOVERED
497	      MarkdownElementType::Title => title_count += 1
498	      MarkdownElementType::Heading1 => heading_count += 1
499	      MarkdownElementType::Heading2 => heading_count += 1
500	      MarkdownElementType::Heading3 => heading_count += 1
501	      MarkdownElementType::Paragraph => paragraph_count += 1
502	      MarkdownElementType::ListItem => list_count += 1
503	      MarkdownElementType::Table => table_count += 1
504	      MarkdownElementType::Image => image_count += 1
505	      MarkdownElementType::Quote => quote_count += 1
506	      MarkdownElementType::CodeBlock => code_count += 1
507	      _ => ()
508	    }
509	  }
510	  [
511	    ("标题", title_count),
512	    ("小标题", heading_count),
513	    ("段落", paragraph_count),
514	    ("列表", list_count),
515	    ("表格", table_count),
516	    ("图片", image_count),
517	    ("引用", quote_count),
518	    ("代码", code_count),
519	  ]
520	}
   	…

31 uncovered line(s) in src\process\ocr\document_understanding.mbt:

 51	pub fn analyze_document_layout(
 52	  img : @types.Image,
 53	) -> DocumentStructure raise @types.LoadError {
 54	  // 输入验证
 55	  if img.width <= 0 || img.height <= 0 {
 56	    raise @types.LoadError::DecodeFailed(
 57	      "analyze_document_layout: 图像尺寸必须大于0",
 58	    )
 59	  }
 60	  if img.channels < 1 || img.channels > 4 {
 61	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 62	      "analyze_document_layout: 通道数必须在1-4之间",
 63	    )
 64	  }
 65	  let expected_size = img.width * img.height * img.channels
 66	  if img.data.length() != expected_size {
 67	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 68	      "analyze_document_layout: 数据大小不匹配，期望 " +
 69	      expected_size.to_string() +	<-- UNCOVERED
 70	      "，实际 " +
 71	      img.data.length().to_string(),	<-- UNCOVERED
 72	    )
 73	  }
 74	  // 1. 预处理
 75	  let preprocessed = ocr_preprocess(img)
 76	  // 2. 文本行检测
 77	  let lines = detect_text_lines_projection(preprocessed)
 78	  // 3. 文本区域检测
 79	  let _ = detect_text_regions(preprocessed)
 80	  // 4. 分类元素类型
 81	  let elements : Array[DocumentElement] = []
 82	  let mut order = 0
 83	  for line in lines {
 84	    // 根据行高和位置判断元素类型
 85	    let element_type = classify_element_type(line, lines)
 86	    let font_size = line.height
 87	    let font_weight = if line.height > 20 { "bold" } else { "normal" }
 88	    elements.push(DocumentElement::{
 89	      element_type,
 90	      x: line.x_start,
 91	      y: line.y_start,
 92	      width: line.width,
 93	      height: line.height,
 94	      text: "",
 95	      confidence: 0.8,
 96	      font_size,
 97	      font_weight,
 98	      reading_order: order,
 99	    })
100	    order += 1
101	  }
102	  // 5. 计算阅读顺序（从上到下，从左到右）
103	  let reading_order : Array[Int] = []
104	  for i in 0..<elements.length() {
105	    reading_order.push(i)
106	  }
107	  // 6. 提取标题
108	  let mut title = ""
109	  for elem in elements {
110	    if elem.element_type == DocumentElementType::Title ||
111	      elem.element_type == DocumentElementType::Heading1 {
112	      title = elem.text	<-- UNCOVERED
113	      break
114	    }
115	  }
116	  // 7. 统计
117	  let mut num_headings = 0
118	  let mut num_paragraphs = 0
119	  for elem in elements {
120	    match elem.element_type {
121	      DocumentElementType::Heading1 => num_headings += 1	<-- UNCOVERED
122	      DocumentElementType::Heading2 => num_headings += 1
123	      DocumentElementType::Heading3 => num_headings += 1
124	      DocumentElementType::Body => num_paragraphs += 1
125	      _ => ignore(())
126	    }
127	  }
128	  // 8. 边界框
129	  let mut min_x = img.width
130	  let mut min_y = img.height
131	  let mut max_x = 0
132	  let mut max_y = 0
133	  for elem in elements {
134	    if elem.x < min_x {
135	      min_x = elem.x
136	    }
137	    if elem.y < min_y {
138	      min_y = elem.y
139	    }
140	    if elem.x + elem.width > max_x {
141	      max_x = elem.x + elem.width
142	    }
143	    if elem.y + elem.height > max_y {
144	      max_y = elem.y + elem.height
145	    }
146	  }
147	  DocumentStructure::{
148	    elements,
149	    num_elements: elements.length(),
150	    title,
151	    num_headings,
152	    num_paragraphs,
153	    reading_order,
154	    bounding_box: (min_x, min_y, max_x - min_x, max_y - min_y),
155	  }
156	}
   	…

160	fn classify_element_type(
161	  line : TextLineInfo,
162	  all_lines : Array[TextLineInfo],
163	) -> DocumentElementType {
164	  // 计算平均行高
165	  let mut avg_height = 0
166	  for l in all_lines {
167	    avg_height += l.height
168	  }
169	  if all_lines.length() > 0 {
170	    avg_height = avg_height / all_lines.length()
171	  }
172	  // 根据行高判断
173	  let height_ratio = if avg_height > 0 {
174	    line.height.to_double() / avg_height.to_double()
175	  } else {
176	    1.0	<-- UNCOVERED
177	  }
178	  if height_ratio > 1.8 {
179	    DocumentElementType::Title	<-- UNCOVERED
180	  } else if height_ratio > 1.5 {
181	    DocumentElementType::Heading1	<-- UNCOVERED
182	  } else if height_ratio > 1.3 {
183	    DocumentElementType::Heading2	<-- UNCOVERED
184	  } else if height_ratio > 1.15 {
185	    DocumentElementType::Heading3	<-- UNCOVERED
186	  } else if line.y_start < 30 {
187	    DocumentElementType::Header
188	  } else {
189	    DocumentElementType::Body	<-- UNCOVERED
190	  }
191	}
   	…

195	pub fn compute_reading_order(
196	  elements : Array[DocumentElement],
197	) -> Array[Int] raise @types.LoadError {
198	  // 输入验证
199	  if elements.length() == 0 {
200	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
201	      "compute_reading_order: 元素列表不能为空",
202	    )
203	  }
204	  // 按 y 坐标排序，然后按 x 坐标排序
205	  let indices : Array[Int] = []
206	  for i in 0..<elements.length() {
207	    indices.push(i)
208	  }
209	  // 简单排序（冒泡排序）
210	  for _ in 0..<indices.length() {
211	    for j in 0..<(indices.length() - 1) {
212	      let e1 = elements[indices[j]]	<-- UNCOVERED
213	      let e2 = elements[indices[j + 1]]
214	      // 先按 y 排序，再按 x 排序
215	      let should_swap = if e1.y > e2.y {
216	        true	<-- UNCOVERED
217	      } else if e1.y == e2.y && e1.x > e2.x {
218	        true
219	      } else {
220	        false	<-- UNCOVERED
221	      }
222	      if should_swap {
223	        let temp = indices[j]	<-- UNCOVERED
224	        indices[j] = indices[j + 1]
225	        indices[j + 1] = temp
226	      }
227	    }
228	  }
229	  indices
230	}
   	…

234	pub fn extract_title(structure : DocumentStructure) -> String {
235	  for elem in structure.elements {
236	    if elem.element_type == DocumentElementType::Title {
237	      return elem.text	<-- UNCOVERED
238	    }
239	  }
240	  // 如果没有标题，返回第一个大字体元素
241	  for elem in structure.elements {
242	    if elem.font_size > 20 {
243	      return elem.text
244	    }
245	  }
246	  ""
247	}
   	…

251	pub fn extract_headings(
252	  structure : DocumentStructure,
253	) -> Array[DocumentElement] {
254	  let headings : Array[DocumentElement] = []
255	  for elem in structure.elements {
256	    match elem.element_type {
257	      DocumentElementType::Title => headings.push(elem)	<-- UNCOVERED
258	      DocumentElementType::Heading1 => headings.push(elem)
259	      DocumentElementType::Heading2 => headings.push(elem)
260	      DocumentElementType::Heading3 => headings.push(elem)
261	      _ => ignore(())
262	    }
263	  }
264	  headings
265	}
   	…

269	pub fn extract_paragraphs(
270	  structure : DocumentStructure,
271	) -> Array[DocumentElement] {
272	  let paragraphs : Array[DocumentElement] = []
273	  for elem in structure.elements {
274	    if elem.element_type == DocumentElementType::Body {
275	      paragraphs.push(elem)	<-- UNCOVERED
276	    }
277	  }
278	  paragraphs
279	}
   	…

297	pub fn document_structure_stats(
298	  structure : DocumentStructure,
299	) -> (Int, Int, Int, Int, Int) {
300	  let mut titles = 0
301	  let mut headings = 0
302	  let mut paragraphs = 0
303	  let mut lists = 0
304	  let mut images = 0
305	  for elem in structure.elements {
306	    match elem.element_type {
307	      DocumentElementType::Title => titles += 1	<-- UNCOVERED
308	      DocumentElementType::Heading1 => headings += 1
309	      DocumentElementType::Heading2 => headings += 1
310	      DocumentElementType::Heading3 => headings += 1
311	      DocumentElementType::Body => paragraphs += 1
312	      DocumentElementType::List => lists += 1
313	      DocumentElementType::Image => images += 1
314	      _ => ignore(())
315	    }
316	  }
317	  (titles, headings, paragraphs, lists, images)
318	}
   	…

322	pub fn detect_document_columns(
323	  img : @types.Image,
324	) -> Int raise @types.LoadError {
325	  // 输入验证
326	  if img.width <= 0 || img.height <= 0 {
327	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
328	      "detect_document_columns: 图像尺寸必须大于0",
329	    )
330	  }
331	  if img.channels < 1 || img.channels > 4 {
332	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
333	      "detect_document_columns: 通道数必须在1-4之间",
334	    )
335	  }
336	  let expected_size = img.width * img.height * img.channels
337	  if img.data.length() != expected_size {
338	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
339	      "detect_document_columns: 数据大小不匹配，期望 " +
340	      expected_size.to_string() +	<-- UNCOVERED
341	      "，实际 " +
342	      img.data.length().to_string(),	<-- UNCOVERED
343	    )
344	  }
345	  let (num_cols, _) = detect_columns(img)
346	  num_cols
347	}
   	…

351	pub fn understand_document(
352	  img : @types.Image,
353	) -> DocumentStructure raise @types.LoadError {
354	  // 输入验证
355	  if img.width <= 0 || img.height <= 0 {
356	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
357	      "understand_document: 图像尺寸必须大于0",
358	    )
359	  }
360	  if img.channels < 1 || img.channels > 4 {
361	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
362	      "understand_document: 通道数必须在1-4之间",
363	    )
364	  }
365	  let expected_size = img.width * img.height * img.channels
366	  if img.data.length() != expected_size {
367	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
368	      "understand_document: 数据大小不匹配，期望 " +
369	      expected_size.to_string() +	<-- UNCOVERED
370	      "，实际 " +
371	      img.data.length().to_string(),	<-- UNCOVERED
372	    )
373	  }
374	  // 1. 版面分析
375	  let structure = analyze_document_layout(img)
376	  // 2. 计算阅读顺序
377	  let order = compute_reading_order(structure.elements)
378	  // 3. 更新阅读顺序
379	  DocumentStructure::{
380	    elements: structure.elements,
381	    num_elements: structure.num_elements,
382	    title: extract_title(structure),
383	    num_headings: structure.num_headings,
384	    num_paragraphs: structure.num_paragraphs,
385	    reading_order: order,
386	    bounding_box: structure.bounding_box,
387	  }
388	}
   	…

48 uncovered line(s) in src\process\ocr\document_understanding_enhanced.mbt:

 82	pub fn analyze_layout_enhanced(
 83	  img : @types.Image,
 84	  config? : DocumentUnderstandingEnhancedConfig,
 85	) -> Array[DocumentBlock] raise @types.LoadError {
 86	  // 输入验证
 87	  if img.width <= 0 || img.height <= 0 {
 88	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 89	      "analyze_layout_enhanced: 图像尺寸必须大于0",
 90	    )
 91	  }
 92	  if img.channels < 1 || img.channels > 4 {
 93	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 94	      "analyze_layout_enhanced: 通道数必须在1-4之间",
 95	    )
 96	  }
 97	  let expected_size = img.width * img.height * img.channels
 98	  if img.data.length() != expected_size {
 99	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
100	      "analyze_layout_enhanced: 数据大小不匹配，期望 " +
101	      expected_size.to_string() +	<-- UNCOVERED
102	      "，实际 " +
103	      img.data.length().to_string(),	<-- UNCOVERED
104	    )
105	  }
106	  let cfg = match config {
107	    Some(c) => c
108	    None => document_understanding_enhanced_default_config()
109	  }
110	  // 1. 预处理
111	  let preprocessed = ocr_preprocess(img)
112	  // 2. 检测文本行
113	  let lines = detect_text_lines_projection(preprocessed)
114	  // 3. 检测文本区域（连通域合并）
115	  let _ = detect_text_regions(preprocessed)
116	  // 4. 多栏检测
117	  let num_columns = if cfg.enable_column_detection {
118	    let (cols, _) = detect_columns(preprocessed)
119	    cols
120	  } else {
121	    1	<-- UNCOVERED
122	  }
123	  // 5. 构建文档块
124	  let blocks : Array[DocumentBlock] = []
125	  let mut order = 0
126	  for line in lines {
127	    // 估计字体大小（基于行高）
128	    let font_size = line.height
129	    // 判断块类型
130	    let block_type = classify_block_type_enhanced(line, font_size, cfg, order)
131	    // 判断是否加粗（基于笔画密度）
132	    let is_bold = estimate_bold(preprocessed, line)
133	    // 计算行间距
134	    let line_spacing = if order > 0 && order < blocks.length() {	<-- UNCOVERED
135	      let prev = blocks[order - 1]
136	      (line.y_start - (prev.y + prev.height)).to_double() /
137	      font_size.to_double()
138	    } else {
139	      1.0
140	    }
141	    // 计算缩进级别
142	    let indent_level = if line.x_start > img.width / 10 {
143	      ((line.x_start - img.width / 20) / (img.width / 20)).max(0).min(3)	<-- UNCOVERED
144	    } else {
145	      0
146	    }
147	    // 计算列索引
148	    let column_index = if num_columns > 1 {
149	      let col_width = img.width / num_columns	<-- UNCOVERED
150	      (line.x_start / col_width).min(num_columns - 1)
151	    } else {
152	      0
153	    }
154	    blocks.push(DocumentBlock::{
155	      block_type,
156	      x: line.x_start,
157	      y: line.y_start,
158	      width: line.width,
159	      height: line.height,
160	      text: "",
161	      confidence: 0.8,
162	      font_size,
163	      is_bold,
164	      is_italic: false,
165	      line_spacing,
166	      indent_level,
167	      column_index,
168	      reading_order: order,
169	      parent_id: -1,
170	      children_ids: [],
171	    })
172	    order += 1
173	  }
174	  blocks
175	}
   	…

179	fn classify_block_type_enhanced(
180	  line : TextLineInfo,
181	  font_size : Int,
182	  cfg : DocumentUnderstandingEnhancedConfig,
183	  order : Int,
184	) -> DocumentBlockType {
185	  // 标题检测（第一行且字体大）
186	  if cfg.enable_title_detection &&
187	    order == 0 &&
188	    font_size >= cfg.title_font_size_threshold {
189	    return DocumentBlockType::Title
190	  }
191	  // 标题检测（字体大且加粗）
192	  if font_size >= cfg.heading_font_size_threshold {
193	    if font_size >= cfg.title_font_size_threshold {
194	      return DocumentBlockType::Heading1	<-- UNCOVERED
195	    } else if font_size >= cfg.heading_font_size_threshold + 2 {
196	      return DocumentBlockType::Heading2
197	    } else {
198	      return DocumentBlockType::Heading3	<-- UNCOVERED
199	    }
200	  }
201	  // 列表项检测（缩进且短）
202	  if line.width < 200 && line.x_start > 30 {
203	    return DocumentBlockType::ListItem	<-- UNCOVERED
204	  }
205	  // 普通段落
206	  DocumentBlockType::Paragraph
207	}
   	…

211	fn estimate_bold(img : @types.Image, line : TextLineInfo) -> Bool {
212	  let w = img.width
213	  let h = img.height
214	  let data = img.data
215	  let mut ink_pixels = 0
216	  let mut total = 0
217	  let y_start = if line.y_start > 0 { line.y_start } else { 0 }
218	  let y_end = if line.y_start + line.height < h {
219	    line.y_start + line.height	<-- UNCOVERED
220	  } else {
221	    h
222	  }
223	  let x_start = if line.x_start > 0 { line.x_start } else { 0 }
224	  let x_end = if line.x_start + line.width < w {
225	    line.x_start + line.width	<-- UNCOVERED
226	  } else {
227	    w
228	  }
229	  for y in y_start..<y_end {
230	    for x in x_start..<x_end {
231	      total += 1
232	      if data[y * w + x].to_int() > 128 {
233	        ink_pixels += 1
234	      }
235	    }
236	  }
237	  let density = if total > 0 {
238	    ink_pixels.to_double() / total.to_double()
239	  } else {
240	    0.0	<-- UNCOVERED
241	  }
242	  // 加粗字符通常密度更高
243	  density > 0.35
244	}
   	…

248	pub fn optimize_reading_order(
249	  blocks : Array[DocumentBlock],
250	  num_columns : Int,
251	) -> Array[Int] {
252	  // 按列分组，然后每列内按 y 排序，再按 x 排序
253	  let indices : Array[Int] = []
254	  for col in 0..<num_columns {
255	    let col_indices : Array[Int] = []
256	    for i in 0..<blocks.length() {
257	      if blocks[i].column_index == col {
258	        col_indices.push(i)
259	      }
260	    }
261	    // 列内按 y 排序，再按 x 排序（冒泡排序）
262	    for _ in 0..<col_indices.length() {
263	      for j in 0..<(col_indices.length() - 1) {
264	        let b1 = blocks[col_indices[j]]	<-- UNCOVERED
265	        let b2 = blocks[col_indices[j + 1]]
266	        let should_swap = if b1.y > b2.y {
267	          true	<-- UNCOVERED
268	        } else if b1.y == b2.y && b1.x > b2.x {
269	          true
270	        } else {
271	          false	<-- UNCOVERED
272	        }
273	        if should_swap {
274	          let temp = col_indices[j]	<-- UNCOVERED
275	          col_indices[j] = col_indices[j + 1]
276	          col_indices[j + 1] = temp
277	        }
278	      }
279	    }
280	    for idx in col_indices {
281	      indices.push(idx)
282	    }
283	  }
284	  // 如果没有检测到列，按全局 y/x 排序
285	  if indices.length() == 0 {
286	    for i in 0..<blocks.length() {	<-- UNCOVERED
287	      indices.push(i)
288	    }
289	    for _ in 0..<indices.length() {	<-- UNCOVERED
290	      for j in 0..<(indices.length() - 1) {
291	        let b1 = blocks[indices[j]]
292	        let b2 = blocks[indices[j + 1]]
293	        let should_swap = if b1.y > b2.y {
294	          true	<-- UNCOVERED
295	        } else if b1.y == b2.y && b1.x > b2.x {
296	          true
297	        } else {
298	          false	<-- UNCOVERED
299	        }
300	        if should_swap {
301	          let temp = indices[j]	<-- UNCOVERED
302	          indices[j] = indices[j + 1]
303	          indices[j + 1] = temp
304	        }
305	      }
306	    }
307	  }
308	  indices
309	}
   	…

313	pub fn detect_paragraphs(
314	  blocks : Array[DocumentBlock],
315	  max_line_spacing_ratio? : Double = 2.0,
316	) -> Array[Array[Int]] {
317	  let paragraphs : Array[Array[Int]] = []
318	  let current_paragraph : Array[Int] = []
319	  for i in 0..<blocks.length() {
320	    let block = blocks[i]
321	    // 只处理段落类型的块
322	    if block.block_type != DocumentBlockType::Paragraph &&
323	      block.block_type != DocumentBlockType::ListItem {
324	      if current_paragraph.length() > 0 {
325	        paragraphs.push(current_paragraph)	<-- UNCOVERED
326	        current_paragraph.clear()
327	      }
328	      continue
329	    }
330	    // 检查是否是新段落的开始
331	    let is_new_paragraph = if i > 0 {
332	      let prev = blocks[i - 1]	<-- UNCOVERED
333	      // 行间距过大
334	      let spacing_too_large = block.line_spacing > max_line_spacing_ratio
335	      // 缩进变化
336	      let indent_changed = block.indent_level != prev.indent_level
337	      // 前一个块不是段落
338	      let prev_not_paragraph = prev.block_type != DocumentBlockType::Paragraph &&
339	        prev.block_type != DocumentBlockType::ListItem
340	      spacing_too_large || indent_changed || prev_not_paragraph
341	    } else {
342	      true	<-- UNCOVERED
343	    }
344	    if is_new_paragraph && current_paragraph.length() > 0 {	<-- UNCOVERED
345	      paragraphs.push(current_paragraph)
346	      current_paragraph.clear()
347	    }
348	    current_paragraph.push(i)	<-- UNCOVERED
349	  }
350	  if current_paragraph.length() > 0 {
351	    paragraphs.push(current_paragraph)	<-- UNCOVERED
352	  }
353	  paragraphs
354	}
   	…

378	pub fn build_document_structure_tree(
379	  blocks : Array[DocumentBlock],
380	) -> Array[DocumentTreeNode] {
381	  // 按阅读顺序排序
382	  let order = optimize_reading_order(blocks, 1)
383	  let ordered_blocks : Array[DocumentBlock] = []
384	  for idx in order {
385	    ordered_blocks.push(blocks[idx])
386	  }
387	  // 构建树（基于标题层级）
388	  let roots : Array[DocumentTreeNode] = []
389	  let stack : Array[DocumentTreeNode] = []
390	  let mut node_id = 0
391	  for block in ordered_blocks {
392	    // 计算深度（基于块类型）
393	    let depth = match block.block_type {
394	      DocumentBlockType::Title => 0
395	      DocumentBlockType::Heading1 => 1	<-- UNCOVERED
396	      DocumentBlockType::Heading2 => 2
397	      DocumentBlockType::Heading3 => 3	<-- UNCOVERED
398	      _ => 4
399	    }
400	    let node = DocumentTreeNode::{ node_id, block, children: [], depth }
401	    node_id += 1
402	    // 弹出栈中深度 >= 当前深度的节点
403	    while stack.length() > 0 && stack[stack.length() - 1].depth >= depth {
404	      let _ = stack.pop()	<-- UNCOVERED
405	    }
406	    // 如果栈为空，添加到根节点
407	    if stack.length() == 0 {
408	      roots.push(node)
409	    } else {
410	      // 添加为栈顶节点的子节点
411	      let parent = stack[stack.length() - 1]	<-- UNCOVERED
412	      parent.children.push(node)
413	    }
414	    // 将当前节点压入栈
415	    stack.push(node)
416	  }
417	  roots
418	}
   	…

422	pub fn extract_document_text(
423	  blocks : Array[DocumentBlock],
424	  include_headers? : Bool = true,
425	  include_footers? : Bool = false,
426	) -> String {
427	  let order = optimize_reading_order(blocks, 1)
428	  let mut text = ""
429	  for idx in order {
430	    let block = blocks[idx]
431	    // 过滤页眉页脚
432	    if !include_headers && block.block_type == DocumentBlockType::Header {
433	      continue	<-- UNCOVERED
434	    }
435	    if !include_footers && block.block_type == DocumentBlockType::Footer {
436	      continue	<-- UNCOVERED
437	    }
438	    if block.text.length() > 0 {
439	      text += block.text	<-- UNCOVERED
440	      // 标题和段落之间加换行
441	      match block.block_type {	<-- UNCOVERED
442	        DocumentBlockType::Title => text += "\n\n"
443	        DocumentBlockType::Heading1 => text += "\n\n"
444	        DocumentBlockType::Heading2 => text += "\n\n"
445	        DocumentBlockType::Heading3 => text += "\n\n"
446	        DocumentBlockType::Paragraph => text += "\n"
447	        DocumentBlockType::ListItem => text += "\n"
448	        _ => text += " "
449	      }
450	    }
451	  }
452	  text
453	}
   	…

457	pub fn document_statistics_enhanced(
458	  blocks : Array[DocumentBlock],
459	) -> (Int, Int, Int, Int, Int, Int, Int) {
460	  let mut titles = 0
461	  let mut headings = 0
462	  let mut paragraphs = 0
463	  let mut list_items = 0
464	  let mut tables = 0
465	  let mut images = 0
466	  let mut total_chars = 0
467	  for block in blocks {
468	    match block.block_type {
469	      DocumentBlockType::Title => titles += 1
470	      DocumentBlockType::Heading1 => headings += 1	<-- UNCOVERED
471	      DocumentBlockType::Heading2 => headings += 1
472	      DocumentBlockType::Heading3 => headings += 1	<-- UNCOVERED
473	      DocumentBlockType::Paragraph => paragraphs += 1
474	      DocumentBlockType::ListItem => list_items += 1
475	      DocumentBlockType::Table => tables += 1
476	      DocumentBlockType::Image => images += 1
477	      _ => ignore(())
478	    }
479	    total_chars += block.text.length()
480	  }
481	  (titles, headings, paragraphs, list_items, tables, images, total_chars)
482	}
   	…

486	pub fn understand_document_enhanced(
487	  img : @types.Image,
488	  config? : DocumentUnderstandingEnhancedConfig,
489	) -> (Array[DocumentBlock], Array[DocumentTreeNode], String) raise @types.LoadError {
490	  // 输入验证
491	  if img.width <= 0 || img.height <= 0 {
492	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
493	      "understand_document_enhanced: 图像尺寸必须大于0",
494	    )
495	  }
496	  if img.channels < 1 || img.channels > 4 {
497	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
498	      "understand_document_enhanced: 通道数必须在1-4之间",
499	    )
500	  }
501	  let expected_size = img.width * img.height * img.channels
502	  if img.data.length() != expected_size {
503	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
504	      "understand_document_enhanced: 数据大小不匹配，期望 " +
505	      expected_size.to_string() +	<-- UNCOVERED
506	      "，实际 " +
507	      img.data.length().to_string(),	<-- UNCOVERED
508	    )
509	  }
510	  let cfg = match config {
511	    Some(c) => c	<-- UNCOVERED
512	    None => document_understanding_enhanced_default_config()
513	  }
514	  // 1. 增强版面分析
515	  let blocks = analyze_layout_enhanced(img, config=cfg)
516	  // 2. 多栏检测
517	  let num_columns = if cfg.enable_column_detection {
518	    let preprocessed = ocr_preprocess(img)
519	    let (cols, _) = detect_columns(preprocessed)
520	    cols
521	  } else {
522	    1	<-- UNCOVERED
523	  }
524	  // 3. 优化阅读顺序
525	  let order = optimize_reading_order(blocks, num_columns)
526	  // 更新块的阅读顺序
527	  for i in 0..<order.length() {
528	    blocks[order[i]].reading_order = i
529	  }
530	  // 4. 构建文档结构树
531	  let tree = if cfg.enable_hierarchy {
532	    build_document_structure_tree(blocks)
533	  } else {
534	    []	<-- UNCOVERED
535	  }
536	  // 5. 提取纯文本
537	  let text = extract_document_text(
538	    blocks,
539	    include_headers=true,
540	    include_footers=false,
541	  )
542	  (blocks, tree, text)
543	}
   	…

37 uncovered line(s) in src\process\ocr\english_ocr.mbt:

29	pub fn generate_english_templates(
30	  case_sensitive? : Bool = false,
31	) -> Array[(String, Array[Double])] {
32	  let templates : Array[(String, Array[Double])] = []
33	  // 数字 0-9
34	  for digit in 0..<10 {
35	    let template = generate_char_template_simple(digit.to_string())
36	    templates.push((digit.to_string(), template))
37	  }
38	  // 大写字母 A-Z
39	  for code in 65..<91 {
40	    let char = code.to_char().map(c => c.to_string()).unwrap_or("")
41	    let template = generate_char_template_simple(char)
42	    templates.push((char, template))
43	  }
44	  // 如果区分大小写，添加小写字母
45	  if case_sensitive {
46	    for code in 97..<123 {	<-- UNCOVERED
47	      let char = code.to_char().map(c => c.to_string()).unwrap_or("")
48	      let template = generate_char_template_simple(char)
49	      templates.push((char, template))
50	    }
51	  }
52	  templates
53	}
  	…

 57	fn generate_char_template_simple(char : String) -> Array[Double] {
 58	  let size = 8
 59	  let features : Array[Double] = Array::make(size * size, 0.0)
 60	  // 简化的字符模式（基于常见字符形状）
 61	  match char {
 62	    "0" | "O" | "o" => {
 63	      // 圆形：四周有像素，中间空
 64	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
 65	      set_char_pixels(features, size, 0, 1, 1, 6) // 左
 66	      set_char_pixels(features, size, 7, 1, 1, 6) // 右
 67	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
 68	    }
 69	    "1" | "I" | "l" =>
 70	      // 竖线
 71	      set_char_pixels(features, size, 3, 0, 2, 8) // 中竖
 72	    "2" | "Z" | "z" => {
 73	      // Z形：上、对角、下
 74	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
 75	      set_char_pixels(features, size, 5, 1, 1, 1)
 76	      set_char_pixels(features, size, 4, 2, 1, 1)
 77	      set_char_pixels(features, size, 3, 3, 1, 1)
 78	      set_char_pixels(features, size, 2, 4, 1, 1)
 79	      set_char_pixels(features, size, 1, 5, 1, 1)
 80	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
 81	    }
 82	    "3" => {
 83	      // 3形：上、中、下、右
 84	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
 85	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
 86	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
 87	      set_char_pixels(features, size, 6, 0, 1, 8) // 右
 88	    }
 89	    "4" | "A" => {
 90	      // A形：倒V + 横
 91	      set_char_pixels(features, size, 1, 0, 1, 4) // 左上
 92	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
 93	      set_char_pixels(features, size, 2, 1, 1, 1)
 94	      set_char_pixels(features, size, 5, 1, 1, 1)
 95	      set_char_pixels(features, size, 3, 2, 2, 1) // 顶
 96	      set_char_pixels(features, size, 1, 4, 6, 1) // 中横
 97	    }
 98	    "5" | "S" | "s" => {
 99	      // S形：上、左、中、右、下
100	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
101	      set_char_pixels(features, size, 0, 0, 1, 4) // 左上
102	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
103	      set_char_pixels(features, size, 7, 4, 1, 4) // 右下
104	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
105	    }
106	    "6" | "b" => {
107	      // b形：左竖 + 上右 + 下圆
108	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
109	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
110	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
111	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
112	      set_char_pixels(features, size, 6, 4, 1, 4) // 右下
113	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
114	    }
115	    "7" | "T" | "t" => {
116	      // T形：上横 + 中竖
117	      set_char_pixels(features, size, 0, 0, 8, 1) // 上
118	      set_char_pixels(features, size, 3, 1, 2, 7) // 中竖
119	    }
120	    "8" | "B" => {
121	      // B形：左竖 + 双圆
122	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
123	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
124	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
125	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
126	      set_char_pixels(features, size, 6, 4, 1, 4) // 右下
127	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
128	    }
129	    "9" | "g" | "q" => {
130	      // g形：上圆 + 下左 + 尾
131	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
132	      set_char_pixels(features, size, 0, 0, 1, 4) // 左上
133	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
134	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
135	      set_char_pixels(features, size, 0, 4, 1, 4) // 左下
136	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
137	      set_char_pixels(features, size, 6, 6, 1, 2) // 尾
138	    }
139	    "C" | "c" | "(" => {
140	      // C形：上、左、下
141	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
142	      set_char_pixels(features, size, 0, 1, 1, 6) // 左
143	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
144	    }
145	    "D" | "d" => {
146	      // D形：左竖 + 右弧
147	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
148	      set_char_pixels(features, size, 1, 0, 5, 1) // 上
149	      set_char_pixels(features, size, 6, 1, 1, 6) // 右
150	      set_char_pixels(features, size, 1, 7, 5, 1) // 下
151	    }
152	    "E" => {
153	      // E形：左竖 + 三横
154	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
155	      set_char_pixels(features, size, 1, 0, 7, 1) // 上
156	      set_char_pixels(features, size, 1, 3, 5, 1) // 中
157	      set_char_pixels(features, size, 1, 7, 7, 1) // 下
158	    }
159	    "F" | "f" => {
160	      // F形：左竖 + 上中横
161	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
162	      set_char_pixels(features, size, 1, 0, 7, 1) // 上
163	      set_char_pixels(features, size, 1, 3, 5, 1) // 中
164	    }
165	    "G" => {
166	      // G形：C + 中右
167	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
168	      set_char_pixels(features, size, 0, 1, 1, 6) // 左
169	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
170	      set_char_pixels(features, size, 6, 4, 1, 3) // 右中
171	      set_char_pixels(features, size, 4, 4, 3, 1) // 中横
172	    }
173	    "H" | "h" => {
174	      // H形：双竖 + 中横
175	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
176	      set_char_pixels(features, size, 7, 0, 1, 8) // 右
177	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
178	    }
179	    "J" | "j" => {
180	      // J形：右竖 + 下左
181	      set_char_pixels(features, size, 7, 0, 1, 7) // 右
182	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
183	      set_char_pixels(features, size, 0, 5, 1, 3) // 左下
184	    }
185	    "K" | "k" => {
186	      // K形：左竖 + 对角
187	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
188	      set_char_pixels(features, size, 6, 0, 1, 1)
189	      set_char_pixels(features, size, 5, 1, 1, 1)
190	      set_char_pixels(features, size, 4, 2, 1, 1)
191	      set_char_pixels(features, size, 3, 3, 1, 1)
192	      set_char_pixels(features, size, 4, 4, 1, 1)
193	      set_char_pixels(features, size, 5, 5, 1, 1)
194	      set_char_pixels(features, size, 6, 6, 1, 2)
195	    }
196	    "L" => {
197	      // L形：左竖 + 下横
198	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
199	      set_char_pixels(features, size, 0, 7, 8, 1) // 下
200	    }
201	    "M" | "m" => {
202	      // M形：双竖 + V
203	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
204	      set_char_pixels(features, size, 7, 0, 1, 8) // 右
205	      set_char_pixels(features, size, 1, 0, 1, 3)
206	      set_char_pixels(features, size, 6, 0, 1, 3)
207	      set_char_pixels(features, size, 2, 1, 1, 2)
208	      set_char_pixels(features, size, 5, 1, 1, 2)
209	      set_char_pixels(features, size, 3, 3, 2, 2) // 中
210	    }
211	    "N" | "n" => {
212	      // N形：双竖 + 对角
213	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
214	      set_char_pixels(features, size, 7, 0, 1, 8) // 右
215	      set_char_pixels(features, size, 6, 1, 1, 1)
216	      set_char_pixels(features, size, 5, 2, 1, 1)
217	      set_char_pixels(features, size, 4, 3, 1, 1)
218	      set_char_pixels(features, size, 3, 4, 1, 1)
219	      set_char_pixels(features, size, 2, 5, 1, 1)
220	      set_char_pixels(features, size, 1, 6, 1, 1)
221	    }
222	    "P" | "p" => {
223	      // P形：左竖 + 上右
224	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
225	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
226	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
227	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
228	    }
229	    "Q" => {
230	      // Q形：O + 尾
231	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
232	      set_char_pixels(features, size, 0, 1, 1, 6) // 左
233	      set_char_pixels(features, size, 7, 1, 1, 6) // 右
234	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
235	      set_char_pixels(features, size, 5, 6, 1, 1)
236	      set_char_pixels(features, size, 6, 7, 1, 1)
237	    }
238	    "R" | "r" => {
239	      // R形：P + 右下对角
240	      set_char_pixels(features, size, 0, 0, 1, 8) // 左
241	      set_char_pixels(features, size, 1, 0, 6, 1) // 上
242	      set_char_pixels(features, size, 6, 0, 1, 4) // 右上
243	      set_char_pixels(features, size, 1, 3, 6, 1) // 中
244	      set_char_pixels(features, size, 5, 4, 1, 1)
245	      set_char_pixels(features, size, 4, 5, 1, 1)
246	      set_char_pixels(features, size, 3, 6, 1, 1)
247	      set_char_pixels(features, size, 2, 7, 1, 1)
248	    }
249	    "U" | "u" => {
250	      // U形：双竖 + 下
251	      set_char_pixels(features, size, 0, 0, 1, 7) // 左
252	      set_char_pixels(features, size, 7, 0, 1, 7) // 右
253	      set_char_pixels(features, size, 1, 7, 6, 1) // 下
254	    }
255	    "V" | "v" => {
256	      // V形：倒V
257	      set_char_pixels(features, size, 0, 0, 1, 4)
258	      set_char_pixels(features, size, 7, 0, 1, 4)
259	      set_char_pixels(features, size, 1, 4, 1, 2)
260	      set_char_pixels(features, size, 6, 4, 1, 2)
261	      set_char_pixels(features, size, 2, 6, 1, 1)
262	      set_char_pixels(features, size, 5, 6, 1, 1)
263	      set_char_pixels(features, size, 3, 7, 2, 1) // 底
264	    }
265	    "W" | "w" => {
266	      // W形：双V
267	      set_char_pixels(features, size, 0, 0, 1, 5)
268	      set_char_pixels(features, size, 7, 0, 1, 5)
269	      set_char_pixels(features, size, 1, 5, 1, 2)
270	      set_char_pixels(features, size, 6, 5, 1, 2)
271	      set_char_pixels(features, size, 2, 7, 1, 1)
272	      set_char_pixels(features, size, 5, 7, 1, 1)
273	      set_char_pixels(features, size, 3, 5, 2, 2) // 中
274	    }
275	    "X" | "x" => {
276	      // X形：双对角
277	      set_char_pixels(features, size, 0, 0, 1, 1)
278	      set_char_pixels(features, size, 7, 0, 1, 1)
279	      set_char_pixels(features, size, 1, 1, 1, 1)
280	      set_char_pixels(features, size, 6, 1, 1, 1)
281	      set_char_pixels(features, size, 2, 2, 1, 1)
282	      set_char_pixels(features, size, 5, 2, 1, 1)
283	      set_char_pixels(features, size, 3, 3, 2, 2) // 中
284	      set_char_pixels(features, size, 2, 5, 1, 1)
285	      set_char_pixels(features, size, 5, 5, 1, 1)
286	      set_char_pixels(features, size, 1, 6, 1, 1)
287	      set_char_pixels(features, size, 6, 6, 1, 1)
288	      set_char_pixels(features, size, 0, 7, 1, 1)
289	      set_char_pixels(features, size, 7, 7, 1, 1)
290	    }
291	    "Y" | "y" => {
292	      // Y形：上双竖 + 下中竖
293	      set_char_pixels(features, size, 0, 0, 1, 4)
294	      set_char_pixels(features, size, 7, 0, 1, 4)
295	      set_char_pixels(features, size, 1, 1, 1, 2)
296	      set_char_pixels(features, size, 6, 1, 1, 2)
297	      set_char_pixels(features, size, 2, 3, 1, 1)
298	      set_char_pixels(features, size, 5, 3, 1, 1)
299	      set_char_pixels(features, size, 3, 4, 2, 4) // 中竖
300	    }
301	    "+" | "=" | "-" => {	<-- UNCOVERED
302	      // 加号/等号/横
303	      set_char_pixels(features, size, 3, 0, 2, 8) // 竖	<-- UNCOVERED
304	      set_char_pixels(features, size, 0, 3, 8, 2) // 横
305	    }
306	    "." | "," =>
307	      // 点/逗号
308	      set_char_pixels(features, size, 3, 6, 2, 2)	<-- UNCOVERED
309	    ":" | ";" => {
310	      // 冒号/分号
311	      set_char_pixels(features, size, 3, 2, 2, 2)	<-- UNCOVERED
312	      set_char_pixels(features, size, 3, 5, 2, 2)
313	    }
314	    "!" => {	<-- UNCOVERED
315	      // 感叹号
316	      set_char_pixels(features, size, 3, 0, 2, 5)	<-- UNCOVERED
317	      set_char_pixels(features, size, 3, 7, 2, 1)
318	    }
319	    "?" => {	<-- UNCOVERED
320	      // 问号
321	      set_char_pixels(features, size, 1, 0, 6, 1) // 上	<-- UNCOVERED
322	      set_char_pixels(features, size, 6, 0, 1, 3) // 右上
323	      set_char_pixels(features, size, 3, 3, 2, 2) // 中
324	      set_char_pixels(features, size, 3, 6, 2, 2) // 点
325	    }
326	    "/" => {	<-- UNCOVERED
327	      // 斜杠
328	      set_char_pixels(features, size, 6, 0, 1, 1)	<-- UNCOVERED
329	      set_char_pixels(features, size, 5, 1, 1, 1)
330	      set_char_pixels(features, size, 4, 2, 1, 1)
331	      set_char_pixels(features, size, 3, 3, 1, 1)
332	      set_char_pixels(features, size, 2, 4, 1, 1)
333	      set_char_pixels(features, size, 1, 5, 1, 1)
334	      set_char_pixels(features, size, 0, 6, 1, 2)
335	    }
336	    "\\" => {	<-- UNCOVERED
337	      // 反斜杠
338	      set_char_pixels(features, size, 0, 0, 1, 2)	<-- UNCOVERED
339	      set_char_pixels(features, size, 1, 2, 1, 1)
340	      set_char_pixels(features, size, 2, 3, 1, 1)
341	      set_char_pixels(features, size, 3, 4, 1, 1)
342	      set_char_pixels(features, size, 4, 5, 1, 1)
343	      set_char_pixels(features, size, 5, 6, 1, 1)
344	      set_char_pixels(features, size, 6, 7, 1, 1)
345	    }
346	    _ =>
347	      // 默认：中心点
348	      set_char_pixels(features, size, 3, 3, 2, 2)	<-- UNCOVERED
349	  }
350	  features
351	}
   	…

376	pub fn recognize_english(
377	  img : @types.Image,
378	  config? : EnglishOcrConfig,
379	) -> OcrResult raise @types.LoadError {
380	  // 输入验证
381	  if img.width <= 0 || img.height <= 0 {
382	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
383	      "recognize_english: 图像尺寸必须大于0",
384	    )
385	  }
386	  if img.channels != 1 {
387	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
388	      "recognize_english: 仅支持单通道灰度图",
389	    )
390	  }
391	  let expected_size = img.width * img.height
392	  if img.data.length() != expected_size {
393	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
394	      "recognize_english: 数据大小不匹配，期望 " +
395	      expected_size.to_string() +	<-- UNCOVERED
396	      "，实际 " +
397	      img.data.length().to_string(),	<-- UNCOVERED
398	    )
399	  }
400	
401	  let cfg = match config {
402	    Some(c) => c	<-- UNCOVERED
403	    None => english_ocr_default_config()
404	  }
405	  // 配置验证
406	  if cfg.min_char_width <= 0 {
407	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
408	      "recognize_english: min_char_width必须大于0",
409	    )
410	  }
411	  if cfg.max_char_width < cfg.min_char_width {
412	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
413	      "recognize_english: max_char_width不能小于min_char_width",
414	    )
415	  }
416	  if cfg.target_size <= 0 {
417	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
418	      "recognize_english: target_size必须大于0",
419	    )
420	  }
421	  // 1. 预处理
422	  let preprocessed = ocr_preprocess(img)
423	  // 2. 字符分割
424	  let char_regions = complete_char_segmentation(
425	    preprocessed,
426	    min_char_width=cfg.min_char_width,
427	    max_char_width=cfg.max_char_width,
428	  )
429	  // 3. 生成模板
430	  let templates = generate_english_templates(case_sensitive=cfg.case_sensitive)
431	  // 4. 对每个字符进行识别
432	  let char_results : Array[OcrCharResult] = []
433	  let mut text = ""
434	  let mut total_confidence = 0.0
435	  for _tup0 in char_regions {
436	    let (x, y, w, h) = _tup0
437	    // 提取字符图像
438	    let char_img = extract_char_region_english(
439	      preprocessed,
440	      x,
441	      y,
442	      w,
443	      h,
444	      cfg.target_size,
445	    )
446	    // 提取特征
447	    let features = extract_char_features_english(char_img, 8)
448	    // 模板匹配
449	    let result = match_template_english(
450	      features,
451	      templates,
452	      cfg.distance_metric,
453	    )
454	    text += result.0
455	    total_confidence += result.1
456	    char_results.push(OcrCharResult::{
457	      char: result.0,
458	      confidence: result.1,
459	      x,
460	      y,
461	      width: w,
462	      height: h,
463	    })
464	  }
465	  let avg_confidence = if char_results.length() > 0 {
466	    total_confidence / char_results.length().to_double()
467	  } else {
468	    0.0	<-- UNCOVERED
469	  }
470	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
471	}
   	…

475	fn extract_char_region_english(
476	  img : @types.Image,
477	  x : Int,
478	  y : Int,
479	  width : Int,
480	  height : Int,
481	  target_size : Int,
482	) -> @types.Image {
483	  let w = img.width
484	  let h = img.height
485	  let crop_w = if x + width > w { w - x } else { width }
486	  let crop_h = if y + height > h { h - y } else { height }
487	  if crop_w <= 0 || crop_h <= 0 {
488	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
489	    return @types.Image::{
490	      width: target_size,
491	      height: target_size,
492	      channels: 1,
493	      data: Bytes::from_array(empty),	<-- UNCOVERED
494	    }
495	  }
496	  let scale = if crop_w > crop_h {
497	    target_size.to_double() / crop_w.to_double()
498	  } else {
499	    target_size.to_double() / crop_h.to_double()
500	  }
501	  let new_w = max_eng(1, (crop_w.to_double() * scale).to_int())
502	  let new_h = max_eng(1, (crop_h.to_double() * scale).to_int())
503	  let offset_x = (target_size - new_w) / 2
504	  let offset_y = (target_size - new_h) / 2
505	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
506	  let data = img.data
507	  for py in 0..<new_h {
508	    for px in 0..<new_w {
509	      let src_x = x + px * crop_w / new_w
510	      let src_y = y + py * crop_h / new_h
511	      if src_x < w && src_y < h {
512	        out[(offset_y + py) * target_size + (offset_x + px)] = data[src_y * w +
513	          src_x]
514	      }
515	    }
516	  }
517	  @types.Image::{
518	    width: target_size,
519	    height: target_size,
520	    channels: 1,
521	    data: Bytes::from_array(out),
522	  }
523	}
   	…

527	fn extract_char_features_english(
528	  char_img : @types.Image,
529	  grid_size : Int,
530	) -> Array[Double] {
531	  let w = char_img.width
532	  let h = char_img.height
533	  let data = char_img.data
534	  let cell_w = w / grid_size
535	  let cell_h = h / grid_size
536	  let features : Array[Double] = []
537	  for r in 0..<grid_size {
538	    for c in 0..<grid_size {
539	      let mut count = 0
540	      let mut total = 0
541	      for py in 0..<cell_h {
542	        for px in 0..<cell_w {
543	          let sx = c * cell_w + px
544	          let sy = r * cell_h + py
545	          if sx < w && sy < h {
546	            total += 1
547	            if data[sy * w + sx].to_int() > 128 {
548	              count += 1
549	            }
550	          }
551	        }
552	      }
553	      let density = if total > 0 {
554	        count.to_double() / total.to_double()
555	      } else {
556	        0.0	<-- UNCOVERED
557	      }
558	      features.push(density)
559	    }
560	  }
561	  features
562	}
   	…

566	fn match_template_english(
567	  features : Array[Double],
568	  templates : Array[(String, Array[Double])],
569	  distance_metric : String,
570	) -> (String, Double) {
571	  let mut best_label = "?"
572	  let mut best_dist = 1000000.0
573	  for _tup1 in templates {
574	    let (label, template) = _tup1
575	    let dist = match distance_metric {
576	      "manhattan" => manhattan_distance_eng(features, template)	<-- UNCOVERED
577	      "cosine" => cosine_distance_eng(features, template)
578	      _ => euclidean_distance_eng(features, template)
579	    }
580	    if dist < best_dist {
581	      best_dist = dist
582	      best_label = label
583	    }
584	  }
585	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
586	  (best_label, confidence)
587	}
   	…

591	fn euclidean_distance_eng(a : Array[Double], b : Array[Double]) -> Double {
592	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
593	  let mut sum_sq = 0.0
594	  for i in 0..<n {
595	    let diff = a[i] - b[i]
596	    sum_sq += diff * diff
597	  }
598	  sum_sq.sqrt()
599	}
   	…

603	fn manhattan_distance_eng(a : Array[Double], b : Array[Double]) -> Double {
604	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
605	  let mut sum = 0.0
606	  for i in 0..<n {
607	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
608	  }
609	  sum
610	}
   	…

614	fn cosine_distance_eng(a : Array[Double], b : Array[Double]) -> Double {
615	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
616	  let mut dot = 0.0
617	  let mut norm_a = 0.0
618	  let mut norm_b = 0.0
619	  for i in 0..<n {
620	    dot += a[i] * b[i]	<-- UNCOVERED
621	    norm_a += a[i] * a[i]
622	    norm_b += b[i] * b[i]
623	  }
624	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
625	  if denom > 0.0 {
626	    1.0 - dot / denom	<-- UNCOVERED
627	  } else {
628	    1.0	<-- UNCOVERED
629	  }
630	}
   	…

634	fn max_eng(a : Int, b : Int) -> Int {
635	  if a > b {
636	    a	<-- UNCOVERED
637	  } else {
638	    b
639	  }
640	}
   	…

86 uncovered line(s) in src\process\ocr\enhanced_layout_analysis.mbt:

110	pub fn detect_layout_type(
111	  text_lines : Array[TextLineAttributes],
112	  page_width : Int,
113	  page_height : Int,
114	) -> LayoutType raise @types.LoadError {
115	  // 输入验证
116	  if text_lines.length() == 0 {	<-- UNCOVERED
117	    raise @types.LoadError::DecodeFailed(
118	      "detect_layout_type: 文本行列表不能为空",
119	    )
120	  }
121	  if page_width <= 0 {
122	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
123	      "detect_layout_type: 页面宽度必须大于0",
124	    )
125	  }
126	  if page_height <= 0 {
127	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
128	      "detect_layout_type: 页面高度必须大于0",
129	    )
130	  }
131	  // 统计每行的水平覆盖范围
132	  let mut full_width_count = 0
133	  let mut left_half_count = 0
134	  let mut right_half_count = 0
135	  let mid_x = page_width / 2
136	  for line in text_lines {
137	    let line_center = line.x + line.width / 2	<-- UNCOVERED
138	    if line.width.to_double() > page_width.to_double() * 0.7 {
139	      full_width_count += 1
140	    } else if line_center < mid_x {
141	      left_half_count += 1
142	    } else {
143	      right_half_count += 1	<-- UNCOVERED
144	    }
145	  }
146	  let total = text_lines.length().to_double()	<-- UNCOVERED
147	  let full_ratio = full_width_count.to_double() / total
148	  let left_ratio = left_half_count.to_double() / total
149	  let right_ratio = right_half_count.to_double() / total
150	  if full_ratio > 0.8 {
151	    LayoutType::SingleColumn	<-- UNCOVERED
152	  } else if left_ratio > 0.3 && right_ratio > 0.3 && full_ratio < 0.3 {
153	    LayoutType::DoubleColumn
154	  } else if full_ratio > 0.3 && (left_ratio > 0.2 || right_ratio > 0.2) {
155	    LayoutType::MixedLayout
156	  } else {
157	    LayoutType::SingleColumn	<-- UNCOVERED
158	  }
159	}
   	…

163	pub fn detect_header_footer(
164	  text_lines : Array[TextLineAttributes],
165	  page_height : Int,
166	) -> (Int, Int) raise @types.LoadError {
167	  // 输入验证
168	  if text_lines.length() == 0 {	<-- UNCOVERED
169	    raise @types.LoadError::DecodeFailed(
170	      "detect_header_footer: 文本行列表不能为空",
171	    )
172	  }
173	  if page_height <= 0 {
174	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
175	      "detect_header_footer: 页面高度必须大于0",
176	    )
177	  }
178	  // 找出最顶部和最底部的文本行
179	  let mut min_y = page_height
180	  let mut max_y = 0
181	  for line in text_lines {
182	    if line.y < min_y {	<-- UNCOVERED
183	      min_y = line.y
184	    }
185	    if line.y + line.height > max_y {
186	      max_y = line.y + line.height	<-- UNCOVERED
187	    }
188	  }
189	  // 页眉：页面顶部 10% 区域内的文本
190	  let header_threshold = page_height / 10
191	  let mut header_bottom = 0
192	  for line in text_lines {
193	    if line.y < header_threshold && line.y + line.height > header_bottom {	<-- UNCOVERED
194	      header_bottom = line.y + line.height
195	    }
196	  }
197	  // 页脚：页面底部 10% 区域内的文本
198	  let footer_threshold = page_height * 9 / 10
199	  let mut footer_top = page_height
200	  for line in text_lines {
201	    if line.y > footer_threshold && line.y < footer_top {	<-- UNCOVERED
202	      footer_top = line.y
203	    }
204	  }
205	  (header_bottom, footer_top)
206	}
   	…

210	pub fn detect_column_boundaries(
211	  text_lines : Array[TextLineAttributes],
212	  page_width : Int,
213	  layout_type : LayoutType,
214	) -> Array[Int] {
215	  ignore(text_lines)	<-- UNCOVERED
216	  let boundaries : Array[Int] = []
217	  boundaries.push(0)	<-- UNCOVERED
218	  match layout_type {
219	    LayoutType::DoubleColumn => {
220	      // 找中间空白区域
221	      let mid_x = page_width / 2
222	      boundaries.push(mid_x - 30)	<-- UNCOVERED
223	      boundaries.push(mid_x + 30)
224	    }
225	    LayoutType::TripleColumn => {	<-- UNCOVERED
226	      let col_width = page_width / 3
227	      boundaries.push(col_width - 20)	<-- UNCOVERED
228	      boundaries.push(col_width + 20)
229	      boundaries.push(col_width * 2 - 20)
230	      boundaries.push(col_width * 2 + 20)
231	    }
232	    _ => boundaries.push(page_width)	<-- UNCOVERED
233	  }
234	  boundaries.push(page_width)	<-- UNCOVERED
235	  boundaries
236	}
   	…

240	pub fn is_heading_line(
241	  line : TextLineAttributes,
242	  avg_font_size : Double,
243	) -> (Bool, Int) {
244	  let size_ratio = line.font_size / avg_font_size	<-- UNCOVERED
245	  if line.font_weight == "bold" && size_ratio > 1.3 {
246	    if size_ratio > 2.0 {	<-- UNCOVERED
247	      (true, 1)
248	    } else if size_ratio > 1.6 {
249	      (true, 2)
250	    } else {
251	      (true, 3)	<-- UNCOVERED
252	    }
253	  } else if size_ratio > 1.5 {	<-- UNCOVERED
254	    if size_ratio > 2.5 {
255	      (true, 1)
256	    } else if size_ratio > 2.0 {
257	      (true, 2)
258	    } else {
259	      (true, 3)	<-- UNCOVERED
260	    }
261	  } else {
262	    (false, 0)	<-- UNCOVERED
263	  }
264	}
   	…

268	pub fn detect_list_item(line : TextLineAttributes) -> ListItemInfo {
269	  let text = line.text	<-- UNCOVERED
270	  if text.length() == 0 {
271	    return ListItemInfo::{
272	      item_type: ListItemType::None,
273	      marker: "",
274	      level: 0,
275	      number: 0,
276	    }
277	  }
278	  // 检测项目符号
279	  let first_char = text[0]
280	  if first_char == '•' ||
281	    first_char == '-' ||
282	    first_char == '*' ||
283	    first_char == '○' ||
284	    first_char == '●' {
285	    let level = if line.is_indented { line.indent_level } else { 0 }	<-- UNCOVERED
286	    return ListItemInfo::{
287	      item_type: ListItemType::Bullet,
288	      marker: "•",
289	      level,
290	      number: 0,
291	    }
292	  }
293	  // 检测数字编号 (1. 2. 3. 或 1) 2) 3))
294	  let mut num_end = 0
295	  let mut _has_number = true
296	  for i in 0..<text.length() {	<-- UNCOVERED
297	    let c = text[i]
298	    if c >= '0' && c <= '9' {
299	      num_end = i + 1	<-- UNCOVERED
300	    } else {
301	      break	<-- UNCOVERED
302	    }
303	  }
304	  if num_end > 0 && num_end < text.length() {	<-- UNCOVERED
305	    let next_char = text[num_end]
306	    if next_char == '.' || next_char == ')' || next_char == '、' {
307	      let number_str = "1"	<-- UNCOVERED
308	      let number = 0
309	      let level = if line.is_indented { line.indent_level } else { 0 }	<-- UNCOVERED
310	      return ListItemInfo::{
311	        item_type: ListItemType::Numbered,
312	        marker: number_str,
313	        level,
314	        number,
315	      }
316	    }
317	  }
318	  // 检测复选框
319	  if text.length() >= 3 &&	<-- UNCOVERED
320	    text[0] == '[' &&
321	    (text[1] == ' ' || text[1] == 'x' || text[1] == 'X') &&
322	    text[2] == ']' {
323	    let level = if line.is_indented { line.indent_level } else { 0 }	<-- UNCOVERED
324	    return ListItemInfo::{
325	      item_type: ListItemType::Checkbox,
326	      marker: "[ ]",
327	      level,
328	      number: 0,
329	    }
330	  }
331	  ListItemInfo::{
332	    item_type: ListItemType::None,
333	    marker: "",
334	    level: 0,
335	    number: 0,
336	  }
337	}
   	…

341	pub fn is_code_block_line(line : TextLineAttributes, avg_indent : Int) -> Bool {
342	  // 代码块特征：缩进大于平均缩进、包含特殊字符、等宽字体
343	  let has_code_chars = line.text.length() > 0 ||	<-- UNCOVERED
344	    line.text.length() > 0 ||
345	    line.text.length() > 0 ||
346	    line.text.length() > 0 ||
347	    line.text.length() > 0 ||
348	    line.text.length() > 0
349	  let is_indented = line.indent_level > 0 || line.x > avg_indent + 20
350	  is_indented && has_code_chars
351	}
   	…

355	pub fn is_quote_line(line : TextLineAttributes, avg_x : Int) -> Bool {
356	  // 引用块特征：左边距大于平均值、可能以 > 开头
357	  let starts_with_quote = line.text.length() > 0 && line.text[0] == '>'	<-- UNCOVERED
358	  let is_indented = line.x > avg_x + 30
359	  starts_with_quote || is_indented
360	}
   	…

364	pub fn segment_paragraphs(
365	  lines : Array[TextLineAttributes],
366	) -> Array[Array[TextLineAttributes]] {
367	  let paragraphs : Array[Array[TextLineAttributes]] = []	<-- UNCOVERED
368	  if lines.length() == 0 {
369	    return paragraphs
370	  }
371	  let current_paragraph : Array[TextLineAttributes] = []
372	  current_paragraph.push(lines[0])	<-- UNCOVERED
373	  for i in 1..<lines.length() {
374	    let prev = lines[i - 1]
375	    let curr = lines[i]
376	    let gap = curr.y - (prev.y + prev.height)
377	    let avg_line_height = (prev.height + curr.height).to_double() / 2.0	<-- UNCOVERED
378	    // 段落分割条件：行间距大于 1.5 倍行高，或当前行有首行缩进
379	    let is_new_paragraph = gap.to_double() > avg_line_height * 1.5 ||	<-- UNCOVERED
380	      curr.is_indented
381	    if is_new_paragraph && current_paragraph.length() > 0 {	<-- UNCOVERED
382	      paragraphs.push(current_paragraph.copy())
383	      current_paragraph.clear()
384	    }
385	    current_paragraph.push(curr)	<-- UNCOVERED
386	  }
387	  if current_paragraph.length() > 0 {	<-- UNCOVERED
388	    paragraphs.push(current_paragraph.copy())
389	  }
390	  paragraphs
391	}
   	…

395	pub fn smart_reading_order(
396	  elements : Array[EnhancedDocumentElement],
397	  layout_type : LayoutType,
398	  column_boundaries : Array[Int],
399	) -> Array[Int] raise @types.LoadError {
400	  ignore(column_boundaries)	<-- UNCOVERED
401	  // 输入验证
402	  if elements.length() == 0 {	<-- UNCOVERED
403	    raise @types.LoadError::DecodeFailed(
404	      "smart_reading_order: 文档元素列表不能为空",
405	    )
406	  }
407	  let indices : Array[Int] = []
408	  for i in 0..<elements.length() {	<-- UNCOVERED
409	    indices.push(i)
410	  }
411	  // 按列分组，然后在列内按 y 排序
412	  let _ = match layout_type {	<-- UNCOVERED
413	    LayoutType::DoubleColumn => 2
414	    LayoutType::TripleColumn => 3
415	    _ => 1
416	  }
417	  // 简单排序：先按列索引，再按 y 坐标
418	  for i in 0..<indices.length() {	<-- UNCOVERED
419	    for j in 0..<(indices.length() - i - 1) {
420	      let a = indices[j]
421	      let b = indices[j + 1]
422	      let should_swap = if elements[a].column_index == elements[b].column_index {
423	        elements[a].y > elements[b].y	<-- UNCOVERED
424	      } else {
425	        elements[a].column_index > elements[b].column_index	<-- UNCOVERED
426	      }
427	      if should_swap {
428	        let temp = indices[j]	<-- UNCOVERED
429	        indices[j] = indices[j + 1]
430	        indices[j + 1] = temp
431	      }
432	    }
433	  }
434	  indices
435	}
   	…

439	pub fn build_enhanced_elements(
440	  lines : Array[TextLineAttributes],
441	  page_width : Int,
442	  page_height : Int,
443	) -> EnhancedDocument raise @types.LoadError {
444	  // 输入验证
445	  if lines.length() == 0 {	<-- UNCOVERED
446	    raise @types.LoadError::DecodeFailed(
447	      "build_enhanced_elements: 文本行列表不能为空",
448	    )
449	  }
450	  if page_width <= 0 {
451	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
452	      "build_enhanced_elements: 页面宽度必须大于0",
453	    )
454	  }
455	  if page_height <= 0 {
456	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
457	      "build_enhanced_elements: 页面高度必须大于0",
458	    )
459	  }
460	  // 1. 检测布局类型
461	  let layout = detect_layout_type(lines, page_width, page_height)	<-- UNCOVERED
462	  // 2. 检测页眉页脚
463	  let (header_bottom, footer_top) = detect_header_footer(lines, page_height)	<-- UNCOVERED
464	  // 3. 检测列边界
465	  let boundaries = detect_column_boundaries(lines, page_width, layout)	<-- UNCOVERED
466	  // 4. 计算平均字体大小
467	  let mut total_font_size = 0.0
468	  for line in lines {
469	    total_font_size += line.font_size	<-- UNCOVERED
470	  }
471	  let avg_font_size = if lines.length() > 0 {	<-- UNCOVERED
472	    total_font_size / lines.length().to_double()
473	  } else {
474	    16.0	<-- UNCOVERED
475	  }
476	  // 5. 计算平均 x 坐标
477	  let mut total_x = 0
478	  for line in lines {
479	    total_x += line.x	<-- UNCOVERED
480	  }
481	  let avg_x = if lines.length() > 0 { total_x / lines.length() } else { 0 }	<-- UNCOVERED
482	  // 6. 构建元素
483	  let elements : Array[EnhancedDocumentElement] = []
484	  for line in lines {
485	    // 判断是否为页眉页脚
486	    let is_header = line.y + line.height <= header_bottom	<-- UNCOVERED
487	    let is_footer = line.y >= footer_top
488	    // 判断列索引
489	    let mut column_index = 0
490	    let line_center = line.x + line.width / 2
491	    for ci in 0..<(boundaries.length() / 2) {	<-- UNCOVERED
492	      let col_start = boundaries[ci * 2]
493	      let col_end = boundaries[ci * 2 + 1]
494	      if line_center >= col_start && line_center <= col_end {
495	        column_index = ci	<-- UNCOVERED
496	        break
497	      }
498	    }
499	    // 判断元素类型
500	    let (is_heading, heading_level) = is_heading_line(line, avg_font_size)	<-- UNCOVERED
501	    let list_info = detect_list_item(line)
502	    let is_code = is_code_block_line(line, avg_x)
503	    let is_quote = is_quote_line(line, avg_x)
504	    let element_type = if is_header || is_footer {
505	      MarkdownElementType::Paragraph	<-- UNCOVERED
506	    } else if is_heading {
507	      match heading_level {
508	        1 => MarkdownElementType::Heading1
509	        2 => MarkdownElementType::Heading2
510	        _ => MarkdownElementType::Heading3
511	      }
512	    } else if list_info.item_type != ListItemType::None {	<-- UNCOVERED
513	      MarkdownElementType::ListItem
514	    } else if is_code {
515	      MarkdownElementType::CodeBlock
516	    } else if is_quote {
517	      MarkdownElementType::Quote
518	    } else {
519	      MarkdownElementType::Paragraph	<-- UNCOVERED
520	    }
521	    let element = EnhancedDocumentElement::{
522	      element_type,
523	      x: line.x,
524	      y: line.y,
525	      width: line.width,
526	      height: line.height,
527	      content: line.text,
528	      confidence: 0.85,
529	      font_size: line.font_size,
530	      font_weight: line.font_weight,
531	      alignment: line.alignment,
532	      list_info,
533	      column_index,
534	      indent_level: line.indent_level,
535	      is_header,
536	      is_footer,
537	    }
538	    elements.push(element)	<-- UNCOVERED
539	  }
540	  // 7. 智能阅读顺序
541	  let reading_order = smart_reading_order(elements, layout, boundaries)	<-- UNCOVERED
542	  EnhancedDocument::{
543	    layout_type: layout,
544	    num_columns: match layout {	<-- UNCOVERED
545	      LayoutType::DoubleColumn => 2
546	      LayoutType::TripleColumn => 3
547	      _ => 1
548	    },
549	    page_width,
550	    page_height,
551	    header_height: header_bottom,
552	    footer_height: page_height - footer_top,
553	    body_top: header_bottom,
554	    body_bottom: footer_top,
555	    elements,
556	    reading_order,
557	    column_boundaries: boundaries,
558	  }
559	}
   	…

563	pub fn enhanced_document_to_markdown(
564	  doc : EnhancedDocument,
565	  include_header_footer? : Bool = false,
566	) -> String {
567	  let include_flag = include_header_footer	<-- UNCOVERED
568	  let mut md = ""
569	  // 输出布局信息（作为 HTML 注释）
570	  md = md +
571	    "<!-- 布局类型: " +
572	    (match doc.layout_type {	<-- UNCOVERED
573	      LayoutType::SingleColumn => "单栏"
574	      LayoutType::DoubleColumn => "双栏"
575	      LayoutType::TripleColumn => "三栏"
576	      LayoutType::MixedLayout => "混合布局"
577	      LayoutType::UnknownLayout => "未知"
578	    }) +
579	    " -->\n\n"
580	  // 按阅读顺序输出元素
581	  for idx in doc.reading_order {
582	    if idx < doc.elements.length() {	<-- UNCOVERED
583	      let element = doc.elements[idx]
584	      // 跳过页眉页脚（除非指定包含）
585	      if !include_flag && (element.is_header || element.is_footer) {
586	        continue	<-- UNCOVERED
587	      }
588	      // 转换元素
589	      let element_md = match element.element_type {	<-- UNCOVERED
590	        MarkdownElementType::Title => "# " + element.content + "\n\n"
591	        MarkdownElementType::Heading1 => "## " + element.content + "\n\n"
592	        MarkdownElementType::Heading2 => "### " + element.content + "\n\n"
593	        MarkdownElementType::Heading3 => "#### " + element.content + "\n\n"
594	        MarkdownElementType::Paragraph => element.content + "\n\n"
595	        MarkdownElementType::ListItem => {
596	          let indent = "  "
597	          indent + "- " + element.content + "\n"
598	        }
599	        MarkdownElementType::Table => element.content + "\n"	<-- UNCOVERED
600	        MarkdownElementType::Image => "![图片](" + element.content + ")\n\n"
601	        MarkdownElementType::Quote => "> " + element.content + "\n\n"
602	        MarkdownElementType::CodeBlock =>
603	          "```\n" + element.content + "\n```\n\n"	<-- UNCOVERED
604	        MarkdownElementType::HorizontalRule => "---\n\n"
605	        MarkdownElementType::PageBreak => "\n\n---\n\n"
606	        MarkdownElementType::Unknown => element.content + "\n\n"
607	      }
608	      md = md + element_md
609	    }
610	  }
611	  md
612	}
   	…

616	pub fn enhanced_element_distribution(
617	  doc : EnhancedDocument,
618	) -> Array[(String, Int)] {
619	  let mut title_count = 0	<-- UNCOVERED
620	  let mut heading_count = 0
621	  let mut paragraph_count = 0
622	  let mut list_count = 0
623	  let mut table_count = 0
624	  let mut image_count = 0
625	  let mut quote_count = 0
626	  let mut code_count = 0
627	  let mut header_count = 0
628	  let mut footer_count = 0
629	  for element in doc.elements {
630	    match element.element_type {	<-- UNCOVERED
631	      MarkdownElementType::Title => title_count += 1
632	      MarkdownElementType::Heading1 => heading_count += 1
633	      MarkdownElementType::Heading2 => heading_count += 1
634	      MarkdownElementType::Heading3 => heading_count += 1
635	      MarkdownElementType::Paragraph => paragraph_count += 1
636	      MarkdownElementType::ListItem => list_count += 1
637	      MarkdownElementType::Table => table_count += 1
638	      MarkdownElementType::Image => image_count += 1
639	      MarkdownElementType::Quote => quote_count += 1
640	      MarkdownElementType::CodeBlock => code_count += 1
641	      _ => ()
642	    }
643	    if element.is_header {
644	      header_count += 1	<-- UNCOVERED
645	    }
646	    if element.is_footer {
647	      footer_count += 1	<-- UNCOVERED
648	    }
649	  }
650	  [
651	    ("标题", title_count),
652	    ("小标题", heading_count),
653	    ("段落", paragraph_count),
654	    ("列表", list_count),
655	    ("表格", table_count),
656	    ("图片", image_count),
657	    ("引用", quote_count),
658	    ("代码", code_count),
659	    ("页眉", header_count),
660	    ("页脚", footer_count),
661	  ]
662	}
   	…

42 uncovered line(s) in src\process\ocr\feature_extraction.mbt:

 57	pub fn hog_features(
 58	  img : @types.Image,
 59	  cell_size? : Int = 8,
 60	  num_bins? : Int = 9,
 61	) -> HogFeatures raise @types.LoadError {
 62	  // 输入验证
 63	  if img.width <= 0 || img.height <= 0 {
 64	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 65	      "hog_features: 图像尺寸必须大于0",
 66	    )
 67	  }
 68	  if img.channels != 1 {
 69	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 70	      "hog_features: 仅支持单通道灰度图",
 71	    )
 72	  }
 73	  let expected_size = img.width * img.height
 74	  if img.data.length() != expected_size {
 75	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 76	      "hog_features: 数据大小不匹配，期望 " +
 77	      expected_size.to_string() +	<-- UNCOVERED
 78	      "，实际 " +
 79	      img.data.length().to_string(),	<-- UNCOVERED
 80	    )
 81	  }
 82	  if cell_size <= 0 {
 83	    raise @types.LoadError::DecodeFailed("hog_features: cell_size必须大于0")	<-- UNCOVERED
 84	  }
 85	  if num_bins <= 0 {
 86	    raise @types.LoadError::DecodeFailed("hog_features: num_bins必须大于0")	<-- UNCOVERED
 87	  }
 88	
 89	  let w = img.width
 90	  let h = img.height
 91	  let data = img.data
 92	  // 计算梯度
 93	  let grad_x : Array[Double] = Array::make(w * h, 0.0)
 94	  let grad_y : Array[Double] = Array::make(w * h, 0.0)
 95	  let magnitude : Array[Double] = Array::make(w * h, 0.0)
 96	  let orientation : Array[Double] = Array::make(w * h, 0.0)
 97	  for y in 1..<(h - 1) {
 98	    for x in 1..<(w - 1) {
 99	      let gx = data[y * w + (x + 1)].to_int() - data[y * w + (x - 1)].to_int()
100	      let gy = data[(y + 1) * w + x].to_int() - data[(y - 1) * w + x].to_int()
101	      grad_x[y * w + x] = gx.to_double()
102	      grad_y[y * w + x] = gy.to_double()
103	      magnitude[y * w + x] = (gx.to_double() * gx.to_double() +
104	      gy.to_double() * gy.to_double()).sqrt()
105	      let mut angle = @math.atan2(gy.to_double(), gx.to_double()) *
106	        180.0 /
107	        3.141592653589793
108	      if angle < 0.0 {
109	        angle += 180.0
110	      }
111	      orientation[y * w + x] = angle
112	    }
113	  }
114	  // 计算每个 cell 的直方图
115	  let cells_x = w / cell_size
116	  let cells_y = h / cell_size
117	  let features : Array[Double] = []
118	  for cy in 0..<cells_y {
119	    for cx in 0..<cells_x {
120	      let hist : Array[Double] = Array::make(num_bins, 0.0)
121	      for y in 0..<cell_size {
122	        for x in 0..<cell_size {
123	          let px = cx * cell_size + x
124	          let py = cy * cell_size + y
125	          if px < w && py < h {
126	            let mag = magnitude[py * w + px]
127	            let angle = orientation[py * w + px]
128	            let bin = (angle / (180.0 / num_bins.to_double())).to_int()
129	            let bin_idx = if bin >= num_bins { num_bins - 1 } else { bin }
130	            hist[bin_idx] += mag
131	          }
132	        }
133	      }
134	      // L2 归一化
135	      let mut sum_sq = 0.0
136	      for i in 0..<num_bins {
137	        sum_sq += hist[i] * hist[i]
138	      }
139	      let norm = if sum_sq > 0.0 { sum_sq.sqrt() } else { 1.0 }
140	      for i in 0..<num_bins {
141	        features.push(hist[i] / norm)
142	      }
143	    }
144	  }
145	  HogFeatures::{
146	    features,
147	    cell_size,
148	    num_bins,
149	    width: cells_x,
150	    height: cells_y,
151	  }
152	}
   	…

156	pub fn lbp_features(
157	  img : @types.Image,
158	  radius? : Int = 1,
159	  num_points? : Int = 8,
160	  num_bins? : Int = 256,
161	) -> LbpFeatures raise @types.LoadError {
162	  // 输入验证
163	  if img.width <= 0 || img.height <= 0 {
164	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
165	      "lbp_features: 图像尺寸必须大于0",
166	    )
167	  }
168	  if img.channels != 1 {
169	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
170	      "lbp_features: 仅支持单通道灰度图",
171	    )
172	  }
173	  let expected_size = img.width * img.height
174	  if img.data.length() != expected_size {
175	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
176	      "lbp_features: 数据大小不匹配，期望 " +
177	      expected_size.to_string() +	<-- UNCOVERED
178	      "，实际 " +
179	      img.data.length().to_string(),	<-- UNCOVERED
180	    )
181	  }
182	  if radius <= 0 {
183	    raise @types.LoadError::DecodeFailed("lbp_features: radius必须大于0")	<-- UNCOVERED
184	  }
185	  if num_points <= 0 {
186	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
187	      "lbp_features: num_points必须大于0",
188	    )
189	  }
190	  if num_bins <= 0 {
191	    raise @types.LoadError::DecodeFailed("lbp_features: num_bins必须大于0")	<-- UNCOVERED
192	  }
193	
194	  let w = img.width
195	  let h = img.height
196	  let data = img.data
197	  // 计算 LBP 码
198	  let lbp_codes : Array[Int] = Array::make(w * h, 0)
199	  for y in radius..<(h - radius) {
200	    for x in radius..<(w - radius) {
201	      let center = data[y * w + x].to_int()
202	      let mut code = 0
203	      for i in 0..<num_points {
204	        let angle = 2.0 *
205	          3.141592653589793 *
206	          i.to_double() /
207	          num_points.to_double()
208	        let nx = (x.to_double() + radius.to_double() * @math.cos(angle)).to_int()
209	        let ny = (y.to_double() + radius.to_double() * @math.sin(angle)).to_int()
210	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
211	          let neighbor = data[ny * w + nx].to_int()
212	          if neighbor >= center {
213	            code = code | (1 << i)
214	          }
215	        }
216	      }
217	      lbp_codes[y * w + x] = code
218	    }
219	  }
220	  // 计算直方图
221	  let hist : Array[Double] = Array::make(num_bins, 0.0)
222	  let mut total = 0
223	  for i in 0..<(w * h) {
224	    let code = lbp_codes[i]
225	    if code < num_bins {
226	      hist[code] += 1.0
227	      total += 1
228	    }
229	  }
230	  // 归一化
231	  if total > 0 {
232	    for i in 0..<num_bins {
233	      hist[i] /= total.to_double()
234	    }
235	  }
236	  LbpFeatures::{ features: hist, num_bins, radius, num_points }
237	}
   	…

241	pub fn projection_features(
242	  img : @types.Image,
243	) -> ProjectionFeatures raise @types.LoadError {
244	  // 输入验证
245	  if img.width <= 0 || img.height <= 0 {
246	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
247	      "projection_features: 图像尺寸必须大于0",
248	    )
249	  }
250	  if img.channels != 1 {
251	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
252	      "projection_features: 仅支持单通道二值图",
253	    )
254	  }
255	  let expected_size = img.width * img.height
256	  if img.data.length() != expected_size {
257	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
258	      "projection_features: 数据大小不匹配，期望 " +
259	      expected_size.to_string() +	<-- UNCOVERED
260	      "，实际 " +
261	      img.data.length().to_string(),	<-- UNCOVERED
262	    )
263	  }
264	
265	  let w = img.width
266	  let h = img.height
267	  let data = img.data
268	  // 水平投影
269	  let horizontal : Array[Double] = Array::make(h, 0.0)
270	  for y in 0..<h {
271	    let mut count = 0
272	    for x in 0..<w {
273	      if data[y * w + x].to_int() > 128 {
274	        count += 1
275	      }
276	    }
277	    horizontal[y] = count.to_double() / w.to_double()
278	  }
279	  // 垂直投影
280	  let vertical : Array[Double] = Array::make(w, 0.0)
281	  for x in 0..<w {
282	    let mut count = 0
283	    for y in 0..<h {
284	      if data[y * w + x].to_int() > 128 {
285	        count += 1
286	      }
287	    }
288	    vertical[x] = count.to_double() / h.to_double()
289	  }
290	  // 计算统计量
291	  let h_mean = mean_array(horizontal)
292	  let h_std = std_array(horizontal, h_mean)
293	  let v_mean = mean_array(vertical)
294	  let v_std = std_array(vertical, v_mean)
295	  ProjectionFeatures::{
296	    horizontal,
297	    vertical,
298	    horizontal_mean: h_mean,
299	    horizontal_std: h_std,
300	    vertical_mean: v_mean,
301	    vertical_std: v_std,
302	  }
303	}
   	…

307	pub fn grid_features(
308	  img : @types.Image,
309	  grid_rows? : Int = 4,
310	  grid_cols? : Int = 4,
311	) -> GridFeatures raise @types.LoadError {
312	  // 输入验证
313	  if img.width <= 0 || img.height <= 0 {
314	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
315	      "grid_features: 图像尺寸必须大于0",
316	    )
317	  }
318	  if img.channels != 1 {
319	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
320	      "grid_features: 仅支持单通道二值图",
321	    )
322	  }
323	  let expected_size = img.width * img.height
324	  if img.data.length() != expected_size {
325	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
326	      "grid_features: 数据大小不匹配，期望 " +
327	      expected_size.to_string() +	<-- UNCOVERED
328	      "，实际 " +
329	      img.data.length().to_string(),	<-- UNCOVERED
330	    )
331	  }
332	  if grid_rows <= 0 {
333	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
334	      "grid_features: grid_rows必须大于0",
335	    )
336	  }
337	  if grid_cols <= 0 {
338	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
339	      "grid_features: grid_cols必须大于0",
340	    )
341	  }
342	
343	  let w = img.width
344	  let h = img.height
345	  let data = img.data
346	  let cell_w = w / grid_cols
347	  let cell_h = h / grid_rows
348	  let features : Array[Double] = []
349	  for r in 0..<grid_rows {
350	    for c in 0..<grid_cols {
351	      let mut count = 0
352	      let mut total = 0
353	      for y in 0..<cell_h {
354	        for x in 0..<cell_w {
355	          let px = c * cell_w + x
356	          let py = r * cell_h + y
357	          if px < w && py < h {
358	            total += 1
359	            if data[py * w + px].to_int() > 128 {
360	              count += 1
361	            }
362	          }
363	        }
364	      }
365	      let density = if total > 0 {
366	        count.to_double() / total.to_double()
367	      } else {
368	        0.0	<-- UNCOVERED
369	      }
370	      features.push(density)
371	    }
372	  }
373	  GridFeatures::{ features, grid_rows, grid_cols }
374	}
   	…

378	pub fn statistical_features(
379	  img : @types.Image,
380	) -> StatisticalFeatures raise @types.LoadError {
381	  // 输入验证
382	  if img.width <= 0 || img.height <= 0 {
383	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
384	      "statistical_features: 图像尺寸必须大于0",
385	    )
386	  }
387	  if img.channels != 1 {
388	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
389	      "statistical_features: 仅支持单通道灰度图",
390	    )
391	  }
392	  let expected_size = img.width * img.height
393	  if img.data.length() != expected_size {
394	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
395	      "statistical_features: 数据大小不匹配，期望 " +
396	      expected_size.to_string() +	<-- UNCOVERED
397	      "，实际 " +
398	      img.data.length().to_string(),	<-- UNCOVERED
399	    )
400	  }
401	
402	  let w = img.width
403	  let h = img.height
404	  let n = w * h
405	  let data = img.data
406	  // 计算直方图
407	  let hist : Array[Double] = Array::make(256, 0.0)
408	  for i in 0..<n {
409	    hist[data[i].to_int()] += 1.0
410	  }
411	  // 归一化
412	  for i in 0..<256 {
413	    hist[i] /= n.to_double()
414	  }
415	  // 均值
416	  let mut mean = 0.0
417	  for i in 0..<256 {
418	    mean += i.to_double() * hist[i]
419	  }
420	  // 方差/标准差
421	  let mut variance = 0.0
422	  for i in 0..<256 {
423	    let diff = i.to_double() - mean
424	    variance += diff * diff * hist[i]
425	  }
426	  let std = if variance > 0.0 { variance.sqrt() } else { 0.0 }
427	  // 偏度
428	  let mut skewness = 0.0
429	  if std > 0.0 {
430	    for i in 0..<256 {
431	      let diff = (i.to_double() - mean) / std
432	      skewness += diff * diff * diff * hist[i]
433	    }
434	  }
435	  // 峰度
436	  let mut kurtosis = 0.0
437	  if std > 0.0 {
438	    for i in 0..<256 {
439	      let diff = (i.to_double() - mean) / std
440	      kurtosis += diff * diff * diff * diff * hist[i]
441	    }
442	    kurtosis -= 3.0 // 超额峰度
443	  }
444	  // 熵
445	  let mut entropy = 0.0
446	  for i in 0..<256 {
447	    if hist[i] > 0.0 {
448	      entropy -= hist[i] * @math.log2(hist[i])
449	    }
450	  }
451	  // 能量
452	  let mut energy = 0.0
453	  for i in 0..<256 {
454	    energy += hist[i] * hist[i]
455	  }
456	  // 对比度（简化版）
457	  let mut contrast = 0.0
458	  for i in 0..<256 {
459	    contrast += (i.to_double() - mean) * (i.to_double() - mean) * hist[i]
460	  }
461	  // 同质性（简化版）
462	  let mut homogeneity = 0.0
463	  for i in 0..<256 {
464	    homogeneity += hist[i] / (1.0 + (i.to_double() - mean).abs())
465	  }
466	  StatisticalFeatures::{
467	    mean,
468	    std,
469	    skewness,
470	    kurtosis,
471	    entropy,
472	    energy,
473	    contrast,
474	    homogeneity,
475	  }
476	}
   	…

480	pub fn combined_features(
481	  img : @types.Image,
482	) -> Array[Double] raise @types.LoadError {
483	  // 输入验证
484	  if img.width <= 0 || img.height <= 0 {
485	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
486	      "combined_features: 图像尺寸必须大于0",
487	    )
488	  }
489	  if img.channels != 1 {
490	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
491	      "combined_features: 仅支持单通道灰度图",
492	    )
493	  }
494	  let expected_size = img.width * img.height
495	  if img.data.length() != expected_size {
496	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
497	      "combined_features: 数据大小不匹配，期望 " +
498	      expected_size.to_string() +	<-- UNCOVERED
499	      "，实际 " +
500	      img.data.length().to_string(),	<-- UNCOVERED
501	    )
502	  }
503	
504	  let hog = hog_features(img)
505	  let lbp = lbp_features(img)
506	  let proj = projection_features(img)
507	  let grid = grid_features(img)
508	  let stats = statistical_features(img)
509	  let combined : Array[Double] = []
510	  // HOG 特征
511	  for f in hog.features {
512	    combined.push(f)
513	  }
514	  // LBP 特征（降采样，只取前64维）
515	  let lbp_dim = if lbp.features.length() > 64 {
516	    64
517	  } else {
518	    lbp.features.length()	<-- UNCOVERED
519	  }
520	  for i in 0..<lbp_dim {
521	    combined.push(lbp.features[i])
522	  }
523	  // 投影特征统计量
524	  combined.push(proj.horizontal_mean)
525	  combined.push(proj.horizontal_std)
526	  combined.push(proj.vertical_mean)
527	  combined.push(proj.vertical_std)
528	  // 网格特征
529	  for f in grid.features {
530	    combined.push(f)
531	  }
532	  // 统计特征
533	  combined.push(stats.mean / 255.0)
534	  combined.push(stats.std / 128.0)
535	  combined.push(stats.skewness)
536	  combined.push(stats.kurtosis)
537	  combined.push(stats.entropy / 8.0)
538	  combined.push(stats.energy)
539	  combined.push(stats.contrast / 65025.0)
540	  combined.push(stats.homogeneity)
541	  combined
542	}
   	…

546	fn mean_array(arr : Array[Double]) -> Double {
547	  if arr.length() == 0 {
548	    return 0.0	<-- UNCOVERED
549	  }
550	  let mut sum = 0.0
551	  for v in arr {
552	    sum += v
553	  }
554	  sum / arr.length().to_double()
555	}
   	…

559	fn std_array(arr : Array[Double], mean : Double) -> Double {
560	  if arr.length() == 0 {
561	    return 0.0	<-- UNCOVERED
562	  }
563	  let mut sum_sq = 0.0
564	  for v in arr {
565	    let diff = v - mean
566	    sum_sq += diff * diff
567	  }
568	  let variance = sum_sq / arr.length().to_double()
569	  if variance > 0.0 {
570	    variance.sqrt()
571	  } else {
572	    0.0	<-- UNCOVERED
573	  }
574	}
   	…

30 uncovered line(s) in src\process\ocr\handwriting_ocr.mbt:

34	pub fn generate_handwriting_templates(
35	  char_set? : String = "digits",
36	) -> Array[(String, Array[Double])] {
37	  let templates : Array[(String, Array[Double])] = []
38	  let chars = if char_set == "digits+letters" {
39	    [	<-- UNCOVERED
40	      "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E",
41	      "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
42	      "U", "V", "W", "X", "Y", "Z",
43	    ]
44	  } else {
45	    ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
46	  }
47	  for char in chars {
48	    // 生成多个变体（手写体有多种写法）
49	    for variant in 0..<3 {
50	      let template = generate_handwriting_char_template(char, variant)
51	      let label = char + "_v" + variant.to_string()
52	      templates.push((label, template))
53	    }
54	  }
55	  templates
56	}
  	…

60	fn generate_handwriting_char_template(
61	  char : String,
62	  variant : Int,
63	) -> Array[Double] {
64	  let size = 16
65	  let features : Array[Double] = Array::make(size * size, 0.0)
66	  let seed = if char.length() > 0 {
67	    char[0].to_int() + variant * 100
68	  } else {
69	    0	<-- UNCOVERED
70	  }
71	  // 手写体特征：更随机、更灵活的形状
72	  for y in 0..<size {
73	    for x in 0..<size {
74	      // 基础哈希
75	      let h1 = (seed * 31 + x * 17 + y * 13) % 100
76	      // 变体哈希
77	      let h2 = (seed * 7 + (x + variant * 3) * 11 + (y + variant * 2) * 19) %
78	        100
79	      // 中心权重（手写体通常居中但有偏移）
80	      let center_x = 8 + (variant - 1) * 2
81	      let center_y = 8 + variant % 2 * 2
82	      let center_dist = ((x - center_x) * (x - center_x) +
83	      (y - center_y) * (y - center_y)).to_double()
84	      let center_factor = 1.0 - center_dist / 128.0
85	      // 手写体灵活性：更宽松的阈值
86	      let threshold = 35 + (center_factor * 30.0).to_int() + h2 / 5
87	      if h1 < threshold {
88	        features[y * size + x] = 1.0
89	      }
90	    }
91	  }
92	  features
93	}
  	…

 97	pub fn correct_slant(
 98	  img : @types.Image,
 99	  max_slant? : Double = 15.0,
100	) -> @types.Image raise @types.LoadError {
101	  ignore(max_slant)
102	  // 输入验证
103	  if img.width <= 0 || img.height <= 0 {
104	    raise @types.LoadError::DecodeFailed(
105	      "correct_slant: 图像尺寸必须大于0",
106	    )
107	  }
108	  if img.channels != 1 {
109	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
110	      "correct_slant: 仅支持单通道灰度图",
111	    )
112	  }
113	  let expected_size = img.width * img.height
114	  if img.data.length() != expected_size {
115	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
116	      "correct_slant: 数据大小不匹配，期望 " +
117	      expected_size.to_string() +	<-- UNCOVERED
118	      "，实际 " +
119	      img.data.length().to_string(),	<-- UNCOVERED
120	    )
121	  }
122	
123	  // 简化版：检测主要倾斜角度并校正
124	  let angle = estimate_slant_angle(img)
125	  if angle.abs() > 1.0 {
126	    rotate_document(img, -angle)
127	  } else {
128	    img
129	  }
130	}
   	…

260	pub fn recognize_handwriting(
261	  img : @types.Image,
262	  config? : HandwritingOcrConfig,
263	) -> OcrResult raise @types.LoadError {
264	  // 输入验证
265	  if img.width <= 0 || img.height <= 0 {
266	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
267	      "recognize_handwriting: 图像尺寸必须大于0",
268	    )
269	  }
270	  if img.channels < 1 || img.channels > 4 {
271	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
272	      "recognize_handwriting: 通道数必须在1-4之间",
273	    )
274	  }
275	  let expected_size = img.width * img.height * img.channels
276	  if img.data.length() != expected_size {
277	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
278	      "recognize_handwriting: 数据大小不匹配，期望 " +
279	      expected_size.to_string() +	<-- UNCOVERED
280	      "，实际 " +
281	      img.data.length().to_string(),	<-- UNCOVERED
282	    )
283	  }
284	
285	  let cfg = match config {
286	    Some(c) => c	<-- UNCOVERED
287	    None => handwriting_ocr_default_config()
288	  }
289	  // 配置验证
290	  if cfg.min_char_width <= 0 {
291	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
292	      "recognize_handwriting: min_char_width必须大于0",
293	    )
294	  }
295	  if cfg.max_char_width < cfg.min_char_width {
296	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
297	      "recognize_handwriting: max_char_width不能小于min_char_width",
298	    )
299	  }
300	  if cfg.target_size <= 0 {
301	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
302	      "recognize_handwriting: target_size必须大于0",
303	    )
304	  }
305	  // 1. 预处理
306	  let mut preprocessed = ocr_preprocess(img)
307	  // 2. 倾斜校正（手写体专用）
308	  if cfg.use_slant_correction {
309	    preprocessed = correct_slant(preprocessed)
310	  }
311	  // 3. 字符分割（手写体更宽松）
312	  let char_regions = segment_handwriting_chars(preprocessed, cfg)
313	  // 4. 生成手写体模板
314	  let templates = generate_handwriting_templates(char_set="digits")
315	  // 5. 对每个字符进行识别
316	  let char_results : Array[OcrCharResult] = []
317	  let mut text = ""
318	  let mut total_confidence = 0.0
319	  for _tup0 in char_regions {
320	    let (x, y, w, h) = _tup0
321	    // 提取字符图像
322	    let char_img = extract_handwriting_char_region(
323	      preprocessed,
324	      x,
325	      y,
326	      w,
327	      h,
328	      cfg.target_size,
329	    )
330	    // 提取特征
331	    let features = extract_handwriting_char_features(char_img, 16)
332	    // 模板匹配（多变体投票）
333	    let result = match_handwriting_template(
334	      features,
335	      templates,
336	      cfg.distance_metric,
337	    )
338	    text += result.0
339	    total_confidence += result.1
340	    char_results.push(OcrCharResult::{
341	      char: result.0,
342	      confidence: result.1,
343	      x,
344	      y,
345	      width: w,
346	      height: h,
347	    })
348	  }
349	  let avg_confidence = if char_results.length() > 0 {
350	    total_confidence / char_results.length().to_double()
351	  } else {
352	    0.0	<-- UNCOVERED
353	  }
354	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
355	}
   	…

359	fn extract_handwriting_char_region(
360	  img : @types.Image,
361	  x : Int,
362	  y : Int,
363	  width : Int,
364	  height : Int,
365	  target_size : Int,
366	) -> @types.Image {
367	  let w = img.width
368	  let h = img.height
369	  let crop_w = if x + width > w { w - x } else { width }
370	  let crop_h = if y + height > h { h - y } else { height }
371	  if crop_w <= 0 || crop_h <= 0 {
372	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
373	    return @types.Image::{
374	      width: target_size,
375	      height: target_size,
376	      channels: 1,
377	      data: Bytes::from_array(empty),	<-- UNCOVERED
378	    }
379	  }
380	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
381	  let data = img.data
382	  for py in 0..<target_size {
383	    for px in 0..<target_size {
384	      let src_x = x + px * crop_w / target_size
385	      let src_y = y + py * crop_h / target_size
386	      if src_x < w && src_y < h {
387	        out[py * target_size + px] = data[src_y * w + src_x]
388	      }
389	    }
390	  }
391	  @types.Image::{
392	    width: target_size,
393	    height: target_size,
394	    channels: 1,
395	    data: Bytes::from_array(out),
396	  }
397	}
   	…

401	fn extract_handwriting_char_features(
402	  char_img : @types.Image,
403	  grid_size : Int,
404	) -> Array[Double] {
405	  let w = char_img.width
406	  let h = char_img.height
407	  let data = char_img.data
408	  let cell_w = w / grid_size
409	  let cell_h = h / grid_size
410	  let features : Array[Double] = []
411	  for r in 0..<grid_size {
412	    for c in 0..<grid_size {
413	      let mut count = 0
414	      let mut total = 0
415	      for py in 0..<cell_h {
416	        for px in 0..<cell_w {
417	          let sx = c * cell_w + px
418	          let sy = r * cell_h + py
419	          if sx < w && sy < h {
420	            total += 1
421	            if data[sy * w + sx].to_int() > 128 {
422	              count += 1
423	            }
424	          }
425	        }
426	      }
427	      let density = if total > 0 {
428	        count.to_double() / total.to_double()
429	      } else {
430	        0.0	<-- UNCOVERED
431	      }
432	      features.push(density)
433	    }
434	  }
435	  features
436	}
   	…

440	fn match_handwriting_template(
441	  features : Array[Double],
442	  templates : Array[(String, Array[Double])],
443	  distance_metric : String,
444	) -> (String, Double) {
445	  // 收集所有变体的预测
446	  let label_votes : Array[(String, Double)] = []
447	  for _tup1 in templates {
448	    let (label_full, template) = _tup1
449	    let dist = match distance_metric {
450	      "manhattan" => manhattan_hw(features, template)	<-- UNCOVERED
451	      "cosine" => cosine_hw(features, template)
452	      _ => euclidean_hw(features, template)
453	    }
454	    // 提取基础标签（去掉变体后缀）
455	    // 提取基础标签（去掉变体后缀 _v0, _v1, _v2）
456	    // 简化：取第一个字符作为基础标签（数字0-9）
457	    let base_label = if label_full.length() > 0 {
458	      label_full[0].to_string()
459	    } else {
460	      label_full	<-- UNCOVERED
461	    }
462	    let confidence = if dist > 0.0 { 1.0 / (1.0 + dist) } else { 1.0 }
463	    // 投票
464	    let mut found = false
465	    for i in 0..<label_votes.length() {
466	      if label_votes[i].0 == base_label {
467	        label_votes[i] = (base_label, label_votes[i].1 + confidence)
468	        found = true
469	      }
470	    }
471	    if !found {
472	      label_votes.push((base_label, confidence))
473	    }
474	  }
475	  // 找得票最多的标签
476	  let mut best_label = "?"
477	  let mut best_votes = 0.0
478	  for _tup2 in label_votes {
479	    let (label, votes) = _tup2
480	    if votes > best_votes {
481	      best_votes = votes
482	      best_label = label
483	    }
484	  }
485	  let total_votes = if templates.length() > 0 {
486	    templates.length().to_double() / 3.0
487	  } else {
488	    1.0	<-- UNCOVERED
489	  }
490	  let confidence = best_votes / total_votes
491	  (best_label, confidence)
492	}
   	…

496	fn euclidean_hw(a : Array[Double], b : Array[Double]) -> Double {
497	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
498	  let mut sum_sq = 0.0
499	  for i in 0..<n {
500	    let diff = a[i] - b[i]
501	    sum_sq += diff * diff
502	  }
503	  sum_sq.sqrt()
504	}
   	…

508	fn manhattan_hw(a : Array[Double], b : Array[Double]) -> Double {
509	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
510	  let mut sum = 0.0
511	  for i in 0..<n {
512	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
513	  }
514	  sum
515	}
   	…

519	fn cosine_hw(a : Array[Double], b : Array[Double]) -> Double {
520	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
521	  let mut dot = 0.0
522	  let mut norm_a = 0.0
523	  let mut norm_b = 0.0
524	  for i in 0..<n {
525	    dot += a[i] * b[i]	<-- UNCOVERED
526	    norm_a += a[i] * a[i]
527	    norm_b += b[i] * b[i]
528	  }
529	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
530	  if denom > 0.0 {
531	    1.0 - dot / denom	<-- UNCOVERED
532	  } else {
533	    1.0	<-- UNCOVERED
534	  }
535	}
   	…

47 uncovered line(s) in src\process\ocr\handwriting_ocr_enhanced.mbt:

52	pub fn generate_enhanced_handwriting_templates(
53	  char_set? : String = "digits+letters",
54	  num_variants? : Int = 5,
55	) -> Array[(String, Array[Double])] {
56	  let templates : Array[(String, Array[Double])] = []
57	  let chars = if char_set == "digits" {
58	    ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]	<-- UNCOVERED
59	  } else {
60	    [
61	      "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E",
62	      "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
63	      "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i",
64	      "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
65	      "y", "z",
66	    ]
67	  }
68	  for char in chars {
69	    for variant in 0..<num_variants {
70	      let template = generate_enhanced_handwriting_char_template(
71	        char, variant, num_variants,
72	      )
73	      let label = char + "_v" + variant.to_string()
74	      templates.push((label, template))
75	    }
76	  }
77	  templates
78	}
  	…

 82	fn generate_enhanced_handwriting_char_template(
 83	  char : String,
 84	  variant : Int,
 85	  num_variants : Int,
 86	) -> Array[Double] {
 87	  let size = 32
 88	  let features : Array[Double] = Array::make(size * size, 0.0)
 89	  let seed = if char.length() > 0 {
 90	    char[0].to_int() + variant * 137
 91	  } else {
 92	    0	<-- UNCOVERED
 93	  }
 94	  // 变体参数（每个变体有不同的倾斜、宽度、位置）
 95	  let slant = ((variant - num_variants / 2) * 3).to_double() // 倾斜角度
 96	  let width_scale = 0.85 + (variant % 3).to_double() * 0.1 // 宽度缩放
 97	  let y_offset = variant % 2 * 2 - 1 // 垂直偏移
 98	  let center_x = (size.to_double() / 2.0 * width_scale).to_int()
 99	  let center_y = size / 2 + y_offset
100	  for y in 0..<size {
101	    for x in 0..<size {
102	      // 应用倾斜变换
103	      let slant_rad = slant * 3.141592653589793 / 180.0
104	      let transformed_x = x.to_double() -
105	        center_x.to_double() -
106	        (y.to_double() - center_y.to_double()) * @math.tan(slant_rad) +
107	        center_x.to_double()
108	      let tx = transformed_x.to_int()
109	      // 多层哈希
110	      let h1 = (seed * 31 + tx * 17 + y * 13) % 100
111	      let h2 = (seed * 7 + tx * tx * 3 + y * y * 5) % 100
112	      let h3 = ((seed << 2) ^ (tx << 1) ^ y) % 100
113	      let combined = (h1 * 4 + h2 * 3 + h3 * 2) / 9
114	      // 中心权重（手写体通常居中但有偏移）
115	      let center_dist = ((tx - center_x) * (tx - center_x) +
116	      (y - center_y) * (y - center_y)).to_double()
117	      let center_factor = 1.0 -
118	        center_dist / (size.to_double() * size.to_double() / 2.5)
119	      // 弹性变形（边缘更模糊）
120	      let edge_factor = if tx < 2 || tx >= size - 2 || y < 2 || y >= size - 2 {
121	        0.6
122	      } else {
123	        1.0
124	      }
125	      let threshold = (30.0 + center_factor * 25.0 + edge_factor * 10.0).to_int()
126	      if combined < threshold && tx >= 0 && tx < size {
127	        features[y * size + x] = 1.0
128	      }
129	    }
130	  }
131	  features
132	}
   	…

136	pub fn extract_handwriting_stroke_features(
137	  char_img : @types.Image,
138	) -> HandwritingStrokeFeatures raise @types.LoadError {
139	  // 输入验证
140	  if char_img.width <= 0 || char_img.height <= 0 {
141	    raise @types.LoadError::DecodeFailed(
142	      "extract_handwriting_stroke_features: 图像尺寸必须大于0",
143	    )
144	  }
145	  if char_img.channels != 1 {
146	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
147	      "extract_handwriting_stroke_features: 仅支持单通道灰度图",
148	    )
149	  }
150	  let expected_size = char_img.width * char_img.height
151	  if char_img.data.length() != expected_size {
152	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
153	      "extract_handwriting_stroke_features: 数据大小不匹配，期望 " +
154	      expected_size.to_string() +	<-- UNCOVERED
155	      "，实际 " +
156	      char_img.data.length().to_string(),	<-- UNCOVERED
157	    )
158	  }
159	  let w = char_img.width
160	  let h = char_img.height
161	  let data = char_img.data
162	  // 估计笔画数（基于连通域数量）
163	  let labels : Array[Int] = Array::make(w * h, 0)
164	  let mut num_components = 0
165	  for y in 0..<h {
166	    for x in 0..<w {
167	      if data[y * w + x].to_int() > 128 && labels[y * w + x] == 0 {
168	        num_components += 1
169	        // 简单洪水填充
170	        let stack : Array[(Int, Int)] = []
171	        stack.push((x, y))
172	        labels[y * w + x] = num_components
173	        while stack.length() > 0 {
174	          match stack.pop() {
175	            Some((cx, cy)) => {
176	              let neighbors = [
177	                (cx + 1, cy),
178	                (cx - 1, cy),
179	                (cx, cy + 1),
180	                (cx, cy - 1),
181	              ]
182	              for _tup0 in neighbors {
183	                let (nx, ny) = _tup0
184	                if nx >= 0 &&
185	                  nx < w &&
186	                  ny >= 0 &&
187	                  ny < h &&
188	                  data[ny * w + nx].to_int() > 128 &&
189	                  labels[ny * w + nx] == 0 {
190	                  labels[ny * w + nx] = num_components
191	                  stack.push((nx, ny))
192	                }
193	              }
194	            }
195	            None => ignore(())	<-- UNCOVERED
196	          }
197	        }
198	      }
199	    }
200	  }
201	  // 计算主要笔画方向（基于梯度方向直方图）
202	  let directions : Array[Double] = []
203	  let mut _total_grad = 0.0
204	  for y in 1..<(h - 1) {
205	    for x in 1..<(w - 1) {
206	      if data[y * w + x].to_int() > 128 {
207	        let gx = data[y * w + x + 1].to_int().to_double() -
208	          data[y * w + x - 1].to_int().to_double()
209	        let gy = data[(y + 1) * w + x].to_int().to_double() -
210	          data[(y - 1) * w + x].to_int().to_double()
211	        let grad = (gx * gx + gy * gy).sqrt()
212	        if grad > 10.0 {
213	          let angle = @math.atan2(gy, gx)
214	          directions.push(angle)
215	          _total_grad += grad
216	        }
217	      }
218	    }
219	  }
220	  // 计算连通性（连笔程度）
221	  let mut ink_pixels = 0
222	  for i in 0..<(w * h) {
223	    if data[i].to_int() > 128 {
224	      ink_pixels += 1
225	    }
226	  }
227	  let connectivity = if num_components > 0 {
228	    ink_pixels.to_double() / (num_components.to_double() * 50.0)
229	  } else {
230	    0.0
231	  }
232	  // 计算曲率（基于方向变化）
233	  let mut curvature = 0.0
234	  if directions.length() > 10 {
235	    let mut prev_angle = directions[0]
236	    for i in 1..<directions.length() {
237	      let diff = (directions[i] - prev_angle).abs()
238	      curvature += if diff > 3.141592653589793 {
239	        6.283185307179586 - diff
240	      } else {
241	        diff
242	      }
243	      prev_angle = directions[i]
244	    }
245	    curvature = curvature / directions.length().to_double()
246	  }
247	  HandwritingStrokeFeatures::{
248	    num_strokes: num_components,
249	    stroke_directions: directions,
250	    stroke_lengths: [],
251	    connectivity: connectivity.min(1.0),
252	    curvature,
253	  }
254	}
   	…

298	pub fn segment_cursive_chars(
299	  img : @types.Image,
300	  min_char_width? : Int = 8,
301	  max_char_width? : Int = 80,
302	) -> Array[(Int, Int, Int, Int)] {
303	  let w = img.width
304	  let h = img.height
305	  let data = img.data
306	  // 计算垂直投影
307	  let projection : Array[Int] = Array::make(w, 0)
308	  for x in 0..<w {
309	    let mut count = 0
310	    for y in 0..<h {
311	      if data[y * w + x].to_int() > 128 {
312	        count += 1
313	      }
314	    }
315	    projection[x] = count
316	  }
317	  // 检测分割点（投影局部最小值）
318	  let split_points : Array[Int] = []
319	  for x in 2..<(w - 2) {
320	    if projection[x] < projection[x - 1] &&
321	      projection[x] < projection[x + 1] &&
322	      projection[x] < h / 10 {
323	      split_points.push(x)	<-- UNCOVERED
324	    }
325	  }
326	  // 基于分割点提取字符区域
327	  let regions : Array[(Int, Int, Int, Int)] = []
328	  let mut prev_x = 0
329	  for split_x in split_points {
330	    let char_w = split_x - prev_x	<-- UNCOVERED
331	    if char_w >= min_char_width && char_w <= max_char_width {
332	      // 找到字符的上下边界
333	      let mut min_y = h	<-- UNCOVERED
334	      let mut max_y = 0
335	      for x in prev_x..<split_x {
336	        for y in 0..<h {	<-- UNCOVERED
337	          if data[y * w + x].to_int() > 128 {
338	            if y < min_y {
339	              min_y = y
340	            }
341	            if y > max_y {
342	              max_y = y	<-- UNCOVERED
343	            }
344	          }
345	        }
346	      }
347	      if max_y > min_y {
348	        regions.push((prev_x, min_y, char_w, max_y - min_y + 1))	<-- UNCOVERED
349	      }
350	    }
351	    prev_x = split_x
352	  }
353	  // 处理最后一个字符
354	  let char_w = w - prev_x
355	  if char_w >= min_char_width && char_w <= max_char_width {
356	    let mut min_y = h
357	    let mut max_y = 0
358	    for x in prev_x..<w {
359	      for y in 0..<h {
360	        if data[y * w + x].to_int() > 128 {
361	          if y < min_y {
362	            min_y = y
363	          }
364	          if y > max_y {
365	            max_y = y
366	          }
367	        }
368	      }
369	    }
370	    if max_y > min_y {
371	      regions.push((prev_x, min_y, char_w, max_y - min_y + 1))
372	    }
373	  }
374	  regions
375	}
   	…

397	pub fn recognize_handwriting_enhanced(
398	  img : @types.Image,
399	  config? : HandwritingOcrEnhancedConfig,
400	) -> OcrResult raise @types.LoadError {
401	  // 输入验证
402	  if img.width <= 0 || img.height <= 0 {
403	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
404	      "recognize_handwriting_enhanced: 图像尺寸必须大于0",
405	    )
406	  }
407	  if img.channels < 1 || img.channels > 4 {
408	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
409	      "recognize_handwriting_enhanced: 通道数必须在1-4之间",
410	    )
411	  }
412	  let expected_size = img.width * img.height * img.channels
413	  if img.data.length() != expected_size {
414	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
415	      "recognize_handwriting_enhanced: 数据大小不匹配，期望 " +
416	      expected_size.to_string() +	<-- UNCOVERED
417	      "，实际 " +
418	      img.data.length().to_string(),	<-- UNCOVERED
419	    )
420	  }
421	  let cfg = match config {
422	    Some(c) => c	<-- UNCOVERED
423	    None => handwriting_ocr_enhanced_default_config()
424	  }
425	  // 1. 预处理
426	  let mut preprocessed = ocr_preprocess(img)
427	  // 2. 倾斜校正（手写体专用）
428	  if cfg.use_slant_correction {
429	    preprocessed = correct_slant(preprocessed)
430	  }
431	  // 3. 连笔字符分割
432	  let char_regions = if cfg.use_elastic_matching {
433	    segment_cursive_chars(
434	      preprocessed,
435	      min_char_width=cfg.min_char_width,
436	      max_char_width=cfg.max_char_width,
437	    )
438	  } else {
439	    segment_handwriting_chars_simple(preprocessed, cfg)	<-- UNCOVERED
440	  }
441	  // 4. 生成增强手写体模板
442	  let templates = generate_enhanced_handwriting_templates(
443	    char_set="digits+letters",
444	    num_variants=cfg.num_variants,
445	  )
446	  // 5. 对每个字符进行识别
447	  let char_results : Array[OcrCharResult] = []
448	  let mut text = ""
449	  let mut total_confidence = 0.0
450	  for _tup1 in char_regions {
451	    let (x, y, w, h) = _tup1
452	    // 提取字符图像
453	    let char_img = extract_handwriting_char_region_enhanced(
454	      preprocessed,
455	      x,
456	      y,
457	      w,
458	      h,
459	      cfg.target_size,
460	    )
461	    // 提取网格特征
462	    let features = extract_handwriting_grid_features(char_img, 16)
463	    // 提取笔画特征（如果启用）
464	    let stroke_features = if cfg.use_stroke_features {
465	      extract_handwriting_stroke_features(char_img)
466	    } else {
467	      HandwritingStrokeFeatures::{	<-- UNCOVERED
468	        num_strokes: 0,
469	        stroke_directions: [],
470	        stroke_lengths: [],
471	        connectivity: 0.0,
472	        curvature: 0.0,
473	      }
474	    }
475	    // 模板匹配（多变体投票 + 弹性匹配）
476	    let result = match_handwriting_enhanced(
477	      features, templates, stroke_features, cfg,
478	    )
479	    // 后处理（如果启用）
480	    let (final_char, final_conf) = if cfg.use_postprocessing {
481	      handwriting_postprocess(result.0, result.1)
482	    } else {
483	      result	<-- UNCOVERED
484	    }
485	    text += final_char
486	    total_confidence += final_conf
487	    char_results.push(OcrCharResult::{
488	      char: final_char,
489	      confidence: final_conf,
490	      x,
491	      y,
492	      width: w,
493	      height: h,
494	    })
495	  }
496	  let avg_confidence = if char_results.length() > 0 {
497	    total_confidence / char_results.length().to_double()
498	  } else {
499	    0.0	<-- UNCOVERED
500	  }
501	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
502	}
   	…

506	fn segment_handwriting_chars_simple(
507	  img : @types.Image,
508	  cfg : HandwritingOcrEnhancedConfig,
509	) -> Array[(Int, Int, Int, Int)] {
510	  let w = img.width	<-- UNCOVERED
511	  let h = img.height
512	  let data = img.data
513	  let labels : Array[Int] = Array::make(w * h, 0)	<-- UNCOVERED
514	  let mut next_label = 1
515	  for y in 0..<h {
516	    for x in 0..<w {	<-- UNCOVERED
517	      if data[y * w + x].to_int() > 128 {
518	        let mut label = 0
519	        if x > 0 && labels[y * w + x - 1] > 0 {
520	          label = labels[y * w + x - 1]	<-- UNCOVERED
521	        }
522	        if y > 0 && labels[(y - 1) * w + x] > 0 {
523	          if label == 0 {	<-- UNCOVERED
524	            label = labels[(y - 1) * w + x]
525	          }
526	        }
527	        if label == 0 {
528	          label = next_label	<-- UNCOVERED
529	          next_label += 1
530	        }
531	        labels[y * w + x] = label
532	      }
533	    }
534	  }
535	  let min_x : Array[Int] = Array::make(next_label, w)	<-- UNCOVERED
536	  let max_x : Array[Int] = Array::make(next_label, 0)
537	  let min_y : Array[Int] = Array::make(next_label, h)
538	  let max_y : Array[Int] = Array::make(next_label, 0)
539	  let area : Array[Int] = Array::make(next_label, 0)
540	  for y in 0..<h {
541	    for x in 0..<w {	<-- UNCOVERED
542	      let label = labels[y * w + x]
543	      if label > 0 {
544	        area[label] += 1	<-- UNCOVERED
545	        if x < min_x[label] {
546	          min_x[label] = x	<-- UNCOVERED
547	        }
548	        if x > max_x[label] {
549	          max_x[label] = x	<-- UNCOVERED
550	        }
551	        if y < min_y[label] {
552	          min_y[label] = y	<-- UNCOVERED
553	        }
554	        if y > max_y[label] {
555	          max_y[label] = y	<-- UNCOVERED
556	        }
557	      }
558	    }
559	  }
560	  let regions : Array[(Int, Int, Int, Int)] = []
561	  for label in 1..<next_label {
562	    let rw = max_x[label] - min_x[label] + 1	<-- UNCOVERED
563	    let rh = max_y[label] - min_y[label] + 1
564	    if rw >= cfg.min_char_width &&
565	      rw <= cfg.max_char_width &&
566	      rh >= cfg.min_char_height &&
567	      rh <= cfg.max_char_height &&
568	      area[label] > 15 {
569	      regions.push((min_x[label], min_y[label], rw, rh))	<-- UNCOVERED
570	    }
571	  }
572	  regions
573	}
   	…

577	fn extract_handwriting_char_region_enhanced(
578	  img : @types.Image,
579	  x : Int,
580	  y : Int,
581	  width : Int,
582	  height : Int,
583	  target_size : Int,
584	) -> @types.Image {
585	  let w = img.width
586	  let h = img.height
587	  let crop_w = if x + width > w { w - x } else { width }
588	  let crop_h = if y + height > h { h - y } else { height }
589	  if crop_w <= 0 || crop_h <= 0 {
590	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
591	    return @types.Image::{
592	      width: target_size,
593	      height: target_size,
594	      channels: 1,
595	      data: Bytes::from_array(empty),	<-- UNCOVERED
596	    }
597	  }
598	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
599	  let data = img.data
600	  for py in 0..<target_size {
601	    for px in 0..<target_size {
602	      let src_x = x + px * crop_w / target_size
603	      let src_y = y + py * crop_h / target_size
604	      if src_x < w && src_y < h {
605	        out[py * target_size + px] = data[src_y * w + src_x]
606	      }
607	    }
608	  }
609	  @types.Image::{
610	    width: target_size,
611	    height: target_size,
612	    channels: 1,
613	    data: Bytes::from_array(out),
614	  }
615	}
   	…

619	fn extract_handwriting_grid_features(
620	  char_img : @types.Image,
621	  grid_size : Int,
622	) -> Array[Double] {
623	  let w = char_img.width
624	  let h = char_img.height
625	  let data = char_img.data
626	  let cell_w = w / grid_size
627	  let cell_h = h / grid_size
628	  let features : Array[Double] = []
629	  for r in 0..<grid_size {
630	    for c in 0..<grid_size {
631	      let mut count = 0
632	      let mut total = 0
633	      for py in 0..<cell_h {
634	        for px in 0..<cell_w {
635	          let sx = c * cell_w + px
636	          let sy = r * cell_h + py
637	          if sx < w && sy < h {
638	            total += 1
639	            if data[sy * w + sx].to_int() > 128 {
640	              count += 1
641	            }
642	          }
643	        }
644	      }
645	      let density = if total > 0 {
646	        count.to_double() / total.to_double()
647	      } else {
648	        0.0	<-- UNCOVERED
649	      }
650	      features.push(density)
651	    }
652	  }
653	  features
654	}
   	…

658	fn match_handwriting_enhanced(
659	  features : Array[Double],
660	  templates : Array[(String, Array[Double])],
661	  _stroke_features : HandwritingStrokeFeatures,
662	  cfg : HandwritingOcrEnhancedConfig,
663	) -> (String, Double) {
664	  // 收集所有变体的预测
665	  let label_votes : Array[(String, Double)] = []
666	  for _tup2 in templates {
667	    let (label_full, template) = _tup2
668	    // 弹性匹配（如果启用）
669	    let dist = if cfg.use_elastic_matching {
670	      elastic_match(features, template, elasticity=cfg.elasticity, grid_size=16)
671	    } else {
672	      let n = if features.length() < template.length() {	<-- UNCOVERED
673	        features.length()
674	      } else {
675	        template.length()	<-- UNCOVERED
676	      }
677	      let mut sum_sq = 0.0
678	      for i in 0..<n {
679	        let diff = features[i] - template[i]	<-- UNCOVERED
680	        sum_sq += diff * diff
681	      }
682	      sum_sq.sqrt()	<-- UNCOVERED
683	    }
684	    let confidence = if dist > 0.0 { 1.0 / (1.0 + dist) } else { 1.0 }
685	    // 提取基础标签（去掉变体后缀）
686	    let base_label = if label_full.length() > 0 {
687	      label_full[0].to_string()
688	    } else {
689	      label_full	<-- UNCOVERED
690	    }
691	    // 投票
692	    let mut found = false
693	    for i in 0..<label_votes.length() {
694	      if label_votes[i].0 == base_label {
695	        label_votes[i] = (base_label, label_votes[i].1 + confidence)
696	        found = true
697	      }
698	    }
699	    if !found {
700	      label_votes.push((base_label, confidence))
701	    }
702	  }
703	  // 找得票最多的标签
704	  let mut best_label = "?"
705	  let mut best_votes = 0.0
706	  let mut total_votes = 0.0
707	  for _tup3 in label_votes {
708	    let (label, votes) = _tup3
709	    total_votes += votes
710	    if votes > best_votes {
711	      best_votes = votes
712	      best_label = label
713	    }
714	  }
715	  let confidence = if total_votes > 0.0 {
716	    best_votes / total_votes
717	  } else {
718	    0.0	<-- UNCOVERED
719	  }
720	  (best_label, confidence)
721	}
   	…

50 uncovered line(s) in src\process\ocr\handwriting_recognition.mbt:

125	pub fn detect_handwriting_slant(
126	  img : @types.Image,
127	) -> Double raise @types.LoadError {
128	  // 输入验证
129	  if img.width <= 0 || img.height <= 0 {
130	    raise @types.LoadError::DecodeFailed(
131	      "detect_handwriting_slant: 图像尺寸必须大于0",
132	    )
133	  }
134	  if img.channels < 1 || img.channels > 4 {
135	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
136	      "detect_handwriting_slant: 通道数必须在1-4之间",
137	    )
138	  }
139	  let expected_size = img.width * img.height * img.channels
140	  if img.data.length() != expected_size {
141	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
142	      "detect_handwriting_slant: 数据大小不匹配，期望 " +
143	      expected_size.to_string() +	<-- UNCOVERED
144	      "，实际 " +
145	      img.data.length().to_string(),	<-- UNCOVERED
146	    )
147	  }
148	  let w = img.width
149	  let h = img.height
150	  // 基于文本行的倾斜检测
151	  let mut total_angle = 0.0
152	  let mut line_count = 0
153	
154	  for y in 0..<(h - 1) {
155	    let mut start_x = -1
156	    let mut end_x = -1
157	    for x in 0..<w {
158	      if img.data[y * w + x].to_int() < 128 {
159	        if start_x == -1 {	<-- UNCOVERED
160	          start_x = x
161	        }
162	        end_x = x
163	      }
164	    }
165	    if start_x != -1 && end_x != -1 && end_x - start_x > w / 4 {
166	      let center_x = (start_x + end_x) / 2	<-- UNCOVERED
167	      // 简化：用行中心的变化计算倾斜
168	      if y > 0 {
169	        let prev_center = center_x	<-- UNCOVERED
170	        let angle = (prev_center - center_x).to_double() /
171	          h.to_double() *
172	          57.2958
173	        total_angle += angle
174	        line_count += 1
175	      }
176	    }
177	  }
178	
179	  if line_count > 0 {
180	    total_angle / line_count.to_double()	<-- UNCOVERED
181	  } else {
182	    0.0
183	  }
184	}
   	…

188	pub fn normalize_handwriting_size(
189	  img : @types.Image,
190	  target_size : Int,
191	) -> @types.Image raise @types.LoadError {
192	  // 输入验证
193	  if img.width <= 0 || img.height <= 0 {
194	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
195	      "normalize_handwriting_size: 图像尺寸必须大于0",
196	    )
197	  }
198	  if img.channels < 1 || img.channels > 4 {
199	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
200	      "normalize_handwriting_size: 通道数必须在1-4之间",
201	    )
202	  }
203	  let expected_size = img.width * img.height * img.channels
204	  if img.data.length() != expected_size {
205	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
206	      "normalize_handwriting_size: 数据大小不匹配，期望 " +
207	      expected_size.to_string() +	<-- UNCOVERED
208	      "，实际 " +
209	      img.data.length().to_string(),	<-- UNCOVERED
210	    )
211	  }
212	  if target_size <= 0 {
213	    raise @types.LoadError::DecodeFailed(
214	      "normalize_handwriting_size: 目标尺寸必须大于0",
215	    )
216	  }
217	  // 简化版：返回原图像，实际应进行缩放
218	  img
219	}
   	…

223	pub fn extract_hw_stroke_features(img : @types.Image) -> Array[StrokeFeature] {
224	  let w = img.width
225	  let h = img.height
226	  let strokes : Array[StrokeFeature] = []
227	
228	  // 简化版：基于连通域的笔画提取
229	  let visited : Array[Bool] = []
230	  for _ in 0..<(w * h) {
231	    visited.push(false)
232	  }
233	
234	  for y in 0..<h {
235	    for x in 0..<w {
236	      let idx = y * w + x
237	      if !visited[idx] && img.data[idx].to_int() < 128 {
238	        // 找到一个连通域（简化为单点）
239	        visited[idx] = true	<-- UNCOVERED
240	        let stroke = StrokeFeature::{
241	          stroke_type: StrokeType::Unknown,
242	          start_x: x.to_double(),	<-- UNCOVERED
243	          start_y: y.to_double(),
244	          end_x: x.to_double(),
245	          end_y: y.to_double(),
246	          length: 1.0,
247	          angle: 0.0,
248	          curvature: 0.0,
249	          pressure: 0.5,
250	        }
251	        strokes.push(stroke)	<-- UNCOVERED
252	      }
253	    }
254	  }
255	
256	  strokes
257	}
   	…

261	pub fn segment_connected_strokes(
262	  img : @types.Image,
263	  config : HandwritingRecognitionConfig,
264	) -> StrokeSegmentationResult {
265	  let strokes = extract_hw_stroke_features(img)
266	  let segmentation_points : Array[(Int, Int)] = []
267	
268	  // 简化版：基于笔画长度的分割
269	  for i in 0..<strokes.length() {
270	    if strokes[i].length < config.min_stroke_length {	<-- UNCOVERED
271	      segmentation_points.push(
272	        (strokes[i].start_x.to_int(), strokes[i].start_y.to_int()),
273	      )
274	    }
275	  }
276	
277	  StrokeSegmentationResult::{
278	    num_strokes: strokes.length(),
279	    strokes,
280	    segmentation_points,
281	    confidence: 0.7,
282	  }
283	}
   	…

287	pub fn extract_handwriting_features(
288	  img : @types.Image,
289	  char : String,
290	) -> HandwritingCharFeature raise @types.LoadError {
291	  // 输入验证
292	  if img.width <= 0 || img.height <= 0 {
293	    raise @types.LoadError::DecodeFailed(
294	      "extract_handwriting_features: 图像尺寸必须大于0",
295	    )
296	  }
297	  if img.channels < 1 || img.channels > 4 {
298	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
299	      "extract_handwriting_features: 通道数必须在1-4之间",
300	    )
301	  }
302	  let expected_size = img.width * img.height * img.channels
303	  if img.data.length() != expected_size {
304	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
305	      "extract_handwriting_features: 数据大小不匹配，期望 " +
306	      expected_size.to_string() +	<-- UNCOVERED
307	      "，实际 " +
308	      img.data.length().to_string(),	<-- UNCOVERED
309	    )
310	  }
311	  let w = img.width
312	  let h = img.height
313	
314	  // 计算水平投影
315	  let horizontal : Array[Double] = []
316	  for y in 0..<h {
317	    let mut count = 0.0
318	    for x in 0..<w {
319	      if img.data[y * w + x].to_int() < 128 {
320	        count += 1.0	<-- UNCOVERED
321	      }
322	    }
323	    horizontal.push(count / w.to_double())
324	  }
325	
326	  // 计算垂直投影
327	  let vertical : Array[Double] = []
328	  for x in 0..<w {
329	    let mut count = 0.0
330	    for y in 0..<h {
331	      if img.data[y * w + x].to_int() < 128 {
332	        count += 1.0	<-- UNCOVERED
333	      }
334	    }
335	    vertical.push(count / h.to_double())
336	  }
337	
338	  // 计算重心
339	  let mut total_mass = 0.0
340	  let mut sum_x = 0.0
341	  let mut sum_y = 0.0
342	  for y in 0..<h {
343	    for x in 0..<w {
344	      if img.data[y * w + x].to_int() < 128 {
345	        total_mass += 1.0	<-- UNCOVERED
346	        sum_x += x.to_double()
347	        sum_y += y.to_double()
348	      }
349	    }
350	  }
351	
352	  let center_x = if total_mass > 0 {
353	    sum_x / total_mass	<-- UNCOVERED
354	  } else {
355	    w.to_double() / 2.0
356	  }
357	  let center_y = if total_mass > 0 {
358	    sum_y / total_mass	<-- UNCOVERED
359	  } else {
360	    h.to_double() / 2.0
361	  }
362	
363	  // 计算密度
364	  let density = total_mass / (w * h).to_double()
365	
366	  // 提取笔画
367	  let strokes = extract_hw_stroke_features(img)
368	  let stroke_types : Array[StrokeType] = []
369	  for s in strokes {
370	    stroke_types.push(s.stroke_type)	<-- UNCOVERED
371	  }
372	
373	  HandwritingCharFeature::{
374	    char,
375	    num_strokes: strokes.length(),
376	    stroke_types,
377	    aspect_ratio: w.to_double() / h.to_double(),
378	    density,
379	    center_of_mass_x: center_x / w.to_double(),
380	    center_of_mass_y: center_y / h.to_double(),
381	    horizontal_projection: horizontal,
382	    vertical_projection: vertical,
383	    contour_complexity: strokes.length().to_double() / 10.0,
384	    slant_angle: detect_handwriting_slant(img),
385	    size_normalized: true,
386	  }
387	}
   	…

391	pub fn handwriting_feature_similarity(
392	  feat1 : HandwritingCharFeature,
393	  feat2 : HandwritingCharFeature,
394	) -> Double {
395	  let mut score = 0.0
396	
397	  // 笔画数相似度（20%）
398	  let stroke_diff = (feat1.num_strokes - feat2.num_strokes).abs().to_double()
399	  let stroke_sim = if stroke_diff < 1 {
400	    1.0
401	  } else if stroke_diff < 3 {	<-- UNCOVERED
402	    0.7
403	  } else {
404	    0.4	<-- UNCOVERED
405	  }
406	  score += stroke_sim * 0.2
407	
408	  // 宽高比相似度（15%）
409	  let aspect_diff = (feat1.aspect_ratio - feat2.aspect_ratio).abs()
410	  let aspect_sim = if aspect_diff < 0.1 {
411	    1.0
412	  } else if aspect_diff < 0.3 {	<-- UNCOVERED
413	    0.7
414	  } else {
415	    0.4	<-- UNCOVERED
416	  }
417	  score += aspect_sim * 0.15
418	
419	  // 密度相似度（15%）
420	  let density_diff = (feat1.density - feat2.density).abs()
421	  let density_sim = if density_diff < 0.05 {
422	    1.0
423	  } else if density_diff < 0.15 {	<-- UNCOVERED
424	    0.7
425	  } else {
426	    0.4	<-- UNCOVERED
427	  }
428	  score += density_sim * 0.15
429	
430	  // 重心相似度（20%）
431	  let center_dist = (@math.pow(
432	    feat1.center_of_mass_x - feat2.center_of_mass_x,
433	    2.0,
434	  ) +
435	  @math.pow(feat1.center_of_mass_y - feat2.center_of_mass_y, 2.0)).sqrt()
436	  let center_sim = if center_dist < 0.05 {
437	    1.0
438	  } else if center_dist < 0.15 {	<-- UNCOVERED
439	    0.7
440	  } else {
441	    0.4	<-- UNCOVERED
442	  }
443	  score += center_sim * 0.2
444	
445	  // 倾斜角度相似度（10%）
446	  let slant_diff = (feat1.slant_angle - feat2.slant_angle).abs()
447	  let slant_sim = if slant_diff < 5.0 {
448	    1.0
449	  } else if slant_diff < 15.0 {	<-- UNCOVERED
450	    0.7
451	  } else {
452	    0.4	<-- UNCOVERED
453	  }
454	  score += slant_sim * 0.1
455	
456	  // 轮廓复杂度相似度（10%）
457	  let contour_diff = (feat1.contour_complexity - feat2.contour_complexity).abs()
458	  let contour_sim = if contour_diff < 0.5 {
459	    1.0
460	  } else if contour_diff < 1.5 {	<-- UNCOVERED
461	    0.7
462	  } else {
463	    0.4	<-- UNCOVERED
464	  }
465	  score += contour_sim * 0.1
466	
467	  score
468	}
   	…

553	pub fn recognize_handwriting_char(
554	  img : @types.Image,
555	  templates : Array[HandwritingTemplate],
556	  config : HandwritingRecognitionConfig,
557	) -> HandwritingRecognitionResult raise @types.LoadError {
558	  // 输入验证
559	  if img.width <= 0 || img.height <= 0 {
560	    raise @types.LoadError::DecodeFailed(
561	      "recognize_handwriting_char: 图像尺寸必须大于0",
562	    )
563	  }
564	  if img.channels < 1 || img.channels > 4 {
565	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
566	      "recognize_handwriting_char: 通道数必须在1-4之间",
567	    )
568	  }
569	  let expected_size = img.width * img.height * img.channels
570	  if img.data.length() != expected_size {
571	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
572	      "recognize_handwriting_char: 数据大小不匹配，期望 " +
573	      expected_size.to_string() +	<-- UNCOVERED
574	      "，实际 " +
575	      img.data.length().to_string(),	<-- UNCOVERED
576	    )
577	  }
578	  if templates.length() == 0 {
579	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
580	      "recognize_handwriting_char: 模板列表不能为空",
581	    )
582	  }
583	  if config.similarity_threshold < 0.0 || config.similarity_threshold > 1.0 {
584	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
585	      "recognize_handwriting_char: 相似度阈值必须在0-1之间",
586	    )
587	  }
588	  let _ = 0.0
589	
590	  // 1. 预处理
591	  let preprocessed = if config.enable_size_normalization {
592	    normalize_handwriting_size(img, 64)
593	  } else {
594	    img	<-- UNCOVERED
595	  }
596	  let preprocess_time = 0.0
597	
598	  // 2. 特征提取
599	  let features = extract_handwriting_features(preprocessed, "?")
600	  let feature_time = 0.0
601	
602	  // 3. 模板匹配
603	  let candidates : Array[(String, Double)] = []
604	  for template in templates {
605	    let sim = handwriting_feature_similarity(features, template.features)
606	    if sim > config.similarity_threshold {
607	      candidates.push((template.char, sim))
608	    }
609	  }
610	
611	  // 排序
612	  for i in 0..<candidates.length() {
613	    for j in 0..<(candidates.length() - i - 1) {
614	      if candidates[j].1 < candidates[j + 1].1 {	<-- UNCOVERED
615	        let temp = candidates[j]
616	        candidates[j] = candidates[j + 1]
617	        candidates[j + 1] = temp
618	      }
619	    }
620	  }
621	
622	  let match_time = 0.0
623	
624	  let predicted = if candidates.length() > 0 { candidates[0].0 } else { "?" }
625	  let confidence = if candidates.length() > 0 { candidates[0].1 } else { 0.0 }
626	
627	  let top_candidates : Array[(String, Double)] = []
628	  for
629	    i in 0..<(if config.max_candidates < candidates.length() {
630	      config.max_candidates	<-- UNCOVERED
631	    } else {
632	      candidates.length()
633	    }) {
634	    top_candidates.push(candidates[i])
635	  }
636	
637	  HandwritingRecognitionResult::{
638	    predicted_char: predicted,
639	    confidence,
640	    top_candidates,
641	    detected_style: config.style_priority,
642	    num_strokes_detected: features.num_strokes,
643	    slant_angle: features.slant_angle,
644	    preprocessing_time_ms: preprocess_time,
645	    feature_extraction_time_ms: feature_time,
646	    matching_time_ms: match_time,
647	    total_time_ms: 0.0,
648	  }
649	}
   	…

653	pub fn handwriting_report_to_string(
654	  result : HandwritingRecognitionResult,
655	) -> String {
656	  let mut report = "## 手写体识别报告\n\n"
657	  report = report + "### 识别结果\n\n"
658	  report = report + "| 指标 | 值 |\n"
659	  report = report + "|------|------|\n"
660	  report = report + "| 预测字符 | **" + result.predicted_char + "** |\n"
661	  report = report +
662	    "| 置信度 | " +
663	    (result.confidence * 100.0).to_string() +
664	    "% |\n"
665	  report = report +
666	    "| 检测字体 | " +
667	    (match result.detected_style {
668	      HandwritingStyle::Print => "印刷体"	<-- UNCOVERED
669	      HandwritingStyle::Cursive => "草书/连笔"
670	      HandwritingStyle::SemiCursive => "半连笔"
671	      HandwritingStyle::Block => "方块字"	<-- UNCOVERED
672	      _ => "未知"
673	    }) +
674	    " |\n"
675	  report = report +
676	    "| 检测笔画数 | " +
677	    result.num_strokes_detected.to_string() +
678	    " |\n"
679	  report = report +
680	    "| 倾斜角度 | " +
681	    result.slant_angle.to_string() +
682	    "° |\n\n"
683	
684	  if result.top_candidates.length() > 0 {
685	    report = report + "### Top-K 候选\n\n"
686	    report = report + "| 排名 | 字符 | 置信度 |\n"
687	    report = report + "|------|------|--------|\n"
688	    for i in 0..<result.top_candidates.length() {
689	      report = report +
690	        "| " +
691	        (i + 1).to_string() +
692	        " | " +
693	        result.top_candidates[i].0 +
694	        " | " +
695	        (result.top_candidates[i].1 * 100.0).to_string() +
696	        "% |\n"
697	    }
698	  }
699	
700	  report
701	}
   	…

45 uncovered line(s) in src\process\ocr\layout_analysis.mbt:

 35	pub fn detect_text_regions(
 36	  img : @types.Image,
 37	) -> Array[TextRegion] raise @types.LoadError {
 38	  // 输入验证
 39	  if img.width <= 0 || img.height <= 0 {
 40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 41	      "detect_text_regions: 图像尺寸必须大于0",
 42	    )
 43	  }
 44	  if img.channels != 1 {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "detect_text_regions: 仅支持单通道灰度图",
 47	    )
 48	  }
 49	  let expected_size = img.width * img.height
 50	  if img.data.length() != expected_size {
 51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 52	      "detect_text_regions: 数据大小不匹配，期望 " +
 53	      expected_size.to_string() +	<-- UNCOVERED
 54	      "，实际 " +
 55	      img.data.length().to_string(),	<-- UNCOVERED
 56	    )
 57	  }
 58	  let w = img.width
 59	  let h = img.height
 60	  let data = img.data
 61	  // 二值化（简单阈值）
 62	  let binary : Array[Bool] = Array::make(w * h, false)
 63	  for i in 0..<(w * h) {
 64	    binary[i] = data[i].to_int() < 128
 65	  }
 66	  // 水平投影（检测文本行）
 67	  let horizontal_proj : Array[Int] = Array::make(h, 0)
 68	  for y in 0..<h {
 69	    for x in 0..<w {
 70	      if binary[y * w + x] {
 71	        horizontal_proj[y] += 1
 72	      }
 73	    }
 74	  }
 75	  // 检测文本行区域
 76	  let regions : Array[TextRegion] = []
 77	  let mut in_text = false
 78	  let mut start_y = 0
 79	  let threshold = w / 20 // 文本行阈值
 80	  for y in 0..<h {
 81	    if horizontal_proj[y] > threshold && !in_text {
 82	      in_text = true
 83	      start_y = y
 84	    } else if horizontal_proj[y] <= threshold && in_text {
 85	      in_text = false
 86	      // 计算这一行的水平范围
 87	      let mut min_x = w
 88	      let mut max_x = 0
 89	      for yy in start_y..<y {
 90	        for x in 0..<w {
 91	          if binary[yy * w + x] {
 92	            if x < min_x {
 93	              min_x = x
 94	            }
 95	            if x > max_x {
 96	              max_x = x
 97	            }
 98	          }
 99	        }
100	      }
101	      if max_x > min_x {
102	        regions.push(TextRegion::{
103	          x: min_x,
104	          y: start_y,
105	          width: max_x - min_x + 1,
106	          height: y - start_y,
107	          region_type: TextRegionType::Text,
108	        })
109	      }
110	    }
111	  }
112	  regions
113	}
   	…

118	pub fn segment_page(
119	  img : @types.Image,
120	) -> Array[TextRegion] raise @types.LoadError {
121	  // 输入验证
122	  if img.width <= 0 || img.height <= 0 {
123	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
124	      "segment_page: 图像尺寸必须大于0",
125	    )
126	  }
127	  if img.channels != 1 {
128	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
129	      "segment_page: 仅支持单通道灰度图",
130	    )
131	  }
132	  let expected_size = img.width * img.height
133	  if img.data.length() != expected_size {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "segment_page: 数据大小不匹配，期望 " +
136	      expected_size.to_string() +	<-- UNCOVERED
137	      "，实际 " +
138	      img.data.length().to_string(),	<-- UNCOVERED
139	    )
140	  }
141	  let w = img.width
142	  let h = img.height
143	  let data = img.data
144	  // 二值化
145	  let binary : Array[Bool] = Array::make(w * h, false)
146	  for i in 0..<(w * h) {
147	    binary[i] = data[i].to_int() < 128
148	  }
149	  // 连通域分析（简化版）
150	  let visited : Array[Bool] = Array::make(w * h, false)
151	  let regions : Array[TextRegion] = []
152	  for y in 0..<h {
153	    for x in 0..<w {
154	      if binary[y * w + x] && !visited[y * w + x] {
155	        // BFS 找连通域
156	        let mut min_x = x
157	        let mut max_x = x
158	        let mut min_y = y
159	        let mut max_y = y
160	        let mut count = 0
161	        let queue : Array[(Int, Int)] = [(x, y)]
162	        visited[y * w + x] = true
163	        let mut queue_idx = 0
164	        while queue_idx < queue.length() {
165	          let (cx, cy) = queue[queue_idx]
166	          queue_idx += 1
167	          count += 1
168	          if cx < min_x {
169	            min_x = cx
170	          }
171	          if cx > max_x {
172	            max_x = cx
173	          }
174	          if cy < min_y {
175	            min_y = cy	<-- UNCOVERED
176	          }
177	          if cy > max_y {
178	            max_y = cy
179	          }
180	          // 8 邻域
181	          for dy in -1..<2 {
182	            for dx in -1..<2 {
183	              let nx = cx + dx
184	              let ny = cy + dy
185	              if nx >= 0 &&
186	                nx < w &&
187	                ny >= 0 &&
188	                ny < h &&
189	                binary[ny * w + nx] &&
190	                !visited[ny * w + nx] {
191	                visited[ny * w + nx] = true
192	                queue.push((nx, ny))
193	              }
194	            }
195	          }
196	        }
197	        // 根据区域大小和宽高比判断类型
198	        let region_w = max_x - min_x + 1
199	        let region_h = max_y - min_y + 1
200	        let region_type = if count < 50 {
201	          TextRegionType::Unknown // 噪声
202	        } else if region_w > 50 && region_h > 50 {	<-- UNCOVERED
203	          TextRegionType::Image // 大区域可能是图像
204	        } else {
205	          TextRegionType::Text // 小区域可能是文字	<-- UNCOVERED
206	        }
207	        regions.push(TextRegion::{
208	          x: min_x,
209	          y: min_y,
210	          width: region_w,
211	          height: region_h,
212	          region_type,
213	        })
214	      }
215	    }
216	  }
217	  regions
218	}
   	…

223	pub fn detect_columns(
224	  img : @types.Image,
225	) -> (Int, Array[(Int, Int)]) raise @types.LoadError {
226	  // 输入验证
227	  if img.width <= 0 || img.height <= 0 {
228	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
229	      "detect_columns: 图像尺寸必须大于0",
230	    )
231	  }
232	  if img.channels != 1 {
233	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
234	      "detect_columns: 仅支持单通道灰度图",
235	    )
236	  }
237	  let expected_size = img.width * img.height
238	  if img.data.length() != expected_size {
239	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
240	      "detect_columns: 数据大小不匹配，期望 " +
241	      expected_size.to_string() +	<-- UNCOVERED
242	      "，实际 " +
243	      img.data.length().to_string(),	<-- UNCOVERED
244	    )
245	  }
246	  let w = img.width
247	  let h = img.height
248	  let data = img.data
249	  // 垂直投影
250	  let vertical_proj : Array[Int] = Array::make(w, 0)
251	  for y in 0..<h {
252	    for x in 0..<w {
253	      if data[y * w + x].to_int() < 128 {
254	        vertical_proj[x] += 1
255	      }
256	    }
257	  }
258	  // 找空白列（栏间距）
259	  let threshold = h / 50 // 空白列阈值
260	  let gaps : Array[(Int, Int)] = []
261	  let mut in_gap = false
262	  let mut gap_start = 0
263	  for x in 0..<w {
264	    if vertical_proj[x] < threshold && !in_gap {
265	      in_gap = true
266	      gap_start = x
267	    } else if vertical_proj[x] >= threshold && in_gap {
268	      in_gap = false	<-- UNCOVERED
269	      let gap_width = x - gap_start
270	      if gap_width > w / 20 { // 足够宽的间隙才算栏间距
271	        gaps.push((gap_start, x))	<-- UNCOVERED
272	      }
273	    }
274	  }
275	  // 根据间隙计算栏数和范围
276	  let num_columns = gaps.length() + 1
277	  let columns : Array[(Int, Int)] = []
278	  let mut prev_end = 0
279	  for i in 0..<gaps.length() {
280	    let (start, end) = gaps[i]	<-- UNCOVERED
281	    columns.push((prev_end, start))
282	    prev_end = end
283	  }
284	  columns.push((prev_end, w))
285	  (num_columns, columns)
286	}
   	…

291	pub fn extract_text_lines(
292	  img : @types.Image,
293	) -> Array[@types.Image] raise @types.LoadError {
294	  // 输入验证
295	  if img.width <= 0 || img.height <= 0 {
296	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
297	      "extract_text_lines: 图像尺寸必须大于0",
298	    )
299	  }
300	  if img.channels != 1 {
301	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
302	      "extract_text_lines: 仅支持单通道灰度图",
303	    )
304	  }
305	  let expected_size = img.width * img.height
306	  if img.data.length() != expected_size {
307	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
308	      "extract_text_lines: 数据大小不匹配，期望 " +
309	      expected_size.to_string() +	<-- UNCOVERED
310	      "，实际 " +
311	      img.data.length().to_string(),	<-- UNCOVERED
312	    )
313	  }
314	  let regions = detect_text_regions(img)
315	  let lines : Array[@types.Image] = []
316	  for region in regions {
317	    if region.region_type == TextRegionType::Text {	<-- UNCOVERED
318	      let line = crop_region(
319	        img,
320	        region.x,
321	        region.y,
322	        region.width,
323	        region.height,
324	      )
325	      match line {	<-- UNCOVERED
326	        Some(l) => lines.push(l)
327	        None => ignore(())
328	      }
329	    }
330	  }
331	  lines
332	}
   	…

336	fn crop_region(
337	  img : @types.Image,
338	  x : Int,
339	  y : Int,
340	  width : Int,
341	  height : Int,
342	) -> @types.Image? {
343	  let w = img.width	<-- UNCOVERED
344	  let h = img.height
345	  let ch = img.channels
346	  if x < 0 || y < 0 || x + width > w || y + height > h {
347	    return None	<-- UNCOVERED
348	  }
349	  let out : Array[Byte] = Array::make(width * height * ch, b'\x00')	<-- UNCOVERED
350	  let data = img.data
351	  for yy in 0..<height {
352	    for xx in 0..<width {	<-- UNCOVERED
353	      for c in 0..<ch {
354	        out[(yy * width + xx) * ch + c] = data[((y + yy) * w + (x + xx)) * ch +
355	          c]
356	      }
357	    }
358	  }
359	  Some(@types.Image::{
360	    width,
361	    height,
362	    channels: ch,
363	    data: Bytes::from_array(out),	<-- UNCOVERED
364	  })
365	}
   	…

370	pub fn ocr_preprocess(
371	  img : @types.Image,
372	  binarization_method? : String = "sauvola",
373	) -> @types.Image raise @types.LoadError {
374	  // 输入验证
375	  if img.width <= 0 || img.height <= 0 {
376	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
377	      "ocr_preprocess: 图像尺寸必须大于0",
378	    )
379	  }
380	  if img.channels < 1 || img.channels > 4 {
381	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
382	      "ocr_preprocess: 通道数必须在1-4之间",
383	    )
384	  }
385	  let expected_size = img.width * img.height * img.channels
386	  if img.data.length() != expected_size {
387	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
388	      "ocr_preprocess: 数据大小不匹配，期望 " +
389	      expected_size.to_string() +	<-- UNCOVERED
390	      "，实际 " +
391	      img.data.length().to_string(),	<-- UNCOVERED
392	    )
393	  }
394	  let mut result = img
395	  // 1. 转灰度
396	  if result.channels != 1 {
397	    result = to_grayscale_ocr(result)	<-- UNCOVERED
398	  }
399	  // 2. 文档增强（去噪+对比度+锐化）
400	  result = document_enhance(result)
401	  // 3. 倾斜校正
402	  result = deskew(result)
403	  // 4. 二值化
404	  result = match binarization_method {
405	    "sauvola" => sauvola_threshold(result)
406	    "niblack" => niblack_threshold(result)	<-- UNCOVERED
407	    "otsu" => otsu_threshold(result)
408	    "adaptive" => adaptive_threshold_ocr(result)
409	    _ => sauvola_threshold(result)
410	  }
411	  result
412	}
   	…

416	fn to_grayscale_ocr(img : @types.Image) -> @types.Image {
417	  if img.channels == 1 {	<-- UNCOVERED
418	    return img
419	  }
420	  let n = img.width * img.height
421	  let ch = img.channels
422	  let out : Array[Byte] = Array::make(n, b'\x00')	<-- UNCOVERED
423	  let data = img.data
424	  for i in 0..<n {
425	    let base = i * ch	<-- UNCOVERED
426	    let r = data[base].to_int()
427	    let g = data[base + 1].to_int()
428	    let b = data[base + 2].to_int()
429	    let gray = (r * 299 + g * 587 + b * 114) / 1000
430	    out[i] = gray.to_byte()	<-- UNCOVERED
431	  }
432	  @types.Image::{
433	    width: img.width,
434	    height: img.height,
435	    channels: 1,
436	    data: Bytes::from_array(out),	<-- UNCOVERED
437	  }
438	}
   	…

57 uncovered line(s) in src\process\ocr\multilingual_ocr.mbt:

139	pub fn detect_char_language(
140	  char_img : @types.Image,
141	) -> LanguageType raise @types.LoadError {
142	  // 输入验证
143	  if char_img.width <= 0 || char_img.height <= 0 {
144	    raise @types.LoadError::DecodeFailed(
145	      "detect_char_language: 图像尺寸必须大于0",
146	    )
147	  }
148	  if char_img.channels < 1 || char_img.channels > 4 {
149	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
150	      "detect_char_language: 通道数必须在1-4之间",
151	    )
152	  }
153	  let expected_size = char_img.width * char_img.height * char_img.channels
154	  if char_img.data.length() != expected_size {
155	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
156	      "detect_char_language: 数据大小不匹配，期望 " +
157	      expected_size.to_string() +	<-- UNCOVERED
158	      "，实际 " +
159	      char_img.data.length().to_string(),	<-- UNCOVERED
160	    )
161	  }
162	  let w = char_img.width
163	  let h = char_img.height
164	  let ratio = if h > 0 { w.to_double() / h.to_double() } else { 1.0 }
165	  // 中文字符通常接近正方形（ratio 0.8-1.2）
166	  if ratio > 0.75 && ratio < 1.35 && w > 20 && h > 20 {
167	    return LanguageType::Chinese
168	  }
169	  // 英文字符通常较窄（ratio 0.4-0.8）
170	  if ratio > 0.3 && ratio < 0.75 {
171	    return LanguageType::English	<-- UNCOVERED
172	  }
173	  // 数字
174	  if ratio > 0.4 && ratio < 0.7 {
175	    return LanguageType::Digit	<-- UNCOVERED
176	  }
177	  // 标点符号通常很窄或很宽
178	  if ratio < 0.4 || ratio > 1.35 {
179	    return LanguageType::Punctuation	<-- UNCOVERED
180	  }
181	  LanguageType::Unknown
182	}
   	…

186	pub fn detect_document_language(
187	  img : @types.Image,
188	) -> LanguageType raise @types.LoadError {
189	  // 输入验证
190	  if img.width <= 0 || img.height <= 0 {
191	    raise @types.LoadError::DecodeFailed(
192	      "detect_document_language: 图像尺寸必须大于0",
193	    )
194	  }
195	  if img.channels < 1 || img.channels > 4 {
196	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
197	      "detect_document_language: 通道数必须在1-4之间",
198	    )
199	  }
200	  let expected_size = img.width * img.height * img.channels
201	  if img.data.length() != expected_size {
202	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
203	      "detect_document_language: 数据大小不匹配，期望 " +
204	      expected_size.to_string() +	<-- UNCOVERED
205	      "，实际 " +
206	      img.data.length().to_string(),	<-- UNCOVERED
207	    )
208	  }
209	  // 预处理
210	  let preprocessed = ocr_preprocess(img)
211	  // 检测文本行
212	  let lines = detect_text_lines_projection(preprocessed)
213	  if lines.length() == 0 {
214	    return LanguageType::Unknown	<-- UNCOVERED
215	  }
216	  // 统计字符宽高比
217	  let mut chinese_count = 0
218	  let mut english_count = 0
219	  let mut digit_count = 0
220	  // 简单统计：基于文本行的平均字符宽度
221	  for line in lines {
222	    let avg_char_width = if line.width > 0 {
223	      line.width.to_double() / (line.width.to_double() / 15.0).max(1.0)
224	    } else {
225	      15.0	<-- UNCOVERED
226	    }
227	    let ratio = avg_char_width / line.height.to_double()
228	    if ratio > 0.75 && ratio < 1.35 {
229	      chinese_count += 1	<-- UNCOVERED
230	    } else if ratio > 0.3 && ratio < 0.75 {
231	      english_count += 1
232	    } else {
233	      digit_count += 1
234	    }
235	  }
236	  // 判断主要语言
237	  if chinese_count > english_count && chinese_count > digit_count {
238	    return LanguageType::Chinese	<-- UNCOVERED
239	  }
240	  if english_count > chinese_count && english_count > digit_count {
241	    return LanguageType::English
242	  }
243	  if chinese_count > 0 && english_count > 0 {
244	    return LanguageType::Mixed	<-- UNCOVERED
245	  }
246	  LanguageType::Unknown
247	}
   	…

251	pub fn recognize_multilingual(
252	  img : @types.Image,
253	  config? : MultilingualOcrConfig,
254	) -> OcrResult raise @types.LoadError {
255	  // 输入验证
256	  if img.width <= 0 || img.height <= 0 {
257	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
258	      "recognize_multilingual: 图像尺寸必须大于0",
259	    )
260	  }
261	  if img.channels < 1 || img.channels > 4 {
262	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
263	      "recognize_multilingual: 通道数必须在1-4之间",
264	    )
265	  }
266	  let expected_size = img.width * img.height * img.channels
267	  if img.data.length() != expected_size {
268	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
269	      "recognize_multilingual: 数据大小不匹配，期望 " +
270	      expected_size.to_string() +	<-- UNCOVERED
271	      "，实际 " +
272	      img.data.length().to_string(),	<-- UNCOVERED
273	    )
274	  }
275	  let cfg = match config {
276	    Some(c) => c	<-- UNCOVERED
277	    None => multilingual_ocr_default_config()
278	  }
279	  // 1. 预处理
280	  let preprocessed = ocr_preprocess(img)
281	  // 2. 检测文档主要语言
282	  let _ = if cfg.auto_language_detection {
283	    detect_document_language(preprocessed)
284	  } else {
285	    LanguageType::Mixed	<-- UNCOVERED
286	  }
287	  // 3. 字符分割
288	  let char_regions = segment_multilingual_chars(preprocessed, cfg)
289	  // 4. 生成各语言模板
290	  let chinese_templates = if cfg.enable_chinese {
291	    generate_3500_chinese_templates(char_set="common100")
292	  } else {
293	    []	<-- UNCOVERED
294	  }
295	  let english_templates = if cfg.enable_english {
296	    generate_english_templates(case_sensitive=false)
297	  } else {
298	    []	<-- UNCOVERED
299	  }
300	  let digit_templates = if cfg.enable_digits {
301	    generate_digit_templates()
302	  } else {
303	    []	<-- UNCOVERED
304	  }
305	  let punctuation_templates = if cfg.enable_punctuation {
306	    generate_punctuation_templates()
307	  } else {
308	    []	<-- UNCOVERED
309	  }
310	  // 5. 对每个字符进行识别
311	  let char_results : Array[OcrCharResult] = []
312	  let mut text = ""
313	  let mut total_confidence = 0.0
314	  for _tup1 in char_regions {
315	    let (x, y, w, h) = _tup1
316	    // 提取字符图像
317	    let char_img = extract_multilingual_char_region(
318	      preprocessed,
319	      x,
320	      y,
321	      w,
322	      h,
323	      cfg.target_size,
324	    )
325	    // 检测字符语言类型
326	    let char_language = detect_char_language(char_img)
327	    // 根据语言类型选择模板
328	    let result = match char_language {
329	      LanguageType::Chinese =>
330	        if chinese_templates.length() > 0 {
331	          match_multilingual_template(
332	            extract_multilingual_features(char_img, 16),
333	            chinese_templates,
334	            cfg.distance_metric,
335	          )
336	        } else {
337	          ("?", 0.0)	<-- UNCOVERED
338	        }
339	      LanguageType::English =>
340	        if english_templates.length() > 0 {	<-- UNCOVERED
341	          match_multilingual_template(
342	            extract_multilingual_features(char_img, 16),
343	            english_templates,
344	            cfg.distance_metric,
345	          )
346	        } else {
347	          ("?", 0.0)	<-- UNCOVERED
348	        }
349	      LanguageType::Digit =>
350	        if digit_templates.length() > 0 {	<-- UNCOVERED
351	          match_multilingual_template(
352	            extract_multilingual_features(char_img, 16),
353	            digit_templates,
354	            cfg.distance_metric,
355	          )
356	        } else {
357	          ("?", 0.0)	<-- UNCOVERED
358	        }
359	      LanguageType::Punctuation =>
360	        if punctuation_templates.length() > 0 {	<-- UNCOVERED
361	          match_punctuation_template(
362	            extract_multilingual_features(char_img, 16),
363	            punctuation_templates,
364	          )
365	        } else {
366	          ("?", 0.0)	<-- UNCOVERED
367	        }
368	      _ => {	<-- UNCOVERED
369	        // 未知语言，尝试所有模板
370	        let all_templates : Array[(String, Array[Double])] = []
371	        for t in chinese_templates {
372	          all_templates.push(t)	<-- UNCOVERED
373	        }
374	        for t in english_templates {
375	          all_templates.push(t)	<-- UNCOVERED
376	        }
377	        for t in digit_templates {
378	          all_templates.push(t)	<-- UNCOVERED
379	        }
380	        if all_templates.length() > 0 {	<-- UNCOVERED
381	          match_multilingual_template(
382	            extract_multilingual_features(char_img, 16),
383	            all_templates,
384	            cfg.distance_metric,
385	          )
386	        } else {
387	          ("?", 0.0)	<-- UNCOVERED
388	        }
389	      }
390	    }
391	    text += result.0
392	    total_confidence += result.1
393	    char_results.push(OcrCharResult::{
394	      char: result.0,
395	      confidence: result.1,
396	      x,
397	      y,
398	      width: w,
399	      height: h,
400	    })
401	  }
402	  let avg_confidence = if char_results.length() > 0 {
403	    total_confidence / char_results.length().to_double()
404	  } else {
405	    0.0	<-- UNCOVERED
406	  }
407	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
408	}
   	…

482	fn extract_multilingual_char_region(
483	  img : @types.Image,
484	  x : Int,
485	  y : Int,
486	  width : Int,
487	  height : Int,
488	  target_size : Int,
489	) -> @types.Image {
490	  let w = img.width
491	  let h = img.height
492	  let crop_w = if x + width > w { w - x } else { width }
493	  let crop_h = if y + height > h { h - y } else { height }
494	  if crop_w <= 0 || crop_h <= 0 {
495	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
496	    return @types.Image::{
497	      width: target_size,
498	      height: target_size,
499	      channels: 1,
500	      data: Bytes::from_array(empty),	<-- UNCOVERED
501	    }
502	  }
503	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
504	  let data = img.data
505	  for py in 0..<target_size {
506	    for px in 0..<target_size {
507	      let src_x = x + px * crop_w / target_size
508	      let src_y = y + py * crop_h / target_size
509	      if src_x < w && src_y < h {
510	        out[py * target_size + px] = data[src_y * w + src_x]
511	      }
512	    }
513	  }
514	  @types.Image::{
515	    width: target_size,
516	    height: target_size,
517	    channels: 1,
518	    data: Bytes::from_array(out),
519	  }
520	}
   	…

524	fn extract_multilingual_features(
525	  char_img : @types.Image,
526	  grid_size : Int,
527	) -> Array[Double] {
528	  let w = char_img.width
529	  let h = char_img.height
530	  let data = char_img.data
531	  let cell_w = w / grid_size
532	  let cell_h = h / grid_size
533	  let features : Array[Double] = []
534	  for r in 0..<grid_size {
535	    for c in 0..<grid_size {
536	      let mut count = 0
537	      let mut total = 0
538	      for py in 0..<cell_h {
539	        for px in 0..<cell_w {
540	          let sx = c * cell_w + px
541	          let sy = r * cell_h + py
542	          if sx < w && sy < h {
543	            total += 1
544	            if data[sy * w + sx].to_int() > 128 {
545	              count += 1
546	            }
547	          }
548	        }
549	      }
550	      let density = if total > 0 {
551	        count.to_double() / total.to_double()
552	      } else {
553	        0.0	<-- UNCOVERED
554	      }
555	      features.push(density)
556	    }
557	  }
558	  features
559	}
   	…

563	fn match_multilingual_template(
564	  features : Array[Double],
565	  templates : Array[(String, Array[Double])],
566	  distance_metric : String,
567	) -> (String, Double) {
568	  let mut best_label = "?"
569	  let mut best_dist = 1000000.0
570	  for _tup2 in templates {
571	    let (label, template) = _tup2
572	    let dist = match distance_metric {
573	      "manhattan" => manhattan_ml(features, template)	<-- UNCOVERED
574	      "cosine" => cosine_ml(features, template)
575	      _ => euclidean_ml(features, template)
576	    }
577	    if dist < best_dist {
578	      best_dist = dist
579	      best_label = label
580	    }
581	  }
582	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
583	  (best_label, confidence)
584	}
   	…

588	fn match_punctuation_template(
589	  features : Array[Double],
590	  templates : Array[PunctuationTemplate],
591	) -> (String, Double) {
592	  let mut best_label = "?"	<-- UNCOVERED
593	  let mut best_dist = 1000000.0
594	  for template in templates {
595	    let dist = euclidean_ml(features, template.features)	<-- UNCOVERED
596	    if dist < best_dist {
597	      best_dist = dist	<-- UNCOVERED
598	      best_label = template.char
599	    }
600	  }
601	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }	<-- UNCOVERED
602	  (best_label, confidence)
603	}
   	…

607	fn euclidean_ml(a : Array[Double], b : Array[Double]) -> Double {
608	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
609	  let mut sum_sq = 0.0
610	  for i in 0..<n {
611	    let diff = a[i] - b[i]
612	    sum_sq += diff * diff
613	  }
614	  sum_sq.sqrt()
615	}
   	…

619	fn manhattan_ml(a : Array[Double], b : Array[Double]) -> Double {
620	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
621	  let mut sum = 0.0
622	  for i in 0..<n {
623	    sum += (a[i] - b[i]).abs()	<-- UNCOVERED
624	  }
625	  sum
626	}
   	…

630	fn cosine_ml(a : Array[Double], b : Array[Double]) -> Double {
631	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
632	  let mut dot = 0.0
633	  let mut norm_a = 0.0
634	  let mut norm_b = 0.0
635	  for i in 0..<n {
636	    dot += a[i] * b[i]	<-- UNCOVERED
637	    norm_a += a[i] * a[i]
638	    norm_b += b[i] * b[i]
639	  }
640	  let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
641	  if denom > 0.0 {
642	    1.0 - dot / denom	<-- UNCOVERED
643	  } else {
644	    1.0	<-- UNCOVERED
645	  }
646	}
   	…

67 uncovered line(s) in src\process\ocr\multilingual_ocr_enhanced.mbt:

 50	pub fn detect_language_enhanced(
 51	  char_img : @types.Image,
 52	) -> LanguageDetectionResult raise @types.LoadError {
 53	  // 输入验证
 54	  if char_img.width <= 0 || char_img.height <= 0 {
 55	    raise @types.LoadError::DecodeFailed(
 56	      "detect_language_enhanced: 图像尺寸必须大于0",
 57	    )
 58	  }
 59	  if char_img.channels != 1 {
 60	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 61	      "detect_language_enhanced: 仅支持单通道灰度图",
 62	    )
 63	  }
 64	  let expected_size = char_img.width * char_img.height
 65	  if char_img.data.length() != expected_size {
 66	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 67	      "detect_language_enhanced: 数据大小不匹配，期望 " +
 68	      expected_size.to_string() +	<-- UNCOVERED
 69	      "，实际 " +
 70	      char_img.data.length().to_string(),	<-- UNCOVERED
 71	    )
 72	  }
 73	  let w = char_img.width
 74	  let h = char_img.height
 75	  let data = char_img.data
 76	  // 特征1：宽高比
 77	  let ratio = if h > 0 { w.to_double() / h.to_double() } else { 1.0 }
 78	  // 特征2：密度
 79	  let mut ink_pixels = 0
 80	  for i in 0..<(w * h) {
 81	    if data[i].to_int() > 128 {
 82	      ink_pixels += 1
 83	    }
 84	  }
 85	  let density = if w * h > 0 {
 86	    ink_pixels.to_double() / (w * h).to_double()
 87	  } else {
 88	    0.0	<-- UNCOVERED
 89	  }
 90	  // 特征3：水平投影方差（中文字符通常更均匀）
 91	  let h_proj : Array[Int] = Array::make(h, 0)
 92	  for y in 0..<h {
 93	    let mut count = 0
 94	    for x in 0..<w {
 95	      if data[y * w + x].to_int() > 128 {
 96	        count += 1
 97	      }
 98	    }
 99	    h_proj[y] = count
100	  }
101	  let mut h_mean = 0.0
102	  for y in 0..<h {
103	    h_mean += h_proj[y].to_double()
104	  }
105	  h_mean = h_mean / h.to_double()
106	  let mut h_var = 0.0
107	  for y in 0..<h {
108	    let diff = h_proj[y].to_double() - h_mean
109	    h_var += diff * diff
110	  }
111	  h_var = h_var / h.to_double()
112	  // 特征4：垂直投影方差
113	  let v_proj : Array[Int] = Array::make(w, 0)
114	  for x in 0..<w {
115	    let mut count = 0
116	    for y in 0..<h {
117	      if data[y * w + x].to_int() > 128 {
118	        count += 1
119	      }
120	    }
121	    v_proj[x] = count
122	  }
123	  let mut v_mean = 0.0
124	  for x in 0..<w {
125	    v_mean += v_proj[x].to_double()
126	  }
127	  v_mean = v_mean / w.to_double()
128	  let mut v_var = 0.0
129	  for x in 0..<w {
130	    let diff = v_proj[x].to_double() - v_mean
131	    v_var += diff * diff
132	  }
133	  v_var = v_var / w.to_double()
134	  // 基于特征的语言分类（简单规则分类器）
135	  let probabilities : Array[(String, Double)] = []
136	  // 中文：接近正方形、密度适中、投影均匀
137	  let chinese_score = if ratio > 0.7 &&
138	    ratio < 1.4 &&
139	    density > 0.15 &&
140	    density < 0.5 {
141	    0.8
142	  } else if ratio > 0.6 && ratio < 1.5 {
143	    0.5
144	  } else {
145	    0.2	<-- UNCOVERED
146	  }
147	  probabilities.push(("chinese", chinese_score))
148	  // 英文：较窄、密度较低、垂直投影方差大
149	  let english_score = if ratio > 0.3 &&
150	    ratio < 0.75 &&
151	    density > 0.1 &&
152	    density < 0.4 {
153	    0.75	<-- UNCOVERED
154	  } else if ratio > 0.25 && ratio < 0.85 {
155	    0.45	<-- UNCOVERED
156	  } else {
157	    0.15
158	  }
159	  probabilities.push(("english", english_score))
160	  // 数字：较窄、密度适中
161	  let digit_score = if ratio > 0.35 &&
162	    ratio < 0.65 &&
163	    density > 0.15 &&
164	    density < 0.45 {
165	    0.7	<-- UNCOVERED
166	  } else if ratio > 0.3 && ratio < 0.75 {
167	    0.4	<-- UNCOVERED
168	  } else {
169	    0.15
170	  }
171	  probabilities.push(("digit", digit_score))
172	  // 标点：很窄或很宽、密度低
173	  let punctuation_score = if ratio < 0.35 || ratio > 1.4 || density < 0.12 {
174	    0.8
175	  } else if ratio < 0.4 || ratio > 1.3 {
176	    0.5	<-- UNCOVERED
177	  } else {
178	    0.1
179	  }
180	  probabilities.push(("punctuation", punctuation_score))
181	  // 找最高分
182	  let mut best_language = "unknown"
183	  let mut best_score = 0.0
184	  let mut total_score = 0.0
185	  for _tup0 in probabilities {
186	    let (lang, score) = _tup0
187	    total_score += score
188	    if score > best_score {
189	      best_score = score
190	      best_language = lang
191	    }
192	  }
193	  let confidence = if total_score > 0.0 {
194	    best_score / total_score
195	  } else {
196	    0.0	<-- UNCOVERED
197	  }
198	  LanguageDetectionResult::{
199	    language: best_language,
200	    confidence,
201	    probabilities,
202	  }
203	}
   	…

207	pub fn smart_segment_chars(
208	  img : @types.Image,
209	  config : MultilingualOcrEnhancedConfig,
210	) -> Array[(Int, Int, Int, Int, String)] raise @types.LoadError {
211	  // 输入验证
212	  if img.width <= 0 || img.height <= 0 {
213	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
214	      "smart_segment_chars: 图像尺寸必须大于0",
215	    )
216	  }
217	  if img.channels != 1 {
218	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
219	      "smart_segment_chars: 仅支持单通道灰度图",
220	    )
221	  }
222	  let expected_size = img.width * img.height
223	  if img.data.length() != expected_size {
224	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
225	      "smart_segment_chars: 数据大小不匹配，期望 " +
226	      expected_size.to_string() +	<-- UNCOVERED
227	      "，实际 " +
228	      img.data.length().to_string(),	<-- UNCOVERED
229	    )
230	  }
231	  let w = img.width
232	  let h = img.height
233	  let data = img.data
234	  // 先进行连通域分割
235	  let labels : Array[Int] = Array::make(w * h, 0)
236	  let mut next_label = 1
237	  for y in 0..<h {
238	    for x in 0..<w {
239	      if data[y * w + x].to_int() > 128 {
240	        let mut label = 0
241	        if x > 0 && labels[y * w + x - 1] > 0 {
242	          label = labels[y * w + x - 1]
243	        }
244	        if y > 0 && labels[(y - 1) * w + x] > 0 {
245	          if label == 0 {
246	            label = labels[(y - 1) * w + x]
247	          }
248	        }
249	        if label == 0 {
250	          label = next_label
251	          next_label += 1
252	        }
253	        labels[y * w + x] = label
254	      }
255	    }
256	  }
257	  // 计算边界框
258	  let min_x : Array[Int] = Array::make(next_label, w)
259	  let max_x : Array[Int] = Array::make(next_label, 0)
260	  let min_y : Array[Int] = Array::make(next_label, h)
261	  let max_y : Array[Int] = Array::make(next_label, 0)
262	  let area : Array[Int] = Array::make(next_label, 0)
263	  for y in 0..<h {
264	    for x in 0..<w {
265	      let label = labels[y * w + x]
266	      if label > 0 {
267	        area[label] += 1
268	        if x < min_x[label] {
269	          min_x[label] = x
270	        }
271	        if x > max_x[label] {
272	          max_x[label] = x
273	        }
274	        if y < min_y[label] {
275	          min_y[label] = y
276	        }
277	        if y > max_y[label] {
278	          max_y[label] = y
279	        }
280	      }
281	    }
282	  }
283	  // 对每个连通域进行语言检测和分类
284	  let regions : Array[(Int, Int, Int, Int, String)] = []
285	  for label in 1..<next_label {
286	    let rw = max_x[label] - min_x[label] + 1
287	    let rh = max_y[label] - min_y[label] + 1
288	    if rw >= 5 && rw <= 100 && rh >= 8 && rh <= 100 && area[label] > 10 {
289	      // 提取字符图像进行语言检测
290	      let char_img = extract_char_region_for_smart(
291	        img,
292	        min_x[label],
293	        min_y[label],
294	        rw,
295	        rh,
296	        32,
297	      )
298	      let lang_result = if config.auto_language_detection {
299	        detect_language_enhanced(char_img)
300	      } else {
301	        LanguageDetectionResult::{	<-- UNCOVERED
302	          language: "unknown",
303	          confidence: 0.0,
304	          probabilities: [],
305	        }
306	      }
307	      regions.push((min_x[label], min_y[label], rw, rh, lang_result.language))
308	    }
309	  }
310	  regions
311	}
   	…

315	fn extract_char_region_for_smart(
316	  img : @types.Image,
317	  x : Int,
318	  y : Int,
319	  width : Int,
320	  height : Int,
321	  target_size : Int,
322	) -> @types.Image {
323	  let w = img.width
324	  let h = img.height
325	  let crop_w = if x + width > w { w - x } else { width }
326	  let crop_h = if y + height > h { h - y } else { height }
327	  if crop_w <= 0 || crop_h <= 0 {
328	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
329	    return @types.Image::{
330	      width: target_size,
331	      height: target_size,
332	      channels: 1,
333	      data: Bytes::from_array(empty),	<-- UNCOVERED
334	    }
335	  }
336	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
337	  let data = img.data
338	  for py in 0..<target_size {
339	    for px in 0..<target_size {
340	      let src_x = x + px * crop_w / target_size
341	      let src_y = y + py * crop_h / target_size
342	      if src_x < w && src_y < h {
343	        out[py * target_size + px] = data[src_y * w + src_x]
344	      }
345	    }
346	  }
347	  @types.Image::{
348	    width: target_size,
349	    height: target_size,
350	    channels: 1,
351	    data: Bytes::from_array(out),
352	  }
353	}
   	…

357	pub fn smart_spacing_process(
358	  text : String,
359	  char_languages : Array[String],
360	) -> String {
361	  if text.length() != char_languages.length() || text.length() == 0 {
362	    return text
363	  }
364	  let mut result = ""
365	  for i in 0..<text.length() {
366	    result += text[i].to_string()
367	    // 在中英文之间加空格
368	    if i < text.length() - 1 {
369	      let curr_lang = char_languages[i]
370	      let next_lang = char_languages[i + 1]
371	      let is_cjk = curr_lang == "chinese" ||
372	        curr_lang == "japanese" ||
373	        curr_lang == "korean"
374	      let next_is_cjk = next_lang == "chinese" ||
375	        next_lang == "japanese" ||
376	        next_lang == "korean"
377	      let is_latin = curr_lang == "english" || curr_lang == "digit"
378	      let next_is_latin = next_lang == "english" || next_lang == "digit"
379	      // CJK 和 Latin 之间加空格
380	      if (is_cjk && next_is_latin) || (is_latin && next_is_cjk) {
381	        result += " "	<-- UNCOVERED
382	      }
383	    }
384	  }
385	  result
386	}
   	…

390	pub fn normalize_punctuation(
391	  text : String,
392	  char_languages : Array[String],
393	) -> String {
394	  if text.length() != char_languages.length() || text.length() == 0 {
395	    return text
396	  }
397	  let mut result = ""
398	  for i in 0..<text.length() {
399	    let c = text[i]
400	    let lang = char_languages[i]
401	    // 中文语境下使用中文标点
402	    if lang == "chinese" {
403	      match c.to_string() {	<-- UNCOVERED
404	        "," => result += "，"
405	        "." => result += "。"
406	        "!" => result += "！"
407	        "?" => result += "？"
408	        ";" => result += "；"
409	        ":" => result += "："
410	        "(" => result += "（"
411	        ")" => result += "）"
412	        _ => result += c.to_string()
413	      }
414	    } else {
415	      // 英文语境下使用英文标点
416	      match c.to_string() {
417	        "，" => result += ","	<-- UNCOVERED
418	        "。" => result += "."
419	        "！" => result += "!"
420	        "？" => result += "?"
421	        "；" => result += ";"
422	        "：" => result += ":"
423	        "（" => result += "("
424	        "）" => result += ")"
425	        _ => result += c.to_string()
426	      }
427	    }
428	  }
429	  result
430	}
   	…

459	pub fn recognize_multilingual_enhanced(
460	  img : @types.Image,
461	  config? : MultilingualOcrEnhancedConfig,
462	) -> OcrResult raise @types.LoadError {
463	  // 输入验证
464	  if img.width <= 0 || img.height <= 0 {
465	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
466	      "recognize_multilingual_enhanced: 图像尺寸必须大于0",
467	    )
468	  }
469	  if img.channels < 1 || img.channels > 4 {
470	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
471	      "recognize_multilingual_enhanced: 通道数必须在1-4之间",
472	    )
473	  }
474	  let expected_size = img.width * img.height * img.channels
475	  if img.data.length() != expected_size {
476	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
477	      "recognize_multilingual_enhanced: 数据大小不匹配，期望 " +
478	      expected_size.to_string() +	<-- UNCOVERED
479	      "，实际 " +
480	      img.data.length().to_string(),	<-- UNCOVERED
481	    )
482	  }
483	  let cfg = match config {
484	    Some(c) => c	<-- UNCOVERED
485	    None => multilingual_ocr_enhanced_default_config()
486	  }
487	  // 1. 预处理
488	  let preprocessed = ocr_preprocess(img)
489	  // 2. 智能字符分割（带语言检测）
490	  let char_regions = smart_segment_chars(preprocessed, cfg)
491	  // 3. 生成各语言模板
492	  let chinese_templates = if cfg.enable_chinese {
493	    generate_enhanced_chinese_templates(char_set_size=500)
494	  } else {
495	    []	<-- UNCOVERED
496	  }
497	  let english_templates = if cfg.enable_english {
498	    generate_enhanced_handwriting_templates(
499	      char_set="digits+letters",
500	      num_variants=3,
501	    )
502	  } else {
503	    []	<-- UNCOVERED
504	  }
505	  let digit_templates = if cfg.enable_digits {
506	    generate_digit_templates()
507	  } else {
508	    []	<-- UNCOVERED
509	  }
510	  let punctuation_templates = if cfg.enable_punctuation {
511	    generate_punctuation_templates()
512	  } else {
513	    []	<-- UNCOVERED
514	  }
515	  // 4. 对每个字符进行识别
516	  let char_results : Array[OcrCharResult] = []
517	  let mut text = ""
518	  let mut total_confidence = 0.0
519	  let char_languages : Array[String] = []
520	  let confidences : Array[Double] = []
521	  for _tup1 in char_regions {
522	    let (x, y, w, h, detected_lang) = _tup1
523	    // 提取字符图像
524	    let char_img = extract_char_region_for_smart(
525	      preprocessed,
526	      x,
527	      y,
528	      w,
529	      h,
530	      cfg.target_size,
531	    )
532	    // 根据检测到的语言选择模板
533	    let result = match detected_lang {
534	      "chinese" =>
535	        if chinese_templates.length() > 0 {
536	          match_multilingual_enhanced(
537	            char_img,
538	            chinese_templates,
539	            cfg.distance_metric,
540	          )
541	        } else {
542	          ("?", 0.0)	<-- UNCOVERED
543	        }
544	      "english" =>
545	        if english_templates.length() > 0 {	<-- UNCOVERED
546	          match_multilingual_enhanced(
547	            char_img,
548	            english_templates,
549	            cfg.distance_metric,
550	          )
551	        } else {
552	          ("?", 0.0)	<-- UNCOVERED
553	        }
554	      "digit" =>
555	        if digit_templates.length() > 0 {	<-- UNCOVERED
556	          match_multilingual_enhanced(
557	            char_img,
558	            digit_templates,
559	            cfg.distance_metric,
560	          )
561	        } else {
562	          ("?", 0.0)	<-- UNCOVERED
563	        }
564	      "punctuation" =>
565	        if punctuation_templates.length() > 0 {	<-- UNCOVERED
566	          match_punctuation_enhanced(char_img, punctuation_templates)
567	        } else {
568	          ("?", 0.0)	<-- UNCOVERED
569	        }
570	      _ => {	<-- UNCOVERED
571	        // 未知语言，尝试所有模板
572	        let all_templates : Array[(String, Array[Double])] = []
573	        for t in chinese_templates {
574	          all_templates.push(t)	<-- UNCOVERED
575	        }
576	        for t in english_templates {
577	          all_templates.push(t)	<-- UNCOVERED
578	        }
579	        for t in digit_templates {
580	          all_templates.push(t)	<-- UNCOVERED
581	        }
582	        if all_templates.length() > 0 {	<-- UNCOVERED
583	          match_multilingual_enhanced(
584	            char_img,
585	            all_templates,
586	            cfg.distance_metric,
587	          )
588	        } else {
589	          ("?", 0.0)	<-- UNCOVERED
590	        }
591	      }
592	    }
593	    text += result.0
594	    total_confidence += result.1
595	    char_languages.push(detected_lang)
596	    confidences.push(result.1)
597	    char_results.push(OcrCharResult::{
598	      char: result.0,
599	      confidence: result.1,
600	      x,
601	      y,
602	      width: w,
603	      height: h,
604	    })
605	  }
606	  // 5. 后处理
607	  let final_text = if char_results.length() > 0 {
608	    let mut processed = text
609	    // 智能空格处理
610	    if cfg.smart_spacing {
611	      processed = smart_spacing_process(processed, char_languages)
612	    }
613	    // 标点符号规范化
614	    if cfg.punctuation_normalization {
615	      processed = normalize_punctuation(processed, char_languages)
616	    }
617	    // 语言模型后处理
618	    if cfg.use_language_model {
619	      let (t, _) = language_model_postprocess(
620	        processed, char_languages, confidences,
621	      )
622	      processed = t
623	    }
624	    processed
625	  } else {
626	    text	<-- UNCOVERED
627	  }
628	  let avg_confidence = if char_results.length() > 0 {
629	    total_confidence / char_results.length().to_double()
630	  } else {
631	    0.0	<-- UNCOVERED
632	  }
633	  OcrResult::{
634	    text: final_text,
635	    confidence: avg_confidence,
636	    characters: char_results,
637	  }
638	}
   	…

642	fn match_multilingual_enhanced(
643	  char_img : @types.Image,
644	  templates : Array[(String, Array[Double])],
645	  distance_metric : String,
646	) -> (String, Double) {
647	  let w = char_img.width
648	  let h = char_img.height
649	  let data = char_img.data
650	  // 提取网格特征
651	  let grid_size = 16
652	  let cell_w = w / grid_size
653	  let cell_h = h / grid_size
654	  let features : Array[Double] = []
655	  for r in 0..<grid_size {
656	    for c in 0..<grid_size {
657	      let mut count = 0
658	      let mut total = 0
659	      for py in 0..<cell_h {
660	        for px in 0..<cell_w {
661	          let sx = c * cell_w + px
662	          let sy = r * cell_h + py
663	          if sx < w && sy < h {
664	            total += 1
665	            if data[sy * w + sx].to_int() > 128 {
666	              count += 1
667	            }
668	          }
669	        }
670	      }
671	      let density = if total > 0 {
672	        count.to_double() / total.to_double()
673	      } else {
674	        0.0	<-- UNCOVERED
675	      }
676	      features.push(density)
677	    }
678	  }
679	  // 模板匹配
680	  let mut best_label = "?"
681	  let mut best_dist = 1000000.0
682	  for _tup2 in templates {
683	    let (label, template) = _tup2
684	    let n = if features.length() < template.length() {
685	      features.length()
686	    } else {
687	      template.length()	<-- UNCOVERED
688	    }
689	    let mut dist = 0.0
690	    match distance_metric {
691	      "manhattan" =>
692	        for i in 0..<n {	<-- UNCOVERED
693	          dist += (features[i] - template[i]).abs()
694	        }
695	      "cosine" => {	<-- UNCOVERED
696	        let mut dot = 0.0
697	        let mut norm_a = 0.0
698	        let mut norm_b = 0.0
699	        for i in 0..<n {
700	          dot += features[i] * template[i]	<-- UNCOVERED
701	          norm_a += features[i] * features[i]
702	          norm_b += template[i] * template[i]
703	        }
704	        let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
705	        dist = if denom > 0.0 { 1.0 - dot / denom } else { 1.0 }
706	      }
707	      _ => {
708	        for i in 0..<n {
709	          let diff = features[i] - template[i]
710	          dist += diff * diff
711	        }
712	        dist = dist.sqrt()
713	      }
714	    }
715	    if dist < best_dist {
716	      best_dist = dist
717	      best_label = label
718	    }
719	  }
720	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
721	  (best_label, confidence)
722	}
   	…

726	fn match_punctuation_enhanced(
727	  char_img : @types.Image,
728	  templates : Array[PunctuationTemplate],
729	) -> (String, Double) {
730	  let w = char_img.width	<-- UNCOVERED
731	  let h = char_img.height
732	  let data = char_img.data
733	  let grid_size = 16
734	  let cell_w = w / grid_size
735	  let cell_h = h / grid_size
736	  let features : Array[Double] = []
737	  for r in 0..<grid_size {
738	    for c in 0..<grid_size {	<-- UNCOVERED
739	      let mut count = 0
740	      let mut total = 0
741	      for py in 0..<cell_h {
742	        for px in 0..<cell_w {	<-- UNCOVERED
743	          let sx = c * cell_w + px
744	          let sy = r * cell_h + py
745	          if sx < w && sy < h {
746	            total += 1	<-- UNCOVERED
747	            if data[sy * w + sx].to_int() > 128 {
748	              count += 1
749	            }
750	          }
751	        }
752	      }
753	      let density = if total > 0 {
754	        count.to_double() / total.to_double()	<-- UNCOVERED
755	      } else {
756	        0.0	<-- UNCOVERED
757	      }
758	      features.push(density)	<-- UNCOVERED
759	    }
760	  }
761	  let mut best_label = "?"
762	  let mut best_dist = 1000000.0
763	  for template in templates {
764	    let n = if features.length() < template.features.length() {	<-- UNCOVERED
765	      features.length()
766	    } else {
767	      template.features.length()	<-- UNCOVERED
768	    }
769	    let mut dist = 0.0
770	    for i in 0..<n {
771	      let diff = features[i] - template.features[i]	<-- UNCOVERED
772	      dist += diff * diff
773	    }
774	    dist = dist.sqrt()	<-- UNCOVERED
775	    if dist < best_dist {
776	      best_dist = dist	<-- UNCOVERED
777	      best_label = template.char
778	    }
779	  }
780	  let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }	<-- UNCOVERED
781	  (best_label, confidence)
782	}
   	…

21 uncovered line(s) in src\process\ocr\ocr_accuracy.mbt:

43	pub fn dataset_add_sample(
44	  dataset : TrainingDataset,
45	  features : Array[Double],
46	  label : String,
47	) -> TrainingDataset {
48	  let new_samples = dataset.samples
49	  new_samples.push(TrainingSample::{ features, label })
50	  let new_labels = dataset.labels
51	  let mut found = false
52	  for l in new_labels {
53	    if l == label {
54	      found = true	<-- UNCOVERED
55	    }
56	  }
57	  if !found {
58	    new_labels.push(label)
59	  }
60	  TrainingDataset::{
61	    samples: new_samples,
62	    labels: new_labels,
63	    num_samples: new_samples.length(),
64	    num_classes: new_labels.length(),
65	  }
66	}
  	…

 88	pub fn augment_features(
 89	  features : Array[Double],
 90	  noise_level? : Double = 0.05,
 91	) -> Array[Double] {
 92	  let augmented : Array[Double] = []
 93	  for i in 0..<features.length() {
 94	    // 添加高斯噪声（简化为均匀噪声）
 95	    let noise = (pseudo_random(i) - 0.5) * 2.0 * noise_level
 96	    let new_val = features[i] + noise
 97	    let clamped = if new_val < 0.0 {
 98	      0.0	<-- UNCOVERED
 99	    } else if new_val > 1.0 {
100	      1.0
101	    } else {
102	      new_val
103	    }
104	    augmented.push(clamped)
105	  }
106	  augmented
107	}
   	…

118	pub fn hu_moments_features(
119	  img : @types.Image,
120	) -> Array[Double] raise @types.LoadError {
121	  // 输入验证
122	  if img.width <= 0 || img.height <= 0 {
123	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
124	      "hu_moments_features: 图像尺寸必须大于0",
125	    )
126	  }
127	  if img.channels != 1 {
128	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
129	      "hu_moments_features: 仅支持单通道图",
130	    )
131	  }
132	  let expected_size = img.width * img.height
133	  if img.data.length() != expected_size {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "hu_moments_features: 数据大小不匹配，期望 " +
136	      expected_size.to_string() +	<-- UNCOVERED
137	      "，实际 " +
138	      img.data.length().to_string(),	<-- UNCOVERED
139	    )
140	  }
141	  let w = img.width
142	  let h = img.height
143	  let data = img.data
144	  // 计算零阶和一阶矩
145	  let mut m00 = 0.0
146	  let mut m10 = 0.0
147	  let mut m01 = 0.0
148	  for y in 0..<h {
149	    for x in 0..<w {
150	      let val = data[y * w + x].to_int().to_double() / 255.0
151	      m00 += val
152	      m10 += x.to_double() * val
153	      m01 += y.to_double() * val
154	    }
155	  }
156	  if m00 == 0.0 {
157	    return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]	<-- UNCOVERED
158	  }
159	  // 质心
160	  let x_bar = m10 / m00
161	  let y_bar = m01 / m00
162	  // 计算中心矩
163	  let mut mu20 = 0.0
164	  let mut mu02 = 0.0
165	  let mut mu11 = 0.0
166	  let mut mu30 = 0.0
167	  let mut mu03 = 0.0
168	  let mut mu21 = 0.0
169	  let mut mu12 = 0.0
170	  for y in 0..<h {
171	    for x in 0..<w {
172	      let val = data[y * w + x].to_int().to_double() / 255.0
173	      let dx = x.to_double() - x_bar
174	      let dy = y.to_double() - y_bar
175	      mu20 += dx * dx * val
176	      mu02 += dy * dy * val
177	      mu11 += dx * dy * val
178	      mu30 += dx * dx * dx * val
179	      mu03 += dy * dy * dy * val
180	      mu21 += dx * dx * dy * val
181	      mu12 += dx * dy * dy * val
182	    }
183	  }
184	  // 归一化中心矩
185	  let eta20 = mu20 / (m00 * m00)
186	  let eta02 = mu02 / (m00 * m00)
187	  let eta11 = mu11 / (m00 * m00)
188	  let eta30 = mu30 /
189	    (m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00).sqrt()
190	  let eta03 = mu03 /
191	    (m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00).sqrt()
192	  let eta21 = mu21 /
193	    (m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00).sqrt()
194	  let eta12 = mu12 /
195	    (m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00 * m00).sqrt()
196	  // Hu 不变矩
197	  let h1 = eta20 + eta02
198	  let h2 = (eta20 - eta02) * (eta20 - eta02) + 4.0 * eta11 * eta11
199	  let h3 = (eta30 - 3.0 * eta12) * (eta30 - 3.0 * eta12) +
200	    (3.0 * eta21 - eta03) * (3.0 * eta21 - eta03)
201	  let h4 = (eta30 + eta12) * (eta30 + eta12) + (eta21 + eta03) * (eta21 + eta03)
202	  let h5 = (eta30 - 3.0 * eta12) *
203	    (eta30 + eta12) *
204	    (
205	      (eta30 + eta12) * (eta30 + eta12) -
206	      3.0 * (eta21 + eta03) * (eta21 + eta03)
207	    ) +
208	    (3.0 * eta21 - eta03) *
209	    (eta21 + eta03) *
210	    (
211	      3.0 * (eta30 + eta12) * (eta30 + eta12) -
212	      (eta21 + eta03) * (eta21 + eta03)
213	    )
214	  let h6 = (eta20 - eta02) *
215	    ((eta30 + eta12) * (eta30 + eta12) - (eta21 + eta03) * (eta21 + eta03)) +
216	    4.0 * eta11 * (eta30 + eta12) * (eta21 + eta03)
217	  let h7 = (3.0 * eta21 - eta03) *
218	    (eta30 + eta12) *
219	    (
220	      (eta30 + eta12) * (eta30 + eta12) -
221	      3.0 * (eta21 + eta03) * (eta21 + eta03)
222	    ) -
223	    (eta30 - 3.0 * eta12) *
224	    (eta21 + eta03) *
225	    (
226	      3.0 * (eta30 + eta12) * (eta30 + eta12) -
227	      (eta21 + eta03) * (eta21 + eta03)
228	    )
229	  [h1, h2, h3, h4, h5, h6, h7]
230	}
   	…

234	pub fn ensemble_predict(
235	  features : Array[Double],
236	  knn_classifier : KnnClassifier,
237	  templates : Array[(String, Array[Double])],
238	  config? : EnsembleConfig,
239	) -> ClassificationResult raise @types.LoadError {
240	  // 输入验证
241	  if features.length() == 0 {
242	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
243	      "ensemble_predict: 特征向量不能为空",
244	    )
245	  }
246	
247	  let cfg = match config {
248	    Some(c) => c	<-- UNCOVERED
249	    None => ensemble_default_config()
250	  }
251	  // 收集所有分类器的预测
252	  let predictions : Array[(String, Double)] = []
253	  // KNN 预测
254	  if cfg.use_knn {
255	    let knn_result = knn_predict(knn_classifier, features) catch {
256	      _ => ClassificationResult::{ label: "?", confidence: 0.0, top_k: [] }
257	    }
258	    predictions.push((knn_result.label, knn_result.confidence))
259	  }
260	  // 模板匹配预测
261	  if cfg.use_template {
262	    let mut best_label = "?"
263	    let mut best_dist = 1000000.0
264	    for _tup0 in templates {
265	      let (label, template) = _tup0
266	      let dist = euclidean_distance_ensemble(features, template)
267	      if dist < best_dist {
268	        best_dist = dist
269	        best_label = label
270	      }
271	    }
272	    let confidence = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }
273	    predictions.push((best_label, confidence))
274	  }
275	  // SVM-like 预测（简化版：基于距离的线性分类）
276	  if cfg.use_svm_like {
277	    // 简化：使用最近邻作为SVM-like预测
278	    if templates.length() > 0 {
279	      let mut best_label = "?"
280	      let mut best_dist = 1000000.0
281	      for _tup1 in templates {
282	        let (label, template) = _tup1
283	        let dist = manhattan_distance_ensemble(features, template)
284	        if dist < best_dist {
285	          best_dist = dist
286	          best_label = label
287	        }
288	      }
289	      let confidence = if best_dist > 0.0 {
290	        1.0 / (1.0 + best_dist)	<-- UNCOVERED
291	      } else {
292	        1.0
293	      }
294	      predictions.push((best_label, confidence))
295	    }
296	  }
297	  // 投票
298	  let label_votes : Array[(String, Double)] = []
299	  for _tup2 in predictions {
300	    let (label, conf) = _tup2
301	    let mut found = false
302	    for i in 0..<label_votes.length() {
303	      if label_votes[i].0 == label {
304	        let weight = match cfg.voting_method {
305	          "majority" => 1.0	<-- UNCOVERED
306	          "confidence" => conf
307	          _ => conf
308	        }
309	        label_votes[i] = (label, label_votes[i].1 + weight)
310	        found = true
311	      }
312	    }
313	    if !found {
314	      let weight = match cfg.voting_method {
315	        "majority" => 1.0	<-- UNCOVERED
316	        "confidence" => conf
317	        _ => conf
318	      }
319	      label_votes.push((label, weight))
320	    }
321	  }
322	  // 找得票最多的标签
323	  let mut best_label = "?"
324	  let mut best_votes = 0.0
325	  for _tup3 in label_votes {
326	    let (label, votes) = _tup3
327	    if votes > best_votes {
328	      best_votes = votes
329	      best_label = label
330	    }
331	  }
332	  let total_votes = if predictions.length() > 0 {
333	    predictions.length().to_double()
334	  } else {
335	    1.0	<-- UNCOVERED
336	  }
337	  let confidence = best_votes / total_votes
338	  ClassificationResult::{ label: best_label, confidence, top_k: predictions }
339	}
   	…

343	fn euclidean_distance_ensemble(a : Array[Double], b : Array[Double]) -> Double {
344	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
345	  let mut sum_sq = 0.0
346	  for i in 0..<n {
347	    let diff = a[i] - b[i]
348	    sum_sq += diff * diff
349	  }
350	  sum_sq.sqrt()
351	}
   	…

355	fn manhattan_distance_ensemble(a : Array[Double], b : Array[Double]) -> Double {
356	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
357	  let mut sum = 0.0
358	  for i in 0..<n {
359	    sum += (a[i] - b[i]).abs()
360	  }
361	  sum
362	}
   	…

366	pub fn extract_combined_ocr_features(
367	  img : @types.Image,
368	) -> Array[Double] raise @types.LoadError {
369	  // 输入验证
370	  if img.width <= 0 || img.height <= 0 {
371	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
372	      "extract_combined_ocr_features: 图像尺寸必须大于0",
373	    )
374	  }
375	  if img.channels != 1 {
376	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
377	      "extract_combined_ocr_features: 仅支持单通道灰度图",
378	    )
379	  }
380	  let expected_size = img.width * img.height
381	  if img.data.length() != expected_size {
382	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
383	      "extract_combined_ocr_features: 数据大小不匹配，期望 " +
384	      expected_size.to_string() +	<-- UNCOVERED
385	      "，实际 " +
386	      img.data.length().to_string(),	<-- UNCOVERED
387	    )
388	  }
389	  // 网格特征
390	  let grid = grid_features(img, grid_rows=8, grid_cols=8)
391	  // HOG 特征
392	  let hog = hog_features(img, cell_size=8, num_bins=9)
393	  // LBP 特征（降采样）
394	  let lbp = lbp_features(img, radius=1, num_points=8, num_bins=64)
395	  // Hu 矩特征
396	  let hu = hu_moments_features(img)
397	  // 组合
398	  let combined : Array[Double] = []
399	  for f in grid.features {
400	    combined.push(f)
401	  }
402	  for f in hog.features {
403	    combined.push(f)
404	  }
405	  for i in 0..<lbp.features.length() {
406	    if i < 64 {
407	      combined.push(lbp.features[i])
408	    }
409	  }
410	  for f in hu {
411	    combined.push(f)
412	  }
413	  combined
414	}
   	…

41 uncovered line(s) in src\process\ocr\ocr_accuracy_enhanced.mbt:

 34	pub fn extract_zernike_moments(
 35	  char_img : @types.Image,
 36	  max_order? : Int = 4,
 37	) -> Array[Double] raise @types.LoadError {
 38	  // 输入验证
 39	  if char_img.width <= 0 || char_img.height <= 0 {
 40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 41	      "extract_zernike_moments: 图像尺寸必须大于0",
 42	    )
 43	  }
 44	  if char_img.channels != 1 {
 45	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 46	      "extract_zernike_moments: 仅支持单通道灰度图",
 47	    )
 48	  }
 49	  let expected_size = char_img.width * char_img.height
 50	  if char_img.data.length() != expected_size {
 51	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 52	      "extract_zernike_moments: 数据大小不匹配，期望 " +
 53	      expected_size.to_string() +	<-- UNCOVERED
 54	      "，实际 " +
 55	      char_img.data.length().to_string(),	<-- UNCOVERED
 56	    )
 57	  }
 58	  if max_order <= 0 {
 59	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 60	      "extract_zernike_moments: 最大阶数必须大于0",
 61	    )
 62	  }
 63	  let w = char_img.width
 64	  let h = char_img.height
 65	  let data = char_img.data
 66	  let features : Array[Double] = []
 67	  // 计算质心
 68	  let mut sum_x = 0.0
 69	  let mut sum_y = 0.0
 70	  let mut total = 0.0
 71	  for y in 0..<h {
 72	    for x in 0..<w {
 73	      if data[y * w + x].to_int() > 128 {
 74	        sum_x += x.to_double()
 75	        sum_y += y.to_double()
 76	        total += 1.0
 77	      }
 78	    }
 79	  }
 80	  let cx = if total > 0.0 { sum_x / total } else { w.to_double() / 2.0 }	<-- UNCOVERED
 81	  let cy = if total > 0.0 { sum_y / total } else { h.to_double() / 2.0 }
 82	  // 计算半径（归一化）
 83	  let max_r = w.to_double().max(h.to_double()) / 2.0
 84	  // 计算 Zernike 矩（简化版：前几阶的径向矩）
 85	  for n in 0..<(max_order + 1) {
 86	    for m in 0..<(n + 1) {
 87	      if (n - m) % 2 == 0 {
 88	        let mut z_real = 0.0
 89	        let mut z_imag = 0.0
 90	        for y in 0..<h {
 91	          for x in 0..<w {
 92	            if data[y * w + x].to_int() > 128 {
 93	              // 归一化坐标
 94	              let nx = (x.to_double() - cx) / max_r
 95	              let ny = (y.to_double() - cy) / max_r
 96	              let r = (nx * nx + ny * ny).sqrt()
 97	              let theta = @math.atan2(ny, nx)
 98	              if r <= 1.0 {
 99	                // 简化的径向多项式
100	                let mut radial = 0.0
101	                let k_max = (n - m) / 2
102	                for k in 0..<(k_max + 1) {
103	                  let sign = if k % 2 == 0 { 1.0 } else { -1.0 }
104	                  let numerator = factorial(n - k)
105	                  let denominator = factorial(k) *
106	                    factorial((n + m) / 2 - k) *
107	                    factorial((n - m) / 2 - k)
108	                  if denominator > 0.0 {
109	                    radial += sign *
110	                      numerator /
111	                      denominator *
112	                      @math.pow(r, (n - 2 * k).to_double())
113	                  }
114	                }
115	                z_real += radial * @math.cos(m.to_double() * theta)
116	                z_imag += radial * @math.sin(m.to_double() * theta)
117	              }
118	            }
119	          }
120	        }
121	        // 归一化
122	        let magnitude = (z_real * z_real + z_imag * z_imag).sqrt()
123	        features.push(magnitude / (w * h).to_double())
124	      }
125	    }
126	  }
127	  features
128	}
   	…

247	pub fn weighted_knn_classify(
248	  features : Array[Double],
249	  training_data : Array[(String, Array[Double])],
250	  k? : Int = 5,
251	  distance_metric? : String = "euclidean",
252	) -> (String, Double, Array[(String, Double)]) {
253	  // 计算所有训练样本的距离
254	  let distances : Array[(String, Double)] = []
255	  for _tup1 in training_data {
256	    let (label, sample) = _tup1
257	    let dist = calculate_distance(features, sample, distance_metric)
258	    distances.push((label, dist))
259	  }
260	  // 按距离排序（冒泡排序）
261	  for _ in 0..<distances.length() {
262	    for j in 0..<(distances.length() - 1) {
263	      if distances[j].1 > distances[j + 1].1 {
264	        let temp = distances[j]
265	        distances[j] = distances[j + 1]
266	        distances[j + 1] = temp
267	      }
268	    }
269	  }
270	  // 取前 k 个最近邻
271	  let k_actual = if k < distances.length() { k } else { distances.length() }
272	  // 距离加权投票
273	  let votes : Array[(String, Double)] = []
274	  for i in 0..<k_actual {
275	    let (label, dist) = distances[i]
276	    let weight = if dist > 0.001 { 1.0 / dist } else { 1000.0 }
277	    let mut found = false
278	    for j in 0..<votes.length() {
279	      if votes[j].0 == label {
280	        votes[j] = (label, votes[j].1 + weight)	<-- UNCOVERED
281	        found = true
282	      }
283	    }
284	    if !found {
285	      votes.push((label, weight))
286	    }
287	  }
288	  // 找得票最多的标签
289	  let mut best_label = "?"
290	  let mut best_votes = 0.0
291	  let mut total_votes = 0.0
292	  for _tup2 in votes {
293	    let (label, v) = _tup2
294	    total_votes += v
295	    if v > best_votes {
296	      best_votes = v
297	      best_label = label
298	    }
299	  }
300	  let confidence = if total_votes > 0.0 {
301	    best_votes / total_votes
302	  } else {
303	    0.0	<-- UNCOVERED
304	  }
305	  (best_label, confidence, votes)
306	}
   	…

310	fn calculate_distance(
311	  a : Array[Double],
312	  b : Array[Double],
313	  metric : String,
314	) -> Double {
315	  let n = if a.length() < b.length() { a.length() } else { b.length() }	<-- UNCOVERED
316	  match metric {
317	    "manhattan" => {	<-- UNCOVERED
318	      let mut sum = 0.0
319	      for i in 0..<n {
320	        sum += (a[i] - b[i]).abs()	<-- UNCOVERED
321	      }
322	      sum
323	    }
324	    "cosine" => {	<-- UNCOVERED
325	      let mut dot = 0.0
326	      let mut norm_a = 0.0
327	      let mut norm_b = 0.0
328	      for i in 0..<n {
329	        dot += a[i] * b[i]	<-- UNCOVERED
330	        norm_a += a[i] * a[i]
331	        norm_b += b[i] * b[i]
332	      }
333	      let denom = norm_a.sqrt() * norm_b.sqrt()	<-- UNCOVERED
334	      if denom > 0.0 {
335	        1.0 - dot / denom	<-- UNCOVERED
336	      } else {
337	        1.0	<-- UNCOVERED
338	      }
339	    }
340	    _ => {
341	      let mut sum_sq = 0.0
342	      for i in 0..<n {
343	        let diff = a[i] - b[i]
344	        sum_sq += diff * diff
345	      }
346	      sum_sq.sqrt()
347	    }
348	  }
349	}
   	…

353	pub fn calibrate_confidence_enhanced(
354	  raw_confidence : Double,
355	  distance_distribution : Array[Double],
356	) -> Double {
357	  if distance_distribution.length() == 0 {
358	    return raw_confidence	<-- UNCOVERED
359	  }
360	  // 计算距离分布的统计量
361	  let mut mean = 0.0
362	  let mut min_dist = 1000000.0
363	  let mut max_dist = 0.0
364	  for d in distance_distribution {
365	    mean += d
366	    if d < min_dist {
367	      min_dist = d
368	    }
369	    if d > max_dist {
370	      max_dist = d
371	    }
372	  }
373	  mean = mean / distance_distribution.length().to_double()
374	  // 基于距离分布的校准
375	  let range = if max_dist > min_dist { max_dist - min_dist } else { 1.0 }
376	  let normalized_mean = (mean - min_dist) / range
377	  // 如果平均距离较小，说明匹配质量高，提升置信度
378	  let calibration_factor = 1.0 + (1.0 - normalized_mean) * 0.3
379	  let calibrated = raw_confidence * calibration_factor
380	  if calibrated > 1.0 {
381	    1.0	<-- UNCOVERED
382	  } else if calibrated < 0.0 {
383	    0.0	<-- UNCOVERED
384	  } else {
385	    calibrated
386	  }
387	}
   	…

521	pub fn extract_combined_enhanced_features(
522	  char_img : @types.Image,
523	  config : OcrAccuracyEnhancedConfig,
524	) -> Array[Double] raise @types.LoadError {
525	  // 输入验证
526	  if char_img.width <= 0 || char_img.height <= 0 {
527	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
528	      "extract_combined_enhanced_features: 图像尺寸必须大于0",
529	    )
530	  }
531	  if char_img.channels != 1 {
532	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
533	      "extract_combined_enhanced_features: 仅支持单通道灰度图",
534	    )
535	  }
536	  let expected_size = char_img.width * char_img.height
537	  if char_img.data.length() != expected_size {
538	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
539	      "extract_combined_enhanced_features: 数据大小不匹配，期望 " +
540	      expected_size.to_string() +	<-- UNCOVERED
541	      "，实际 " +
542	      char_img.data.length().to_string(),	<-- UNCOVERED
543	    )
544	  }
545	  let features : Array[Double] = []
546	  // 网格特征（基础）
547	  let grid = extract_grid_features(char_img, 16)
548	  for f in grid {
549	    features.push(f)
550	  }
551	  // Zernike 矩（旋转不变）
552	  if config.use_zernike_moments {
553	    let zernike = extract_zernike_moments(char_img, max_order=4)
554	    for f in zernike {
555	      features.push(f)
556	    }
557	  }
558	  // Gabor 特征（多方向多频率）
559	  if config.use_gabor_features {
560	    let gabor = extract_gabor_ocr_features(
561	      char_img,
562	      num_orientations=4,
563	      num_frequencies=2,
564	    )
565	    for f in gabor {
566	      features.push(f)
567	    }
568	  }
569	  // 方向特征（8方向）
570	  if config.use_directional_features {
571	    let directional = extract_directional_features(char_img)
572	    for f in directional {
573	      features.push(f)
574	    }
575	  }
576	  features
577	}
   	…

581	pub fn recognize_ocr_enhanced(
582	  img : @types.Image,
583	  templates : Array[(String, Array[Double])],
584	  config? : OcrAccuracyEnhancedConfig,
585	) -> OcrResult raise @types.LoadError {
586	  // 输入验证
587	  if img.width <= 0 || img.height <= 0 {
588	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
589	      "recognize_ocr_enhanced: 图像尺寸必须大于0",
590	    )
591	  }
592	  if img.channels < 1 || img.channels > 4 {
593	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
594	      "recognize_ocr_enhanced: 通道数必须在1-4之间",
595	    )
596	  }
597	  let expected_size = img.width * img.height * img.channels
598	  if img.data.length() != expected_size {
599	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
600	      "recognize_ocr_enhanced: 数据大小不匹配，期望 " +
601	      expected_size.to_string() +	<-- UNCOVERED
602	      "，实际 " +
603	      img.data.length().to_string(),	<-- UNCOVERED
604	    )
605	  }
606	  if templates.length() == 0 {
607	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
608	      "recognize_ocr_enhanced: 模板列表不能为空",
609	    )
610	  }
611	  let cfg = match config {
612	    Some(c) => c	<-- UNCOVERED
613	    None => ocr_accuracy_enhanced_default_config()
614	  }
615	  // 1. 预处理
616	  let preprocessed = ocr_preprocess(img)
617	  // 2. 字符分割
618	  let char_regions = segment_chars_for_enhanced(preprocessed)
619	  // 3. 对每个字符进行识别
620	  let char_results : Array[OcrCharResult] = []
621	  let mut text = ""
622	  let mut total_confidence = 0.0
623	  for _tup6 in char_regions {
624	    let (x, y, w, h) = _tup6
625	    // 提取字符图像
626	    let char_img = extract_char_region_for_enhanced(
627	      preprocessed, x, y, w, h, 48,
628	    )
629	    // 多尺度识别（如果启用）
630	    let result = if cfg.use_multi_scale {
631	      multi_scale_recognize(	<-- UNCOVERED
632	        char_img,
633	        templates,
634	        cfg.scales,
635	        distance_metric="euclidean",
636	      )
637	    } else {
638	      // 单尺度识别
639	      let features = extract_combined_enhanced_features(char_img, cfg)
640	      // 加权 KNN（如果启用）
641	      if cfg.use_weighted_knn {
642	        let (label, conf, _) = weighted_knn_classify(
643	          features,
644	          templates,
645	          k=cfg.knn_k,
646	          distance_metric="euclidean",
647	        )
648	        // 置信度校准（如果启用）
649	        let final_conf = if cfg.use_confidence_calibration {
650	          let dists : Array[Double] = []
651	          for _tup7 in templates {
652	            let (_, sample) = _tup7
653	            dists.push(calculate_distance(features, sample, "euclidean"))
654	          }
655	          calibrate_confidence_enhanced(conf, dists)
656	        } else {
657	          conf	<-- UNCOVERED
658	        }
659	        (label, final_conf)
660	      } else {
661	        // 简单模板匹配
662	        let mut best_label = "?"	<-- UNCOVERED
663	        let mut best_dist = 1000000.0
664	        for _tup8 in templates {
665	          let (label, template) = _tup8	<-- UNCOVERED
666	          let dist = calculate_distance(features, template, "euclidean")
667	          if dist < best_dist {
668	            best_dist = dist	<-- UNCOVERED
669	            best_label = label
670	          }
671	        }
672	        let conf = if best_dist > 0.0 { 1.0 / (1.0 + best_dist) } else { 1.0 }	<-- UNCOVERED
673	        (best_label, conf)
674	      }
675	    }
676	    text += result.0
677	    total_confidence += result.1
678	    char_results.push(OcrCharResult::{
679	      char: result.0,
680	      confidence: result.1,
681	      x,
682	      y,
683	      width: w,
684	      height: h,
685	    })
686	  }
687	  let avg_confidence = if char_results.length() > 0 {
688	    total_confidence / char_results.length().to_double()
689	  } else {
690	    0.0	<-- UNCOVERED
691	  }
692	  OcrResult::{ text, confidence: avg_confidence, characters: char_results }
693	}
   	…

763	fn extract_char_region_for_enhanced(
764	  img : @types.Image,
765	  x : Int,
766	  y : Int,
767	  width : Int,
768	  height : Int,
769	  target_size : Int,
770	) -> @types.Image {
771	  let w = img.width
772	  let h = img.height
773	  let crop_w = if x + width > w { w - x } else { width }
774	  let crop_h = if y + height > h { h - y } else { height }
775	  if crop_w <= 0 || crop_h <= 0 {
776	    let empty : Array[Byte] = Array::make(target_size * target_size, b'\x00')	<-- UNCOVERED
777	    return @types.Image::{
778	      width: target_size,
779	      height: target_size,
780	      channels: 1,
781	      data: Bytes::from_array(empty),	<-- UNCOVERED
782	    }
783	  }
784	  let out : Array[Byte] = Array::make(target_size * target_size, b'\x00')
785	  let data = img.data
786	  for py in 0..<target_size {
787	    for px in 0..<target_size {
788	      let src_x = x + px * crop_w / target_size
789	      let src_y = y + py * crop_h / target_size
790	      if src_x < w && src_y < h {
791	        out[py * target_size + px] = data[src_y * w + src_x]
792	      }
793	    }
794	  }
795	  @types.Image::{
796	    width: target_size,
797	    height: target_size,
798	    channels: 1,
799	    data: Bytes::from_array(out),
800	  }
801	}
   	…

10 uncovered line(s) in src\process\ocr\ocr_deployment.mbt:

 6	fn parse_int_str(s : String) -> Int {
 7	  let mut result = 0
 8	  let mut sign = 1
 9	  let start = if s.length() > 0 && s[0] == '-' {
10	    sign = -1	<-- UNCOVERED
11	    1
12	  } else {
13	    0
14	  }
15	  for i in start..<s.length() {
16	    let c = s[i]
17	    if c >= '0' && c <= '9' {
18	      result = result * 10 + (c.to_int() - '0'.to_int())
19	    }
20	  }
21	  sign * result
22	}
  	…

146	pub fn generate_dockerfile(config : DockerConfig) -> String {
147	  let mut dockerfile = "FROM " + config.base_image + "\n\n"
148	  dockerfile = dockerfile + "WORKDIR /app\n\n"
149	  dockerfile = dockerfile + "COPY . .\n\n"
150	  dockerfile = dockerfile + "EXPOSE " + config.app_port.to_string() + "\n\n"
151	  dockerfile = dockerfile +
152	    "CMD [\"moon\", \"run\", \"--target\", \"native\"]\n"
153	  if config.memory_limit_mb > 0 {
154	    dockerfile = dockerfile +
155	      "\n# Memory limit: " +
156	      config.memory_limit_mb.to_string() +
157	      "MB\n"
158	  }
159	  if config.cpu_limit > 0.0 {
160	    dockerfile = dockerfile +
161	      "# CPU limit: " +
162	      config.cpu_limit.to_string() +
163	      "\n"
164	  }
165	  if config.enable_gpu {
166	    dockerfile = dockerfile + "# GPU enabled\n"	<-- UNCOVERED
167	  }
168	  dockerfile
169	}
   	…

173	pub fn generate_docker_compose(config : DockerConfig) -> String {
174	  let mut compose = "version: '3.8'\n\n"
175	  compose = compose + "services:\n"
176	  compose = compose + "  ocr-server:\n"
177	  compose = compose + "    build: .\n"
178	  compose = compose + "    ports:\n"
179	  compose = compose +
180	    "      - \"" +
181	    config.app_port.to_string() +
182	    ":" +
183	    config.app_port.to_string() +
184	    "\"\n"
185	  compose = compose + "    deploy:\n"
186	  compose = compose + "      resources:\n"
187	  compose = compose + "        limits:\n"
188	  compose = compose +
189	    "          memory: " +
190	    config.memory_limit_mb.to_string() +
191	    "M\n"
192	  compose = compose + "          cpus: '" + config.cpu_limit.to_string() + "'\n"
193	  if config.volumes.length() > 0 {
194	    compose = compose + "    volumes:\n"	<-- UNCOVERED
195	    for i in 0..<config.volumes.length() {
196	      let (host, container) = config.volumes[i]
197	      compose = compose + "      - \"" + host + ":" + container + "\"\n"
198	    }
199	  }
200	  compose
201	}
   	…

252	pub fn parse_cli_args(args : Array[String]) -> CliConfig raise @types.LoadError {
253	  if args.length() == 0 {
254	    raise @types.LoadError::DecodeFailed(
255	      "parse_cli_args: 至少需要输入路径参数",
256	    )
257	  }
258	  let default = create_default_cli_config()
259	  let mut config = CliConfig::{ ..default, input_path: args[0] }
260	  let mut i = 1
261	  while i < args.length() {
262	    let arg = args[i]
263	    if arg == "-o" || arg == "--output" {
264	      if i + 1 < args.length() {
265	        config = CliConfig::{ ..config, output_path: args[i + 1] }
266	        i += 2
267	      } else {
268	        raise @types.LoadError::DecodeFailed("parse_cli_args: -o 需要参数")	<-- UNCOVERED
269	      }
270	    } else if arg == "-f" || arg == "--format" {
271	      if i + 1 < args.length() {
272	        config = CliConfig::{ ..config, output_format: args[i + 1] }
273	        i += 2
274	      } else {
275	        raise @types.LoadError::DecodeFailed("parse_cli_args: -f 需要参数")	<-- UNCOVERED
276	      }
277	    } else if arg == "-l" || arg == "--language" {
278	      if i + 1 < args.length() {
279	        config = CliConfig::{ ..config, language: args[i + 1] }
280	        i += 2
281	      } else {
282	        raise @types.LoadError::DecodeFailed("parse_cli_args: -l 需要参数")	<-- UNCOVERED
283	      }
284	    } else if arg == "--handwriting" {
285	      config = CliConfig::{ ..config, enable_handwriting: true }
286	      i += 1
287	    } else if arg == "--multilingual" {
288	      config = CliConfig::{ ..config, enable_multilingual: true }	<-- UNCOVERED
289	      i += 1
290	    } else if arg == "--document" {
291	      config = CliConfig::{ ..config, enable_document_analysis: true }
292	      i += 1
293	    } else if arg == "-v" || arg == "--verbose" {
294	      config = CliConfig::{ ..config, verbose: true }
295	      i += 1
296	    } else if arg == "-b" || arg == "--batch" {
297	      if i + 1 < args.length() {
298	        config = CliConfig::{ ..config, batch_size: parse_int_str(args[i + 1]) }
299	        i += 2
300	      } else {
301	        raise @types.LoadError::DecodeFailed("parse_cli_args: -b 需要参数")	<-- UNCOVERED
302	      }
303	    } else {
304	      i += 1	<-- UNCOVERED
305	    }
306	  }
307	  config
308	}
   	…

337	pub fn generate_env_config(env : DeploymentEnvironment) -> String {
338	  match env {
339	    DeploymentEnvironment::Development =>
340	      "NODE_ENV=development\nLOG_LEVEL=debug\n"
341	    DeploymentEnvironment::Staging => "NODE_ENV=staging\nLOG_LEVEL=info\n"	<-- UNCOVERED
342	    DeploymentEnvironment::Production => "NODE_ENV=production\nLOG_LEVEL=warn\n"
343	  }
344	}
   	…

28 uncovered line(s) in src\process\ocr\ocr_performance.mbt:

26	pub fn fast_threshold_lut(
27	  img : @types.Image,
28	  lut : Array[Int],
29	) -> @types.Image raise @types.LoadError {
30	  if img.width <= 0 || img.height <= 0 {
31	    raise @types.LoadError::DecodeFailed(
32	      "fast_threshold_lut: 图像尺寸必须大于0",
33	    )
34	  }
35	  if img.channels < 1 || img.channels > 4 {
36	    raise @types.LoadError::DecodeFailed("fast_threshold_lut: 通道数非法")	<-- UNCOVERED
37	  }
38	  let expected_size = img.width * img.height * img.channels
39	  if img.data.length() != expected_size {
40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
41	      "fast_threshold_lut: 数据大小不匹配",
42	    )
43	  }
44	  let result_data : Array[Byte] = Array::make(expected_size, b'\x00')
45	  for i in 0..<expected_size {
46	    let val = img.data[i].to_int()
47	    result_data[i] = if val < 256 && lut[val] > 0 { b'\xFF' } else { b'\x00' }
48	  }
49	  @types.Image::{
50	    width: img.width,
51	    height: img.height,
52	    channels: img.channels,
53	    data: Bytes::from_array(result_data),
54	  }
55	}
  	…

59	pub fn cache_friendly_horizontal_projection(
60	  img : @types.Image,
61	) -> Array[Int] raise @types.LoadError {
62	  if img.width <= 0 || img.height <= 0 {
63	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
64	      "cache_friendly_horizontal_projection: 图像尺寸必须大于0",
65	    )
66	  }
67	  if img.channels < 1 || img.channels > 4 {
68	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
69	      "cache_friendly_horizontal_projection: 通道数非法",
70	    )
71	  }
72	  let expected_size = img.width * img.height * img.channels
73	  if img.data.length() != expected_size {
74	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
75	      "cache_friendly_horizontal_projection: 数据大小不匹配",
76	    )
77	  }
78	  let projections : Array[Int] = Array::make(img.height, 0)
79	  for y in 0..<img.height {
80	    let mut sum = 0
81	    let row_offset = y * img.width * img.channels
82	    for x in 0..<img.width {
83	      sum += img.data[row_offset + x * img.channels].to_int()
84	    }
85	    projections[y] = sum
86	  }
87	  projections
88	}
  	…

 92	pub fn cache_friendly_vertical_projection(
 93	  img : @types.Image,
 94	) -> Array[Int] raise @types.LoadError {
 95	  if img.width <= 0 || img.height <= 0 {
 96	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 97	      "cache_friendly_vertical_projection: 图像尺寸必须大于0",
 98	    )
 99	  }
100	  if img.channels < 1 || img.channels > 4 {
101	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
102	      "cache_friendly_vertical_projection: 通道数非法",
103	    )
104	  }
105	  let expected_size = img.width * img.height * img.channels
106	  if img.data.length() != expected_size {
107	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
108	      "cache_friendly_vertical_projection: 数据大小不匹配",
109	    )
110	  }
111	  let projections : Array[Int] = Array::make(img.width, 0)
112	  for x in 0..<img.width {
113	    let mut sum = 0
114	    for y in 0..<img.height {
115	      sum += img.data[(y * img.width + x) * img.channels].to_int()
116	    }
117	    projections[x] = sum
118	  }
119	  projections
120	}
   	…

124	pub fn unrolled_pixel_sum(img : @types.Image) -> Int raise @types.LoadError {
125	  if img.width <= 0 || img.height <= 0 {
126	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
127	      "unrolled_pixel_sum: 图像尺寸必须大于0",
128	    )
129	  }
130	  if img.channels < 1 || img.channels > 4 {
131	    raise @types.LoadError::DecodeFailed("unrolled_pixel_sum: 通道数非法")	<-- UNCOVERED
132	  }
133	  let expected_size = img.width * img.height * img.channels
134	  if img.data.length() != expected_size {
135	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
136	      "unrolled_pixel_sum: 数据大小不匹配",
137	    )
138	  }
139	  let mut total = 0
140	  let n = expected_size
141	  let unrolled_end = n - n % 4
142	  let mut i = 0
143	  while i < unrolled_end {
144	    total += img.data[i].to_int()
145	    total += img.data[i + 1].to_int()
146	    total += img.data[i + 2].to_int()
147	    total += img.data[i + 3].to_int()
148	    i += 4
149	  }
150	  while i < n {
151	    total += img.data[i].to_int()	<-- UNCOVERED
152	    i += 1
153	  }
154	  total
155	}
   	…

159	pub fn fast_connected_components(
160	  binary_img : @types.Image,
161	) -> Array[Int] raise @types.LoadError {
162	  if binary_img.width <= 0 || binary_img.height <= 0 {
163	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
164	      "fast_connected_components: 图像尺寸必须大于0",
165	    )
166	  }
167	  if binary_img.channels < 1 || binary_img.channels > 4 {
168	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
169	      "fast_connected_components: 通道数非法",
170	    )
171	  }
172	  let w = binary_img.width
173	  let h = binary_img.height
174	  let expected_size = w * h * binary_img.channels
175	  if binary_img.data.length() != expected_size {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "fast_connected_components: 数据大小不匹配",
178	    )
179	  }
180	  let labels : Array[Int] = Array::make(w * h, 0)
181	  let mut current_label = 0
182	  for y in 0..<h {
183	    for x in 0..<w {
184	      let idx = y * w + x
185	      let pixel = binary_img.data[idx * binary_img.channels].to_int()
186	      if pixel > 128 && labels[idx] == 0 {
187	        current_label += 1	<-- UNCOVERED
188	        labels[idx] = current_label
189	      }
190	    }
191	  }
192	  labels
193	}
   	…

197	pub fn build_gaussian_kernel_lut(
198	  sigma : Double,
199	  radius : Int,
200	) -> Array[Double] raise @types.LoadError {
201	  if sigma <= 0.0 {
202	    raise @types.LoadError::DecodeFailed(
203	      "build_gaussian_kernel_lut: sigma必须大于0",
204	    )
205	  }
206	  if radius <= 0 {
207	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
208	      "build_gaussian_kernel_lut: 半径必须大于0",
209	    )
210	  }
211	  let size = 2 * radius + 1
212	  let kernel : Array[Double] = Array::make(size, 0.0)
213	  let two_sigma_sq = 2.0 * sigma * sigma
214	  let mut sum = 0.0
215	  for i in 0..<size {
216	    let x = (i - radius).to_double()
217	    let val = @math.exp(-x * x / two_sigma_sq)
218	    kernel[i] = val
219	    sum += val
220	  }
221	  for i in 0..<size {
222	    kernel[i] = kernel[i] / sum
223	  }
224	  kernel
225	}
   	…

229	pub fn fast_gaussian_blur_1d(
230	  img : @types.Image,
231	  kernel : Array[Double],
232	) -> @types.Image raise @types.LoadError {
233	  if img.width <= 0 || img.height <= 0 {
234	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
235	      "fast_gaussian_blur_1d: 图像尺寸必须大于0",
236	    )
237	  }
238	  if img.channels < 1 || img.channels > 4 {
239	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
240	      "fast_gaussian_blur_1d: 通道数非法",
241	    )
242	  }
243	  let w = img.width
244	  let h = img.height
245	  let c = img.channels
246	  let expected_size = w * h * c
247	  if img.data.length() != expected_size {
248	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
249	      "fast_gaussian_blur_1d: 数据大小不匹配",
250	    )
251	  }
252	  let radius = (kernel.length() - 1) / 2
253	  let result_data : Array[Byte] = Array::make(expected_size, b'\x00')
254	  for y in 0..<h {
255	    for x in 0..<w {
256	      for ch in 0..<c {
257	        let mut sum = 0.0
258	        for k in 0..<kernel.length() {
259	          let sx = x + k - radius
260	          let clamped_x = if sx < 0 { 0 } else if sx >= w { w - 1 } else { sx }
261	          let idx = (y * w + clamped_x) * c + ch
262	          sum += img.data[idx].to_int().to_double() * kernel[k]
263	        }
264	        let val = if sum > 255.0 {
265	          255	<-- UNCOVERED
266	        } else if sum < 0.0 {
267	          0	<-- UNCOVERED
268	        } else {
269	          sum.to_int()
270	        }
271	        result_data[(y * w + x) * c + ch] = val.to_byte()
272	      }
273	    }
274	  }
275	  @types.Image::{
276	    width: w,
277	    height: h,
278	    channels: c,
279	    data: Bytes::from_array(result_data),
280	  }
281	}
   	…

285	pub fn batch_recognize_chars(
286	  images : Array[@types.Image],
287	  config : DLCnnModelConfig,
288	  labels : Array[String],
289	) -> Array[DLInferenceResult] raise @types.LoadError {
290	  if images.length() == 0 {
291	    raise @types.LoadError::DecodeFailed(
292	      "batch_recognize_chars: 图像列表不能为空",
293	    )
294	  }
295	  if labels.length() != config.num_classes {
296	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
297	      "batch_recognize_chars: 标签数量不匹配",
298	    )
299	  }
300	  let results : Array[DLInferenceResult] = []
301	  for i in 0..<images.length() {
302	    results.push(cnn_recognize_char(images[i], config, labels))
303	  }
304	  results
305	}
   	…

309	pub fn ocr_pipeline_stats(
310	  img : @types.Image,
311	) -> (Int, Int, Double) raise @types.LoadError {
312	  if img.width <= 0 || img.height <= 0 {
313	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
314	      "ocr_pipeline_stats: 图像尺寸必须大于0",
315	    )
316	  }
317	  if img.channels < 1 || img.channels > 4 {
318	    raise @types.LoadError::DecodeFailed("ocr_pipeline_stats: 通道数非法")	<-- UNCOVERED
319	  }
320	  let expected_size = img.width * img.height * img.channels
321	  if img.data.length() != expected_size {
322	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
323	      "ocr_pipeline_stats: 数据大小不匹配",
324	    )
325	  }
326	  let pixel_count = img.width * img.height
327	  let byte_count = expected_size
328	  let avg_pixel = unrolled_pixel_sum(img).to_double() / pixel_count.to_double()
329	  (pixel_count, byte_count, avg_pixel)
330	}
   	…

334	pub fn fast_downsample_2x(
335	  img : @types.Image,
336	) -> @types.Image raise @types.LoadError {
337	  if img.width <= 1 || img.height <= 1 {
338	    raise @types.LoadError::DecodeFailed(
339	      "fast_downsample_2x: 图像尺寸必须大于1",
340	    )
341	  }
342	  if img.channels < 1 || img.channels > 4 {
343	    raise @types.LoadError::DecodeFailed("fast_downsample_2x: 通道数非法")	<-- UNCOVERED
344	  }
345	  let w = img.width
346	  let h = img.height
347	  let c = img.channels
348	  let expected_size = w * h * c
349	  if img.data.length() != expected_size {
350	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
351	      "fast_downsample_2x: 数据大小不匹配",
352	    )
353	  }
354	  let new_w = w / 2
355	  let new_h = h / 2
356	  let result_data : Array[Byte] = Array::make(new_w * new_h * c, b'\x00')
357	  for y in 0..<new_h {
358	    for x in 0..<new_w {
359	      for ch in 0..<c {
360	        let p00 = img.data[(y * 2 * w + x * 2) * c + ch].to_int()
361	        let p01 = img.data[(y * 2 * w + x * 2 + 1) * c + ch].to_int()
362	        let p10 = img.data[((y * 2 + 1) * w + x * 2) * c + ch].to_int()
363	        let p11 = img.data[((y * 2 + 1) * w + x * 2 + 1) * c + ch].to_int()
364	        let avg = (p00 + p01 + p10 + p11) / 4
365	        result_data[(y * new_w + x) * c + ch] = avg.to_byte()
366	      }
367	    }
368	  }
369	  @types.Image::{
370	    width: new_w,
371	    height: new_h,
372	    channels: c,
373	    data: Bytes::from_array(result_data),
374	  }
375	}
   	…

28 uncovered line(s) in src\process\ocr\postprocess.mbt:

35	pub fn dictionary_create(
36	  words : Array[String],
37	) -> Dictionary raise @types.LoadError {
38	  // 输入验证
39	  if words.length() == 0 {
40	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
41	      "dictionary_create: 单词列表不能为空",
42	    )
43	  }
44	
45	  Dictionary::{ words, word_set: words }
46	}
  	…

 85	pub fn edit_distance(s1 : String, s2 : String) -> Int raise @types.LoadError {
 86	  // 输入验证
 87	  if s1.length() == 0 && s2.length() == 0 {
 88	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 89	      "edit_distance: 两个字符串不能同时为空",
 90	    )
 91	  }
 92	
 93	  let len1 = s1.length()
 94	  let len2 = s2.length()
 95	  if len1 == 0 {
 96	    return len2	<-- UNCOVERED
 97	  }
 98	  if len2 == 0 {
 99	    return len1	<-- UNCOVERED
100	  }
101	  // 创建动态规划表
102	  let dp : Array[Int] = Array::make((len1 + 1) * (len2 + 1), 0)
103	  // 初始化
104	  for i in 0..<(len1 + 1) {
105	    dp[i * (len2 + 1)] = i
106	  }
107	  for j in 0..<(len2 + 1) {
108	    dp[j] = j
109	  }
110	  // 填充DP表
111	  for i in 1..<(len1 + 1) {
112	    for j in 1..<(len2 + 1) {
113	      let cost = if s1[i - 1] == s2[j - 1] { 0 } else { 1 }
114	      let insert = dp[i * (len2 + 1) + (j - 1)] + 1
115	      let delete = dp[(i - 1) * (len2 + 1) + j] + 1
116	      let substitute = dp[(i - 1) * (len2 + 1) + (j - 1)] + cost
117	      let min1 = if insert < delete { insert } else { delete }
118	      dp[i * (len2 + 1) + j] = if min1 < substitute { min1 } else { substitute }
119	    }
120	  }
121	  dp[len1 * (len2 + 1) + len2]
122	}
   	…

126	pub fn dictionary_match(
127	  word : String,
128	  dictionary : Dictionary,
129	  max_distance? : Int = 2,
130	) -> Array[(String, Int)] raise @types.LoadError {
131	  // 输入验证
132	  if word.length() == 0 {
133	    raise @types.LoadError::DecodeFailed("dictionary_match: 单词不能为空")	<-- UNCOVERED
134	  }
135	  if dictionary.words.length() == 0 {
136	    raise @types.LoadError::DecodeFailed("dictionary_match: 词典不能为空")	<-- UNCOVERED
137	  }
138	  if max_distance < 0 {
139	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
140	      "dictionary_match: max_distance不能为负数",
141	    )
142	  }
143	
144	  let results : Array[(String, Int)] = []
145	  for dict_word in dictionary.words {
146	    let dist = edit_distance(word, dict_word)
147	    if dist <= max_distance {
148	      results.push((dict_word, dist))
149	    }
150	  }
151	  // 按距离排序
152	  for _ in 0..<results.length() {
153	    for j in 0..<(results.length() - 1) {
154	      if results[j].1 > results[j + 1].1 {	<-- UNCOVERED
155	        let temp = results[j]
156	        results[j] = results[j + 1]
157	        results[j + 1] = temp
158	      }
159	    }
160	  }
161	  results
162	}
   	…

174	pub fn ngram_create(
175	  texts : Array[String],
176	  n? : Int = 2,
177	) -> NgramModel raise @types.LoadError {
178	  // 输入验证
179	  if texts.length() == 0 {
180	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
181	      "ngram_create: 文本列表不能为空",
182	    )
183	  }
184	  if n < 1 {
185	    raise @types.LoadError::DecodeFailed("ngram_create: n必须大于等于1")	<-- UNCOVERED
186	  }
187	
188	  let bigram_counts : Array[(String, Int)] = []
189	  let unigram_counts : Array[(String, Int)] = []
190	  for text in texts {
191	    // 简化：按字符处理
192	    for i in 0..<(text.length() - 1) {
193	      let bigram = text[i].to_string() + text[i + 1].to_string()
194	      // 查找是否已存在
195	      let mut found = false
196	      for j in 0..<bigram_counts.length() {
197	        if bigram_counts[j].0 == bigram {
198	          bigram_counts[j] = (bigram, bigram_counts[j].1 + 1)
199	          found = true
200	        }
201	      }
202	      if !found {
203	        bigram_counts.push((bigram, 1))
204	      }
205	    }
206	    // unigram
207	    for i in 0..<text.length() {
208	      let unigram = text[i].to_string()
209	      let mut found = false
210	      for j in 0..<unigram_counts.length() {
211	        if unigram_counts[j].0 == unigram {
212	          unigram_counts[j] = (unigram, unigram_counts[j].1 + 1)
213	          found = true
214	        }
215	      }
216	      if !found {
217	        unigram_counts.push((unigram, 1))
218	      }
219	    }
220	  }
221	  NgramModel::{ n, bigram_counts, unigram_counts }
222	}
   	…

226	pub fn ngram_score(
227	  text : String,
228	  model : NgramModel,
229	) -> Double raise @types.LoadError {
230	  // 输入验证
231	  if text.length() == 0 {
232	    raise @types.LoadError::DecodeFailed("ngram_score: 文本不能为空")	<-- UNCOVERED
233	  }
234	  if model.bigram_counts.length() == 0 && model.unigram_counts.length() == 0 {
235	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
236	      "ngram_score: N-gram模型不能为空",
237	    )
238	  }
239	
240	  if text.length() < 2 {
241	    return 0.0	<-- UNCOVERED
242	  }
243	  let mut total_score = 0.0
244	  let mut count = 0
245	  for i in 0..<(text.length() - 1) {
246	    let bigram = text[i].to_string() + text[i + 1].to_string()
247	    // 查找bigram概率
248	    let mut bigram_count = 0
249	    let mut unigram_count = 0
250	    for j in 0..<model.bigram_counts.length() {
251	      if model.bigram_counts[j].0 == bigram {
252	        bigram_count = model.bigram_counts[j].1
253	      }
254	    }
255	    let first_char = text[i].to_string()
256	    for j in 0..<model.unigram_counts.length() {
257	      if model.unigram_counts[j].0 == first_char {
258	        unigram_count = model.unigram_counts[j].1
259	      }
260	    }
261	    if unigram_count > 0 {
262	      total_score += bigram_count.to_double() / unigram_count.to_double()
263	      count += 1
264	    }
265	  }
266	  if count > 0 {
267	    total_score / count.to_double()
268	  } else {
269	    0.0	<-- UNCOVERED
270	  }
271	}
   	…

275	pub fn ocr_postprocess(
276	  result : OcrResult,
277	  config? : PostProcessConfig,
278	  dictionary? : Dictionary,
279	  ngram_model? : NgramModel,
280	) -> OcrResult raise @types.LoadError {
281	  ignore(ngram_model)
282	  // 输入验证
283	  if result.characters.length() == 0 && result.text.length() == 0 {
284	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
285	      "ocr_postprocess: OCR结果不能为空",
286	    )
287	  }
288	
289	  let cfg = match config {
290	    Some(c) => c	<-- UNCOVERED
291	    None => postprocess_default_config()
292	  }
293	  let dict = match dictionary {
294	    Some(d) => d	<-- UNCOVERED
295	    None => dictionary_create_common_english()
296	  }
297	  // 1. 低置信度字符标记
298	  let mut corrected_text = ""
299	  let mut total_confidence = 0.0
300	  let corrected_chars : Array[OcrCharResult] = []
301	  for char_result in result.characters {
302	    let mut corrected_char = char_result.char
303	    let mut confidence = char_result.confidence
304	    // 2. 词典匹配（对低置信度字符）
305	    if cfg.use_dictionary && confidence < cfg.min_confidence {
306	      // 简化：只对单个字符进行常见纠错
307	      let corrections = common_char_corrections(char_result.char)
308	      if corrections.length() > 0 {
309	        corrected_char = corrections[0]
310	        confidence = confidence + 0.1
311	      }
312	    }
313	    corrected_text += corrected_char
314	    total_confidence += confidence
315	    corrected_chars.push(OcrCharResult::{
316	      char: corrected_char,
317	      confidence,
318	      x: char_result.x,
319	      y: char_result.y,
320	      width: char_result.width,
321	      height: char_result.height,
322	    })
323	  }
324	  // 3. 词典匹配（对整个文本中的单词）
325	  if cfg.use_dictionary {
326	    let words = split_into_words(corrected_text)
327	    let mut corrected_words = ""
328	    for word in words {
329	      if word.length() >= 2 {
330	        let matches = dictionary_match(word, dict, max_distance=1)
331	        if matches.length() > 0 && matches[0].1 > 0 {
332	          corrected_words += matches[0].0	<-- UNCOVERED
333	        } else {
334	          corrected_words += word
335	        }
336	      } else {
337	        corrected_words += word	<-- UNCOVERED
338	      }
339	    }
340	    corrected_text = corrected_words
341	  }
342	  let avg_confidence = if corrected_chars.length() > 0 {
343	    total_confidence / corrected_chars.length().to_double()
344	  } else {
345	    0.0	<-- UNCOVERED
346	  }
347	  OcrResult::{
348	    text: corrected_text,
349	    confidence: avg_confidence,
350	    characters: corrected_chars,
351	  }
352	}
   	…

356	fn common_char_corrections(char : String) -> Array[String] {
357	  let corrections : Array[String] = []
358	  // OCR常见混淆字符对
359	  match char {
360	    "0" => corrections.push("O")	<-- UNCOVERED
361	    "O" => corrections.push("0")
362	    "1" => corrections.push("I")
363	    "I" => corrections.push("1")
364	    "l" => corrections.push("1")
365	    "2" => corrections.push("Z")
366	    "Z" => corrections.push("2")
367	    "5" => corrections.push("S")
368	    "S" => corrections.push("5")
369	    "8" => corrections.push("B")
370	    "B" => corrections.push("8")	<-- UNCOVERED
371	    "6" => corrections.push("G")
372	    "G" => corrections.push("6")
373	    "9" => corrections.push("g")
374	    "g" => corrections.push("9")
375	    _ => ignore(())
376	  }
377	  corrections
378	}
   	…

382	fn split_into_words(text : String) -> Array[String] {
383	  let words : Array[String] = []
384	  let mut current_word = ""
385	  for i in 0..<text.length() {
386	    let c = text[i]
387	    if c == ' ' || c == '\n' || c == '\t' {
388	      if current_word.length() > 0 {	<-- UNCOVERED
389	        words.push(current_word)
390	        current_word = ""
391	      }
392	      words.push(c.to_string())	<-- UNCOVERED
393	    } else {
394	      current_word += c.to_string()
395	    }
396	  }
397	  if current_word.length() > 0 {
398	    words.push(current_word)
399	  }
400	  words
401	}
   	…

405	pub fn calibrate_confidence(
406	  result : OcrResult,
407	  factor? : Double = 1.0,
408	) -> OcrResult raise @types.LoadError {
409	  // 输入验证
410	  if result.characters.length() == 0 {
411	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
412	      "calibrate_confidence: OCR结果字符列表不能为空",
413	    )
414	  }
415	  if factor <= 0.0 {
416	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
417	      "calibrate_confidence: factor必须大于0",
418	    )
419	  }
420	
421	  let calibrated_chars : Array[OcrCharResult] = []
422	  let mut total = 0.0
423	  for char_result in result.characters {
424	    let new_confidence = if char_result.confidence * factor > 1.0 {
425	      1.0	<-- UNCOVERED
426	    } else {
427	      char_result.confidence * factor
428	    }
429	    total += new_confidence
430	    calibrated_chars.push(OcrCharResult::{
431	      char: char_result.char,
432	      confidence: new_confidence,
433	      x: char_result.x,
434	      y: char_result.y,
435	      width: char_result.width,
436	      height: char_result.height,
437	    })
438	  }
439	  let avg = if calibrated_chars.length() > 0 {
440	    total / calibrated_chars.length().to_double()
441	  } else {
442	    0.0	<-- UNCOVERED
443	  }
444	  OcrResult::{
445	    text: result.text,
446	    confidence: avg,
447	    characters: calibrated_chars,
448	  }
449	}
   	…

60 uncovered line(s) in src\process\ocr\postprocess_correction.mbt:

139	fn get_chinese_similar_chars() -> Array[SimilarCharMapping] {
140	  [	<-- UNCOVERED
141	    SimilarCharMapping::{
142	      char: "己",
143	      similar_chars: ["已", "巳"],
144	      context_hint: "形近字",
145	    },
146	    SimilarCharMapping::{
147	      char: "已",
148	      similar_chars: ["己", "巳"],
149	      context_hint: "形近字",
150	    },
151	    SimilarCharMapping::{
152	      char: "未",
153	      similar_chars: ["末"],
154	      context_hint: "形近字",
155	    },
156	    SimilarCharMapping::{
157	      char: "末",
158	      similar_chars: ["未"],
159	      context_hint: "形近字",
160	    },
161	    SimilarCharMapping::{
162	      char: "日",
163	      similar_chars: ["曰"],
164	      context_hint: "形近字",
165	    },
166	    SimilarCharMapping::{
167	      char: "大",
168	      similar_chars: ["太", "犬"],
169	      context_hint: "形近字",
170	    },
171	    SimilarCharMapping::{
172	      char: "人",
173	      similar_chars: ["入", "八"],
174	      context_hint: "形近字",
175	    },
176	    SimilarCharMapping::{
177	      char: "土",
178	      similar_chars: ["士"],
179	      context_hint: "形近字",
180	    },
181	    SimilarCharMapping::{
182	      char: "王",
183	      similar_chars: ["玉", "主"],
184	      context_hint: "形近字",
185	    },
186	    SimilarCharMapping::{
187	      char: "刀",
188	      similar_chars: ["力", "刁"],
189	      context_hint: "形近字",
190	    },
191	    SimilarCharMapping::{
192	      char: "了",
193	      similar_chars: ["子", "孑"],
194	      context_hint: "形近字",
195	    },
196	    SimilarCharMapping::{
197	      char: "我",
198	      similar_chars: ["找", "伐"],
199	      context_hint: "形近字",
200	    },
201	    SimilarCharMapping::{
202	      char: "的",
203	      similar_chars: ["约", "钓"],
204	      context_hint: "形近字",
205	    },
206	    SimilarCharMapping::{
207	      char: "是",
208	      similar_chars: ["足", "走"],
209	      context_hint: "形近字",
210	    },
211	    SimilarCharMapping::{
212	      char: "不",
213	      similar_chars: ["卞", "木"],
214	      context_hint: "形近字",
215	    },
216	    SimilarCharMapping::{
217	      char: "在",
218	      similar_chars: ["有", "存"],
219	      context_hint: "形近字",
220	    },
221	  ]
222	}
   	…

316	pub fn correct_similar_chars(
317	  text : String,
318	  context_type : String, // "number" / "english" / "chinese" / "mixed"
319	) -> CorrectionResult raise @types.LoadError {
320	  // 输入验证
321	  if context_type != "number" &&	<-- UNCOVERED
322	    context_type != "english" &&
323	    context_type != "chinese" &&
324	    context_type != "mixed" {
325	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
326	      "correct_similar_chars: 上下文类型必须是 number/english/chinese/mixed 之一",
327	    )
328	  }
329	  let _ = match context_type {	<-- UNCOVERED
330	    "number" => get_english_similar_chars()
331	    "english" => get_english_similar_chars()
332	    "chinese" => get_chinese_similar_chars()
333	    _ => get_english_similar_chars()
334	  }
335	
336	  let corrections : Array[CorrectionEntry] = []
337	  let mut corrected = text
338	  let mut is_corrected = false
339	  let mut total_confidence = 0.0
340	  let mut correction_count = 0
341	
342	  // 简化版：基于上下文类型的字符替换
343	  // 数字上下文中：O→0, I→1, l→1, Z→2, S→5, B→8
344	  if context_type == "number" {
345	    let mut new_text = ""	<-- UNCOVERED
346	    for i in 0..<text.length() {
347	      let c = text[i]
348	      let mut new_c = c.to_string()
349	      let mut corrected_here = false
350	      if c == 'O' || c == 'o' {
351	        new_c = "0"	<-- UNCOVERED
352	        corrected_here = true
353	      } else if c == 'I' || c == 'l' || c == '|' {	<-- UNCOVERED
354	        new_c = "1"
355	        corrected_here = true
356	      } else if c == 'Z' || c == 'z' {	<-- UNCOVERED
357	        new_c = "2"
358	        corrected_here = true
359	      } else if c == 'S' || c == 's' {	<-- UNCOVERED
360	        new_c = "5"
361	        corrected_here = true
362	      } else if c == 'B' {	<-- UNCOVERED
363	        new_c = "8"
364	        corrected_here = true
365	      }
366	      if corrected_here {
367	        corrections.push(CorrectionEntry::{	<-- UNCOVERED
368	          position: i,
369	          original_char: c.to_string(),	<-- UNCOVERED
370	          corrected_char: new_c,
371	          reason: "数字上下文相似字符纠正",
372	          confidence: 0.85,
373	        })
374	        is_corrected = true
375	        total_confidence += 0.85
376	        correction_count += 1
377	      }
378	      new_text = new_text + new_c
379	    }
380	    corrected = new_text
381	  }
382	
383	  let avg_confidence = if correction_count > 0 {
384	    total_confidence / correction_count.to_double()	<-- UNCOVERED
385	  } else {
386	    1.0	<-- UNCOVERED
387	  }
388	
389	  CorrectionResult::{
390	    original: text,
391	    corrected,
392	    is_corrected,
393	    confidence: avg_confidence,
394	    corrections,
395	  }
396	}
   	…

400	pub fn correct_common_patterns(text : String) -> CorrectionResult {
401	  let patterns = get_common_error_patterns()	<-- UNCOVERED
402	  let corrections : Array[CorrectionEntry] = []
403	  let mut corrected = text
404	  let mut is_corrected = false
405	  let mut total_confidence = 0.0
406	  let mut correction_count = 0
407	
408	  for pattern in patterns {
409	    // 简化版：只处理重复字符模式
410	    if pattern.pattern.length() == pattern.correction.length() * 2 {	<-- UNCOVERED
411	      // 检测重复
412	      let _ = pattern.pattern.length() / 2	<-- UNCOVERED
413	      let mut i = 0
414	      while i < corrected.length() - pattern.pattern.length() + 1 {	<-- UNCOVERED
415	        let mut match_found = true
416	        for j in 0..<pattern.pattern.length() {
417	          if i + j < corrected.length() &&
418	            corrected[i + j] != pattern.pattern[j] {
419	            match_found = false	<-- UNCOVERED
420	            break
421	          }
422	        }
423	        if match_found {
424	          corrections.push(CorrectionEntry::{	<-- UNCOVERED
425	            position: i,
426	            original_char: pattern.pattern,
427	            corrected_char: pattern.correction,
428	            reason: "常见重复错误模式纠正",
429	            confidence: 0.9,
430	          })
431	          is_corrected = true
432	          total_confidence += 0.9
433	          correction_count += 1
434	          // 替换
435	          let mut new_text = ""
436	          for k in 0..<i {
437	            new_text = new_text + corrected[k].to_string()	<-- UNCOVERED
438	          }
439	          new_text = new_text + pattern.correction
440	          for k in (i + pattern.pattern.length())..<corrected.length() {	<-- UNCOVERED
441	            new_text = new_text + corrected[k].to_string()
442	          }
443	          corrected = new_text
444	          i += pattern.correction.length()	<-- UNCOVERED
445	        } else {
446	          i += 1	<-- UNCOVERED
447	        }
448	      }
449	    }
450	  }
451	
452	  let avg_confidence = if correction_count > 0 {
453	    total_confidence / correction_count.to_double()	<-- UNCOVERED
454	  } else {
455	    1.0	<-- UNCOVERED
456	  }
457	
458	  CorrectionResult::{
459	    original: text,
460	    corrected,
461	    is_corrected,
462	    confidence: avg_confidence,
463	    corrections,
464	  }
465	}
   	…

469	pub fn full_postprocess_correction(
470	  text : String,
471	  context_type? : String = "mixed",
472	) -> CorrectionResult raise @types.LoadError {
473	  // 输入验证
474	  if context_type != "number" &&	<-- UNCOVERED
475	    context_type != "english" &&
476	    context_type != "chinese" &&
477	    context_type != "mixed" {
478	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
479	      "full_postprocess_correction: 上下文类型必须是 number/english/chinese/mixed 之一",
480	    )
481	  }
482	  let ctx = context_type
483	
484	  // 第一步：相似字符纠正
485	  let step1 = correct_similar_chars(text, ctx)	<-- UNCOVERED
486	
487	  // 第二步：常见错误模式纠正
488	  let step2 = correct_common_patterns(step1.corrected)	<-- UNCOVERED
489	
490	  // 合并结果
491	  let all_corrections : Array[CorrectionEntry] = []
492	  for c in step1.corrections {
493	    all_corrections.push(c)	<-- UNCOVERED
494	  }
495	  for c in step2.corrections {
496	    all_corrections.push(c)	<-- UNCOVERED
497	  }
498	
499	  let is_corrected = step1.is_corrected || step2.is_corrected
500	  let avg_confidence = if all_corrections.length() > 0 {	<-- UNCOVERED
501	    let mut total = 0.0
502	    for c in all_corrections {
503	      total += c.confidence	<-- UNCOVERED
504	    }
505	    total / all_corrections.length().to_double()	<-- UNCOVERED
506	  } else {
507	    1.0	<-- UNCOVERED
508	  }
509	
510	  CorrectionResult::{
511	    original: text,
512	    corrected: step2.corrected,
513	    is_corrected,
514	    confidence: avg_confidence,
515	    corrections: all_corrections,
516	  }
517	}
   	…

521	pub fn batch_correct_texts(
522	  texts : Array[String],
523	  context_type? : String = "mixed",
524	) -> Array[CorrectionResult] raise @types.LoadError {
525	  // 输入验证
526	  if texts.length() == 0 {	<-- UNCOVERED
527	    raise @types.LoadError::DecodeFailed(
528	      "batch_correct_texts: 文本列表不能为空",
529	    )
530	  }
531	  if context_type != "number" &&
532	    context_type != "english" &&
533	    context_type != "chinese" &&
534	    context_type != "mixed" {
535	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
536	      "batch_correct_texts: 上下文类型必须是 number/english/chinese/mixed 之一",
537	    )
538	  }
539	  let results : Array[CorrectionResult] = []
540	  for text in texts {
541	    let result = full_postprocess_correction(text, context_type~)	<-- UNCOVERED
542	    results.push(result)
543	  }
544	  results
545	}
   	…

549	pub fn generate_correction_report(results : Array[CorrectionResult]) -> String {
550	  let mut report = "## OCR 后处理纠错报告\n\n"	<-- UNCOVERED
551	  let total_texts = results.length()
552	  let mut corrected_texts = 0
553	  let mut total_corrections = 0
554	  let mut total_confidence = 0.0
555	
556	  for result in results {
557	    if result.is_corrected {	<-- UNCOVERED
558	      corrected_texts += 1
559	      total_corrections += result.corrections.length()
560	      total_confidence += result.confidence
561	    }
562	  }
563	
564	  let avg_confidence = if corrected_texts > 0 {
565	    total_confidence / corrected_texts.to_double()	<-- UNCOVERED
566	  } else {
567	    1.0	<-- UNCOVERED
568	  }
569	  let correction_rate = if total_texts > 0 {
570	    corrected_texts.to_double() / total_texts.to_double() * 100.0	<-- UNCOVERED
571	  } else {
572	    0.0	<-- UNCOVERED
573	  }
574	
575	  report = report + "### 整体统计\n\n"
576	  report = report + "| 指标 | 数值 |\n"
577	  report = report + "|------|------|\n"
578	  report = report + "| 处理文本数 | " + total_texts.to_string() + " |\n"	<-- UNCOVERED
579	  report = report +
580	    "| 纠错文本数 | " +
581	    corrected_texts.to_string() +	<-- UNCOVERED
582	    " |\n"
583	  report = report + "| 纠错率 | " + correction_rate.to_string() + "% |\n"	<-- UNCOVERED
584	  report = report +
585	    "| 总纠错次数 | " +
586	    total_corrections.to_string() +	<-- UNCOVERED
587	    " |\n"
588	  report = report +
589	    "| 平均置信度 | " +
590	    (avg_confidence * 100.0).to_string() +	<-- UNCOVERED
591	    "% |\n\n"
592	
593	  report = report + "### 纠错详情\n\n"
594	  for i in 0..<results.length() {	<-- UNCOVERED
595	    let result = results[i]
596	    if result.is_corrected {
597	      report = report + "**文本 " + (i + 1).to_string() + "**\n\n"	<-- UNCOVERED
598	      report = report + "- 原文: " + result.original + "\n"
599	      report = report + "- 纠正: " + result.corrected + "\n"
600	      report = report +
601	        "- 置信度: " +
602	        (result.confidence * 100.0).to_string() +	<-- UNCOVERED
603	        "%\n"
604	      report = report +
605	        "- 纠错次数: " +
606	        result.corrections.length().to_string() +	<-- UNCOVERED
607	        "\n\n"
608	    }
609	  }
610	
611	  report
612	}
   	…

616	pub fn calculate_accuracy_improvement(
617	  before_results : Array[CorrectionResult],
618	  expected_texts : Array[String],
619	) -> (Double, Double, Double) {
620	  let mut before_accuracy = 0.0	<-- UNCOVERED
621	  let mut after_accuracy = 0.0
622	
623	  for i in 0..<before_results.length() {	<-- UNCOVERED
624	    if i < expected_texts.length() {
625	      let before = before_results[i].original
626	      let after = before_results[i].corrected
627	      let expected = expected_texts[i]
628	
629	      let before_correct = calculate_char_accuracy(expected, before)	<-- UNCOVERED
630	      let after_correct = calculate_char_accuracy(expected, after)
631	
632	      before_accuracy += before_correct
633	      after_accuracy += after_correct
634	    }
635	  }
636	
637	  let count = if before_results.length() < expected_texts.length() {	<-- UNCOVERED
638	    before_results.length()
639	  } else {
640	    expected_texts.length()	<-- UNCOVERED
641	  }
642	  if count > 0 {
643	    before_accuracy = before_accuracy / count.to_double()	<-- UNCOVERED
644	    after_accuracy = after_accuracy / count.to_double()
645	  }
646	
647	  let improvement = after_accuracy - before_accuracy
648	  (before_accuracy, after_accuracy, improvement)
649	}
   	…

75 uncovered line(s) in src\process\ocr\projection_analysis.mbt:

16	pub fn horizontal_projection(
17	  img : @types.Image,
18	) -> ProjectionResult raise @types.LoadError {
19	  // 输入验证
20	  if img.width <= 0 || img.height <= 0 {
21	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
22	      "horizontal_projection: 图像尺寸必须大于0",
23	    )
24	  }
25	  if img.channels != 1 {
26	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
27	      "horizontal_projection: 仅支持单通道二值图",
28	    )
29	  }
30	  let expected_size = img.width * img.height
31	  if img.data.length() != expected_size {
32	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
33	      "horizontal_projection: 数据大小不匹配，期望 " +
34	      expected_size.to_string() +	<-- UNCOVERED
35	      "，实际 " +
36	      img.data.length().to_string(),	<-- UNCOVERED
37	    )
38	  }
39	
40	  let w = img.width
41	  let h = img.height
42	  let data = img.data
43	  let projection : Array[Int] = Array::make(h, 0)
44	  for y in 0..<h {
45	    for x in 0..<w {
46	      if data[y * w + x].to_int() > 128 {
47	        projection[y] += 1
48	      }
49	    }
50	  }
51	  analyze_projection(projection)
52	}
  	…

56	pub fn vertical_projection(
57	  img : @types.Image,
58	) -> ProjectionResult raise @types.LoadError {
59	  // 输入验证
60	  if img.width <= 0 || img.height <= 0 {
61	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
62	      "vertical_projection: 图像尺寸必须大于0",
63	    )
64	  }
65	  if img.channels != 1 {
66	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
67	      "vertical_projection: 仅支持单通道二值图",
68	    )
69	  }
70	  let expected_size = img.width * img.height
71	  if img.data.length() != expected_size {
72	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
73	      "vertical_projection: 数据大小不匹配，期望 " +
74	      expected_size.to_string() +	<-- UNCOVERED
75	      "，实际 " +
76	      img.data.length().to_string(),	<-- UNCOVERED
77	    )
78	  }
79	
80	  let w = img.width
81	  let h = img.height
82	  let data = img.data
83	  let projection : Array[Int] = Array::make(w, 0)
84	  for y in 0..<h {
85	    for x in 0..<w {
86	      if data[y * w + x].to_int() > 128 {
87	        projection[x] += 1
88	      }
89	    }
90	  }
91	  analyze_projection(projection)
92	}
  	…

 96	fn analyze_projection(projection : Array[Int]) -> ProjectionResult {
 97	  let n = projection.length()
 98	  // 计算均值和标准差
 99	  let mut sum = 0
100	  let mut sum_sq = 0
101	  for i in 0..<n {
102	    sum += projection[i]
103	    sum_sq += projection[i] * projection[i]
104	  }
105	  let mean = if n > 0 { sum.to_double() / n.to_double() } else { 0.0 }
106	  let variance = if n > 0 {
107	    sum_sq.to_double() / n.to_double() - mean * mean
108	  } else {
109	    0.0	<-- UNCOVERED
110	  }
111	  let stddev = if variance > 0.0 { variance.sqrt() } else { 0.0 }
112	  // 找峰值（局部最大值）
113	  let peaks : Array[Int] = []
114	  for i in 1..<(n - 1) {
115	    if projection[i] > projection[i - 1] &&
116	      projection[i] >= projection[i + 1] &&
117	      projection[i].to_double() > mean {
118	      peaks.push(i)
119	    }
120	  }
121	  // 找谷值（局部最小值）
122	  let valleys : Array[Int] = []
123	  for i in 1..<(n - 1) {
124	    if projection[i] < projection[i - 1] &&
125	      projection[i] <= projection[i + 1] &&
126	      projection[i].to_double() < mean {
127	      valleys.push(i)
128	    }
129	  }
130	  ProjectionResult::{ projection, peaks, valleys, mean, stddev }
131	}
   	…

148	pub fn detect_text_lines_projection(
149	  img : @types.Image,
150	  min_height? : Int = 5,
151	  max_gap? : Int = 3,
152	) -> Array[TextLineInfo] raise @types.LoadError {
153	  // 输入验证
154	  if img.width <= 0 || img.height <= 0 {
155	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
156	      "detect_text_lines_projection: 图像尺寸必须大于0",
157	    )
158	  }
159	  if img.channels != 1 {
160	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
161	      "detect_text_lines_projection: 仅支持单通道二值图",
162	    )
163	  }
164	  let expected_size = img.width * img.height
165	  if img.data.length() != expected_size {
166	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
167	      "detect_text_lines_projection: 数据大小不匹配，期望 " +
168	      expected_size.to_string() +	<-- UNCOVERED
169	      "，实际 " +
170	      img.data.length().to_string(),	<-- UNCOVERED
171	    )
172	  }
173	  if min_height <= 0 {
174	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
175	      "detect_text_lines_projection: min_height必须大于0",
176	    )
177	  }
178	  if max_gap < 0 {
179	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
180	      "detect_text_lines_projection: max_gap不能为负数",
181	    )
182	  }
183	
184	  let w = img.width
185	  let h = img.height
186	  let data = img.data
187	  // 水平投影
188	  let projection : Array[Int] = Array::make(h, 0)
189	  for y in 0..<h {
190	    for x in 0..<w {
191	      if data[y * w + x].to_int() > 128 {
192	        projection[y] += 1
193	      }
194	    }
195	  }
196	  // 找文本行（连续的非零行）
197	  let lines : Array[TextLineInfo] = []
198	  let mut in_line = false
199	  let mut start_y = 0
200	  let mut gap_count = 0
201	  for y in 0..<h {
202	    if projection[y] > 0 {
203	      if !in_line {
204	        in_line = true
205	        start_y = y
206	      }
207	      gap_count = 0
208	    } else if in_line {	<-- UNCOVERED
209	      gap_count += 1
210	      if gap_count > max_gap {
211	        // 结束当前行
212	        let end_y = y - gap_count	<-- UNCOVERED
213	        let line_height = end_y - start_y + 1
214	        if line_height >= min_height {
215	          // 计算这一行的水平范围和像素数
216	          let mut min_x = w	<-- UNCOVERED
217	          let mut max_x = 0
218	          let mut pixel_count = 0
219	          let mut baseline_y = start_y
220	          let mut max_proj = 0
221	          for yy in start_y..<(end_y + 1) {
222	            if projection[yy] > max_proj {	<-- UNCOVERED
223	              max_proj = projection[yy]
224	              baseline_y = yy
225	            }
226	            for x in 0..<w {
227	              if data[yy * w + x].to_int() > 128 {	<-- UNCOVERED
228	                pixel_count += 1
229	                if x < min_x {
230	                  min_x = x	<-- UNCOVERED
231	                }
232	                if x > max_x {
233	                  max_x = x	<-- UNCOVERED
234	                }
235	              }
236	            }
237	          }
238	          if max_x > min_x {
239	            lines.push(TextLineInfo::{	<-- UNCOVERED
240	              y_start: start_y,
241	              y_end: end_y,
242	              height: line_height,
243	              x_start: min_x,
244	              x_end: max_x,
245	              width: max_x - min_x + 1,
246	              pixel_count,
247	              baseline: baseline_y,
248	            })
249	          }
250	        }
251	        in_line = false
252	        gap_count = 0
253	      }
254	    }
255	  }
256	  // 处理最后一行
257	  if in_line {
258	    let end_y = h - 1
259	    let line_height = end_y - start_y + 1
260	    if line_height >= min_height {
261	      let mut min_x = w
262	      let mut max_x = 0
263	      let mut pixel_count = 0
264	      for yy in start_y..<(end_y + 1) {
265	        for x in 0..<w {
266	          if data[yy * w + x].to_int() > 128 {
267	            pixel_count += 1
268	            if x < min_x {
269	              min_x = x
270	            }
271	            if x > max_x {
272	              max_x = x
273	            }
274	          }
275	        }
276	      }
277	      if max_x > min_x {
278	        lines.push(TextLineInfo::{
279	          y_start: start_y,
280	          y_end: end_y,
281	          height: line_height,
282	          x_start: min_x,
283	          x_end: max_x,
284	          width: max_x - min_x + 1,
285	          pixel_count,
286	          baseline: start_y + line_height / 2,
287	        })
288	      }
289	    }
290	  }
291	  lines
292	}
   	…

308	pub fn segment_chars_projection(
309	  img : @types.Image,
310	  line : TextLineInfo,
311	  min_width? : Int = 2,
312	  max_gap? : Int = 2,
313	) -> Array[CharSegment] raise @types.LoadError {
314	  // 输入验证
315	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
316	    raise @types.LoadError::DecodeFailed(
317	      "segment_chars_projection: 图像尺寸必须大于0",
318	    )
319	  }
320	  if img.channels != 1 {
321	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
322	      "segment_chars_projection: 仅支持单通道二值图",
323	    )
324	  }
325	  let expected_size = img.width * img.height
326	  if img.data.length() != expected_size {	<-- UNCOVERED
327	    raise @types.LoadError::DecodeFailed(
328	      "segment_chars_projection: 数据大小不匹配，期望 " +
329	      expected_size.to_string() +	<-- UNCOVERED
330	      "，实际 " +
331	      img.data.length().to_string(),	<-- UNCOVERED
332	    )
333	  }
334	  if line.width <= 0 || line.height <= 0 {
335	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
336	      "segment_chars_projection: 文本行尺寸必须大于0",
337	    )
338	  }
339	  if min_width <= 0 {
340	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
341	      "segment_chars_projection: min_width必须大于0",
342	    )
343	  }
344	  if max_gap < 0 {
345	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
346	      "segment_chars_projection: max_gap不能为负数",
347	    )
348	  }
349	
350	  let w = img.width
351	  let data = img.data
352	  let y_start = line.y_start
353	  let y_end = line.y_end
354	  let x_start = line.x_start
355	  let x_end = line.x_end
356	  // 垂直投影（只在文本行范围内）
357	  let projection : Array[Int] = Array::make(w, 0)	<-- UNCOVERED
358	  for x in x_start..<(x_end + 1) {
359	    for y in y_start..<(y_end + 1) {	<-- UNCOVERED
360	      if data[y * w + x].to_int() > 128 {
361	        projection[x] += 1
362	      }
363	    }
364	  }
365	  // 找字符（连续的非零列）
366	  let chars : Array[CharSegment] = []
367	  let mut in_char = false
368	  let mut start_x = 0
369	  let mut gap_count = 0
370	  for x in x_start..<(x_end + 1) {
371	    if projection[x] > 0 {	<-- UNCOVERED
372	      if !in_char {
373	        in_char = true
374	        start_x = x
375	      }
376	      gap_count = 0
377	    } else if in_char {	<-- UNCOVERED
378	      gap_count += 1
379	      if gap_count > max_gap {
380	        let end_x = x - gap_count	<-- UNCOVERED
381	        let char_width = end_x - start_x + 1
382	        if char_width >= min_width {
383	          // 计算字符的垂直范围和像素数
384	          let mut min_y = y_end	<-- UNCOVERED
385	          let mut max_y = y_start
386	          let mut pixel_count = 0
387	          for xx in start_x..<(end_x + 1) {
388	            for yy in y_start..<(y_end + 1) {	<-- UNCOVERED
389	              if data[yy * w + xx].to_int() > 128 {
390	                pixel_count += 1
391	                if yy < min_y {
392	                  min_y = yy	<-- UNCOVERED
393	                }
394	                if yy > max_y {
395	                  max_y = yy	<-- UNCOVERED
396	                }
397	              }
398	            }
399	          }
400	          chars.push(CharSegment::{	<-- UNCOVERED
401	            x_start: start_x,
402	            x_end: end_x,
403	            width: char_width,
404	            y_start: min_y,
405	            y_end: max_y,
406	            height: max_y - min_y + 1,
407	            pixel_count,
408	          })
409	        }
410	        in_char = false
411	        gap_count = 0
412	      }
413	    }
414	  }
415	  // 处理最后一个字符
416	  if in_char {
417	    let end_x = x_end	<-- UNCOVERED
418	    let char_width = end_x - start_x + 1
419	    if char_width >= min_width {
420	      let mut min_y = y_end	<-- UNCOVERED
421	      let mut max_y = y_start
422	      let mut pixel_count = 0
423	      for xx in start_x..<(end_x + 1) {
424	        for yy in y_start..<(y_end + 1) {	<-- UNCOVERED
425	          if data[yy * w + xx].to_int() > 128 {
426	            pixel_count += 1
427	            if yy < min_y {
428	              min_y = yy	<-- UNCOVERED
429	            }
430	            if yy > max_y {
431	              max_y = yy	<-- UNCOVERED
432	            }
433	          }
434	        }
435	      }
436	      chars.push(CharSegment::{	<-- UNCOVERED
437	        x_start: start_x,
438	        x_end: end_x,
439	        width: char_width,
440	        y_start: min_y,
441	        y_end: max_y,
442	        height: max_y - min_y + 1,
443	        pixel_count,
444	      })
445	    }
446	  }
447	  chars
448	}
   	…

452	pub fn extract_text_line_image(
453	  img : @types.Image,
454	  line : TextLineInfo,
455	  padding? : Int = 2,
456	) -> @types.Image raise @types.LoadError {
457	  // 输入验证
458	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
459	    raise @types.LoadError::DecodeFailed(
460	      "extract_text_line_image: 图像尺寸必须大于0",
461	    )
462	  }
463	  if img.channels < 1 || img.channels > 4 {
464	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
465	      "extract_text_line_image: 通道数必须在1-4之间",
466	    )
467	  }
468	  let expected_size = img.width * img.height * img.channels
469	  if img.data.length() != expected_size {	<-- UNCOVERED
470	    raise @types.LoadError::DecodeFailed(
471	      "extract_text_line_image: 数据大小不匹配，期望 " +
472	      expected_size.to_string() +	<-- UNCOVERED
473	      "，实际 " +
474	      img.data.length().to_string(),	<-- UNCOVERED
475	    )
476	  }
477	  if line.width <= 0 || line.height <= 0 {
478	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
479	      "extract_text_line_image: 文本行尺寸必须大于0",
480	    )
481	  }
482	  if padding < 0 {
483	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
484	      "extract_text_line_image: padding不能为负数",
485	    )
486	  }
487	
488	  let w = img.width
489	  let h = img.height
490	  let ch = img.channels
491	  let x_start = max(0, line.x_start - padding)	<-- UNCOVERED
492	  let x_end = min(w - 1, line.x_end + padding)
493	  let y_start = max(0, line.y_start - padding)
494	  let y_end = min(h - 1, line.y_end + padding)
495	  let new_w = x_end - x_start + 1
496	  let new_h = y_end - y_start + 1
497	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')	<-- UNCOVERED
498	  let data = img.data
499	  for y in 0..<new_h {
500	    for x in 0..<new_w {	<-- UNCOVERED
501	      for c in 0..<ch {
502	        out[(y * new_w + x) * ch + c] = data[((y_start + y) * w + (x_start + x)) *
503	          ch +
504	          c]
505	      }
506	    }
507	  }
508	  @types.Image::{
509	    width: new_w,
510	    height: new_h,
511	    channels: ch,
512	    data: Bytes::from_array(out),	<-- UNCOVERED
513	  }
514	}
   	…

518	pub fn extract_char_image(
519	  img : @types.Image,
520	  char : CharSegment,
521	  target_size? : Int = 32,
522	  padding? : Int = 2,
523	) -> @types.Image raise @types.LoadError {
524	  // 输入验证
525	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
526	    raise @types.LoadError::DecodeFailed(
527	      "extract_char_image: 图像尺寸必须大于0",
528	    )
529	  }
530	  if img.channels < 1 || img.channels > 4 {
531	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
532	      "extract_char_image: 通道数必须在1-4之间",
533	    )
534	  }
535	  let expected_size = img.width * img.height * img.channels
536	  if img.data.length() != expected_size {	<-- UNCOVERED
537	    raise @types.LoadError::DecodeFailed(
538	      "extract_char_image: 数据大小不匹配，期望 " +
539	      expected_size.to_string() +	<-- UNCOVERED
540	      "，实际 " +
541	      img.data.length().to_string(),	<-- UNCOVERED
542	    )
543	  }
544	  if char.width <= 0 || char.height <= 0 {
545	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
546	      "extract_char_image: 字符尺寸必须大于0",
547	    )
548	  }
549	  if target_size <= 0 {
550	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
551	      "extract_char_image: target_size必须大于0",
552	    )
553	  }
554	  if padding < 0 {
555	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
556	      "extract_char_image: padding不能为负数",
557	    )
558	  }
559	
560	  let w = img.width
561	  let h = img.height
562	  let ch = img.channels
563	  let x_start = max(0, char.x_start - padding)	<-- UNCOVERED
564	  let x_end = min(w - 1, char.x_end + padding)
565	  let y_start = max(0, char.y_start - padding)
566	  let y_end = min(h - 1, char.y_end + padding)
567	  let src_w = x_end - x_start + 1
568	  let src_h = y_end - y_start + 1
569	  // 缩放到目标尺寸（保持宽高比，居中）
570	  let scale_w = target_size.to_double() / src_w.to_double()	<-- UNCOVERED
571	  let scale_h = target_size.to_double() / src_h.to_double()
572	  let scale = if scale_w < scale_h { scale_w } else { scale_h }
573	  let new_w = max(1, (src_w.to_double() * scale).to_int())
574	  let new_h = max(1, (src_h.to_double() * scale).to_int())
575	  let offset_x = (target_size - new_w) / 2
576	  let offset_y = (target_size - new_h) / 2
577	  let out : Array[Byte] = Array::make(target_size * target_size * ch, b'\x00')	<-- UNCOVERED
578	  let data = img.data
579	  // 最近邻缩放
580	  for y in 0..<new_h {
581	    for x in 0..<new_w {	<-- UNCOVERED
582	      let src_x = x_start + x * src_w / new_w
583	      let src_y = y_start + y * src_h / new_h
584	      for c in 0..<ch {
585	        out[((offset_y + y) * target_size + (offset_x + x)) * ch + c] = data[(	<-- UNCOVERED
586	            src_y * w + src_x
587	          ) *
588	          ch +
589	          c]
590	      }
591	    }
592	  }
593	  @types.Image::{
594	    width: target_size,
595	    height: target_size,
596	    channels: ch,
597	    data: Bytes::from_array(out),	<-- UNCOVERED
598	  }
599	}
   	…

92 uncovered line(s) in src\process\ocr\robustness_enhancement.mbt:

 91	pub fn select_adaptive_config(
 92	  quality : ScanQuality,
 93	) -> AdaptivePreprocessConfig raise @types.LoadError {
 94	  // 输入验证
 95	  if quality.overall_score < 0.0 || quality.overall_score > 100.0 {	<-- UNCOVERED
 96	    raise @types.LoadError::DecodeFailed(
 97	      "select_adaptive_config: 质量评分必须在0-100之间",
 98	    )
 99	  }
100	  let score = quality.overall_score
101	  if score >= 90.0 {
102	    AdaptivePreprocessConfig::{	<-- UNCOVERED
103	      quality_level: ScanQualityLevel::Excellent,
104	      denoise_strength: 1,
105	      contrast_enhance: 1.0,
106	      binarization_method: "otsu",
107	      deskew_enabled: false,
108	      sharpen_enabled: false,
109	      morph_open_enabled: false,
110	      morph_close_enabled: false,
111	      recommended_dpi: 200,
112	    }
113	  } else if score >= 75.0 {	<-- UNCOVERED
114	    AdaptivePreprocessConfig::{
115	      quality_level: ScanQualityLevel::Good,
116	      denoise_strength: 2,
117	      contrast_enhance: 1.2,
118	      binarization_method: "otsu",
119	      deskew_enabled: true,
120	      sharpen_enabled: false,
121	      morph_open_enabled: false,
122	      morph_close_enabled: true,
123	      recommended_dpi: 250,
124	    }
125	  } else if score >= 60.0 {	<-- UNCOVERED
126	    AdaptivePreprocessConfig::{
127	      quality_level: ScanQualityLevel::Fair,
128	      denoise_strength: 3,
129	      contrast_enhance: 1.5,
130	      binarization_method: "adaptive",
131	      deskew_enabled: true,
132	      sharpen_enabled: true,
133	      morph_open_enabled: true,
134	      morph_close_enabled: true,
135	      recommended_dpi: 300,
136	    }
137	  } else if score >= 40.0 {	<-- UNCOVERED
138	    AdaptivePreprocessConfig::{
139	      quality_level: ScanQualityLevel::Poor,
140	      denoise_strength: 4,
141	      contrast_enhance: 2.0,
142	      binarization_method: "sauvola",
143	      deskew_enabled: true,
144	      sharpen_enabled: true,
145	      morph_open_enabled: true,
146	      morph_close_enabled: true,
147	      recommended_dpi: 400,
148	    }
149	  } else {
150	    AdaptivePreprocessConfig::{	<-- UNCOVERED
151	      quality_level: ScanQualityLevel::VeryPoor,
152	      denoise_strength: 5,
153	      contrast_enhance: 2.5,
154	      binarization_method: "sauvola",
155	      deskew_enabled: true,
156	      sharpen_enabled: true,
157	      morph_open_enabled: true,
158	      morph_close_enabled: true,
159	      recommended_dpi: 600,
160	    }
161	  }
162	}
   	…

166	pub fn detect_skew_angle_robust(
167	  binary_img : @types.Image,
168	) -> Double raise @types.LoadError {
169	  // 输入验证
170	  if binary_img.width <= 0 || binary_img.height <= 0 {	<-- UNCOVERED
171	    raise @types.LoadError::DecodeFailed(
172	      "detect_skew_angle_robust: 图像尺寸必须大于0",
173	    )
174	  }
175	  if binary_img.channels != 1 {
176	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
177	      "detect_skew_angle_robust: 仅支持单通道二值图",
178	    )
179	  }
180	  let expected_size = binary_img.width * binary_img.height
181	  if binary_img.data.length() != expected_size {	<-- UNCOVERED
182	    raise @types.LoadError::DecodeFailed(
183	      "detect_skew_angle_robust: 数据大小不匹配，期望 " +
184	      expected_size.to_string() +	<-- UNCOVERED
185	      "，实际 " +
186	      binary_img.data.length().to_string(),	<-- UNCOVERED
187	    )
188	  }
189	  let w = binary_img.width
190	  let h = binary_img.height
191	  // 简化版：基于文本行的倾斜检测
192	  // 计算每行的黑色像素重心
193	  let mut total_x = 0
194	  let mut total_y = 0
195	  let mut count = 0
196	  for y in 0..<h {
197	    let mut row_black = 0	<-- UNCOVERED
198	    let mut row_x_sum = 0
199	    for x in 0..<w {
200	      if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
201	        row_black += 1
202	        row_x_sum += x
203	      }
204	    }
205	    if row_black > w / 10 {
206	      let center_x = row_x_sum / row_black	<-- UNCOVERED
207	      total_x += center_x
208	      total_y += y
209	      count += 1
210	    }
211	  }
212	  if count < 2 {
213	    return 0.0	<-- UNCOVERED
214	  }
215	  // 线性回归计算倾斜角度
216	  let _ = total_x.to_double() / count.to_double()	<-- UNCOVERED
217	  let _ = total_y.to_double() / count.to_double()
218	  let mut _numerator = 0.0
219	  let mut _denominator = 0.0
220	  // 简化：用首尾两行的重心差计算角度
221	  let mut first_y = h
222	  let mut first_x = 0
223	  let mut last_y = 0
224	  let mut last_x = 0
225	  for y in 0..<h {
226	    let mut row_black = 0	<-- UNCOVERED
227	    let mut row_x_sum = 0
228	    for x in 0..<w {
229	      if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
230	        row_black += 1
231	        row_x_sum += x
232	      }
233	    }
234	    if row_black > w / 10 {
235	      let center_x = row_x_sum / row_black	<-- UNCOVERED
236	      if y < first_y {
237	        first_y = y	<-- UNCOVERED
238	        first_x = center_x
239	      }
240	      if y > last_y {
241	        last_y = y	<-- UNCOVERED
242	        last_x = center_x
243	      }
244	    }
245	  }
246	  if last_y - first_y > 0 {
247	    let angle_rad = (last_x - first_x).to_double() /	<-- UNCOVERED
248	      (last_y - first_y).to_double()
249	    let angle_deg = angle_rad * 57.2958 // rad to deg
250	    if angle_deg > 45.0 {
251	      90.0 - angle_deg	<-- UNCOVERED
252	    } else if angle_deg < -45.0 {
253	      -90.0 - angle_deg
254	    } else {
255	      angle_deg	<-- UNCOVERED
256	    }
257	  } else {
258	    0.0	<-- UNCOVERED
259	  }
260	}
   	…

264	pub fn detect_distortions(
265	  img : @types.Image,
266	) -> Array[DistortionDetectionResult] raise @types.LoadError {
267	  // 输入验证
268	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
269	    raise @types.LoadError::DecodeFailed(
270	      "detect_distortions: 图像尺寸必须大于0",
271	    )
272	  }
273	  if img.channels < 1 || img.channels > 4 {
274	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
275	      "detect_distortions: 通道数必须在1-4之间",
276	    )
277	  }
278	  let expected_size = img.width * img.height * img.channels
279	  if img.data.length() != expected_size {	<-- UNCOVERED
280	    raise @types.LoadError::DecodeFailed(
281	      "detect_distortions: 数据大小不匹配，期望 " +
282	      expected_size.to_string() +	<-- UNCOVERED
283	      "，实际 " +
284	      img.data.length().to_string(),	<-- UNCOVERED
285	    )
286	  }
287	  let results : Array[DistortionDetectionResult] = []
288	  let w = img.width
289	  let h = img.height
290	
291	  // 1. 检测噪声
292	  let mut noise_count = 0
293	  let mut total_pixels = 0
294	  for y in 1..<(h - 1) {
295	    for x in 1..<(w - 1) {	<-- UNCOVERED
296	      let idx = y * w + x
297	      let center = img.data[idx].to_int()
298	      let neighbors = [
299	        img.data[idx - 1].to_int(),	<-- UNCOVERED
300	        img.data[idx + 1].to_int(),
301	        img.data[idx - w].to_int(),
302	        img.data[idx + w].to_int(),
303	      ]
304	      let mut neighbor_sum = 0
305	      for n in neighbors {
306	        neighbor_sum += n	<-- UNCOVERED
307	      }
308	      let neighbor_avg = neighbor_sum / 4
309	      if (center - neighbor_avg).abs() > 50 {	<-- UNCOVERED
310	        noise_count += 1
311	      }
312	      total_pixels += 1
313	    }
314	  }
315	  let noise_ratio = if total_pixels > 0 {
316	    noise_count.to_double() / total_pixels.to_double()	<-- UNCOVERED
317	  } else {
318	    0.0	<-- UNCOVERED
319	  }
320	  if noise_ratio > 0.01 {
321	    results.push(DistortionDetectionResult::{	<-- UNCOVERED
322	      distortion_type: DistortionType::Noise,
323	      severity: if noise_ratio > 0.1 {
324	        0.8	<-- UNCOVERED
325	      } else if noise_ratio > 0.05 {
326	        0.5
327	      } else {
328	        0.2	<-- UNCOVERED
329	      },
330	      angle: 0.0,
331	      confidence: 0.8,
332	      correction_params: [
333	        (
334	          "denoise_strength",
335	          if noise_ratio > 0.1 {
336	            5.0	<-- UNCOVERED
337	          } else if noise_ratio > 0.05 {
338	            3.0
339	          } else {
340	            2.0	<-- UNCOVERED
341	          },
342	        ),
343	      ],
344	    })
345	  }
346	
347	  // 2. 检测模糊（拉普拉斯方差）
348	  let mut laplacian_sum = 0.0
349	  let mut laplacian_count = 0
350	  for y in 1..<(h - 1) {
351	    for x in 1..<(w - 1) {	<-- UNCOVERED
352	      let idx = y * w + x
353	      let center = img.data[idx].to_double()
354	      let lap = 4.0 * center -
355	        img.data[idx - 1].to_double() -	<-- UNCOVERED
356	        img.data[idx + 1].to_double() -
357	        img.data[idx - w].to_double() -
358	        img.data[idx + w].to_double()
359	      laplacian_sum += lap * lap
360	      laplacian_count += 1
361	    }
362	  }
363	  let laplacian_var = if laplacian_count > 0 {
364	    laplacian_sum / laplacian_count.to_double()	<-- UNCOVERED
365	  } else {
366	    0.0	<-- UNCOVERED
367	  }
368	  if laplacian_var < 100.0 {
369	    results.push(DistortionDetectionResult::{	<-- UNCOVERED
370	      distortion_type: DistortionType::Blur,
371	      severity: if laplacian_var < 20.0 {
372	        0.9	<-- UNCOVERED
373	      } else if laplacian_var < 50.0 {
374	        0.6
375	      } else {
376	        0.3	<-- UNCOVERED
377	      },
378	      angle: 0.0,
379	      confidence: 0.75,
380	      correction_params: [
381	        (
382	          "sharpen_strength",
383	          if laplacian_var < 20.0 {
384	            3.0	<-- UNCOVERED
385	          } else if laplacian_var < 50.0 {
386	            2.0
387	          } else {
388	            1.0	<-- UNCOVERED
389	          },
390	        ),
391	      ],
392	    })
393	  }
394	
395	  // 3. 检测光照不均（四角亮度差）
396	  if w > 20 && h > 20 {
397	    let regions = [	<-- UNCOVERED
398	      (10, 10),
399	      (w - 10, 10),
400	      (10, h - 10),
401	      (w - 10, h - 10),
402	      (w / 2, h / 2),
403	    ]
404	    let brightnesses : Array[Double] = []
405	    for _tup0 in regions {
406	      let (rx, ry) = _tup0	<-- UNCOVERED
407	      let mut sum = 0.0
408	      let mut count = 0
409	      for dy in 0..<10 {
410	        for dx in 0..<10 {	<-- UNCOVERED
411	          if rx + dx < w && ry + dy < h {
412	            sum += img.data[(ry + dy) * w + (rx + dx)].to_double()
413	            count += 1
414	          }
415	        }
416	      }
417	      if count > 0 {
418	        brightnesses.push(sum / count.to_double())	<-- UNCOVERED
419	      }
420	    }
421	    if brightnesses.length() >= 4 {	<-- UNCOVERED
422	      let mut max_b = brightnesses[0]
423	      let mut min_b = brightnesses[0]
424	      for b in brightnesses {
425	        if b > max_b {	<-- UNCOVERED
426	          max_b = b
427	        }
428	        if b < min_b {
429	          min_b = b	<-- UNCOVERED
430	        }
431	      }
432	      let brightness_diff = max_b - min_b
433	      if brightness_diff > 50.0 {
434	        results.push(DistortionDetectionResult::{	<-- UNCOVERED
435	          distortion_type: DistortionType::UnevenLighting,
436	          severity: if brightness_diff > 100.0 {
437	            0.8	<-- UNCOVERED
438	          } else if brightness_diff > 75.0 {
439	            0.5
440	          } else {
441	            0.3	<-- UNCOVERED
442	          },
443	          angle: 0.0,
444	          confidence: 0.7,
445	          correction_params: [
446	            ("adaptive_threshold", 1.0),
447	            ("contrast_enhance", 1.5),
448	          ],
449	        })
450	      }
451	    }
452	  }
453	
454	  results
455	}
   	…

459	pub fn analyze_complex_layout(
460	  text_blocks : Array[TextBlock],
461	  page_width : Int,
462	  page_height : Int,
463	) -> ComplexLayoutAnalysis {
464	  ignore(page_height)	<-- UNCOVERED
465	  if text_blocks.length() == 0 {
466	    return ComplexLayoutAnalysis::{
467	      layout_type: ComplexLayoutType::Unknown,
468	      num_columns: 1,
469	      has_floating_elements: false,
470	      has_cross_page_elements: false,
471	      column_boundaries: [],
472	      reading_order_strategy: "default",
473	      confidence: 0.0,
474	    }
475	  }
476	
477	  // 统计水平覆盖范围
478	  let mut full_width_count = 0
479	  let mut left_count = 0
480	  let mut right_count = 0
481	  let mid_x = page_width / 2
482	
483	  for block in text_blocks {
484	    let center = block.x + block.width / 2	<-- UNCOVERED
485	    if block.width.to_double() > page_width.to_double() * 0.7 {
486	      full_width_count += 1
487	    } else if center < mid_x {
488	      left_count += 1
489	    } else {
490	      right_count += 1	<-- UNCOVERED
491	    }
492	  }
493	
494	  let total = text_blocks.length().to_double()	<-- UNCOVERED
495	  let full_ratio = full_width_count.to_double() / total
496	  let left_ratio = left_count.to_double() / total
497	  let right_ratio = right_count.to_double() / total
498	
499	  let layout_type = if full_ratio > 0.8 {
500	    ComplexLayoutType::SingleColumn	<-- UNCOVERED
501	  } else if left_ratio > 0.3 && right_ratio > 0.3 && full_ratio < 0.2 {
502	    ComplexLayoutType::DoubleColumn
503	  } else if full_ratio > 0.2 && (left_ratio > 0.2 || right_ratio > 0.2) {
504	    ComplexLayoutType::MixedLayout
505	  } else {
506	    ComplexLayoutType::SingleColumn	<-- UNCOVERED
507	  }
508	
509	  let num_columns = match layout_type {	<-- UNCOVERED
510	    ComplexLayoutType::DoubleColumn => 2
511	    ComplexLayoutType::TripleColumn => 3
512	    ComplexLayoutType::MultiColumnWithFloats => 3
513	    _ => 1
514	  }
515	
516	  let has_floats = full_ratio > 0.1 && full_ratio < 0.5
517	
518	  let reading_order_strategy = match layout_type {	<-- UNCOVERED
519	    ComplexLayoutType::SingleColumn => "top_to_bottom"
520	    ComplexLayoutType::DoubleColumn => "left_column_first"
521	    ComplexLayoutType::TripleColumn => "column_by_column"
522	    ComplexLayoutType::MixedLayout => "adaptive"
523	    _ => "default"
524	  }
525	
526	  let boundaries : Array[Int] = []
527	  boundaries.push(0)	<-- UNCOVERED
528	  if num_columns >= 2 {
529	    boundaries.push(page_width / 2 - 20)	<-- UNCOVERED
530	    boundaries.push(page_width / 2 + 20)
531	  }
532	  boundaries.push(page_width)	<-- UNCOVERED
533	
534	  ComplexLayoutAnalysis::{
535	    layout_type,
536	    num_columns,
537	    has_floating_elements: has_floats,
538	    has_cross_page_elements: false,
539	    column_boundaries: boundaries,
540	    reading_order_strategy,
541	    confidence: 0.75,
542	  }
543	}
   	…

547	pub fn generate_robustness_report(
548	  quality : ScanQuality,
549	  distortions : Array[DistortionDetectionResult],
550	  layout : ComplexLayoutAnalysis,
551	) -> RobustnessReport {
552	  // 质量分数
553	  let quality_score = quality.overall_score / 100.0	<-- UNCOVERED
554	
555	  // 畸变分数（畸变越少分数越高）
556	  let mut distortion_penalty = 0.0
557	  for d in distortions {
558	    distortion_penalty += d.severity * 0.2	<-- UNCOVERED
559	  }
560	  let distortion_score = if 1.0 - distortion_penalty > 0 {
561	    1.0 - distortion_penalty	<-- UNCOVERED
562	  } else {
563	    0.0	<-- UNCOVERED
564	  }
565	
566	  // 版面复杂度分数（越简单分数越高）
567	  let layout_score = match layout.layout_type {	<-- UNCOVERED
568	    ComplexLayoutType::SingleColumn => 1.0
569	    ComplexLayoutType::DoubleColumn => 0.8
570	    ComplexLayoutType::TripleColumn => 0.6
571	    ComplexLayoutType::MixedLayout => 0.5
572	    ComplexLayoutType::MultiColumnWithFloats => 0.4
573	    _ => 0.7
574	  }
575	
576	  // 噪声抵抗分数
577	  let mut noise_severity = 0.0
578	  for d in distortions {
579	    if d.distortion_type == DistortionType::Noise {	<-- UNCOVERED
580	      noise_severity = d.severity
581	    }
582	  }
583	  let noise_score = 1.0 - noise_severity
584	
585	  // 综合分数
586	  let overall = quality_score * 0.3 +
587	    distortion_score * 0.3 +
588	    layout_score * 0.2 +
589	    noise_score * 0.2
590	
591	  // 建议
592	  let recommendations : Array[String] = []
593	  if quality_score < 0.6 {
594	    recommendations.push("建议提高扫描分辨率至 300 DPI 以上")	<-- UNCOVERED
595	  }
596	  if noise_severity > 0.3 {
597	    recommendations.push("建议启用强去噪处理（中值滤波+NLM）")	<-- UNCOVERED
598	  }
599	  if layout.num_columns > 1 {
600	    recommendations.push(	<-- UNCOVERED
601	      "检测到多栏布局，建议使用分栏阅读顺序",
602	    )
603	  }
604	  if distortion_penalty > 0.3 {
605	    recommendations.push(	<-- UNCOVERED
606	      "检测到严重畸变，建议启用完整预处理流水线",
607	    )
608	  }
609	  if recommendations.length() == 0 {	<-- UNCOVERED
610	    recommendations.push("文档质量良好，可使用标准预处理流程")
611	  }
612	
613	  // 预估准确率
614	  let estimated_accuracy = overall * 0.95 + 0.05
615	
616	  let processing_recommendation = if overall > 0.8 {
617	    "标准处理流程"	<-- UNCOVERED
618	  } else if overall > 0.6 {
619	    "增强处理流程"
620	  } else {
621	    "重度预处理+人工复核"	<-- UNCOVERED
622	  }
623	
624	  RobustnessReport::{
625	    overall_score: overall,
626	    quality_score,
627	    distortion_score,
628	    layout_complexity_score: layout_score,
629	    noise_resistance_score: noise_score,
630	    recommendations,
631	    estimated_accuracy,
632	    processing_recommendation,
633	  }
634	}
   	…

638	pub fn robustness_report_to_string(report : RobustnessReport) -> String {
639	  let mut output = "## OCR 鲁棒性评估报告\n\n"	<-- UNCOVERED
640	  output = output + "### 综合评分\n\n"
641	  output = output + "| 指标 | 分数 |\n"
642	  output = output + "|------|------|\n"
643	  output = output +
644	    "| **综合评分** | **" +
645	    (report.overall_score * 100.0).to_string() +	<-- UNCOVERED
646	    "/100** |\n"
647	  output = output +
648	    "| 扫描质量 | " +
649	    (report.quality_score * 100.0).to_string() +	<-- UNCOVERED
650	    "/100 |\n"
651	  output = output +
652	    "| 畸变控制 | " +
653	    (report.distortion_score * 100.0).to_string() +	<-- UNCOVERED
654	    "/100 |\n"
655	  output = output +
656	    "| 版面复杂度 | " +
657	    (report.layout_complexity_score * 100.0).to_string() +	<-- UNCOVERED
658	    "/100 |\n"
659	  output = output +
660	    "| 噪声抵抗 | " +
661	    (report.noise_resistance_score * 100.0).to_string() +	<-- UNCOVERED
662	    "/100 |\n\n"
663	  output = output +
664	    "### 预估识别准确率: " +
665	    (report.estimated_accuracy * 100.0).to_string() +	<-- UNCOVERED
666	    "%\n\n"
667	  output = output +
668	    "### 推荐处理方式: " +
669	    report.processing_recommendation +
670	    "\n\n"
671	  output = output + "### 改进建议\n\n"
672	  for i in 0..<report.recommendations.length() {	<-- UNCOVERED
673	    output = output +
674	      (i + 1).to_string() +
675	      ". " +
676	      report.recommendations[i] +
677	      "\n"
678	  }
679	  output
680	}
   	…

73 uncovered line(s) in src\process\ocr\scan_document_ocr.mbt:

 69	pub fn evaluate_scan_quality(
 70	  img : @types.Image,
 71	) -> ScanQuality raise @types.LoadError {
 72	  // 输入验证
 73	  if img.width <= 0 || img.height <= 0 {
 74	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 75	      "evaluate_scan_quality: 图像尺寸必须大于0",
 76	    )
 77	  }
 78	  if img.channels < 1 || img.channels > 4 {
 79	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 80	      "evaluate_scan_quality: 通道数必须在1-4之间",
 81	    )
 82	  }
 83	  let expected_size = img.width * img.height * img.channels
 84	  if img.data.length() != expected_size {
 85	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 86	      "evaluate_scan_quality: 数据大小不匹配，期望 " +
 87	      expected_size.to_string() +	<-- UNCOVERED
 88	      "，实际 " +
 89	      img.data.length().to_string(),	<-- UNCOVERED
 90	    )
 91	  }
 92	  let w = img.width
 93	  let h = img.height
 94	  let channels = img.channels
 95	  // 计算亮度和对比度
 96	  let mut sum = 0.0
 97	  let mut sum_sq = 0.0
 98	  let total = (w * h).to_double()
 99	  for y in 0..<h {
100	    for x in 0..<w {
101	      let idx = (y * w + x) * channels
102	      let gray = if channels >= 3 {
103	        0.299 * img.data[idx].to_double() +
104	        0.587 * img.data[idx + 1].to_double() +
105	        0.114 * img.data[idx + 2].to_double()
106	      } else {
107	        img.data[idx].to_double()	<-- UNCOVERED
108	      }
109	      sum += gray
110	      sum_sq += gray * gray
111	    }
112	  }
113	  let brightness = sum / total
114	  let variance = sum_sq / total - brightness * brightness
115	  let contrast = if variance > 0.0 { variance.sqrt() } else { 0.0 }
116	  // 计算锐度（拉普拉斯算子简化版）
117	  let mut sharpness_sum = 0.0
118	  let mut sharpness_count = 0
119	  for y in 1..<(h - 1) {
120	    for x in 1..<(w - 1) {
121	      let idx = (y * w + x) * channels
122	      let center = if channels >= 3 {
123	        0.299 * img.data[idx].to_double() +
124	        0.587 * img.data[idx + 1].to_double() +
125	        0.114 * img.data[idx + 2].to_double()
126	      } else {
127	        img.data[idx].to_double()	<-- UNCOVERED
128	      }
129	      let up = if channels >= 3 {
130	        0.299 * img.data[((y - 1) * w + x) * channels].to_double() +
131	        0.587 * img.data[((y - 1) * w + x) * channels + 1].to_double() +
132	        0.114 * img.data[((y - 1) * w + x) * channels + 2].to_double()
133	      } else {
134	        img.data[((y - 1) * w + x) * channels].to_double()	<-- UNCOVERED
135	      }
136	      let down = if channels >= 3 {
137	        0.299 * img.data[((y + 1) * w + x) * channels].to_double() +
138	        0.587 * img.data[((y + 1) * w + x) * channels + 1].to_double() +
139	        0.114 * img.data[((y + 1) * w + x) * channels + 2].to_double()
140	      } else {
141	        img.data[((y + 1) * w + x) * channels].to_double()	<-- UNCOVERED
142	      }
143	      let left = if channels >= 3 {
144	        0.299 * img.data[(y * w + x - 1) * channels].to_double() +
145	        0.587 * img.data[(y * w + x - 1) * channels + 1].to_double() +
146	        0.114 * img.data[(y * w + x - 1) * channels + 2].to_double()
147	      } else {
148	        img.data[(y * w + x - 1) * channels].to_double()	<-- UNCOVERED
149	      }
150	      let right = if channels >= 3 {
151	        0.299 * img.data[(y * w + x + 1) * channels].to_double() +
152	        0.587 * img.data[(y * w + x + 1) * channels + 1].to_double() +
153	        0.114 * img.data[(y * w + x + 1) * channels + 2].to_double()
154	      } else {
155	        img.data[(y * w + x + 1) * channels].to_double()	<-- UNCOVERED
156	      }
157	      let laplacian = 4.0 * center - up - down - left - right
158	      sharpness_sum += laplacian * laplacian
159	      sharpness_count += 1
160	    }
161	  }
162	  let sharpness = if sharpness_count > 0 {
163	    sharpness_sum / sharpness_count.to_double()
164	  } else {
165	    0.0	<-- UNCOVERED
166	  }
167	  // 估计噪声水平（基于平坦区域的方差）
168	  let noise_level = if contrast > 0 { (contrast * 0.1).min(50.0) } else { 10.0 }
169	  // 估计倾斜角度（简化版：基于水平投影的方差）
170	  let skew_angle = 0.0 // 简化，实际需要霍夫变换
171	  // 综合质量评分
172	  let mut score = 50.0
173	  // 亮度评分（理想 128）
174	  let brightness_score = 100.0 - (brightness - 128.0).abs() * 0.5
175	  score += brightness_score * 0.2
176	  // 对比度评分（理想 > 50）
177	  let contrast_score = if contrast > 50.0 { 100.0 } else { contrast * 2.0 }
178	  score += contrast_score * 0.3
179	  // 锐度评分
180	  let sharpness_score = if sharpness > 100.0 { 100.0 } else { sharpness }
181	  score += sharpness_score * 0.3
182	  // 噪声评分
183	  let noise_score = if noise_level < 10.0 {
184	    100.0
185	  } else {
186	    (100.0 - noise_level).max(0.0)	<-- UNCOVERED
187	  }
188	  score += noise_score * 0.2
189	  // 检测问题
190	  let issues : Array[String] = []
191	  if brightness < 50.0 {
192	    issues.push("图像过暗")	<-- UNCOVERED
193	  }
194	  if brightness > 200.0 {
195	    issues.push("图像过亮")
196	  }
197	  if contrast < 30.0 {
198	    issues.push("对比度不足")	<-- UNCOVERED
199	  }
200	  if sharpness < 20.0 {
201	    issues.push("图像模糊")	<-- UNCOVERED
202	  }
203	  if noise_level > 30.0 {
204	    issues.push("噪声较高")	<-- UNCOVERED
205	  }
206	  ScanQuality::{
207	    brightness,
208	    contrast,
209	    sharpness,
210	    noise_level,
211	    skew_angle,
212	    overall_score: score.min(100.0).max(0.0),
213	    issues,
214	  }
215	}
   	…

219	pub fn scan_document_preprocess(
220	  img : @types.Image,
221	  config : ScanOCRConfig,
222	) -> @types.Image raise @types.LoadError {
223	  // 输入验证
224	  if img.width <= 0 || img.height <= 0 {
225	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
226	      "scan_document_preprocess: 图像尺寸必须大于0",
227	    )
228	  }
229	  if img.channels < 1 || img.channels > 4 {
230	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
231	      "scan_document_preprocess: 通道数必须在1-4之间",
232	    )
233	  }
234	  let expected_size = img.width * img.height * img.channels
235	  if img.data.length() != expected_size {
236	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
237	      "scan_document_preprocess: 数据大小不匹配，期望 " +
238	      expected_size.to_string() +	<-- UNCOVERED
239	      "，实际 " +
240	      img.data.length().to_string(),	<-- UNCOVERED
241	    )
242	  }
243	  let mut result = img
244	  // 1. 去噪（中值滤波简化版）
245	  if config.denoise {
246	    result = denoise_scan(result)
247	  }
248	  // 2. 对比度增强
249	  result = enhance_contrast_scan(result)
250	  // 3. 二值化
251	  result = match config.binarization_method {
252	    "otsu" => binarize_otsu_scan(result)
253	    "adaptive" => binarize_adaptive_scan(result)	<-- UNCOVERED
254	    "sauvola" => binarize_sauvola_scan(result)
255	    _ => binarize_otsu_scan(result)
256	  }
257	  result
258	}
   	…

316	fn enhance_contrast_scan(img : @types.Image) -> @types.Image {
317	  let w = img.width
318	  let h = img.height
319	  let channels = img.channels
320	  // 计算最小值和最大值（忽略 1% 极值）
321	  let histogram : Array[Int] = []
322	  for _ in 0..<256 {
323	    histogram.push(0)
324	  }
325	  for y in 0..<h {
326	    for x in 0..<w {
327	      let idx = (y * w + x) * channels
328	      let gray = if channels >= 3 {
329	        (0.299 * img.data[idx].to_double() +
330	        0.587 * img.data[idx + 1].to_double() +
331	        0.114 * img.data[idx + 2].to_double()).to_int()
332	      } else {
333	        img.data[idx].to_int()	<-- UNCOVERED
334	      }
335	      histogram[gray] += 1
336	    }
337	  }
338	  let total = w * h
339	  let lower_count = total / 100
340	  let upper_count = total - lower_count
341	  let mut min_val = 0
342	  let mut max_val = 255
343	  let mut cum = 0
344	  for i in 0..<256 {
345	    cum += histogram[i]
346	    if cum >= lower_count && min_val == 0 {
347	      min_val = i
348	    }
349	    if cum >= upper_count {
350	      max_val = i
351	      break
352	    }
353	  }
354	  // 应用拉伸
355	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
356	  let range = (max_val - min_val).max(1)
357	  for y in 0..<h {
358	    for x in 0..<w {
359	      let idx = (y * w + x) * channels
360	      for c in 0..<channels {
361	        let val = img.data[idx + c].to_int()
362	        let stretched = ((val - min_val) * 255 / range).min(255).max(0)
363	        out_data[idx + c] = stretched.to_byte()
364	      }
365	    }
366	  }
367	  @types.Image::{
368	    width: w,
369	    height: h,
370	    channels,
371	    data: Bytes::from_array(out_data),
372	  }
373	}
   	…

377	fn binarize_otsu_scan(img : @types.Image) -> @types.Image {
378	  let w = img.width
379	  let h = img.height
380	  let channels = img.channels
381	  // 计算直方图
382	  let histogram : Array[Int] = []
383	  for _ in 0..<256 {
384	    histogram.push(0)
385	  }
386	  for y in 0..<h {
387	    for x in 0..<w {
388	      let idx = (y * w + x) * channels
389	      let gray = if channels >= 3 {
390	        (0.299 * img.data[idx].to_double() +
391	        0.587 * img.data[idx + 1].to_double() +
392	        0.114 * img.data[idx + 2].to_double()).to_int()
393	      } else {
394	        img.data[idx].to_int()	<-- UNCOVERED
395	      }
396	      histogram[gray] += 1
397	    }
398	  }
399	  // Otsu 阈值计算
400	  let total = w * h
401	  let mut sum_total = 0.0
402	  for i in 0..<256 {
403	    sum_total += i.to_double() * histogram[i].to_double()
404	  }
405	  let mut sum_bg = 0.0
406	  let mut weight_bg = 0
407	  let mut max_variance = 0.0
408	  let mut threshold = 128
409	  for i in 0..<256 {
410	    weight_bg += histogram[i]
411	    if weight_bg == 0 {
412	      continue	<-- UNCOVERED
413	    }
414	    let weight_fg = total - weight_bg
415	    if weight_fg == 0 {
416	      break
417	    }
418	    sum_bg += i.to_double() * histogram[i].to_double()
419	    let mean_bg = sum_bg / weight_bg.to_double()
420	    let mean_fg = (sum_total - sum_bg) / weight_fg.to_double()
421	    let variance = weight_bg.to_double() *
422	      weight_fg.to_double() *
423	      (mean_bg - mean_fg) *
424	      (mean_bg - mean_fg)
425	    if variance > max_variance {
426	      max_variance = variance
427	      threshold = i
428	    }
429	  }
430	  // 应用二值化
431	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
432	  for y in 0..<h {
433	    for x in 0..<w {
434	      let idx = (y * w + x) * channels
435	      let gray = if channels >= 3 {
436	        (0.299 * img.data[idx].to_double() +
437	        0.587 * img.data[idx + 1].to_double() +
438	        0.114 * img.data[idx + 2].to_double()).to_int()
439	      } else {
440	        img.data[idx].to_int()	<-- UNCOVERED
441	      }
442	      out_data[y * w + x] = if gray > threshold { b'\xFF' } else { b'\x00' }
443	    }
444	  }
445	  @types.Image::{
446	    width: w,
447	    height: h,
448	    channels: 1,
449	    data: Bytes::from_array(out_data),
450	  }
451	}
   	…

455	fn binarize_adaptive_scan(img : @types.Image) -> @types.Image {
456	  let w = img.width	<-- UNCOVERED
457	  let h = img.height
458	  let channels = img.channels
459	  let block_size = 15
460	  let c = 10
461	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')	<-- UNCOVERED
462	  for y in 0..<h {
463	    for x in 0..<w {	<-- UNCOVERED
464	      // 计算局部均值
465	      let mut sum = 0	<-- UNCOVERED
466	      let mut count = 0
467	      for dy_idx in 0..<block_size {
468	        let dy = dy_idx - block_size / 2	<-- UNCOVERED
469	        for dx_idx in 0..<block_size {
470	          let dx = dx_idx - block_size / 2	<-- UNCOVERED
471	          let ny = y + dy
472	          let nx = x + dx
473	          if ny >= 0 && ny < h && nx >= 0 && nx < w {
474	            let idx = (ny * w + nx) * channels	<-- UNCOVERED
475	            let gray = if channels >= 3 {
476	              (0.299 * img.data[idx].to_double() +	<-- UNCOVERED
477	              0.587 * img.data[idx + 1].to_double() +
478	              0.114 * img.data[idx + 2].to_double()).to_int()
479	            } else {
480	              img.data[idx].to_int()	<-- UNCOVERED
481	            }
482	            sum += gray
483	            count += 1
484	          }
485	        }
486	      }
487	      let local_mean = if count > 0 { sum / count } else { 128 }	<-- UNCOVERED
488	      let idx = (y * w + x) * channels
489	      let gray = if channels >= 3 {
490	        (0.299 * img.data[idx].to_double() +	<-- UNCOVERED
491	        0.587 * img.data[idx + 1].to_double() +
492	        0.114 * img.data[idx + 2].to_double()).to_int()
493	      } else {
494	        img.data[idx].to_int()	<-- UNCOVERED
495	      }
496	      out_data[y * w + x] = if gray > local_mean - c {
497	        b'\xFF'	<-- UNCOVERED
498	      } else {
499	        b'\x00'	<-- UNCOVERED
500	      }
501	    }
502	  }
503	  @types.Image::{
504	    width: w,
505	    height: h,
506	    channels: 1,
507	    data: Bytes::from_array(out_data),	<-- UNCOVERED
508	  }
509	}
   	…

513	fn binarize_sauvola_scan(img : @types.Image) -> @types.Image {
514	  let w = img.width	<-- UNCOVERED
515	  let h = img.height
516	  let channels = img.channels
517	  let window_size = 15
518	  let k = 0.2
519	  let r = 128.0
520	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')	<-- UNCOVERED
521	  for y in 0..<h {
522	    for x in 0..<w {	<-- UNCOVERED
523	      // 计算局部均值和标准差
524	      let mut sum = 0.0	<-- UNCOVERED
525	      let mut sum_sq = 0.0
526	      let mut count = 0
527	      for dy_idx in 0..<window_size {
528	        let dy = dy_idx - window_size / 2	<-- UNCOVERED
529	        for dx_idx in 0..<window_size {
530	          let dx = dx_idx - window_size / 2	<-- UNCOVERED
531	          let ny = y + dy
532	          let nx = x + dx
533	          if ny >= 0 && ny < h && nx >= 0 && nx < w {
534	            let idx = (ny * w + nx) * channels	<-- UNCOVERED
535	            let gray = if channels >= 3 {
536	              0.299 * img.data[idx].to_double() +	<-- UNCOVERED
537	              0.587 * img.data[idx + 1].to_double() +
538	              0.114 * img.data[idx + 2].to_double()
539	            } else {
540	              img.data[idx].to_double()	<-- UNCOVERED
541	            }
542	            sum += gray
543	            sum_sq += gray * gray
544	            count += 1
545	          }
546	        }
547	      }
548	      let mean = if count > 0 { sum / count.to_double() } else { 128.0 }	<-- UNCOVERED
549	      let variance = if count > 0 {
550	        sum_sq / count.to_double() - mean * mean	<-- UNCOVERED
551	      } else {
552	        0.0	<-- UNCOVERED
553	      }
554	      let std = if variance > 0.0 { variance.sqrt() } else { 0.0 }	<-- UNCOVERED
555	      // Sauvola 阈值
556	      let threshold = mean * (1.0 + k * (std / r - 1.0))
557	      let idx = (y * w + x) * channels
558	      let gray = if channels >= 3 {
559	        0.299 * img.data[idx].to_double() +	<-- UNCOVERED
560	        0.587 * img.data[idx + 1].to_double() +
561	        0.114 * img.data[idx + 2].to_double()
562	      } else {
563	        img.data[idx].to_double()	<-- UNCOVERED
564	      }
565	      out_data[y * w + x] = if gray > threshold { b'\xFF' } else { b'\x00' }	<-- UNCOVERED
566	    }
567	  }
568	  @types.Image::{
569	    width: w,
570	    height: h,
571	    channels: 1,
572	    data: Bytes::from_array(out_data),	<-- UNCOVERED
573	  }
574	}
   	…

578	pub fn analyze_layout_scan(
579	  binary_img : @types.Image,
580	) -> Array[TextBlock] raise @types.LoadError {
581	  // 输入验证
582	  if binary_img.width <= 0 || binary_img.height <= 0 {
583	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
584	      "analyze_layout_scan: 图像尺寸必须大于0",
585	    )
586	  }
587	  if binary_img.channels != 1 {
588	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
589	      "analyze_layout_scan: 仅支持单通道二值图",
590	    )
591	  }
592	  let expected_size = binary_img.width * binary_img.height
593	  if binary_img.data.length() != expected_size {
594	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
595	      "analyze_layout_scan: 数据大小不匹配，期望 " +
596	      expected_size.to_string() +	<-- UNCOVERED
597	      "，实际 " +
598	      binary_img.data.length().to_string(),	<-- UNCOVERED
599	    )
600	  }
601	  let w = binary_img.width
602	  let h = binary_img.height
603	  // 水平投影（检测文本行）
604	  let horizontal_proj : Array[Int] = []
605	  for y in 0..<h {
606	    let mut count = 0
607	    for x in 0..<w {
608	      if binary_img.data[y * w + x] == b'\x00' {
609	        count += 1
610	      }
611	    }
612	    horizontal_proj.push(count)
613	  }
614	  // 检测文本行区域
615	  let blocks : Array[TextBlock] = []
616	  let mut in_line = false
617	  let mut line_start = 0
618	  let mut line_end = 0
619	  let mut reading_order = 0
620	  for y in 0..<h {
621	    if horizontal_proj[y] > w / 50 {
622	      if !in_line {
623	        line_start = y
624	        in_line = true
625	      }
626	      line_end = y
627	    } else if in_line {
628	      // 检测这一行的文本范围
629	      let mut min_x = w
630	      let mut max_x = 0
631	      for yy in line_start..<=line_end {
632	        for x in 0..<w {
633	          if binary_img.data[yy * w + x] == b'\x00' {
634	            if x < min_x {
635	              min_x = x
636	            }
637	            if x > max_x {
638	              max_x = x
639	            }
640	          }
641	        }
642	      }
643	      if max_x > min_x {
644	        blocks.push(TextBlock::{
645	          x: min_x,
646	          y: line_start,
647	          width: max_x - min_x + 1,
648	          height: line_end - line_start + 1,
649	          block_type: "text",
650	          text: "",
651	          confidence: 0.0,
652	          reading_order,
653	        })
654	        reading_order += 1
655	      }
656	      in_line = false
657	    }
658	  }
659	  blocks
660	}
   	…

664	pub fn scan_ocr_recognize(
665	  img : @types.Image,
666	  config : ScanOCRConfig,
667	) -> OCRPageResult raise @types.LoadError {
668	  // 输入验证
669	  if img.width <= 0 || img.height <= 0 {
670	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
671	      "scan_ocr_recognize: 图像尺寸必须大于0",
672	    )
673	  }
674	  if img.channels < 1 || img.channels > 4 {
675	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
676	      "scan_ocr_recognize: 通道数必须在1-4之间",
677	    )
678	  }
679	  let expected_size = img.width * img.height * img.channels
680	  if img.data.length() != expected_size {
681	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
682	      "scan_ocr_recognize: 数据大小不匹配，期望 " +
683	      expected_size.to_string() +	<-- UNCOVERED
684	      "，实际 " +
685	      img.data.length().to_string(),	<-- UNCOVERED
686	    )
687	  }
688	  let _ = 0.0 // 简化，实际需要计时
689	  // 1. 预处理
690	  let preprocessed = scan_document_preprocess(img, config)
691	  // 2. 版面分析
692	  let blocks = if config.layout_analysis {
693	    analyze_layout_scan(preprocessed)
694	  } else {
695	    let single_block : Array[TextBlock] = []	<-- UNCOVERED
696	    single_block.push(TextBlock::{
697	      x: 0,
698	      y: 0,
699	      width: img.width,
700	      height: img.height,
701	      block_type: "text",
702	      text: "",
703	      confidence: 0.0,
704	      reading_order: 0,
705	    })
706	    single_block
707	  }
708	  // 3. 对每个文本块进行识别（简化版：返回占位文本）
709	  // 实际应该调用对应的 OCR 识别函数
710	  let mut full_text = ""
711	  let mut total_confidence = 0.0
712	  let recognized_blocks : Array[TextBlock] = []
713	  for i in 0..<blocks.length() {
714	    // 简化版识别：返回块的位置信息
715	    let block_text = "[文本块 " +
716	      i.to_string() +
717	      ": " +
718	      blocks[i].width.to_string() +
719	      "x" +
720	      blocks[i].height.to_string() +
721	      "]"
722	    let new_block = TextBlock::{
723	      x: blocks[i].x,
724	      y: blocks[i].y,
725	      width: blocks[i].width,
726	      height: blocks[i].height,
727	      block_type: blocks[i].block_type,
728	      text: block_text,
729	      confidence: 0.85,
730	      reading_order: blocks[i].reading_order,
731	    }
732	    recognized_blocks.push(new_block)
733	    full_text = full_text + block_text + "\n"
734	    total_confidence += new_block.confidence
735	  }
736	  let avg_confidence = if recognized_blocks.length() > 0 {
737	    total_confidence / recognized_blocks.length().to_double()
738	  } else {
739	    0.0	<-- UNCOVERED
740	  }
741	  OCRPageResult::{
742	    page_number: 1,
743	    width: img.width,
744	    height: img.height,
745	    blocks: recognized_blocks,
746	    full_text,
747	    average_confidence: avg_confidence,
748	    processing_time_ms: 0.0,
749	  }
750	}
   	…

754	pub fn batch_scan_ocr(
755	  images : Array[@types.Image],
756	  config : ScanOCRConfig,
757	) -> Array[OCRPageResult] raise @types.LoadError {
758	  // 输入验证
759	  if images.length() == 0 {
760	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
761	      "batch_scan_ocr: 图像列表不能为空",
762	    )
763	  }
764	  for i in 0..<images.length() {
765	    if images[i].width <= 0 || images[i].height <= 0 {
766	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
767	        "batch_scan_ocr: 第 " + i.to_string() + " 张图像尺寸必须大于0",
768	      )
769	    }
770	  }
771	  let results : Array[OCRPageResult] = []
772	  for i in 0..<images.length() {
773	    let result = scan_ocr_recognize(images[i], config)
774	    let new_result = OCRPageResult::{
775	      page_number: i + 1,
776	      width: result.width,
777	      height: result.height,
778	      blocks: result.blocks,
779	      full_text: result.full_text,
780	      average_confidence: result.average_confidence,
781	      processing_time_ms: result.processing_time_ms,
782	    }
783	    results.push(new_result)
784	  }
785	  results
786	}
   	…

99 uncovered line(s) in src\process\ocr\table_image_extraction.mbt:

 64	pub fn detect_horizontal_table_lines(
 65	  binary_img : @types.Image,
 66	  min_length? : Int = 50,
 67	  max_gap? : Int = 3,
 68	) -> Array[(Int, Int, Int)] raise @types.LoadError {
 69	  // 输入验证
 70	  if binary_img.width <= 0 || binary_img.height <= 0 {	<-- UNCOVERED
 71	    raise @types.LoadError::DecodeFailed(
 72	      "detect_horizontal_table_lines: 图像尺寸必须大于0",
 73	    )
 74	  }
 75	  if binary_img.channels != 1 {
 76	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 77	      "detect_horizontal_table_lines: 仅支持单通道二值图",
 78	    )
 79	  }
 80	  let expected_size = binary_img.width * binary_img.height
 81	  if binary_img.data.length() != expected_size {	<-- UNCOVERED
 82	    raise @types.LoadError::DecodeFailed(
 83	      "detect_horizontal_table_lines: 数据大小不匹配，期望 " +
 84	      expected_size.to_string() +	<-- UNCOVERED
 85	      "，实际 " +
 86	      binary_img.data.length().to_string(),	<-- UNCOVERED
 87	    )
 88	  }
 89	  if min_length <= 0 {
 90	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 91	      "detect_horizontal_table_lines: 最小线长必须大于0",
 92	    )
 93	  }
 94	  if max_gap < 0 {
 95	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 96	      "detect_horizontal_table_lines: 最大间隙不能为负",
 97	    )
 98	  }
 99	  let min_len = min_length
100	  let max_g = max_gap
101	  let w = binary_img.width
102	  let h = binary_img.height
103	  let lines : Array[(Int, Int, Int)] = []
104	  for y in 0..<h {
105	    let mut x = 0	<-- UNCOVERED
106	    while x < w {
107	      if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
108	        let start_x = x
109	        let mut end_x = x
110	        let mut gap = 0
111	        while x < w && gap <= max_g {
112	          if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
113	            end_x = x
114	            gap = 0
115	          } else {
116	            gap += 1	<-- UNCOVERED
117	          }
118	          x += 1
119	        }
120	        let length = end_x - start_x + 1
121	        if length >= min_len {
122	          lines.push((y, start_x, end_x))	<-- UNCOVERED
123	        }
124	      } else {
125	        x += 1	<-- UNCOVERED
126	      }
127	    }
128	  }
129	  // 合并相近的线（y 差 <= 2）
130	  let merged : Array[(Int, Int, Int)] = []
131	  for line in lines {
132	    let mut found = false	<-- UNCOVERED
133	    for i in 0..<merged.length() {
134	      let (my, msx, mex) = merged[i]
135	      if (line.0 - my).abs() <= 2 {
136	        // 合并
137	        let new_y = (line.0 + my) / 2	<-- UNCOVERED
138	        let new_sx = if line.1 < msx { line.1 } else { msx }
139	        let new_ex = if line.2 > mex { line.2 } else { mex }
140	        merged[i] = (new_y, new_sx, new_ex)
141	        found = true
142	        break
143	      }
144	    }
145	    if !found {
146	      merged.push(line)	<-- UNCOVERED
147	    }
148	  }
149	  merged
150	}
   	…

154	pub fn detect_vertical_table_lines(
155	  binary_img : @types.Image,
156	  min_length? : Int = 50,
157	  max_gap? : Int = 3,
158	) -> Array[(Int, Int, Int)] raise @types.LoadError {
159	  // 输入验证
160	  if binary_img.width <= 0 || binary_img.height <= 0 {	<-- UNCOVERED
161	    raise @types.LoadError::DecodeFailed(
162	      "detect_vertical_table_lines: 图像尺寸必须大于0",
163	    )
164	  }
165	  if binary_img.channels != 1 {
166	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
167	      "detect_vertical_table_lines: 仅支持单通道二值图",
168	    )
169	  }
170	  let expected_size = binary_img.width * binary_img.height
171	  if binary_img.data.length() != expected_size {	<-- UNCOVERED
172	    raise @types.LoadError::DecodeFailed(
173	      "detect_vertical_table_lines: 数据大小不匹配，期望 " +
174	      expected_size.to_string() +	<-- UNCOVERED
175	      "，实际 " +
176	      binary_img.data.length().to_string(),	<-- UNCOVERED
177	    )
178	  }
179	  if min_length <= 0 {
180	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
181	      "detect_vertical_table_lines: 最小线长必须大于0",
182	    )
183	  }
184	  if max_gap < 0 {
185	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
186	      "detect_vertical_table_lines: 最大间隙不能为负",
187	    )
188	  }
189	  let min_len = min_length
190	  let max_g = max_gap
191	  let w = binary_img.width
192	  let h = binary_img.height
193	  let lines : Array[(Int, Int, Int)] = []
194	  for x in 0..<w {
195	    let mut y = 0	<-- UNCOVERED
196	    while y < h {
197	      if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
198	        let start_y = y
199	        let mut end_y = y
200	        let mut gap = 0
201	        while y < h && gap <= max_g {
202	          if binary_img.data[y * w + x].to_int() < 128 {	<-- UNCOVERED
203	            end_y = y
204	            gap = 0
205	          } else {
206	            gap += 1	<-- UNCOVERED
207	          }
208	          y += 1
209	        }
210	        let length = end_y - start_y + 1
211	        if length >= min_len {
212	          lines.push((x, start_y, end_y))	<-- UNCOVERED
213	        }
214	      } else {
215	        y += 1	<-- UNCOVERED
216	      }
217	    }
218	  }
219	  // 合并相近的线
220	  let merged : Array[(Int, Int, Int)] = []
221	  for line in lines {
222	    let mut found = false	<-- UNCOVERED
223	    for i in 0..<merged.length() {
224	      let (mx, msy, mey) = merged[i]
225	      if (line.0 - mx).abs() <= 2 {
226	        let new_x = (line.0 + mx) / 2
227	        let new_sy = if line.1 < msy { line.1 } else { msy }
228	        let new_ey = if line.2 > mey { line.2 } else { mey }
229	        merged[i] = (new_x, new_sy, new_ey)
230	        found = true
231	        break
232	      }
233	    }
234	    if !found {
235	      merged.push(line)	<-- UNCOVERED
236	    }
237	  }
238	  merged
239	}
   	…

243	pub fn find_line_intersections(
244	  h_lines : Array[(Int, Int, Int)],
245	  v_lines : Array[(Int, Int, Int)],
246	) -> Array[(Int, Int)] {
247	  let intersections : Array[(Int, Int)] = []	<-- UNCOVERED
248	  for _tup0 in h_lines {
249	    let (hy, hsx, hex) = _tup0	<-- UNCOVERED
250	    for _tup1 in v_lines {
251	      let (vx, vsy, vey) = _tup1	<-- UNCOVERED
252	      if vx >= hsx && vx <= hex && hy >= vsy && hy <= vey {
253	        intersections.push((vx, hy))	<-- UNCOVERED
254	      }
255	    }
256	  }
257	  intersections
258	}
   	…

262	pub fn detect_table_enhanced(
263	  binary_img : @types.Image,
264	  min_table_size? : Int = 100,
265	) -> Array[EnhancedTableResult] raise @types.LoadError {
266	  // 输入验证
267	  if binary_img.width <= 0 || binary_img.height <= 0 {	<-- UNCOVERED
268	    raise @types.LoadError::DecodeFailed(
269	      "detect_table_enhanced: 图像尺寸必须大于0",
270	    )
271	  }
272	  if binary_img.channels != 1 {
273	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
274	      "detect_table_enhanced: 仅支持单通道二值图",
275	    )
276	  }
277	  let expected_size = binary_img.width * binary_img.height
278	  if binary_img.data.length() != expected_size {	<-- UNCOVERED
279	    raise @types.LoadError::DecodeFailed(
280	      "detect_table_enhanced: 数据大小不匹配，期望 " +
281	      expected_size.to_string() +	<-- UNCOVERED
282	      "，实际 " +
283	      binary_img.data.length().to_string(),	<-- UNCOVERED
284	    )
285	  }
286	  if min_table_size <= 0 {
287	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
288	      "detect_table_enhanced: 最小表格尺寸必须大于0",
289	    )
290	  }
291	  let min_size = min_table_size
292	  let h_lines = detect_horizontal_table_lines(	<-- UNCOVERED
293	    binary_img,
294	    min_length=50,
295	    max_gap=3,
296	  )
297	  let v_lines = detect_vertical_table_lines(	<-- UNCOVERED
298	    binary_img,
299	    min_length=50,
300	    max_gap=3,
301	  )
302	  let intersections = find_line_intersections(h_lines, v_lines)	<-- UNCOVERED
303	
304	  let results : Array[EnhancedTableResult] = []
305	
306	  if h_lines.length() < 2 || v_lines.length() < 2 {	<-- UNCOVERED
307	    return results
308	  }
309	
310	  // 提取唯一的行 y 坐标和列 x 坐标
311	  let row_ys : Array[Int] = []
312	  for _tup2 in h_lines {
313	    let (y, _, _) = _tup2	<-- UNCOVERED
314	    let mut exists = false
315	    for ry in row_ys {
316	      if (ry - y).abs() <= 3 {	<-- UNCOVERED
317	        exists = true
318	        break
319	      }
320	    }
321	    if !exists {
322	      row_ys.push(y)	<-- UNCOVERED
323	    }
324	  }
325	
326	  let col_xs : Array[Int] = []
327	  for _tup3 in v_lines {
328	    let (x, _, _) = _tup3	<-- UNCOVERED
329	    let mut exists = false
330	    for cx in col_xs {
331	      if (cx - x).abs() <= 3 {	<-- UNCOVERED
332	        exists = true
333	        break
334	      }
335	    }
336	    if !exists {
337	      col_xs.push(x)	<-- UNCOVERED
338	    }
339	  }
340	
341	  // 排序
342	  for i in 0..<row_ys.length() {	<-- UNCOVERED
343	    for j in 0..<(row_ys.length() - i - 1) {
344	      if row_ys[j] > row_ys[j + 1] {
345	        let temp = row_ys[j]
346	        row_ys[j] = row_ys[j + 1]
347	        row_ys[j + 1] = temp
348	      }
349	    }
350	  }
351	  for i in 0..<col_xs.length() {	<-- UNCOVERED
352	    for j in 0..<(col_xs.length() - i - 1) {
353	      if col_xs[j] > col_xs[j + 1] {
354	        let temp = col_xs[j]
355	        col_xs[j] = col_xs[j + 1]
356	        col_xs[j + 1] = temp
357	      }
358	    }
359	  }
360	
361	  let num_rows = row_ys.length() - 1	<-- UNCOVERED
362	  let num_cols = col_xs.length() - 1
363	
364	  if num_rows < 1 || num_cols < 1 {
365	    return results	<-- UNCOVERED
366	  }
367	
368	  let table_x = col_xs[0]
369	  let table_y = row_ys[0]
370	  let table_width = col_xs[col_xs.length() - 1] - table_x	<-- UNCOVERED
371	  let table_height = row_ys[row_ys.length() - 1] - table_y
372	
373	  if table_width < min_size || table_height < min_size {
374	    return results	<-- UNCOVERED
375	  }
376	
377	  // 构建单元格
378	  let cells : Array[TableCellContent] = []
379	  for row in 0..<num_rows {
380	    for col in 0..<num_cols {	<-- UNCOVERED
381	      let cell_x = col_xs[col] + 2
382	      let cell_y = row_ys[row] + 2
383	      let cell_w = col_xs[col + 1] - col_xs[col] - 4
384	      let cell_h = row_ys[row + 1] - row_ys[row] - 4
385	      let cell = TableCellContent::{
386	        row,
387	        col,
388	        row_span: 1,
389	        col_span: 1,
390	        x: cell_x,
391	        y: cell_y,
392	        width: cell_w,
393	        height: cell_h,
394	        text: "",
395	        is_header: row == 0,
396	        is_merged: false,
397	      }
398	      cells.push(cell)	<-- UNCOVERED
399	    }
400	  }
401	
402	  let detection = TableDetectionResult::{
403	    horizontal_lines: h_lines,
404	    vertical_lines: v_lines,
405	    intersections,
406	    num_rows,
407	    num_cols,
408	    confidence: 0.85,
409	  }
410	
411	  let result = EnhancedTableResult::{
412	    x: table_x,
413	    y: table_y,
414	    width: table_width,
415	    height: table_height,
416	    rows: num_rows,
417	    cols: num_cols,
418	    cells,
419	    has_header: true,
420	    header_rows: 1,
421	    detection,
422	    confidence: 0.85,
423	  }
424	
425	  results.push(result)	<-- UNCOVERED
426	  results
427	}
   	…

431	pub fn enhanced_table_to_markdown(table : EnhancedTableResult) -> String {
432	  let mut md = ""	<-- UNCOVERED
433	  // 表头
434	  if table.has_header && table.rows > 0 {
435	    md = md + "| "	<-- UNCOVERED
436	    for col in 0..<table.cols {
437	      let cell_text = get_cell_text(table, 0, col)	<-- UNCOVERED
438	      md = md + cell_text + " | "
439	    }
440	    md = md + "\n| "
441	    for _ in 0..<table.cols {
442	      md = md + "--- | "	<-- UNCOVERED
443	    }
444	    md = md + "\n"
445	    // 数据行
446	    for row in 1..<table.rows {
447	      md = md + "| "	<-- UNCOVERED
448	      for col in 0..<table.cols {
449	        let cell_text = get_cell_text(table, row, col)	<-- UNCOVERED
450	        md = md + cell_text + " | "
451	      }
452	      md = md + "\n"
453	    }
454	  } else {
455	    for row in 0..<table.rows {	<-- UNCOVERED
456	      md = md + "| "
457	      for col in 0..<table.cols {
458	        let cell_text = get_cell_text(table, row, col)	<-- UNCOVERED
459	        md = md + cell_text + " | "
460	      }
461	      md = md + "\n"
462	    }
463	  }
464	  md
465	}
   	…

469	fn get_cell_text(table : EnhancedTableResult, row : Int, col : Int) -> String {
470	  for cell in table.cells {	<-- UNCOVERED
471	    if cell.row == row && cell.col == col {
472	      return cell.text
473	    }
474	  }
475	  ""
476	}
   	…

480	pub fn extract_images_enhanced(
481	  binary_img : @types.Image,
482	  min_area? : Int = 500,
483	) -> Array[ExtractedImage] {
484	  let min = min_area	<-- UNCOVERED
485	  let w = binary_img.width
486	  let h = binary_img.height
487	  let images : Array[ExtractedImage] = []
488	  let visited : Array[Bool] = []
489	  for _ in 0..<(w * h) {
490	    visited.push(false)	<-- UNCOVERED
491	  }
492	  for y in 0..<h {
493	    for x in 0..<w {	<-- UNCOVERED
494	      let idx = y * w + x
495	      if !visited[idx] && binary_img.data[idx].to_int() < 128 {
496	        let stack_x : Array[Int] = []
497	        let stack_y : Array[Int] = []
498	        stack_x.push(x)	<-- UNCOVERED
499	        stack_y.push(y)
500	        visited[idx] = true
501	        let mut min_x = x
502	        let mut max_x = x
503	        let mut min_y = y
504	        let mut max_y = y
505	        let mut area = 0
506	        let mut edge_pixels = 0
507	        while stack_x.length() > 0 {	<-- UNCOVERED
508	          let cx_opt = stack_x.pop()
509	          let cy_opt = stack_y.pop()
510	          match cx_opt {
511	            Some(cx) =>
512	              match cy_opt {	<-- UNCOVERED
513	                Some(cy) => {
514	                  area += 1
515	                  if cx < min_x {
516	                    min_x = cx	<-- UNCOVERED
517	                  }
518	                  if cx > max_x {
519	                    max_x = cx	<-- UNCOVERED
520	                  }
521	                  if cy < min_y {
522	                    min_y = cy	<-- UNCOVERED
523	                  }
524	                  if cy > max_y {
525	                    max_y = cy	<-- UNCOVERED
526	                  }
527	                  // 边缘检测：检查是否有白色邻域
528	                  let mut is_edge = false
529	                  let neighbors = [
530	                    (cx - 1, cy),
531	                    (cx + 1, cy),
532	                    (cx, cy - 1),
533	                    (cx, cy + 1),
534	                  ]
535	                  for _tup4 in neighbors {
536	                    let (nx, ny) = _tup4	<-- UNCOVERED
537	                    if nx >= 0 && nx < w && ny >= 0 && ny < h {
538	                      let nidx = ny * w + nx	<-- UNCOVERED
539	                      if !visited[nidx] && binary_img.data[nidx].to_int() >= 128 {
540	                        is_edge = true
541	                      }
542	                      if !visited[nidx] && binary_img.data[nidx].to_int() < 128 {	<-- UNCOVERED
543	                        visited[nidx] = true
544	                        stack_x.push(nx)
545	                        stack_y.push(ny)
546	                      }
547	                    }
548	                  }
549	                  if is_edge {
550	                    edge_pixels += 1	<-- UNCOVERED
551	                  }
552	                }
553	                None => ()	<-- UNCOVERED
554	              }
555	            None => ()	<-- UNCOVERED
556	          }
557	        }
558	        let region_width = max_x - min_x + 1
559	        let region_height = max_y - min_y + 1
560	        if area >= min && region_width > 20 && region_height > 20 {
561	          let fill_ratio = area.to_double() /	<-- UNCOVERED
562	            (region_width * region_height).to_double()
563	          let aspect_ratio = region_width.to_double() /
564	            region_height.to_double()
565	          let edge_density = edge_pixels.to_double() / area.to_double()
566	          // 图片类型判断
567	          let image_type = if fill_ratio > 0.9 &&
568	            aspect_ratio > 0.8 &&
569	            aspect_ratio < 1.2 {
570	            "logo"	<-- UNCOVERED
571	          } else if fill_ratio > 0.8 {
572	            "diagram"
573	          } else if fill_ratio > 0.5 && edge_density > 0.3 {
574	            "chart"
575	          } else if aspect_ratio > 2.0 || aspect_ratio < 0.5 {
576	            "signature"
577	          } else {
578	            "photo"	<-- UNCOVERED
579	          }
580	          let file_name = "image_" + images.length().to_string() + ".png"	<-- UNCOVERED
581	          images.push(ExtractedImage::{
582	            x: min_x,
583	            y: min_y,
584	            width: region_width,
585	            height: region_height,
586	            image_type,
587	            confidence: 0.75,
588	            aspect_ratio,
589	            fill_ratio,
590	            edge_density,
591	            file_name,
592	          })
593	        }
594	      }
595	    }
596	  }
597	  images
598	}
   	…

602	pub fn generate_image_extraction_report(
603	  images : Array[ExtractedImage],
604	) -> String {
605	  let mut report = "## 图片提取报告\n\n"	<-- UNCOVERED
606	  report = report +
607	    "共提取 " +
608	    images.length().to_string() +	<-- UNCOVERED
609	    " 张图片\n\n"
610	  report = report +
611	    "| 序号 | 类型 | 位置 | 尺寸 | 宽高比 | 填充率 | 置信度 | 文件名 |\n"
612	  report = report +
613	    "|------|------|------|------|--------|--------|--------|--------|\n"
614	  for i in 0..<images.length() {	<-- UNCOVERED
615	    let img = images[i]
616	    report = report + "| " + (i + 1).to_string() + " | "
617	    report = report + img.image_type + " | "
618	    report = report + "(" + img.x.to_string() + "," + img.y.to_string() + ") | "	<-- UNCOVERED
619	    report = report +
620	      img.width.to_string() +	<-- UNCOVERED
621	      "x" +
622	      img.height.to_string() +	<-- UNCOVERED
623	      " | "
624	    report = report + img.aspect_ratio.to_string() + " | "	<-- UNCOVERED
625	    report = report + (img.fill_ratio * 100.0).to_string() + "% | "
626	    report = report + (img.confidence * 100.0).to_string() + "% | "
627	    report = report + img.file_name + " |\n"
628	  }
629	  report
630	}
   	…

634	pub fn analyze_table_results(
635	  tables : Array[EnhancedTableResult],
636	) -> Array[(String, Int)] {
637	  let total_tables = tables.length()	<-- UNCOVERED
638	  let mut total_cells = 0
639	  let mut total_rows = 0
640	  let mut total_cols = 0
641	  let mut with_header = 0
642	  for table in tables {
643	    total_cells += table.cells.length()	<-- UNCOVERED
644	    total_rows += table.rows
645	    total_cols += table.cols
646	    if table.has_header {
647	      with_header += 1	<-- UNCOVERED
648	    }
649	  }
650	  [
651	    ("表格总数", total_tables),
652	    ("单元格总数", total_cells),
653	    ("总行数", total_rows),
654	    ("总列数", total_cols),
655	    ("含表头表格", with_header),
656	  ]
657	}
   	…

74 uncovered line(s) in src\process\ocr\table_recognition.mbt:

 48	pub fn detect_horizontal_lines(
 49	  img : @types.Image,
 50	  min_length? : Int = 20,
 51	  max_gap? : Int = 2,
 52	) -> Array[TableLine] raise @types.LoadError {
 53	  // 输入验证
 54	  if img.width <= 0 || img.height <= 0 {
 55	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 56	      "detect_horizontal_lines: 图像尺寸必须大于0",
 57	    )
 58	  }
 59	  if img.channels != 1 {
 60	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 61	      "detect_horizontal_lines: 仅支持单通道二值图",
 62	    )
 63	  }
 64	  let expected_size = img.width * img.height
 65	  if img.data.length() != expected_size {
 66	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 67	      "detect_horizontal_lines: 数据大小不匹配，期望 " +
 68	      expected_size.to_string() +	<-- UNCOVERED
 69	      "，实际 " +
 70	      img.data.length().to_string(),	<-- UNCOVERED
 71	    )
 72	  }
 73	  if min_length <= 0 {
 74	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 75	      "detect_horizontal_lines: min_length必须大于0",
 76	    )
 77	  }
 78	  if max_gap < 0 {
 79	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 80	      "detect_horizontal_lines: max_gap不能为负数",
 81	    )
 82	  }
 83	  let w = img.width
 84	  let h = img.height
 85	  let data = img.data
 86	  let lines : Array[TableLine] = []
 87	  // 逐行检测水平线
 88	  for y in 0..<h {
 89	    let mut x = 0
 90	    while x < w {
 91	      // 找连续的黑色像素
 92	      if data[y * w + x].to_int() < 128 {
 93	        let start_x = x
 94	        let mut end_x = x
 95	        let mut gap = 0
 96	        while x < w && gap <= max_gap {
 97	          if data[y * w + x].to_int() < 128 {
 98	            end_x = x
 99	            gap = 0
100	          } else {
101	            gap += 1
102	          }
103	          x += 1
104	        }
105	        let length = end_x - start_x + 1
106	        if length >= min_length {
107	          lines.push(TableLine::{	<-- UNCOVERED
108	            x1: start_x,
109	            y1: y,
110	            x2: end_x,
111	            y2: y,
112	            line_type: TableLineType::Horizontal,
113	            thickness: 1,
114	          })
115	        }
116	      } else {
117	        x += 1
118	      }
119	    }
120	  }
121	  // 合并相邻的水平线（同一表格线可能有多行像素）
122	  merge_horizontal_lines(lines, 3)
123	}
   	…

127	pub fn detect_vertical_lines(
128	  img : @types.Image,
129	  min_length? : Int = 20,
130	  max_gap? : Int = 2,
131	) -> Array[TableLine] raise @types.LoadError {
132	  // 输入验证
133	  if img.width <= 0 || img.height <= 0 {
134	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
135	      "detect_vertical_lines: 图像尺寸必须大于0",
136	    )
137	  }
138	  if img.channels != 1 {
139	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
140	      "detect_vertical_lines: 仅支持单通道二值图",
141	    )
142	  }
143	  let expected_size = img.width * img.height
144	  if img.data.length() != expected_size {
145	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
146	      "detect_vertical_lines: 数据大小不匹配，期望 " +
147	      expected_size.to_string() +	<-- UNCOVERED
148	      "，实际 " +
149	      img.data.length().to_string(),	<-- UNCOVERED
150	    )
151	  }
152	  if min_length <= 0 {
153	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
154	      "detect_vertical_lines: min_length必须大于0",
155	    )
156	  }
157	  if max_gap < 0 {
158	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
159	      "detect_vertical_lines: max_gap不能为负数",
160	    )
161	  }
162	  let w = img.width
163	  let h = img.height
164	  let data = img.data
165	  let lines : Array[TableLine] = []
166	  // 逐列检测垂直线
167	  for x in 0..<w {
168	    let mut y = 0
169	    while y < h {
170	      if data[y * w + x].to_int() < 128 {
171	        let start_y = y
172	        let mut end_y = y
173	        let mut gap = 0
174	        while y < h && gap <= max_gap {
175	          if data[y * w + x].to_int() < 128 {
176	            end_y = y
177	            gap = 0
178	          } else {
179	            gap += 1
180	          }
181	          y += 1
182	        }
183	        let length = end_y - start_y + 1
184	        if length >= min_length {
185	          lines.push(TableLine::{	<-- UNCOVERED
186	            x1: x,
187	            y1: start_y,
188	            x2: x,
189	            y2: end_y,
190	            line_type: TableLineType::Vertical,
191	            thickness: 1,
192	          })
193	        }
194	      } else {
195	        y += 1
196	      }
197	    }
198	  }
199	  // 合并相邻的垂直线
200	  merge_vertical_lines(lines, 3)
201	}
   	…

205	fn merge_horizontal_lines(
206	  lines : Array[TableLine],
207	  max_y_diff : Int,
208	) -> Array[TableLine] {
209	  if lines.length() == 0 {
210	    return lines
211	  }
212	  let merged : Array[TableLine] = []
213	  let mut current = lines[0]
214	  for i in 1..<lines.length() {	<-- UNCOVERED
215	    let next = lines[i]
216	    // 检查是否是同一条线（y坐标接近，x范围重叠）
217	    let y_diff = (next.y1 - current.y1).abs()	<-- UNCOVERED
218	    let x_overlap = min_table(current.x2, next.x2) -
219	      max_table(current.x1, next.x1)
220	    if y_diff <= max_y_diff && x_overlap > 0 {
221	      // 合并
222	      current = TableLine::{	<-- UNCOVERED
223	        x1: min_table(current.x1, next.x1),
224	        y1: (current.y1 + next.y1) / 2,
225	        x2: max_table(current.x2, next.x2),	<-- UNCOVERED
226	        y2: (current.y2 + next.y2) / 2,
227	        line_type: TableLineType::Horizontal,
228	        thickness: current.thickness + 1,
229	      }
230	    } else {
231	      merged.push(current)	<-- UNCOVERED
232	      current = next
233	    }
234	  }
235	  merged.push(current)	<-- UNCOVERED
236	  merged
237	}
   	…

241	fn merge_vertical_lines(
242	  lines : Array[TableLine],
243	  max_x_diff : Int,
244	) -> Array[TableLine] {
245	  if lines.length() == 0 {
246	    return lines
247	  }
248	  let merged : Array[TableLine] = []
249	  let mut current = lines[0]
250	  for i in 1..<lines.length() {	<-- UNCOVERED
251	    let next = lines[i]
252	    let x_diff = (next.x1 - current.x1).abs()
253	    let y_overlap = min_table(current.y2, next.y2) -
254	      max_table(current.y1, next.y1)
255	    if x_diff <= max_x_diff && y_overlap > 0 {
256	      current = TableLine::{	<-- UNCOVERED
257	        x1: (current.x1 + next.x1) / 2,
258	        y1: min_table(current.y1, next.y1),	<-- UNCOVERED
259	        x2: (current.x2 + next.x2) / 2,
260	        y2: max_table(current.y2, next.y2),	<-- UNCOVERED
261	        line_type: TableLineType::Vertical,
262	        thickness: current.thickness + 1,
263	      }
264	    } else {
265	      merged.push(current)	<-- UNCOVERED
266	      current = next
267	    }
268	  }
269	  merged.push(current)	<-- UNCOVERED
270	  merged
271	}
   	…

275	pub fn analyze_table_structure(
276	  horizontal_lines : Array[TableLine],
277	  vertical_lines : Array[TableLine],
278	) -> TableStructure raise @types.LoadError {
279	  // 输入验证
280	  if horizontal_lines.length() == 0 {
281	    raise @types.LoadError::DecodeFailed(
282	      "analyze_table_structure: 水平线列表不能为空",
283	    )
284	  }
285	  if vertical_lines.length() == 0 {	<-- UNCOVERED
286	    raise @types.LoadError::DecodeFailed(
287	      "analyze_table_structure: 垂直线列表不能为空",
288	    )
289	  }
290	  if horizontal_lines.length() < 2 || vertical_lines.length() < 2 {	<-- UNCOVERED
291	    return TableStructure::{
292	      rows: 0,
293	      cols: 0,
294	      cells: [],
295	      horizontal_lines,
296	      vertical_lines,
297	      bounding_box: (0, 0, 0, 0),
298	    }
299	  }
300	  // 提取水平线的y坐标（按y排序）
301	  let h_ys : Array[Int] = []
302	  for line in horizontal_lines {
303	    h_ys.push(line.y1)	<-- UNCOVERED
304	  }
305	  // 排序
306	  for _ in 0..<h_ys.length() {	<-- UNCOVERED
307	    for j in 0..<(h_ys.length() - 1) {
308	      if h_ys[j] > h_ys[j + 1] {
309	        let temp = h_ys[j]
310	        h_ys[j] = h_ys[j + 1]
311	        h_ys[j + 1] = temp
312	      }
313	    }
314	  }
315	  // 提取垂直线的x坐标（按x排序）
316	  let v_xs : Array[Int] = []
317	  for line in vertical_lines {
318	    v_xs.push(line.x1)	<-- UNCOVERED
319	  }
320	  for _ in 0..<v_xs.length() {	<-- UNCOVERED
321	    for j in 0..<(v_xs.length() - 1) {
322	      if v_xs[j] > v_xs[j + 1] {
323	        let temp = v_xs[j]
324	        v_xs[j] = v_xs[j + 1]
325	        v_xs[j + 1] = temp
326	      }
327	    }
328	  }
329	  // 计算边界框
330	  let min_x = v_xs[0]
331	  let max_x = v_xs[v_xs.length() - 1]	<-- UNCOVERED
332	  let min_y = h_ys[0]
333	  let max_y = h_ys[h_ys.length() - 1]	<-- UNCOVERED
334	  // 生成单元格
335	  let cells : Array[TableCell] = []
336	  let rows = h_ys.length() - 1	<-- UNCOVERED
337	  let cols = v_xs.length() - 1
338	  for row in 0..<rows {
339	    for col in 0..<cols {	<-- UNCOVERED
340	      let cell_x = v_xs[col]
341	      let cell_y = h_ys[row]
342	      let cell_w = v_xs[col + 1] - v_xs[col]
343	      let cell_h = h_ys[row + 1] - h_ys[row]
344	      if cell_w > 0 && cell_h > 0 {
345	        cells.push(TableCell::{	<-- UNCOVERED
346	          x: cell_x,
347	          y: cell_y,
348	          width: cell_w,
349	          height: cell_h,
350	          row,
351	          col,
352	          row_span: 1,
353	          col_span: 1,
354	        })
355	      }
356	    }
357	  }
358	  TableStructure::{
359	    rows,
360	    cols,
361	    cells,
362	    horizontal_lines,
363	    vertical_lines,
364	    bounding_box: (min_x, min_y, max_x - min_x, max_y - min_y),
365	  }
366	}
   	…

370	pub fn recognize_table(
371	  img : @types.Image,
372	  min_line_length? : Int = 20,
373	) -> TableStructure raise @types.LoadError {
374	  // 输入验证
375	  if img.width <= 0 || img.height <= 0 {
376	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
377	      "recognize_table: 图像尺寸必须大于0",
378	    )
379	  }
380	  if img.channels < 1 || img.channels > 4 {
381	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
382	      "recognize_table: 通道数必须在1-4之间",
383	    )
384	  }
385	  let expected_size = img.width * img.height * img.channels
386	  if img.data.length() != expected_size {
387	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
388	      "recognize_table: 数据大小不匹配，期望 " +
389	      expected_size.to_string() +	<-- UNCOVERED
390	      "，实际 " +
391	      img.data.length().to_string(),	<-- UNCOVERED
392	    )
393	  }
394	  if min_line_length <= 0 {
395	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
396	      "recognize_table: min_line_length必须大于0",
397	    )
398	  }
399	  // 1. 预处理（二值化）
400	  let binary = if img.channels == 1 { img } else { to_grayscale_table(img) }
401	  // 2. 检测水平线
402	  let h_lines = detect_horizontal_lines(binary, min_length=min_line_length)
403	  // 3. 检测垂直线
404	  let v_lines = detect_vertical_lines(binary, min_length=min_line_length)
405	  // 4. 分析表格结构
406	  analyze_table_structure(h_lines, v_lines)
407	}
   	…

411	pub fn extract_cell_image(
412	  img : @types.Image,
413	  cell : TableCell,
414	  padding? : Int = 1,
415	) -> @types.Image raise @types.LoadError {
416	  // 输入验证
417	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
418	    raise @types.LoadError::DecodeFailed(
419	      "extract_cell_image: 图像尺寸必须大于0",
420	    )
421	  }
422	  if img.channels < 1 || img.channels > 4 {
423	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
424	      "extract_cell_image: 通道数必须在1-4之间",
425	    )
426	  }
427	  let expected_size = img.width * img.height * img.channels
428	  if img.data.length() != expected_size {	<-- UNCOVERED
429	    raise @types.LoadError::DecodeFailed(
430	      "extract_cell_image: 数据大小不匹配，期望 " +
431	      expected_size.to_string() +	<-- UNCOVERED
432	      "，实际 " +
433	      img.data.length().to_string(),	<-- UNCOVERED
434	    )
435	  }
436	  if cell.width <= 0 || cell.height <= 0 {
437	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
438	      "extract_cell_image: 单元格尺寸必须大于0",
439	    )
440	  }
441	  if padding < 0 {
442	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
443	      "extract_cell_image: padding不能为负数",
444	    )
445	  }
446	  let w = img.width
447	  let h = img.height
448	  let ch = img.channels
449	  let x = max_table(0, cell.x - padding)	<-- UNCOVERED
450	  let y = max_table(0, cell.y - padding)
451	  let width = min_table(w - x, cell.width + padding * 2)
452	  let height = min_table(h - y, cell.height + padding * 2)
453	  if width <= 0 || height <= 0 {
454	    let empty : Array[Byte] = Array::make(1 * 1 * ch, b'\x00')	<-- UNCOVERED
455	    return @types.Image::{
456	      width: 1,
457	      height: 1,
458	      channels: ch,
459	      data: Bytes::from_array(empty),	<-- UNCOVERED
460	    }
461	  }
462	  let out : Array[Byte] = Array::make(width * height * ch, b'\x00')	<-- UNCOVERED
463	  let data = img.data
464	  for yy in 0..<height {
465	    for xx in 0..<width {	<-- UNCOVERED
466	      for c in 0..<ch {
467	        out[(yy * width + xx) * ch + c] = data[((y + yy) * w + (x + xx)) * ch +
468	          c]
469	      }
470	    }
471	  }
472	  @types.Image::{ width, height, channels: ch, data: Bytes::from_array(out) }	<-- UNCOVERED
473	}
   	…

477	pub fn extract_all_cells(
478	  img : @types.Image,
479	  structure : TableStructure,
480	  padding? : Int = 1,
481	) -> Array[@types.Image] raise @types.LoadError {
482	  // 输入验证
483	  if img.width <= 0 || img.height <= 0 {	<-- UNCOVERED
484	    raise @types.LoadError::DecodeFailed(
485	      "extract_all_cells: 图像尺寸必须大于0",
486	    )
487	  }
488	  if img.channels < 1 || img.channels > 4 {
489	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
490	      "extract_all_cells: 通道数必须在1-4之间",
491	    )
492	  }
493	  let expected_size = img.width * img.height * img.channels
494	  if img.data.length() != expected_size {	<-- UNCOVERED
495	    raise @types.LoadError::DecodeFailed(
496	      "extract_all_cells: 数据大小不匹配，期望 " +
497	      expected_size.to_string() +	<-- UNCOVERED
498	      "，实际 " +
499	      img.data.length().to_string(),	<-- UNCOVERED
500	    )
501	  }
502	  if padding < 0 {
503	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
504	      "extract_all_cells: padding不能为负数",
505	    )
506	  }
507	  let images : Array[@types.Image] = []
508	  for cell in structure.cells {
509	    let cell_img = extract_cell_image(img, cell, padding~)	<-- UNCOVERED
510	    images.push(cell_img)
511	  }
512	  images
513	}
   	…

517	fn to_grayscale_table(img : @types.Image) -> @types.Image {
518	  if img.channels == 1 {	<-- UNCOVERED
519	    return img
520	  }
521	  let n = img.width * img.height
522	  let ch = img.channels
523	  let out : Array[Byte] = Array::make(n, b'\x00')	<-- UNCOVERED
524	  let data = img.data
525	  for i in 0..<n {
526	    let base = i * ch	<-- UNCOVERED
527	    let r = data[base].to_int()
528	    let g = data[base + 1].to_int()
529	    let b = data[base + 2].to_int()
530	    let gray = (r * 299 + g * 587 + b * 114) / 1000
531	    out[i] = gray.to_byte()	<-- UNCOVERED
532	  }
533	  @types.Image::{
534	    width: img.width,
535	    height: img.height,
536	    channels: 1,
537	    data: Bytes::from_array(out),	<-- UNCOVERED
538	  }
539	}
   	…

543	fn min_table(a : Int, b : Int) -> Int {
544	  if a < b {	<-- UNCOVERED
545	    a
546	  } else {
547	    b	<-- UNCOVERED
548	  }
549	}
   	…

553	fn max_table(a : Int, b : Int) -> Int {
554	  if a > b {	<-- UNCOVERED
555	    a
556	  } else {
557	    b	<-- UNCOVERED
558	  }
559	}
   	…

31 uncovered line(s) in src\process\ocr\training_framework.mbt:

211	pub fn next_batch(
212	  loader : DataLoader,
213	) -> (Array[DLTrainingSample], DataLoader) raise @types.LoadError {
214	  // 输入验证
215	  if loader.samples.length() == 0 {
216	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
217	      "next_batch: 数据加载器的样本列表不能为空",
218	    )
219	  }
220	  if loader.batch_size <= 0 {
221	    raise @types.LoadError::DecodeFailed("next_batch: 批大小必须大于0")	<-- UNCOVERED
222	  }
223	  let end_index = if loader.current_index + loader.batch_size <
224	    loader.samples.length() {
225	    loader.current_index + loader.batch_size
226	  } else {
227	    loader.samples.length()
228	  }
229	  let batch : Array[DLTrainingSample] = []
230	  for i in loader.current_index..<end_index {
231	    batch.push(loader.samples[i])
232	  }
233	  let new_index = if end_index >= loader.samples.length() {
234	    0
235	  } else {
236	    end_index
237	  }
238	  let new_epoch = if end_index >= loader.samples.length() {
239	    loader.epoch + 1
240	  } else {
241	    loader.epoch
242	  }
243	  (
244	    batch,
245	    DataLoader::{
246	      samples: loader.samples,
247	      batch_size: loader.batch_size,
248	      shuffle: loader.shuffle,
249	      current_index: new_index,
250	      epoch: new_epoch,
251	    },
252	  )
253	}
   	…

257	fn power_double(base : Double, exp : Int) -> Double {
258	  let mut result = 1.0	<-- UNCOVERED
259	  for _ in 0..<exp {
260	    result = result * base	<-- UNCOVERED
261	  }
262	  result
263	}
   	…

267	fn approx_cos(x : Double) -> Double {
268	  // 归一化到 [-pi, pi]
269	  let pi = 3.141592653589793
270	  let mut t = x % (2.0 * pi)
271	  if t > pi {
272	    t = t - 2.0 * pi	<-- UNCOVERED
273	  }
274	  if t < -pi {
275	    t = t + 2.0 * pi	<-- UNCOVERED
276	  }
277	  // 泰勒级数
278	  let mut result = 1.0
279	  let mut term = 1.0
280	  for i in 1..<10 {
281	    term = term *
282	      (-t * t) /
283	      ((2.0 * i.to_double() - 1.0) * (2.0 * i.to_double()))
284	    result = result + term
285	  }
286	  result
287	}
   	…

291	pub fn calculate_learning_rate(
292	  config : LRSchedulerConfig,
293	  epoch : Int,
294	  current_lr : Double,
295	  val_loss : Double,
296	) -> Double raise @types.LoadError {
297	  // 输入验证
298	  if epoch < 0 {
299	    raise @types.LoadError::DecodeFailed(
300	      "calculate_learning_rate: 轮次不能为负",
301	    )
302	  }
303	  if current_lr <= 0.0 {
304	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
305	      "calculate_learning_rate: 当前学习率必须大于0",
306	    )
307	  }
308	  if val_loss < 0.0 {
309	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
310	      "calculate_learning_rate: 验证损失不能为负",
311	    )
312	  }
313	  if config.initial_lr <= 0.0 {
314	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
315	      "calculate_learning_rate: 初始学习率必须大于0",
316	    )
317	  }
318	  if config.min_lr < 0.0 {
319	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
320	      "calculate_learning_rate: 最小学习率不能为负",
321	    )
322	  }
323	  match config.scheduler_type {
324	    LRSchedulerType::Constant => config.initial_lr
325	    LRSchedulerType::StepDecay => {	<-- UNCOVERED
326	      let decay_factor = (epoch / config.decay_steps).to_double()
327	      let lr = config.initial_lr *
328	        power_double(config.decay_rate, decay_factor.to_int())	<-- UNCOVERED
329	      if lr > config.min_lr {
330	        lr	<-- UNCOVERED
331	      } else {
332	        config.min_lr	<-- UNCOVERED
333	      }
334	    }
335	    LRSchedulerType::ExponentialDecay => {	<-- UNCOVERED
336	      let lr = config.initial_lr * power_double(config.decay_rate, epoch)
337	      if lr > config.min_lr {
338	        lr	<-- UNCOVERED
339	      } else {
340	        config.min_lr	<-- UNCOVERED
341	      }
342	    }
343	    LRSchedulerType::CosineAnnealing => {
344	      let pi = 3.141592653589793
345	      let cos_val = approx_cos(pi * epoch.to_double() / 50.0)
346	      let lr = config.min_lr +
347	        0.5 * (config.initial_lr - config.min_lr) * (1.0 + cos_val)
348	      lr
349	    }
350	    LRSchedulerType::WarmupCosine =>
351	      if epoch < config.warmup_steps {	<-- UNCOVERED
352	        config.initial_lr *
353	        (epoch.to_double() + 1.0) /
354	        config.warmup_steps.to_double()
355	      } else {
356	        let pi = 3.141592653589793	<-- UNCOVERED
357	        let progress = (epoch - config.warmup_steps).to_double() /
358	          (50.0 - config.warmup_steps.to_double())
359	        let cos_val = approx_cos(pi * progress)
360	        config.min_lr +
361	        0.5 * (config.initial_lr - config.min_lr) * (1.0 + cos_val)
362	      }
363	    LRSchedulerType::ReduceOnPlateau => current_lr	<-- UNCOVERED
364	  }
365	}
   	…

402	pub fn augment_random_noise(
403	  sample : DLTrainingSample,
404	  noise_level : Double,
405	) -> DLTrainingSample {
406	  let augmented_data : Array[Double] = []
407	  for pixel in sample.image_data {
408	    let noise = pixel * noise_level * (if pixel > 0.5 { 1.0 } else { -1.0 })
409	    let new_pixel = if pixel + noise > 1.0 {
410	      1.0	<-- UNCOVERED
411	    } else if pixel + noise < 0.0 {
412	      0.0	<-- UNCOVERED
413	    } else {
414	      pixel + noise
415	    }
416	    augmented_data.push(new_pixel)
417	  }
418	  DLTrainingSample::{
419	    image_data: augmented_data,
420	    label: sample.label,
421	    label_text: sample.label_text,
422	    width: sample.width,
423	    height: sample.height,
424	  }
425	}
   	…

429	pub fn apply_augmentation(
430	  sample : DLTrainingSample,
431	  config : AugmentationConfig,
432	) -> DLTrainingSample {
433	  if !config.enabled {
434	    return sample
435	  }
436	  let mut augmented = sample
437	  for aug_type in config.augmentation_types {
438	    // 简化：固定应用噪声增强
439	    match aug_type {
440	      AugmentationType::RandomNoise =>
441	        augmented = augment_random_noise(augmented, config.noise_level)
442	      _ => ()	<-- UNCOVERED
443	    }
444	  }
445	  augmented
446	}
   	…

450	pub fn calculate_accuracy(
451	  predictions : Array[Int],
452	  labels : Array[Int],
453	) -> Double {
454	  if predictions.length() == 0 || labels.length() == 0 {
455	    return 0.0
456	  }
457	  let mut correct = 0
458	  let len = if predictions.length() < labels.length() {
459	    predictions.length()	<-- UNCOVERED
460	  } else {
461	    labels.length()
462	  }
463	  for i in 0..<len {
464	    if predictions[i] == labels[i] {
465	      correct += 1
466	    }
467	  }
468	  correct.to_double() / len.to_double()
469	}
   	…

473	pub fn check_early_stopping(
474	  state : TrainingState,
475	  config : TrainingConfig,
476	  current_val_accuracy : Double,
477	) -> (Bool, TrainingState) {
478	  let improvement = current_val_accuracy - state.best_val_accuracy
479	  if improvement > config.early_stopping_min_delta {
480	    let new_state = TrainingState::{
481	      current_epoch: state.current_epoch,
482	      best_val_accuracy: current_val_accuracy,
483	      best_epoch: state.current_epoch,
484	      epochs_without_improvement: 0,
485	      total_training_time_ms: state.total_training_time_ms,
486	      history: state.history,
487	      is_completed: false,
488	      stop_reason: "",
489	    }
490	    (false, new_state)
491	  } else {
492	    let new_epochs_without = state.epochs_without_improvement + 1
493	    let should_stop = new_epochs_without >= config.early_stopping_patience
494	    let new_state = TrainingState::{
495	      current_epoch: state.current_epoch,
496	      best_val_accuracy: state.best_val_accuracy,
497	      best_epoch: state.best_epoch,
498	      epochs_without_improvement: new_epochs_without,
499	      total_training_time_ms: state.total_training_time_ms,
500	      history: state.history,
501	      is_completed: should_stop,
502	      stop_reason: if should_stop {
503	        "early_stopping"
504	      } else {
505	        ""	<-- UNCOVERED
506	      },
507	    }
508	    (should_stop, new_state)
509	  }
510	}
   	…

529	pub fn record_training_metrics(
530	  state : TrainingState,
531	  metrics : TrainingMetrics,
532	) -> TrainingState {
533	  let new_history = state.history
534	  new_history.push(metrics)
535	  TrainingState::{
536	    current_epoch: metrics.epoch,
537	    best_val_accuracy: if metrics.val_accuracy > state.best_val_accuracy {
538	      metrics.val_accuracy
539	    } else {
540	      state.best_val_accuracy	<-- UNCOVERED
541	    },
542	    best_epoch: if metrics.val_accuracy > state.best_val_accuracy {
543	      metrics.epoch
544	    } else {
545	      state.best_epoch	<-- UNCOVERED
546	    },
547	    epochs_without_improvement: if metrics.val_accuracy >
548	      state.best_val_accuracy {
549	      0
550	    } else {
551	      state.epochs_without_improvement + 1	<-- UNCOVERED
552	    },
553	    total_training_time_ms: state.total_training_time_ms + metrics.time_ms,
554	    history: new_history,
555	    is_completed: state.is_completed,
556	    stop_reason: state.stop_reason,
557	  }
558	}
   	…

562	pub fn generate_training_progress_report(
563	  state : TrainingState,
564	  config : TrainingConfig,
565	) -> String {
566	  let mut report = "## 训练进度报告\n\n"
567	  report = report + "### 当前状态\n\n"
568	  report = report + "| 指标 | 值 |\n"
569	  report = report + "|------|------|\n"
570	  report = report +
571	    "| 当前轮次 | " +
572	    state.current_epoch.to_string() +
573	    " / " +
574	    config.epochs.to_string() +
575	    " |\n"
576	  report = report +
577	    "| 最佳验证准确率 | " +
578	    (state.best_val_accuracy * 100.0).to_string() +
579	    "% |\n"
580	  report = report + "| 最佳轮次 | " + state.best_epoch.to_string() + " |\n"
581	  report = report +
582	    "| 无改进轮次 | " +
583	    state.epochs_without_improvement.to_string() +
584	    " / " +
585	    config.early_stopping_patience.to_string() +
586	    " |\n"
587	  report = report +
588	    "| 总训练时间 | " +
589	    (state.total_training_time_ms / 1000.0).to_string() +
590	    " 秒 |\n"
591	  report = report +
592	    "| 训练状态 | " +
593	    (if state.is_completed {
594	      "已完成 (" + state.stop_reason + ")"	<-- UNCOVERED
595	    } else {
596	      "进行中"
597	    }) +
598	    " |\n\n"
599	
600	  if state.history.length() > 0 {
601	    report = report + "### 最近 5 轮训练记录\n\n"
602	    report = report +
603	      "| Epoch | Train Loss | Train Acc | Val Loss | Val Acc | LR |\n"
604	    report = report +
605	      "|-------|------------|-----------|----------|---------|----|\n"
606	    let start = if state.history.length() > 5 {
607	      state.history.length() - 5	<-- UNCOVERED
608	    } else {
609	      0
610	    }
611	    for i in start..<state.history.length() {
612	      let h = state.history[i]
613	      report = report + "| " + h.epoch.to_string() + " | "
614	      report = report + h.train_loss.to_string() + " | "
615	      report = report + (h.train_accuracy * 100.0).to_string() + "% | "
616	      report = report + h.val_loss.to_string() + " | "
617	      report = report + (h.val_accuracy * 100.0).to_string() + "% | "
618	      report = report + h.learning_rate.to_string() + " |\n"
619	    }
620	  }
621	
622	  report
623	}
   	…

652	pub fn compute_confusion_matrix(
653	  predictions : Array[Int],
654	  labels : Array[Int],
655	  num_classes : Int,
656	) -> Array[Array[Int]] {
657	  let matrix : Array[Array[Int]] = []
658	  for _ in 0..<num_classes {
659	    let row : Array[Int] = []
660	    for _ in 0..<num_classes {
661	      row.push(0)
662	    }
663	    matrix.push(row)
664	  }
665	  let len = if predictions.length() < labels.length() {
666	    predictions.length()	<-- UNCOVERED
667	  } else {
668	    labels.length()
669	  }
670	  for i in 0..<len {
671	    let true_label = labels[i]
672	    let pred_label = predictions[i]
673	    if true_label < num_classes && pred_label < num_classes {
674	      let row = matrix[true_label]
675	      row[pred_label] = row[pred_label] + 1
676	    }
677	  }
678	  matrix
679	}
   	…

6 uncovered line(s) in src\process\performance\performance_opt.mbt:

45	pub fn extract_tile(
46	  img : @types.Image,
47	  x0 : Int,
48	  y0 : Int,
49	  tile_w : Int,
50	  tile_h : Int,
51	) -> @types.Image raise @types.LoadError {
52	  if x0 < 0 || y0 < 0 || tile_w <= 0 || tile_h <= 0 {
53	    raise @types.LoadError::DecodeFailed("Invalid tile parameters")	<-- UNCOVERED
54	  }
55	  if x0 + tile_w > img.width || y0 + tile_h > img.height {
56	    raise @types.LoadError::DecodeFailed("Tile out of image bounds")
57	  }
58	  let ch = img.channels
59	  let tile_data : Array[Byte] = Array::make(tile_w * tile_h * ch, b'\x00')
60	  for y in 0..<tile_h {
61	    let src_row = (y0 + y) * img.width * ch + x0 * ch
62	    let dst_row = y * tile_w * ch
63	    for x in 0..<(tile_w * ch) {
64	      tile_data[dst_row + x] = img.data[src_row + x]
65	    }
66	  }
67	  @types.Image::{
68	    width: tile_w,
69	    height: tile_h,
70	    channels: ch,
71	    data: Bytes::from_array(tile_data),
72	  }
73	}
  	…

 77	pub fn paste_tile(
 78	  img : @types.Image,
 79	  tile : @types.Image,
 80	  x0 : Int,
 81	  y0 : Int,
 82	) -> @types.Image raise @types.LoadError {
 83	  if x0 < 0 || y0 < 0 {
 84	    raise @types.LoadError::DecodeFailed("Invalid paste position")	<-- UNCOVERED
 85	  }
 86	  if x0 + tile.width > img.width || y0 + tile.height > img.height {
 87	    raise @types.LoadError::DecodeFailed("Tile out of image bounds")	<-- UNCOVERED
 88	  }
 89	  if tile.channels != img.channels {
 90	    raise @types.LoadError::DecodeFailed("Channel mismatch")	<-- UNCOVERED
 91	  }
 92	  let ch = img.channels
 93	  let result_data : Array[Byte] = Array::make(
 94	    img.width * img.height * ch,
 95	    b'\x00',
 96	  )
 97	  for i in 0..<img.data.length() {
 98	    result_data[i] = img.data[i]
 99	  }
100	  for y in 0..<tile.height {
101	    let dst_row = (y0 + y) * img.width * ch + x0 * ch
102	    let src_row = y * tile.width * ch
103	    for x in 0..<(tile.width * ch) {
104	      result_data[dst_row + x] = tile.data[src_row + x]
105	    }
106	  }
107	  @types.Image::{
108	    width: img.width,
109	    height: img.height,
110	    channels: ch,
111	    data: Bytes::from_array(result_data),
112	  }
113	}
   	…

120	pub fn process_tiled(
121	  img : @types.Image,
122	  config : TileConfig,
123	  process_fn : (@types.Image) -> @types.Image,
124	) -> @types.Image raise @types.LoadError {
125	  let ch = img.channels
126	  let result_data : Array[Byte] = Array::make(
127	    img.width * img.height * ch,
128	    b'\x00',
129	  )
130	  let (cols, rows) = num_tiles(img.width, img.height, config)
131	  for row in 0..<rows {
132	    for col in 0..<cols {
133	      let (x0, y0, x1, y1) = tile_region(col, row, config)
134	      let tw = if x1 > img.width { img.width - x0 } else { config.tile_width }
135	      let th = if y1 > img.height {
136	        img.height - y0	<-- UNCOVERED
137	      } else {
138	        config.tile_height
139	      }
140	      let tile = extract_tile(img, x0, y0, tw, th)
141	      let processed = process_fn(tile)
142	      let _ = paste_tile(
143	        @types.Image::{
144	          width: img.width,
145	          height: img.height,
146	          channels: ch,
147	          data: Bytes::from_array(result_data),
148	        },
149	        processed,
150	        x0,
151	        y0,
152	      )
153	      // 直接写入 result_data
154	      for y in 0..<processed.height {
155	        let dst_row = (y0 + y) * img.width * ch + x0 * ch
156	        let src_row = y * processed.width * ch
157	        for x in 0..<(processed.width * ch) {
158	          result_data[dst_row + x] = processed.data[src_row + x]
159	        }
160	      }
161	    }
162	  }
163	  @types.Image::{
164	    width: img.width,
165	    height: img.height,
166	    channels: ch,
167	    data: Bytes::from_array(result_data),
168	  }
169	}
   	…

199	pub fn bulk_fill_4x(
200	  dst : Array[Byte],
201	  offset : Int,
202	  len : Int,
203	  value : Byte,
204	) -> Unit {
205	  let mut i = 0
206	  let len4 = len / 4 * 4
207	  while i < len4 {
208	    dst[offset + i] = value
209	    dst[offset + i + 1] = value
210	    dst[offset + i + 2] = value
211	    dst[offset + i + 3] = value
212	    i += 4
213	  }
214	  while i < len {
215	    dst[offset + i] = value	<-- UNCOVERED
216	    i += 1
217	  }
218	}
   	…

6 uncovered line(s) in src\process\photo_enhance\computational_photography.mbt:

133	pub fn detect_exposure_type(img : @types.Image) -> (ExposureType, Double) {
134	  let w = img.width
135	  let h = img.height
136	  let mut sum = 0.0
137	  let mut underexposed = 0
138	  let mut overexposed = 0
139	  let total = w * h
140	
141	  for y in 0..<h {
142	    for x in 0..<w {
143	      let pixel = img.data[y * w + x].to_double()
144	      sum += pixel
145	      if pixel < 50.0 {
146	        underexposed += 1
147	      }
148	      if pixel > 230.0 {
149	        overexposed += 1	<-- UNCOVERED
150	      }
151	    }
152	  }
153	
154	  let avg = if total > 0 { sum / total.to_double() } else { 0.0 }
155	  let under_ratio = underexposed.to_double() / total.to_double()
156	  let over_ratio = overexposed.to_double() / total.to_double()
157	
158	  let exposure_type = if under_ratio > 0.5 || avg < 60.0 {
159	    ExposureType::Underexposed
160	  } else if over_ratio > 0.5 || avg > 200.0 {	<-- UNCOVERED
161	    ExposureType::Overexposed
162	  } else {
163	    ExposureType::Normal	<-- UNCOVERED
164	  }
165	
166	  let ev = avg / 128.0
167	  (exposure_type, ev)
168	}
   	…

172	pub fn hdr_merge(
173	  images : Array[@types.Image],
174	  exposures : Array[Double],
175	) -> HdrImage {
176	  if images.length() == 0 {
177	    return HdrImage::{	<-- UNCOVERED
178	      width: 0,
179	      height: 0,
180	      channels: 1,
181	      data: [],
182	      exposure_values: [],
183	      num_exposures: 0,
184	    }
185	  }
186	
187	  let first = images[0]
188	  let w = first.width
189	  let h = first.height
190	  let hdr_data : Array[Double] = []
191	
192	  for y in 0..<h {
193	    for x in 0..<w {
194	      let mut sum = 0.0
195	      let mut weight_sum = 0.0
196	
197	      for i in 0..<images.length() {
198	        let img = images[i]
199	        if x < img.width && y < img.height {
200	          let pixel = img.data[y * img.width + x].to_double() / 255.0
201	          let weight = if pixel < 0.5 {
202	            pixel * 2.0
203	          } else {
204	            (1.0 - pixel) * 2.0	<-- UNCOVERED
205	          }
206	          sum += pixel * weight
207	          weight_sum += weight
208	        }
209	      }
210	
211	      let hdr_value = if weight_sum > 0 { sum / weight_sum } else { 0.5 }
212	      hdr_data.push(hdr_value)
213	    }
214	  }
215	
216	  HdrImage::{
217	    width: w,
218	    height: h,
219	    channels: first.channels,
220	    data: hdr_data,
221	    exposure_values: exposures,
222	    num_exposures: images.length(),
223	  }
224	}
   	…

255	pub fn stitch_panorama(
256	  images : Array[@types.Image],
257	  config : PanoramaConfig,
258	) -> PanoramaResult {
259	  ignore(config)
260	  if images.length() == 0 {
261	    return PanoramaResult::{	<-- UNCOVERED
262	      width: 0,
263	      height: 0,
264	      num_images: 0,
265	      inliers: 0,
266	      confidence: 0.0,
267	    }
268	  }
269	
270	  let first = images[0]
271	  let total_width = first.width * images.length()
272	  let height = first.height
273	
274	  PanoramaResult::{
275	    width: total_width,
276	    height,
277	    num_images: images.length(),
278	    inliers: total_width * height,
279	    confidence: 0.7,
280	  }
281	}
   	…

23 uncovered line(s) in src\process\photo_enhance\photo_enhancement.mbt:

46	fn log_enhance(img : @types.Image, strength : Double) -> @types.Image {
47	  let w = img.width	<-- UNCOVERED
48	  let h = img.height
49	  let channels = img.channels
50	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')	<-- UNCOVERED
51	  let c = 255.0 / @math.ln(1.0 + 255.0)
52	  for i in 0..<(w * h * channels) {
53	    let val = img.data[i].to_double()	<-- UNCOVERED
54	    let enhanced = c * @math.ln(1.0 + val) * strength
55	    out_data[i] = enhanced.min(255.0).max(0.0).to_int().to_byte()
56	  }
57	  @types.Image::{
58	    width: w,
59	    height: h,
60	    channels,
61	    data: Bytes::from_array(out_data),	<-- UNCOVERED
62	  }
63	}
  	…

 67	fn retinex_enhance(img : @types.Image, sigma? : Double = 50.0) -> @types.Image {
 68	  let _ = sigma	<-- UNCOVERED
 69	  let w = img.width
 70	  let h = img.height
 71	  let channels = img.channels
 72	  // 简化版：使用高斯模糊作为光照估计
 73	  // 这里用简单的均值模糊代替（避免依赖外部高斯模糊）
 74	  let kernel_size = 15
 75	  let half = kernel_size / 2
 76	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')	<-- UNCOVERED
 77	  for y in 0..<h {
 78	    for x in 0..<w {	<-- UNCOVERED
 79	      let idx = (y * w + x) * channels
 80	      for c in 0..<channels {
 81	        // 计算局部均值（光照估计）
 82	        let mut sum = 0.0	<-- UNCOVERED
 83	        let mut count = 0
 84	        for ky in -half..<=half {
 85	          for kx in -half..<=half {	<-- UNCOVERED
 86	            let ny = y + ky
 87	            let nx = x + kx
 88	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
 89	              let nidx = (ny * w + nx) * channels	<-- UNCOVERED
 90	              sum += img.data[nidx + c].to_double()
 91	              count += 1
 92	            }
 93	          }
 94	        }
 95	        let illumination = if count > 0 { sum / count.to_double() } else { 0.0 }	<-- UNCOVERED
 96	        // Retinex: R = log(I) - log(L) = log(I/L)
 97	        let original = img.data[idx + c].to_double() + 1.0	<-- UNCOVERED
 98	        let light = illumination + 1.0
 99	        let ratio = original / light
100	        let reflectance = @math.ln(ratio)	<-- UNCOVERED
101	        // 映射回 0-255
102	        let enhanced = (reflectance + 3.0) / 6.0 * 255.0
103	        out_data[idx + c] = enhanced.min(255.0).max(0.0).to_int().to_byte()	<-- UNCOVERED
104	      }
105	    }
106	  }
107	  @types.Image::{
108	    width: w,
109	    height: h,
110	    channels,
111	    data: Bytes::from_array(out_data),	<-- UNCOVERED
112	  }
113	}
   	…

117	pub fn low_light_enhance(
118	  img : @types.Image,
119	  config? : LowLightConfig,
120	) -> @types.Image {
121	  let cfg = match config {
122	    Some(v) => v	<-- UNCOVERED
123	    None => low_light_default_config()
124	  }
125	  match cfg.method_type {
126	    "gamma" => gamma_enhance(img, cfg.gamma)
127	    "log" => log_enhance(img, cfg.strength)	<-- UNCOVERED
128	    "retinex" => retinex_enhance(img, sigma=50.0)
129	    _ => gamma_enhance(img, cfg.gamma)
130	  }
131	}
   	…

135	pub fn deblur_motion(
136	  img : @types.Image,
137	  kernel_size? : Int = 15,
138	  angle? : Double = 0.0,
139	) -> @types.Image {
140	  let ks = kernel_size
141	  let _ = angle
142	  let w = img.width
143	  let h = img.height
144	  let channels = img.channels
145	  // 简化版：使用非锐化掩模（Unsharp Mask）作为去模糊近似
146	  let amount = 1.5
147	  let threshold = 0
148	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
149	  let blur_radius = ks / 2
150	  for y in 0..<h {
151	    for x in 0..<w {
152	      let idx = (y * w + x) * channels
153	      for c in 0..<channels {
154	        // 计算局部均值（模糊版本）
155	        let mut sum = 0.0
156	        let mut count = 0
157	        for ky in -blur_radius..<=blur_radius {
158	          for kx in -blur_radius..<=blur_radius {
159	            let ny = y + ky
160	            let nx = x + kx
161	            if ny >= 0 && ny < h && nx >= 0 && nx < w {
162	              let nidx = (ny * w + nx) * channels
163	              sum += img.data[nidx + c].to_double()
164	              count += 1
165	            }
166	          }
167	        }
168	        let blurred = if count > 0 { sum / count.to_double() } else { 0.0 }
169	        let original = img.data[idx + c].to_double()
170	        // 非锐化掩模：sharpened = original + amount * (original - blurred)
171	        let diff = original - blurred
172	        let sharpened = if diff.abs() > threshold.to_double() {
173	          original + amount * diff
174	        } else {
175	          original	<-- UNCOVERED
176	        }
177	        out_data[idx + c] = sharpened.min(255.0).max(0.0).to_int().to_byte()
178	      }
179	    }
180	  }
181	  @types.Image::{
182	    width: w,
183	    height: h,
184	    channels,
185	    data: Bytes::from_array(out_data),
186	  }
187	}
   	…

191	pub fn color_enhance(
192	  img : @types.Image,
193	  saturation? : Double = 1.3,
194	  contrast? : Double = 1.1,
195	) -> @types.Image {
196	  let sat = saturation
197	  let con = contrast
198	  let w = img.width
199	  let h = img.height
200	  let channels = img.channels
201	  if channels < 3 {
202	    return img	<-- UNCOVERED
203	  }
204	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
205	  for y in 0..<h {
206	    for x in 0..<w {
207	      let idx = (y * w + x) * channels
208	      let r = img.data[idx].to_double()
209	      let g = img.data[idx + 1].to_double()
210	      let b = img.data[idx + 2].to_double()
211	      // 计算灰度值
212	      let gray = 0.299 * r + 0.587 * g + 0.114 * b
213	      // 饱和度增强
214	      let new_r = gray + (r - gray) * sat
215	      let new_g = gray + (g - gray) * sat
216	      let new_b = gray + (b - gray) * sat
217	      // 对比度增强
218	      let final_r = (new_r - 128.0) * con + 128.0
219	      let final_g = (new_g - 128.0) * con + 128.0
220	      let final_b = (new_b - 128.0) * con + 128.0
221	      out_data[idx] = final_r.min(255.0).max(0.0).to_int().to_byte()
222	      out_data[idx + 1] = final_g.min(255.0).max(0.0).to_int().to_byte()
223	      out_data[idx + 2] = final_b.min(255.0).max(0.0).to_int().to_byte()
224	      // 复制其他通道
225	      for c in 3..<channels {
226	        out_data[idx + c] = img.data[idx + c]	<-- UNCOVERED
227	      }
228	    }
229	  }
230	  @types.Image::{
231	    width: w,
232	    height: h,
233	    channels,
234	    data: Bytes::from_array(out_data),
235	  }
236	}
   	…

317	pub fn white_balance(img : @types.Image) -> @types.Image {
318	  let w = img.width
319	  let h = img.height
320	  let channels = img.channels
321	  if channels < 3 {
322	    return img	<-- UNCOVERED
323	  }
324	  // 计算各通道平均值
325	  let mut sum_r = 0.0
326	  let mut sum_g = 0.0
327	  let mut sum_b = 0.0
328	  let total = (w * h).to_double()
329	  for y in 0..<h {
330	    for x in 0..<w {
331	      let idx = (y * w + x) * channels
332	      sum_r += img.data[idx].to_double()
333	      sum_g += img.data[idx + 1].to_double()
334	      sum_b += img.data[idx + 2].to_double()
335	    }
336	  }
337	  let avg_r = sum_r / total
338	  let avg_g = sum_g / total
339	  let avg_b = sum_b / total
340	  let avg_gray = (avg_r + avg_g + avg_b) / 3.0
341	  // 计算增益
342	  let gain_r = avg_gray / avg_r.max(1.0)
343	  let gain_g = avg_gray / avg_g.max(1.0)
344	  let gain_b = avg_gray / avg_b.max(1.0)
345	  // 应用增益
346	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
347	  for y in 0..<h {
348	    for x in 0..<w {
349	      let idx = (y * w + x) * channels
350	      out_data[idx] = (img.data[idx].to_double() * gain_r)
351	        .min(255.0)
352	        .max(0.0)
353	        .to_int()
354	        .to_byte()
355	      out_data[idx + 1] = (img.data[idx + 1].to_double() * gain_g)
356	        .min(255.0)
357	        .max(0.0)
358	        .to_int()
359	        .to_byte()
360	      out_data[idx + 2] = (img.data[idx + 2].to_double() * gain_b)
361	        .min(255.0)
362	        .max(0.0)
363	        .to_int()
364	        .to_byte()
365	      for c in 3..<channels {
366	        out_data[idx + c] = img.data[idx + c]	<-- UNCOVERED
367	      }
368	    }
369	  }
370	  @types.Image::{
371	    width: w,
372	    height: h,
373	    channels,
374	    data: Bytes::from_array(out_data),
375	  }
376	}
   	…

380	pub fn batch_photo_enhance(
381	  images : Array[@types.Image],
382	  method_type : String,
383	) -> Array[@types.Image] {
384	  let result : Array[@types.Image] = []
385	  for img in images {
386	    let enhanced = match method_type {
387	      "low_light" => low_light_enhance(img)
388	      "deblur" => deblur_motion(img, kernel_size=15, angle=0.0)	<-- UNCOVERED
389	      "color" => color_enhance(img, saturation=1.3, contrast=1.1)
390	      "clahe" => clahe_enhance(img, clip_limit=2.0, tile_size=8)
391	      "white_balance" => white_balance(img)
392	      _ => img
393	    }
394	    result.push(enhanced)
395	  }
396	  result
397	}
   	…

19 uncovered line(s) in src\process\pipeline\pipeline.mbt:

70	pub fn pipeline_describe(p : Pipeline) -> String {
71	  let parts : Array[String] = []
72	  for op in p.operations {
73	    let desc = match op {
74	      Operation::Resize(w, h) => "resize(\{w}x\{h})"
75	      Operation::Rotate(angle) => "rotate(\{angle}°)"	<-- UNCOVERED
76	      Operation::FlipH => "flip_h"
77	      Operation::FlipV => "flip_v"	<-- UNCOVERED
78	      Operation::Grayscale => "grayscale"
79	      Operation::Invert => "invert"	<-- UNCOVERED
80	      Operation::Blur(r) => "blur(\{r})"
81	      Operation::Sharpen(a) => "sharpen(\{a})"
82	      Operation::Brightness(v) => "brightness(\{v})"
83	      Operation::Contrast(v) => "contrast(\{v})"
84	      Operation::Custom(name) => "custom(\{name})"
85	    }
86	    parts.push(desc)
87	  }
88	  parts.join(" -> ")
89	}
  	…

142	pub fn batch_execute(tasks : Array[BatchTask]) -> BatchResult {
143	  let total = tasks.length()
144	  let mut succeeded = 0
145	  let mut failed = 0
146	  let results : Array[ProcessResult] = []
147	  for task in tasks {
148	    // 模拟处理：实际实现需要文件 I/O
149	    let success = task.pipeline.operations.length() >= 0
150	    if success {
151	      succeeded += 1
152	      results.push(ProcessResult::{
153	        success: true,
154	        message: "OK",
155	        output_size: 0,
156	      })
157	    } else {
158	      failed += 1	<-- UNCOVERED
159	      results.push(ProcessResult::{
160	        success: false,
161	        message: "Failed",
162	        output_size: 0,
163	      })
164	    }
165	  }
166	  BatchResult::{ total, succeeded, failed, results }
167	}
   	…

197	pub fn cli_parse(args : Array[String]) -> CliCommand {
198	  if args.length() == 0 {
199	    return CliCommand::Help
200	  }
201	  let cmd = args[0]
202	  if cmd == "convert" {
203	    if args.length() >= 3 {
204	      CliCommand::Convert(args[1], args[2])
205	    } else {
206	      CliCommand::Help	<-- UNCOVERED
207	    }
208	  } else if cmd == "resize" {
209	    if args.length() >= 5 {
210	      CliCommand::Resize(
211	        args[1],
212	        args[2],
213	        parse_int(args[3]),
214	        parse_int(args[4]),
215	      )
216	    } else {
217	      CliCommand::Help	<-- UNCOVERED
218	    }
219	  } else if cmd == "rotate" {
220	    if args.length() >= 4 {	<-- UNCOVERED
221	      CliCommand::Rotate(args[1], args[2], parse_double(args[3]))
222	    } else {
223	      CliCommand::Help	<-- UNCOVERED
224	    }
225	  } else if cmd == "batch" {
226	    if args.length() >= 4 {	<-- UNCOVERED
227	      CliCommand::Batch(args[1], args[2], args[3])
228	    } else {
229	      CliCommand::Help	<-- UNCOVERED
230	    }
231	  } else if cmd == "info" {
232	    if args.length() >= 2 {
233	      CliCommand::Info(args[1])
234	    } else {
235	      CliCommand::Help	<-- UNCOVERED
236	    }
237	  } else if cmd == "help" {
238	    CliCommand::Help	<-- UNCOVERED
239	  } else {
240	    CliCommand::Help
241	  }
242	}
   	…

259	fn parse_int(s : String) -> Int {
260	  let mut result = 0
261	  let mut sign = 1
262	  let mut i = 0
263	  let bytes = s.to_array()
264	  if bytes.length() > 0 && bytes[0] == '-' {
265	    sign = -1	<-- UNCOVERED
266	    i = 1
267	  }
268	  while i < bytes.length() {
269	    let c = bytes[i].to_int()
270	    if c >= 0x30 && c <= 0x39 {
271	      result = result * 10 + (c - 0x30)
272	    }
273	    i += 1
274	  }
275	  result * sign
276	}
   	…

280	fn parse_double(s : String) -> Double {
281	  let mut result = 0.0	<-- UNCOVERED
282	  let mut sign = 1.0
283	  let mut i = 0
284	  let mut decimal = false
285	  let mut divisor = 10.0
286	  let bytes = s.to_array()	<-- UNCOVERED
287	  if bytes.length() > 0 && bytes[0] == '-' {
288	    sign = -1.0
289	    i = 1
290	  }
291	  while i < bytes.length() {	<-- UNCOVERED
292	    let c = bytes[i].to_int()
293	    if c == 0x2E {
294	      decimal = true	<-- UNCOVERED
295	    } else if c >= 0x30 && c <= 0x39 {
296	      let digit = (c - 0x30).to_double()
297	      if decimal {
298	        result += digit / divisor	<-- UNCOVERED
299	        divisor *= 10.0
300	      } else {
301	        result = result * 10.0 + digit	<-- UNCOVERED
302	      }
303	    }
304	    i += 1
305	  }
306	  result * sign
307	}
   	…

15 uncovered line(s) in src\process\pose_estimation\human_pose_estimation.mbt:

373	pub fn extract_skeleton(pose : HumanPose) -> Array[SkeletonConnection] {
374	  let connections = get_coco_skeleton_connections()
375	  let skeleton : Array[SkeletonConnection] = []
376	
377	  for conn in connections {
378	    let j1 = conn[0]
379	    let j2 = conn[1]
380	    if j1 < pose.keypoints.length() && j2 < pose.keypoints.length() {
381	      let k1 = pose.keypoints[j1]
382	      let k2 = pose.keypoints[j2]
383	      let dx = k2.x - k1.x
384	      let dy = k2.y - k1.y
385	      let length = (dx * dx + dy * dy).sqrt()
386	      let angle = if length > 0.0 {
387	        let sin_val = dy / length
388	        // 简化的反正弦近似
389	        sin_val * 57.3
390	      } else {
391	        0.0	<-- UNCOVERED
392	      }
393	      skeleton.push(SkeletonConnection::{
394	        joint1: j1,
395	        joint2: j2,
396	        confidence: (k1.confidence + k2.confidence) / 2.0,
397	        length,
398	        angle,
399	      })
400	    }
401	  }
402	
403	  skeleton
404	}
   	…

408	pub fn calculate_joint_angle(
409	  pose : HumanPose,
410	  joint1 : Int,
411	  joint_center : Int,
412	  joint2 : Int,
413	) -> Double {
414	  if joint1 < pose.keypoints.length() &&
415	    joint_center < pose.keypoints.length() &&
416	    joint2 < pose.keypoints.length() {
417	    let k1 = pose.keypoints[joint1]
418	    let kc = pose.keypoints[joint_center]
419	    let k2 = pose.keypoints[joint2]
420	
421	    let v1x = k1.x - kc.x
422	    let v1y = k1.y - kc.y
423	    let v2x = k2.x - kc.x
424	    let v2y = k2.y - kc.y
425	
426	    let dot = v1x * v2x + v1y * v2y
427	    let len1 = (v1x * v1x + v1y * v1y).sqrt()
428	    let len2 = (v2x * v2x + v2y * v2y).sqrt()
429	
430	    if len1 > 0.0 && len2 > 0.0 {
431	      let cos_angle = dot / (len1 * len2)
432	      let clamped_cos = if cos_angle < -1.0 {
433	        -1.0	<-- UNCOVERED
434	      } else if cos_angle > 1.0 {
435	        1.0	<-- UNCOVERED
436	      } else {
437	        cos_angle
438	      }
439	      // 简化的反余弦
440	      (1.0 - clamped_cos) * 90.0
441	    } else {
442	      0.0	<-- UNCOVERED
443	    }
444	  } else {
445	    0.0
446	  }
447	}
   	…

451	pub fn recognize_action(
452	  poses : Array[HumanPose],
453	  config : ActionRecognitionConfig,
454	) -> ActionRecognitionResult {
455	  ignore(config)
456	  if poses.length() == 0 {
457	    return ActionRecognitionResult::{
458	      action_name: "unknown",
459	      confidence: 0.0,
460	      action_id: -1,
461	      duration_frames: 0,
462	      start_frame: 0,
463	      end_frame: 0,
464	      is_completed: false,
465	    }
466	  }
467	
468	  let pose = poses[0]
469	
470	  // 计算关键关节角度
471	  let left_elbow_angle = calculate_joint_angle(pose, 5, 7, 9) // 左肩-左肘-左腕
472	  let right_elbow_angle = calculate_joint_angle(pose, 6, 8, 10) // 右肩-右肘-右腕
473	  let left_knee_angle = calculate_joint_angle(pose, 11, 13, 15) // 左髋-左膝-左踝
474	  let right_knee_angle = calculate_joint_angle(pose, 12, 14, 16) // 右髋-右膝-右踝
475	
476	  // 简化的动作分类
477	  let mut action_name = "walking"
478	  let mut confidence = 0.5
479	
480	  if left_elbow_angle < 60.0 && right_elbow_angle < 60.0 {
481	    action_name = "arms_crossed"	<-- UNCOVERED
482	    confidence = 0.7
483	  } else if left_elbow_angle > 150.0 && right_elbow_angle > 150.0 {
484	    action_name = "arms_raised"
485	    confidence = 0.75
486	  } else if left_knee_angle < 90.0 || right_knee_angle < 90.0 {	<-- UNCOVERED
487	    action_name = "squatting"
488	    confidence = 0.65
489	  } else if left_knee_angle > 160.0 && right_knee_angle > 160.0 {	<-- UNCOVERED
490	    action_name = "standing"
491	    confidence = 0.8
492	  } else {
493	    action_name = "walking"	<-- UNCOVERED
494	    confidence = 0.5
495	  }
496	
497	  ActionRecognitionResult::{
498	    action_name,
499	    confidence,
500	    action_id: 0,
501	    duration_frames: poses.length(),
502	    start_frame: 0,
503	    end_frame: poses.length() - 1,
504	    is_completed: true,
505	  }
506	}
   	…

510	pub fn recognize_gesture(pose : HumanPose) -> GestureRecognitionResult {
511	  // 简化版：基于手腕位置和手臂角度判断手势
512	  let left_wrist = if 9 < pose.keypoints.length() {
513	    pose.keypoints[9]
514	  } else {
515	    Keypoint::{	<-- UNCOVERED
516	      x: 0.0,
517	      y: 0.0,
518	      confidence: 0.0,
519	      is_visible: false,
520	      part_name: "",
521	    }
522	  }
523	  let right_wrist = if 10 < pose.keypoints.length() {
524	    pose.keypoints[10]
525	  } else {
526	    Keypoint::{	<-- UNCOVERED
527	      x: 0.0,
528	      y: 0.0,
529	      confidence: 0.0,
530	      is_visible: false,
531	      part_name: "",
532	    }
533	  }
534	
535	  let left_shoulder = if 5 < pose.keypoints.length() {
536	    pose.keypoints[5]
537	  } else {
538	    Keypoint::{	<-- UNCOVERED
539	      x: 0.0,
540	      y: 0.0,
541	      confidence: 0.0,
542	      is_visible: false,
543	      part_name: "",
544	    }
545	  }
546	  let right_shoulder = if 6 < pose.keypoints.length() {
547	    pose.keypoints[6]
548	  } else {
549	    Keypoint::{	<-- UNCOVERED
550	      x: 0.0,
551	      y: 0.0,
552	      confidence: 0.0,
553	      is_visible: false,
554	      part_name: "",
555	    }
556	  }
557	
558	  // 简化的手势分类
559	  let mut gesture_type = GestureType::Unknown
560	  let mut confidence = 0.3
561	  let mut is_left_hand = false
562	
563	  let left_arm_up = left_wrist.y < left_shoulder.y
564	  let right_arm_up = right_wrist.y < right_shoulder.y
565	
566	  if right_arm_up && !left_arm_up {
567	    gesture_type = GestureType::Pointing	<-- UNCOVERED
568	    confidence = 0.6
569	    is_left_hand = false
570	  } else if left_arm_up && right_arm_up {
571	    gesture_type = GestureType::Victory	<-- UNCOVERED
572	    confidence = 0.55
573	    is_left_hand = false
574	  } else if !left_arm_up && !right_arm_up {
575	    gesture_type = GestureType::OpenPalm
576	    confidence = 0.5
577	    is_left_hand = false
578	  } else {
579	    gesture_type = GestureType::Unknown	<-- UNCOVERED
580	    confidence = 0.3
581	    is_left_hand = false
582	  }
583	
584	  GestureRecognitionResult::{
585	    gesture_type,
586	    confidence,
587	    hand_keypoints: [],
588	    is_left_hand,
589	    bounding_box: [0.0, 0.0, 100.0, 100.0],
590	  }
591	}
   	…

19 uncovered line(s) in src\process\production_utils\production_utils.mbt:

19	pub fn validate_image(img : @types.Image) -> ImageValidationResult {
20	  let errors : Array[String] = []
21	  let warnings : Array[String] = []
22	
23	  // 验证尺寸
24	  if img.width <= 0 {
25	    errors.push("宽度必须大于0")	<-- UNCOVERED
26	  }
27	  if img.height <= 0 {
28	    errors.push("高度必须大于0")	<-- UNCOVERED
29	  }
30	  if img.width > 65535 {
31	    warnings.push("宽度超过65535，可能导致内存问题")	<-- UNCOVERED
32	  }
33	  if img.height > 65535 {
34	    warnings.push("高度超过65535，可能导致内存问题")	<-- UNCOVERED
35	  }
36	
37	  // 验证通道数
38	  if img.channels < 1 || img.channels > 4 {
39	    errors.push("通道数必须在1-4之间")	<-- UNCOVERED
40	  }
41	
42	  // 验证数据大小
43	  let expected_size = img.width * img.height * img.channels
44	  let actual_size = img.data.length()
45	  if actual_size != expected_size {
46	    errors.push(
47	      "数据大小不匹配：期望 " +
48	      expected_size.to_string() +
49	      "，实际 " +
50	      actual_size.to_string(),
51	    )
52	  }
53	
54	  // 检查潜在的整数溢出
55	  if img.width > 0 && img.height > 0 {
56	    let pixel_count = img.width * img.height
57	    if pixel_count / img.width != img.height {
58	      errors.push("像素计数溢出")	<-- UNCOVERED
59	    }
60	  }
61	
62	  ImageValidationResult::{
63	    is_valid: errors.length() == 0,
64	    errors,
65	    warnings,
66	    width: img.width,
67	    height: img.height,
68	    channels: img.channels,
69	    data_size: actual_size,
70	    expected_data_size: expected_size,
71	  }
72	}
  	…

76	pub fn validate_image_or_raise(
77	  img : @types.Image,
78	) -> Unit raise @types.LoadError {
79	  let result = validate_image(img)
80	  if !result.is_valid {
81	    let mut error_msg = "图像验证失败："	<-- UNCOVERED
82	    for err in result.errors {
83	      error_msg = error_msg + err + "; "	<-- UNCOVERED
84	    }
85	    raise @types.LoadError::DecodeFailed(error_msg)
86	  }
87	}
  	…

105	pub fn validate_channels_or_raise(
106	  img : @types.Image,
107	  expected_channels : Array[Int],
108	  function_name : String,
109	) -> Unit raise @types.LoadError {
110	  if !validate_channels(img, expected_channels) {
111	    let mut expected_str = ""	<-- UNCOVERED
112	    for i in 0..<expected_channels.length() {
113	      if i > 0 {
114	        expected_str = expected_str + ", "
115	      }
116	      expected_str = expected_str + expected_channels[i].to_string()	<-- UNCOVERED
117	    }
118	    raise @types.LoadError::DecodeFailed(
119	      function_name +
120	      " 需要通道数为 [" +
121	      expected_str +
122	      "]，实际为 " +
123	      img.channels.to_string(),	<-- UNCOVERED
124	    )
125	  }
126	}
   	…

153	pub fn validate_range_or_raise(
154	  value : Double,
155	  min : Double,
156	  max : Double,
157	  param_name : String,
158	) -> Unit raise @types.LoadError {
159	  let result = validate_range(value, min, max, param_name)
160	  match result {
161	    Result::Err(msg) => raise @types.LoadError::DecodeFailed(msg)	<-- UNCOVERED
162	    Result::Ok(_) => ()
163	  }
164	}
   	…

191	pub fn validate_int_range_or_raise(
192	  value : Int,
193	  min : Int,
194	  max : Int,
195	  param_name : String,
196	) -> Unit raise @types.LoadError {
197	  let result = validate_int_range(value, min, max, param_name)
198	  match result {
199	    Result::Err(msg) => raise @types.LoadError::DecodeFailed(msg)	<-- UNCOVERED
200	    Result::Ok(_) => ()
201	  }
202	}
   	…

349	pub fn calculate_retry_delay(config : RetryConfig, attempt : Int) -> Double {
350	  let mut factor = 1.0
351	  for _ in 0..<attempt {
352	    factor = factor * config.backoff_factor
353	  }
354	  let base_delay = config.initial_delay_ms * factor
355	  if base_delay > config.max_delay_ms {
356	    config.max_delay_ms	<-- UNCOVERED
357	  } else {
358	    base_delay
359	  }
360	}
   	…

364	pub fn validation_result_to_string(result : ImageValidationResult) -> String {
365	  let mut report = "## 图像验证报告\n\n"
366	  report = report + "| 指标 | 值 |\n|------|------|\n"
367	  report = report +
368	    "| 验证结果 | " +
369	    (if result.is_valid { "通过" } else { "失败" }) +
370	    " |\n"
371	  report = report + "| 宽度 | " + result.width.to_string() + " |\n"
372	  report = report + "| 高度 | " + result.height.to_string() + " |\n"
373	  report = report + "| 通道数 | " + result.channels.to_string() + " |\n"
374	  report = report + "| 数据大小 | " + result.data_size.to_string() + " |\n"
375	  report = report +
376	    "| 期望数据大小 | " +
377	    result.expected_data_size.to_string() +
378	    " |\n"
379	
380	  if result.errors.length() > 0 {
381	    report = report + "\n### 错误\n\n"	<-- UNCOVERED
382	    for err in result.errors {
383	      report = report + "- " + err + "\n"	<-- UNCOVERED
384	    }
385	  }
386	
387	  if result.warnings.length() > 0 {
388	    report = report + "\n### 警告\n\n"	<-- UNCOVERED
389	    for warn in result.warnings {
390	      report = report + "- " + warn + "\n"	<-- UNCOVERED
391	    }
392	  }
393	
394	  report
395	}
   	…

399	pub fn production_error_to_string(err : ProductionError) -> String {
400	  let code_str = match err.code {
401	    ProductionErrorCode::InvalidInput => "无效输入"
402	    ProductionErrorCode::OutOfMemory => "内存不足"	<-- UNCOVERED
403	    ProductionErrorCode::IndexOutOfBounds => "索引越界"
404	    ProductionErrorCode::UnsupportedFormat => "不支持的格式"
405	    ProductionErrorCode::OperationFailed => "操作失败"
406	    ProductionErrorCode::Timeout => "超时"
407	    ProductionErrorCode::InternalError => "内部错误"
408	    _ => "未知错误"
409	  }
410	
411	  "[" + code_str + "] " + err.function_name + ": " + err.message
412	}
   	…

12 uncovered line(s) in src\process\quality\image_quality.mbt:

15	pub fn compute_mse(img1 : @types.Image, img2 : @types.Image) -> Double {
16	  if img1.width != img2.width || img1.height != img2.height {
17	    return -1.0	<-- UNCOVERED
18	  }
19	  let w = img1.width
20	  let h = img1.height
21	  let channels = img1.channels.min(img2.channels)
22	  let mut sum = 0.0
23	  let mut count = 0
24	  for y in 0..<h {
25	    for x in 0..<w {
26	      let idx = (y * w + x) * img1.channels
27	      let idx2 = (y * w + x) * img2.channels
28	      for c in 0..<channels {
29	        let diff = img1.data[idx + c].to_double() -
30	          img2.data[idx2 + c].to_double()
31	        sum += diff * diff
32	        count += 1
33	      }
34	    }
35	  }
36	  if count > 0 {
37	    sum / count.to_double()
38	  } else {
39	    0.0	<-- UNCOVERED
40	  }
41	}
  	…

45	pub fn compute_mae(img1 : @types.Image, img2 : @types.Image) -> Double {
46	  if img1.width != img2.width || img1.height != img2.height {
47	    return -1.0	<-- UNCOVERED
48	  }
49	  let w = img1.width
50	  let h = img1.height
51	  let channels = img1.channels.min(img2.channels)
52	  let mut sum = 0.0
53	  let mut count = 0
54	  for y in 0..<h {
55	    for x in 0..<w {
56	      let idx = (y * w + x) * img1.channels
57	      let idx2 = (y * w + x) * img2.channels
58	      for c in 0..<channels {
59	        let diff = (img1.data[idx + c].to_double() -
60	        img2.data[idx2 + c].to_double()).abs()
61	        sum += diff
62	        count += 1
63	      }
64	    }
65	  }
66	  if count > 0 {
67	    sum / count.to_double()
68	  } else {
69	    0.0	<-- UNCOVERED
70	  }
71	}
  	…

75	pub fn compute_psnr(img1 : @types.Image, img2 : @types.Image) -> Double {
76	  let mse = compute_mse(img1, img2)
77	  if mse < 0.0 {
78	    return -1.0	<-- UNCOVERED
79	  }
80	  if mse < 0.000001 {
81	    return 100.0 // 几乎完全相同
82	  }
83	  let max_pixel = 255.0
84	  10.0 * @math.log10(max_pixel * max_pixel / mse)	<-- UNCOVERED
85	}
  	…

 90	pub fn compute_ssim(img1 : @types.Image, img2 : @types.Image) -> Double {
 91	  if img1.width != img2.width || img1.height != img2.height {
 92	    return -1.0	<-- UNCOVERED
 93	  }
 94	  let w = img1.width
 95	  let h = img1.height
 96	  let channels = img1.channels.min(img2.channels)
 97	  let n = (w * h * channels).to_double()
 98	  if n == 0.0 {
 99	    return 0.0	<-- UNCOVERED
100	  }
101	  // 计算均值
102	  let mut mean1 = 0.0
103	  let mut mean2 = 0.0
104	  for y in 0..<h {
105	    for x in 0..<w {
106	      let idx = (y * w + x) * img1.channels
107	      let idx2 = (y * w + x) * img2.channels
108	      for c in 0..<channels {
109	        mean1 += img1.data[idx + c].to_double()
110	        mean2 += img2.data[idx2 + c].to_double()
111	      }
112	    }
113	  }
114	  mean1 = mean1 / n
115	  mean2 = mean2 / n
116	  // 计算方差和协方差
117	  let mut var1 = 0.0
118	  let mut var2 = 0.0
119	  let mut covar = 0.0
120	  for y in 0..<h {
121	    for x in 0..<w {
122	      let idx = (y * w + x) * img1.channels
123	      let idx2 = (y * w + x) * img2.channels
124	      for c in 0..<channels {
125	        let v1 = img1.data[idx + c].to_double() - mean1
126	        let v2 = img2.data[idx2 + c].to_double() - mean2
127	        var1 += v1 * v1
128	        var2 += v2 * v2
129	        covar += v1 * v2
130	      }
131	    }
132	  }
133	  var1 = var1 / n
134	  var2 = var2 / n
135	  covar = covar / n
136	  // SSIM 公式
137	  let c1 = 0.01 * 255.0 * (0.01 * 255.0)
138	  let c2 = 0.03 * 255.0 * (0.03 * 255.0)
139	  let numerator = (2.0 * mean1 * mean2 + c1) * (2.0 * covar + c2)
140	  let denominator = (mean1 * mean1 + mean2 * mean2 + c1) * (var1 + var2 + c2)
141	  if denominator > 0.0 {
142	    numerator / denominator
143	  } else {
144	    0.0	<-- UNCOVERED
145	  }
146	}
   	…

163	pub fn images_equal(img1 : @types.Image, img2 : @types.Image) -> Bool {
164	  if img1.width != img2.width ||
165	    img1.height != img2.height ||
166	    img1.channels != img2.channels {
167	    return false	<-- UNCOVERED
168	  }
169	  let w = img1.width
170	  let h = img1.height
171	  let channels = img1.channels
172	  for y in 0..<h {
173	    for x in 0..<w {
174	      let idx = (y * w + x) * channels
175	      for c in 0..<channels {
176	        if img1.data[idx + c] != img2.data[idx + c] {
177	          return false
178	        }
179	      }
180	    }
181	  }
182	  true
183	}
   	…

187	pub fn compute_diff_image(
188	  img1 : @types.Image,
189	  img2 : @types.Image,
190	) -> @types.Image {
191	  if img1.width != img2.width || img1.height != img2.height {
192	    return img1	<-- UNCOVERED
193	  }
194	  let w = img1.width
195	  let h = img1.height
196	  let channels = img1.channels.min(img2.channels)
197	  let out_channels = if channels >= 3 { 3 } else { 1 }
198	  let out_data : Array[Byte] = Array::make(w * h * out_channels, b'\x00')
199	  for y in 0..<h {
200	    for x in 0..<w {
201	      let idx = (y * w + x) * img1.channels
202	      let idx2 = (y * w + x) * img2.channels
203	      let out_idx = (y * w + x) * out_channels
204	      let mut max_diff = 0
205	      for c in 0..<channels {
206	        let diff = (img1.data[idx + c].to_int() - img2.data[idx2 + c].to_int()).abs()
207	        if diff > max_diff {
208	          max_diff = diff
209	        }
210	      }
211	      if out_channels >= 3 {
212	        // 红色表示差异大
213	        out_data[out_idx] = max_diff.to_byte()	<-- UNCOVERED
214	        out_data[out_idx + 1] = b'\x00'
215	        out_data[out_idx + 2] = b'\x00'
216	      } else {
217	        out_data[out_idx] = max_diff.to_byte()
218	      }
219	    }
220	  }
221	  @types.Image::{
222	    width: w,
223	    height: h,
224	    channels: out_channels,
225	    data: Bytes::from_array(out_data),
226	  }
227	}
   	…

4 uncovered line(s) in src\process\registration\image_registration.mbt:

81	fn to_gray(img : @types.Image) -> Array[Double] {
82	  let w = img.width
83	  let h = img.height
84	  let channels = img.channels
85	  let gray : Array[Double] = Array::make(w * h, 0.0)
86	  for i in 0..<(w * h) {
87	    let idx = i * channels
88	    let r = img.data[idx].to_double()
89	    let g = if channels >= 2 { img.data[idx + 1].to_double() } else { r }	<-- UNCOVERED
90	    let b = if channels >= 3 { img.data[idx + 2].to_double() } else { r }
91	    gray[i] = 0.299 * r + 0.587 * g + 0.114 * b
92	  }
93	  gray
94	}
  	…

 98	pub fn estimate_translation(
 99	  img1 : @types.Image,
100	  img2 : @types.Image,
101	  max_offset? : Int = 20,
102	) -> RegistrationResult {
103	  let max_off = max_offset
104	  if img1.width != img2.width || img1.height != img2.height {
105	    return RegistrationResult::{	<-- UNCOVERED
106	      dx: 0.0,
107	      dy: 0.0,
108	      rotation: 0.0,
109	      scale: 1.0,
110	      confidence: 0.0,
111	    }
112	  }
113	  let w = img1.width
114	  let h = img1.height
115	  let gray1 = to_gray(img1)
116	  let gray2 = to_gray(img2)
117	  // 计算均值
118	  let mut mean1 = 0.0
119	  let mut mean2 = 0.0
120	  for i in 0..<(w * h) {
121	    mean1 += gray1[i]
122	    mean2 += gray2[i]
123	  }
124	  mean1 = mean1 / (w * h).to_double()
125	  mean2 = mean2 / (w * h).to_double()
126	  // 搜索最佳平移
127	  let mut best_dx = 0
128	  let mut best_dy = 0
129	  let mut best_corr = -10000000000.0
130	  for dy in -max_off..<(max_off + 1) {
131	    for dx in -max_off..<(max_off + 1) {
132	      let mut corr = 0.0
133	      let mut var1 = 0.0
134	      let mut var2 = 0.0
135	      let mut count = 0
136	      for y in 0..<h {
137	        for x in 0..<w {
138	          let x2 = x + dx
139	          let y2 = y + dy
140	          if x2 >= 0 && x2 < w && y2 >= 0 && y2 < h {
141	            let v1 = gray1[y * w + x] - mean1
142	            let v2 = gray2[y2 * w + x2] - mean2
143	            corr += v1 * v2
144	            var1 += v1 * v1
145	            var2 += v2 * v2
146	            count += 1
147	          }
148	        }
149	      }
150	      if count > 0 && var1 > 0.0 && var2 > 0.0 {
151	        let normalized_corr = corr / (var1.sqrt() * var2.sqrt())
152	        if normalized_corr > best_corr {
153	          best_corr = normalized_corr
154	          best_dx = dx
155	          best_dy = dy
156	        }
157	      }
158	    }
159	  }
160	  RegistrationResult::{
161	    dx: best_dx.to_double(),
162	    dy: best_dy.to_double(),
163	    rotation: 0.0,
164	    scale: 1.0,
165	    confidence: if best_corr > 0.0 {
166	      best_corr
167	    } else {
168	      0.0	<-- UNCOVERED
169	    },
170	  }
171	}
   	…

175	pub fn warp_affine_transform(
176	  img : @types.Image,
177	  transform : AffineTransform,
178	  out_width? : Int = 0,
179	  out_height? : Int = 0,
180	) -> @types.Image {
181	  let ow = if out_width == 0 { img.width } else { out_width }
182	  let oh = if out_height == 0 { img.height } else { out_height }
183	  let w = img.width
184	  let h = img.height
185	  let channels = img.channels
186	  let out_data : Array[Byte] = Array::make(ow * oh * channels, b'\x00')
187	  // 计算逆变换
188	  let det = transform.a * transform.e - transform.b * transform.d
189	  if det.abs() < 0.000001 {
190	    return img	<-- UNCOVERED
191	  }
192	  let inv_a = transform.e / det
193	  let inv_b = -transform.b / det
194	  let inv_c = (transform.b * transform.f - transform.e * transform.c) / det
195	  let inv_d = -transform.d / det
196	  let inv_e = transform.a / det
197	  let inv_f = (transform.d * transform.c - transform.a * transform.f) / det
198	  for y in 0..<oh {
199	    for x in 0..<ow {
200	      // 反向映射
201	      let src_x = inv_a * x.to_double() + inv_b * y.to_double() + inv_c
202	      let src_y = inv_d * x.to_double() + inv_e * y.to_double() + inv_f
203	      // 双线性插值
204	      let x0 = src_x.floor().to_int()
205	      let y0 = src_y.floor().to_int()
206	      let x1 = x0 + 1
207	      let y1 = y0 + 1
208	      let fx = src_x - x0.to_double()
209	      let fy = src_y - y0.to_double()
210	      let out_idx = (y * ow + x) * channels
211	      for c in 0..<channels {
212	        if x0 >= 0 && x1 < w && y0 >= 0 && y1 < h {
213	          let v00 = img.data[(y0 * w + x0) * channels + c].to_double()
214	          let v01 = img.data[(y0 * w + x1) * channels + c].to_double()
215	          let v10 = img.data[(y1 * w + x0) * channels + c].to_double()
216	          let v11 = img.data[(y1 * w + x1) * channels + c].to_double()
217	          let top = v00 * (1.0 - fx) + v01 * fx
218	          let bottom = v10 * (1.0 - fx) + v11 * fx
219	          let value = top * (1.0 - fy) + bottom * fy
220	          out_data[out_idx + c] = value.min(255.0).max(0.0).to_int().to_byte()
221	        } else {
222	          out_data[out_idx + c] = b'\x00'
223	        }
224	      }
225	    }
226	  }
227	  @types.Image::{
228	    width: ow,
229	    height: oh,
230	    channels,
231	    data: Bytes::from_array(out_data),
232	  }
233	}
   	…

16 uncovered line(s) in src\process\registration_advanced\advanced_registration.mbt:

228	pub fn normalized_cross_correlation(
229	  img1 : @types.Image,
230	  img2 : @types.Image,
231	) -> Double {
232	  let w = img1.width
233	  let h = img1.height
234	  let total = w * h
235	
236	  // 计算均值
237	  let mut mean1 = 0.0
238	  let mut mean2 = 0.0
239	  for i in 0..<total {
240	    mean1 += img1.data[i].to_double()
241	    mean2 += img2.data[i].to_double()
242	  }
243	  mean1 /= total.to_double()
244	  mean2 /= total.to_double()
245	
246	  // 计算 NCC
247	  let mut numerator = 0.0
248	  let mut denom1 = 0.0
249	  let mut denom2 = 0.0
250	  for i in 0..<total {
251	    let p1 = img1.data[i].to_double() - mean1
252	    let p2 = img2.data[i].to_double() - mean2
253	    numerator += p1 * p2
254	    denom1 += p1 * p1
255	    denom2 += p2 * p2
256	  }
257	
258	  if denom1 > 0.0 && denom2 > 0.0 {
259	    numerator / (denom1.sqrt() * denom2.sqrt())
260	  } else {
261	    0.0	<-- UNCOVERED
262	  }
263	}
   	…

313	pub fn detect_harris_corners(
314	  img : @types.Image,
315	  threshold : Double,
316	) -> Array[FeaturePoint] {
317	  let w = img.width
318	  let h = img.height
319	  let points : Array[FeaturePoint] = []
320	
321	  // 计算梯度
322	  for y in 1..<(h - 1) {
323	    for x in 1..<(w - 1) {
324	      let idx = y * w + x
325	      let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
326	      let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
327	
328	      // Harris 响应（简化版）
329	      let response = gx * gx * gy * gy -
330	        gx * gy * (gx * gy) * 0.04 -
331	        (gx * gx + gy * gy) * (gx * gx + gy * gy) * 0.04
332	
333	      if response > threshold {
334	        points.push(FeaturePoint::{	<-- UNCOVERED
335	          x: x.to_double(),
336	          y: y.to_double(),
337	          response,
338	          orientation: 0.0,
339	          scale: 1.0,
340	          descriptor: [],
341	        })
342	      }
343	    }
344	  }
345	
346	  points
347	}
   	…

351	pub fn match_feature_points(
352	  points1 : Array[FeaturePoint],
353	  points2 : Array[FeaturePoint],
354	  max_distance : Double,
355	) -> Array[FeatureMatch] {
356	  let matches : Array[FeatureMatch] = []
357	
358	  for p1 in points1 {
359	    let mut best_match : FeaturePoint = p1	<-- UNCOVERED
360	    let mut best_dist = max_distance
361	    for p2 in points2 {
362	      let dx = p1.x - p2.x	<-- UNCOVERED
363	      let dy = p1.y - p2.y
364	      let dist = (dx * dx + dy * dy).sqrt()	<-- UNCOVERED
365	      if dist < best_dist {
366	        best_dist = dist	<-- UNCOVERED
367	        best_match = p2
368	      }
369	    }
370	    if best_dist < max_distance {
371	      matches.push(FeatureMatch::{	<-- UNCOVERED
372	        point1: p1,
373	        point2: best_match,
374	        distance: best_dist,
375	        is_inlier: true,
376	      })
377	    }
378	  }
379	
380	  matches
381	}
   	…

385	pub fn estimate_affine_transform(
386	  matches : Array[FeatureMatch],
387	) -> TransformMatrix {
388	  // 简化：使用前3个匹配对估计仿射变换
389	  if matches.length() >= 3 {
390	    let m1 = matches[0]	<-- UNCOVERED
391	    let m2 = matches[1]
392	    let m3 = matches[2]
393	
394	    // 简化的仿射矩阵
395	    let a = (m2.point2.x - m1.point2.x) / (m2.point1.x - m1.point1.x + 0.001)
396	    let b = (m3.point2.y - m1.point2.y) / (m3.point1.y - m1.point1.y + 0.001)
397	    let tx = m1.point2.x - a * m1.point1.x
398	    let ty = m1.point2.y - b * m1.point1.y
399	
400	    TransformMatrix::{
401	      matrix: [a, 0.0, tx, 0.0, b, ty, 0.0, 0.0, 1.0],
402	      transform_type: TransformType::Affine,
403	      width: 3,
404	      height: 3,
405	    }
406	  } else {
407	    TransformMatrix::{
408	      matrix: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
409	      transform_type: TransformType::Unknown,
410	      width: 3,
411	      height: 3,
412	    }
413	  }
414	}
   	…

418	pub fn apply_affine_transform(
419	  img : @types.Image,
420	  matrix : TransformMatrix,
421	) -> @types.Image {
422	  let w = img.width
423	  let h = img.height
424	  let _ = w * h
425	  let out : Array[Byte] = []
426	
427	  let a = matrix.matrix[0]
428	  let b = matrix.matrix[1]
429	  let tx = matrix.matrix[2]
430	  let c = matrix.matrix[3]
431	  let d = matrix.matrix[4]
432	  let ty = matrix.matrix[5]
433	
434	  for y in 0..<h {
435	    for x in 0..<w {
436	      // 逆变换
437	      let src_x = (d * (x.to_double() - tx) - b * (y.to_double() - ty)) /
438	        (a * d - b * c + 0.001)
439	      let src_y = (-c * (x.to_double() - tx) + a * (y.to_double() - ty)) /
440	        (a * d - b * c + 0.001)
441	
442	      let sx = src_x.to_int()
443	      let sy = src_y.to_int()
444	
445	      if sx >= 0 && sx < w && sy >= 0 && sy < h {
446	        out.push(img.data[sy * w + sx])
447	      } else {
448	        out.push(b'\x00')	<-- UNCOVERED
449	      }
450	    }
451	  }
452	
453	  @types.Image::{
454	    width: w,
455	    height: h,
456	    channels: img.channels,
457	    data: Bytes::from_array(out),
458	  }
459	}
   	…

518	pub fn register_images_advanced(
519	  img1 : @types.Image,
520	  img2 : @types.Image,
521	  config : RegistrationConfigAdvanced,
522	) -> RegistrationResultAdvanced {
523	  if config.algorithm == RegistrationAlgorithm::PhaseCorrelation {
524	    phase_correlation_registration(img1, img2)
525	  } else if config.algorithm == RegistrationAlgorithm::FeatureBased {	<-- UNCOVERED
526	    feature_based_registration(img1, img2, config)
527	  } else {
528	    phase_correlation_registration(img1, img2)	<-- UNCOVERED
529	  }
530	}
   	…

534	pub fn registration_report_to_string_advanced(
535	  result : RegistrationResultAdvanced,
536	) -> String {
537	  let mut report = "## 高级图像配准报告\n\n"
538	  report = report + "### 配准信息\n\n"
539	  report = report + "| 指标 | 值 |\n|------|------|\n"
540	  report = report +
541	    "| 算法 | " +
542	    (if result.algorithm == RegistrationAlgorithm::PhaseCorrelation {
543	      "相位相关"
544	    } else if result.algorithm == RegistrationAlgorithm::FeatureBased {	<-- UNCOVERED
545	      "基于特征"
546	    } else if result.algorithm == RegistrationAlgorithm::IntensityBased {
547	      "基于强度"
548	    } else if result.algorithm == RegistrationAlgorithm::MutualInformation {
549	      "互信息"
550	    } else if result.algorithm ==
551	      RegistrationAlgorithm::NormalizedCrossCorrelation {
552	      "归一化互相关"	<-- UNCOVERED
553	    } else {
554	      "未知"	<-- UNCOVERED
555	    }) +
556	    " |\n"
557	  report = report +
558	    "| 变换类型 | " +
559	    (if result.transform_type == TransformType::Translation {
560	      "平移"
561	    } else if result.transform_type == TransformType::Rigid {	<-- UNCOVERED
562	      "刚性"
563	    } else if result.transform_type == TransformType::Similarity {
564	      "相似"
565	    } else if result.transform_type == TransformType::Affine {
566	      "仿射"
567	    } else if result.transform_type == TransformType::Perspective {
568	      "透视"
569	    } else {
570	      "未知"	<-- UNCOVERED
571	    }) +
572	    " |\n"
573	  report = report +
574	    "| 平移 X | " +
575	    result.translation_x.to_string() +
576	    " 像素 |\n"
577	  report = report +
578	    "| 平移 Y | " +
579	    result.translation_y.to_string() +
580	    " 像素 |\n"
581	  report = report +
582	    "| 旋转角度 | " +
583	    result.rotation_angle.to_string() +
584	    " 度 |\n"
585	  report = report + "| 缩放 X | " + result.scale_x.to_string() + " |\n"
586	  report = report + "| 缩放 Y | " + result.scale_y.to_string() + " |\n"
587	  report = report +
588	    "| 相关系数 | " +
589	    result.correlation.to_string() +
590	    " |\n"
591	  report = report +
592	    "| 互信息 | " +
593	    result.mutual_information.to_string() +
594	    " |\n"
595	  report = report + "| RMSE | " + result.rmse.to_string() + " |\n"
596	  report = report +
597	    "| 特征点数 | " +
598	    result.num_feature_points.to_string() +
599	    " |\n"
600	  report = report + "| 内点数 | " + result.num_inliers.to_string() + " |\n"
601	
602	  report
603	}
   	…

10 uncovered line(s) in src\process\reinforcement_learning\reinforcement_learning.mbt:

268	pub fn select_action(agent : RLAgent, state_idx : Int) -> Int {
269	  let mut state = 42
270	  state = (state * 11035 + 12345) % 1000000
271	  let random_val = (state % 1000).to_double() / 1000.0
272	
273	  if random_val < agent.epsilon {
274	    // 探索：随机选择
275	    state = (state * 11035 + 12345) % 1000000	<-- UNCOVERED
276	    state % agent.num_actions
277	  } else {
278	    // 利用：选择Q值最大的动作
279	    let mut best_action = 0
280	    let mut best_q = -1000000000.0
281	    for action in 0..<agent.num_actions {
282	      if state_idx < agent.num_states && action < agent.num_actions {
283	        let q = agent.q_table[state_idx][action]
284	        if q > best_q {
285	          best_q = q
286	          best_action = action
287	        }
288	      }
289	    }
290	    best_action
291	  }
292	}
   	…

296	pub fn q_learning_update(
297	  agent : RLAgent,
298	  state_idx : Int,
299	  action : Int,
300	  reward : Double,
301	  next_state_idx : Int,
302	  is_done : Bool,
303	) -> RLAgent {
304	  let q_table = agent.q_table
305	
306	  // 计算目标Q值
307	  let mut max_next_q = 0.0
308	  if !is_done && next_state_idx < agent.num_states {
309	    for a in 0..<agent.num_actions {
310	      if q_table[next_state_idx][a] > max_next_q {
311	        max_next_q = q_table[next_state_idx][a]	<-- UNCOVERED
312	      }
313	    }
314	  }
315	
316	  let target = reward + agent.gamma * max_next_q
317	  let current_q = q_table[state_idx][action]
318	  let new_q = current_q + agent.learning_rate * (target - current_q)
319	  q_table[state_idx][action] = new_q
320	
321	  // 衰减epsilon
322	  let new_epsilon = if agent.epsilon > agent.epsilon_min {
323	    agent.epsilon * agent.epsilon_decay
324	  } else {
325	    agent.epsilon_min	<-- UNCOVERED
326	  }
327	
328	  RLAgent::{
329	    algorithm: agent.algorithm,
330	    learning_rate: agent.learning_rate,
331	    gamma: agent.gamma,
332	    epsilon: new_epsilon,
333	    epsilon_decay: agent.epsilon_decay,
334	    epsilon_min: agent.epsilon_min,
335	    q_table,
336	    num_states: agent.num_states,
337	    num_actions: agent.num_actions,
338	    total_reward: agent.total_reward + reward,
339	    episodes_completed: agent.episodes_completed,
340	  }
341	}
   	…

345	pub fn add_experience(
346	  buffer : ReplayBuffer,
347	  experience : Experience,
348	) -> ReplayBuffer {
349	  if buffer.current_size < buffer.buffer_size {
350	    let new_buffer = buffer.buffer
351	    new_buffer.push(experience)
352	    ReplayBuffer::{
353	      buffer: new_buffer,
354	      buffer_size: buffer.buffer_size,
355	      current_size: buffer.current_size + 1,
356	      buffer_type: buffer.buffer_type,
357	      batch_size: buffer.batch_size,
358	      alpha: buffer.alpha,
359	      beta: buffer.beta,
360	    }
361	  } else {
362	    // 缓冲区满，替换最旧的
363	    buffer.buffer[0] = experience	<-- UNCOVERED
364	    buffer
365	  }
366	}
   	…

370	pub fn sample_experiences(
371	  buffer : ReplayBuffer,
372	  batch_size : Int,
373	) -> Array[Experience] {
374	  let sampled : Array[Experience] = []
375	  let actual_batch = if batch_size < buffer.current_size {
376	    batch_size	<-- UNCOVERED
377	  } else {
378	    buffer.current_size
379	  }
380	
381	  let mut state = 42
382	  for _ in 0..<actual_batch {
383	    state = (state * 11035 + 12345) % 1000000
384	    let idx = state % buffer.current_size
385	    sampled.push(buffer.buffer[idx])
386	  }
387	
388	  sampled
389	}
   	…

393	pub fn simulate_rl_training(
394	  config : RLConfig,
395	  env : RLEnvironment,
396	) -> RLTrainingResult {
397	  ignore(env)
398	  let reward_history : Array[Double] = []
399	  let loss_history : Array[Double] = []
400	  let epsilon_history : Array[Double] = []
401	
402	  let mut epsilon = config.epsilon_start
403	  let mut total_reward = 0.0
404	  let mut convergence_episode = config.num_episodes
405	
406	  for episode in 0..<config.num_episodes {
407	    // 模拟单回合奖励
408	    let progress = episode.to_double() / config.num_episodes.to_double()
409	    let episode_reward = -100.0 +
410	      progress * 200.0 +
411	      (episode % 10).to_double() * 5.0
412	    reward_history.push(episode_reward)
413	    total_reward += episode_reward
414	
415	    // 模拟损失
416	    let loss = 1.0 * (1.0 - progress) + 0.1
417	    loss_history.push(loss)
418	
419	    // 衰减epsilon
420	    epsilon = if epsilon > config.epsilon_end {
421	      epsilon * config.epsilon_decay
422	    } else {
423	      config.epsilon_end
424	    }
425	    epsilon_history.push(epsilon)
426	
427	    // 检查收敛
428	    if episode_reward > 190.0 && convergence_episode == config.num_episodes {
429	      convergence_episode = episode	<-- UNCOVERED
430	    }
431	  }
432	
433	  let average_reward = total_reward / config.num_episodes.to_double()
434	
435	  RLTrainingResult::{
436	    algorithm: config.algorithm,
437	    num_episodes: config.num_episodes,
438	    total_reward,
439	    average_reward,
440	    final_epsilon: epsilon,
441	    episodes_to_solve: convergence_episode,
442	    reward_history,
443	    loss_history,
444	    epsilon_history,
445	    training_time_s: config.num_episodes.to_double() * 0.1,
446	    convergence_episode,
447	  }
448	}
   	…

452	pub fn evaluate_policy(
453	  agent : RLAgent,
454	  env : RLEnvironment,
455	  num_episodes : Int,
456	) -> PolicyEvaluationResult {
457	  ignore(agent)
458	  ignore(env)
459	  let rewards : Array[Double] = []
460	  let mut total_reward = 0.0
461	  let mut success_count = 0
462	  let mut total_steps = 0
463	
464	  for episode in 0..<num_episodes {
465	    // 模拟评估
466	    let reward = 150.0 + (episode % 20).to_double() * 2.5
467	    rewards.push(reward)
468	    total_reward += reward
469	    total_steps += 100
470	
471	    if reward > 190.0 {
472	      success_count += 1	<-- UNCOVERED
473	    }
474	  }
475	
476	  let average_reward = total_reward / num_episodes.to_double()
477	  let mut variance = 0.0
478	  for r in rewards {
479	    variance += (r - average_reward) * (r - average_reward)
480	  }
481	  let std_reward = (variance / num_episodes.to_double()).sqrt()
482	
483	  let mut min_reward = 1000000000.0
484	  let mut max_reward = -1000000000.0
485	  for r in rewards {
486	    if r < min_reward {
487	      min_reward = r
488	    }
489	    if r > max_reward {
490	      max_reward = r
491	    }
492	  }
493	
494	  PolicyEvaluationResult::{
495	    average_reward,
496	    std_reward,
497	    min_reward,
498	    max_reward,
499	    num_episodes,
500	    success_rate: success_count.to_double() / num_episodes.to_double(),
501	    average_steps: total_steps.to_double() / num_episodes.to_double(),
502	  }
503	}
   	…

507	pub fn reinforcement_learning_report_to_string(
508	  result : RLTrainingResult,
509	  config : RLConfig,
510	  env : RLEnvironment,
511	) -> String {
512	  let mut report = "## 强化学习与决策系统报告\n\n"
513	
514	  report = report + "### 环境信息\n\n"
515	  report = report + "| 指标 | 值 |\n|------|------|\n"
516	  report = report + "| 环境名称 | " + env.env_name + " |\n"
517	  report = report + "| 状态数 | " + env.num_states.to_string() + " |\n"
518	  report = report + "| 动作数 | " + env.num_actions.to_string() + " |\n"
519	  report = report + "| 状态维度 | " + env.state_dim.to_string() + " |\n"
520	  report = report + "| 动作维度 | " + env.action_dim.to_string() + " |\n"
521	  report = report +
522	    "| 离散动作 | " +
523	    (if env.is_discrete { "是" } else { "否" }) +
524	    " |\n"
525	  report = report + "| 最大步数 | " + env.max_steps.to_string() + " |\n"
526	
527	  report = report + "\n### 算法配置\n\n"
528	  report = report + "| 指标 | 值 |\n|------|------|\n"
529	  report = report +
530	    "| 算法 | " +
531	    (match config.algorithm {
532	      RLAlgorithm::QLearning => "Q-Learning"	<-- UNCOVERED
533	      RLAlgorithm::DQN => "DQN"
534	      RLAlgorithm::DoubleDQN => "Double DQN"	<-- UNCOVERED
535	      RLAlgorithm::DuelingDQN => "Dueling DQN"
536	      RLAlgorithm::PPO => "PPO"
537	      RLAlgorithm::A2C => "A2C"
538	      RLAlgorithm::DDPG => "DDPG"
539	      RLAlgorithm::SAC => "SAC"
540	      _ => "未知"
541	    }) +
542	    " |\n"
543	  report = report +
544	    "| 探索策略 | " +
545	    (match config.exploration_strategy {
546	      ExplorationStrategy::EpsilonGreedy => "Epsilon-Greedy"
547	      ExplorationStrategy::Boltzmann => "Boltzmann"	<-- UNCOVERED
548	      ExplorationStrategy::UCB => "UCB"
549	      _ => "未知"
550	    }) +
551	    " |\n"
552	  report = report +
553	    "| 训练回合数 | " +
554	    config.num_episodes.to_string() +
555	    " |\n"
556	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
557	  report = report + "| 折扣因子 | " + config.gamma.to_string() + " |\n"
558	  report = report +
559	    "| 初始Epsilon | " +
560	    config.epsilon_start.to_string() +
561	    " |\n"
562	  report = report +
563	    "| 最终Epsilon | " +
564	    config.epsilon_end.to_string() +
565	    " |\n"
566	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
567	  report = report +
568	    "| 缓冲区大小 | " +
569	    config.buffer_size.to_string() +
570	    " |\n"
571	
572	  report = report + "\n### 训练结果\n\n"
573	  report = report + "| 指标 | 值 |\n|------|------|\n"
574	  report = report + "| 总奖励 | " + result.total_reward.to_string() + " |\n"
575	  report = report +
576	    "| 平均奖励 | " +
577	    result.average_reward.to_string() +
578	    " |\n"
579	  report = report +
580	    "| 最终Epsilon | " +
581	    result.final_epsilon.to_string() +
582	    " |\n"
583	  report = report +
584	    "| 收敛回合 | " +
585	    result.convergence_episode.to_string() +
586	    " |\n"
587	  report = report +
588	    "| 训练时间 | " +
589	    result.training_time_s.to_string() +
590	    " s |\n"
591	
592	  report
593	}
   	…

15 uncovered line(s) in src\process\remote_sensing\remote_sensing.mbt:

15	pub fn multispectral_new(
16	  width : Int,
17	  height : Int,
18	  bands : Array[@types.Image],
19	  band_names? : Array[String] = [],
20	) -> MultispectralImage {
21	  let names = if band_names.length() > 0 {
22	    band_names	<-- UNCOVERED
23	  } else {
24	    let default_names : Array[String] = []
25	    for i in 0..<bands.length() {
26	      default_names.push("Band" + i.to_string())
27	    }
28	    default_names
29	  }
30	  MultispectralImage::{ width, height, bands, band_names: names }
31	}
  	…

36	pub fn compute_ndvi(nir : @types.Image, red : @types.Image) -> @types.Image {
37	  if nir.width != red.width || nir.height != red.height {
38	    return nir	<-- UNCOVERED
39	  }
40	  let w = nir.width
41	  let h = nir.height
42	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
43	  for y in 0..<h {
44	    for x in 0..<w {
45	      let idx = y * w + x
46	      let nir_val = nir.data[idx].to_double()
47	      let red_val = red.data[idx].to_double()
48	      let sum = nir_val + red_val
49	      let ndvi = if sum > 0.0 { (nir_val - red_val) / sum } else { 0.0 }
50	      // 映射到 0-255（NDVI 范围 -1 到 1）
51	      let mapped = ((ndvi + 1.0) / 2.0 * 255.0).to_int()
52	      out_data[idx] = mapped.min(255).max(0).to_byte()
53	    }
54	  }
55	  @types.Image::{
56	    width: w,
57	    height: h,
58	    channels: 1,
59	    data: Bytes::from_array(out_data),
60	  }
61	}
  	…

66	pub fn compute_ndwi(green : @types.Image, nir : @types.Image) -> @types.Image {
67	  if green.width != nir.width || green.height != nir.height {
68	    return green	<-- UNCOVERED
69	  }
70	  let w = green.width
71	  let h = green.height
72	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
73	  for y in 0..<h {
74	    for x in 0..<w {
75	      let idx = y * w + x
76	      let green_val = green.data[idx].to_double()
77	      let nir_val = nir.data[idx].to_double()
78	      let sum = green_val + nir_val
79	      let ndwi = if sum > 0.0 { (green_val - nir_val) / sum } else { 0.0 }
80	      let mapped = ((ndwi + 1.0) / 2.0 * 255.0).to_int()
81	      out_data[idx] = mapped.min(255).max(0).to_byte()
82	    }
83	  }
84	  @types.Image::{
85	    width: w,
86	    height: h,
87	    channels: 1,
88	    data: Bytes::from_array(out_data),
89	  }
90	}
  	…

 95	pub fn compute_ndbi(swir : @types.Image, nir : @types.Image) -> @types.Image {
 96	  if swir.width != nir.width || swir.height != nir.height {
 97	    return swir	<-- UNCOVERED
 98	  }
 99	  let w = swir.width
100	  let h = swir.height
101	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
102	  for y in 0..<h {
103	    for x in 0..<w {
104	      let idx = y * w + x
105	      let swir_val = swir.data[idx].to_double()
106	      let nir_val = nir.data[idx].to_double()
107	      let sum = swir_val + nir_val
108	      let ndbi = if sum > 0.0 { (swir_val - nir_val) / sum } else { 0.0 }
109	      let mapped = ((ndbi + 1.0) / 2.0 * 255.0).to_int()
110	      out_data[idx] = mapped.min(255).max(0).to_byte()
111	    }
112	  }
113	  @types.Image::{
114	    width: w,
115	    height: h,
116	    channels: 1,
117	    data: Bytes::from_array(out_data),
118	  }
119	}
   	…

124	pub fn compute_evi(
125	  nir : @types.Image,
126	  red : @types.Image,
127	  blue : @types.Image,
128	) -> @types.Image {
129	  if nir.width != red.width ||
130	    nir.height != red.height ||
131	    nir.width != blue.width ||
132	    nir.height != blue.height {
133	    return nir	<-- UNCOVERED
134	  }
135	  let w = nir.width
136	  let h = nir.height
137	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
138	  for y in 0..<h {
139	    for x in 0..<w {
140	      let idx = y * w + x
141	      let nir_val = nir.data[idx].to_double()
142	      let red_val = red.data[idx].to_double()
143	      let blue_val = blue.data[idx].to_double()
144	      let denominator = nir_val + 6.0 * red_val - 7.5 * blue_val + 1.0
145	      let evi = if denominator.abs() > 0.0001 {
146	        2.5 * (nir_val - red_val) / denominator
147	      } else {
148	        0.0	<-- UNCOVERED
149	      }
150	      let mapped = ((evi + 1.0) / 2.0 * 255.0).to_int()
151	      out_data[idx] = mapped.min(255).max(0).to_byte()
152	    }
153	  }
154	  @types.Image::{
155	    width: w,
156	    height: h,
157	    channels: 1,
158	    data: Bytes::from_array(out_data),
159	  }
160	}
   	…

164	pub fn false_color_composite(
165	  nir : @types.Image,
166	  red : @types.Image,
167	  green : @types.Image,
168	) -> @types.Image {
169	  if nir.width != red.width ||
170	    nir.height != red.height ||
171	    nir.width != green.width ||
172	    nir.height != green.height {
173	    return nir	<-- UNCOVERED
174	  }
175	  let w = nir.width
176	  let h = nir.height
177	  let out_data : Array[Byte] = Array::make(w * h * 3, b'\x00')
178	  for y in 0..<h {
179	    for x in 0..<w {
180	      let idx = y * w + x
181	      let out_idx = idx * 3
182	      // 标准假彩色：NIR -> 红, Red -> 绿, Green -> 蓝
183	      out_data[out_idx] = nir.data[idx]
184	      out_data[out_idx + 1] = red.data[idx]
185	      out_data[out_idx + 2] = green.data[idx]
186	    }
187	  }
188	  @types.Image::{
189	    width: w,
190	    height: h,
191	    channels: 3,
192	    data: Bytes::from_array(out_data),
193	  }
194	}
   	…

198	pub fn true_color_composite(
199	  red : @types.Image,
200	  green : @types.Image,
201	  blue : @types.Image,
202	) -> @types.Image {
203	  if red.width != green.width ||
204	    red.height != green.height ||
205	    red.width != blue.width ||
206	    red.height != blue.height {
207	    return red	<-- UNCOVERED
208	  }
209	  let w = red.width
210	  let h = red.height
211	  let out_data : Array[Byte] = Array::make(w * h * 3, b'\x00')
212	  for y in 0..<h {
213	    for x in 0..<w {
214	      let idx = y * w + x
215	      let out_idx = idx * 3
216	      out_data[out_idx] = red.data[idx]
217	      out_data[out_idx + 1] = green.data[idx]
218	      out_data[out_idx + 2] = blue.data[idx]
219	    }
220	  }
221	  @types.Image::{
222	    width: w,
223	    height: h,
224	    channels: 3,
225	    data: Bytes::from_array(out_data),
226	  }
227	}
   	…

231	pub fn vegetation_coverage(
232	  ndvi : @types.Image,
233	  ndvi_soil? : Double = 0.2,
234	  ndvi_veg? : Double = 0.8,
235	) -> @types.Image {
236	  let soil = ndvi_soil
237	  let veg = ndvi_veg
238	  let w = ndvi.width
239	  let h = ndvi.height
240	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
241	  for y in 0..<h {
242	    for x in 0..<w {
243	      let idx = y * w + x
244	      // 将 0-255 映射回 -1 到 1
245	      let ndvi_val = ndvi.data[idx].to_double() / 255.0 * 2.0 - 1.0
246	      let coverage = if ndvi_val <= soil {
247	        0.0
248	      } else if ndvi_val >= veg {	<-- UNCOVERED
249	        1.0
250	      } else {
251	        (ndvi_val - soil) / (veg - soil)	<-- UNCOVERED
252	      }
253	      out_data[idx] = (coverage * 255.0).to_int().min(255).max(0).to_byte()
254	    }
255	  }
256	  @types.Image::{
257	    width: w,
258	    height: h,
259	    channels: 1,
260	    data: Bytes::from_array(out_data),
261	  }
262	}
   	…

267	pub fn land_cover_classification(
268	  ndvi : @types.Image,
269	  ndwi : @types.Image,
270	  ndbi : @types.Image,
271	) -> @types.Image {
272	  if ndvi.width != ndwi.width ||
273	    ndvi.height != ndwi.height ||
274	    ndvi.width != ndbi.width ||
275	    ndvi.height != ndbi.height {
276	    return ndvi	<-- UNCOVERED
277	  }
278	  let w = ndvi.width
279	  let h = ndvi.height
280	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
281	  for y in 0..<h {
282	    for x in 0..<w {
283	      let idx = y * w + x
284	      // 将 0-255 映射回 -1 到 1
285	      let ndvi_val = ndvi.data[idx].to_double() / 255.0 * 2.0 - 1.0
286	      let ndwi_val = ndwi.data[idx].to_double() / 255.0 * 2.0 - 1.0
287	      let ndbi_val = ndbi.data[idx].to_double() / 255.0 * 2.0 - 1.0
288	      // 简单分类规则
289	      let class_id = if ndwi_val > 0.2 {
290	        0 // 水体	<-- UNCOVERED
291	      } else if ndvi_val > 0.3 {
292	        1 // 植被	<-- UNCOVERED
293	      } else if ndbi_val > 0.1 {
294	        2 // 建筑	<-- UNCOVERED
295	      } else if ndvi_val > 0.1 {
296	        3 // 裸土	<-- UNCOVERED
297	      } else {
298	        4 // 其他
299	      }
300	      out_data[idx] = class_id.to_byte()
301	    }
302	  }
303	  @types.Image::{
304	    width: w,
305	    height: h,
306	    channels: 1,
307	    data: Bytes::from_array(out_data),
308	  }
309	}
   	…

22 uncovered line(s) in src\process\remote_sensing\remote_sensing_enhancement.mbt:

145	pub fn calculate_ndvi(
146	  image : RemoteSensingImage,
147	  red_band : Int,
148	  nir_band : Int,
149	) -> Array[Double] {
150	  let w = image.width
151	  let h = image.height
152	  let total = w * h
153	  let ndvi : Array[Double] = []
154	
155	  for i in 0..<total {
156	    let red = image.data[i * image.bands + red_band]
157	    let nir = image.data[i * image.bands + nir_band]
158	    let denominator = nir + red
159	    if denominator > 0.001 {
160	      ndvi.push((nir - red) / denominator)	<-- UNCOVERED
161	    } else {
162	      ndvi.push(0.0)
163	    }
164	  }
165	
166	  ndvi
167	}
   	…

171	pub fn calculate_ndwi(
172	  image : RemoteSensingImage,
173	  green_band : Int,
174	  nir_band : Int,
175	) -> Array[Double] {
176	  let w = image.width
177	  let h = image.height
178	  let total = w * h
179	  let ndwi : Array[Double] = []
180	
181	  for i in 0..<total {
182	    let green = image.data[i * image.bands + green_band]
183	    let nir = image.data[i * image.bands + nir_band]
184	    let denominator = nir + green
185	    if denominator > 0.001 {
186	      ndwi.push((green - nir) / denominator)	<-- UNCOVERED
187	    } else {
188	      ndwi.push(0.0)
189	    }
190	  }
191	
192	  ndwi
193	}
   	…

197	pub fn calculate_ndbi(
198	  image : RemoteSensingImage,
199	  swir_band : Int,
200	  nir_band : Int,
201	) -> Array[Double] {
202	  let w = image.width
203	  let h = image.height
204	  let total = w * h
205	  let ndbi : Array[Double] = []
206	
207	  for i in 0..<total {
208	    let swir = image.data[i * image.bands + swir_band]
209	    let nir = image.data[i * image.bands + nir_band]
210	    let denominator = swir + nir
211	    if denominator > 0.001 {
212	      ndbi.push((swir - nir) / denominator)	<-- UNCOVERED
213	    } else {
214	      ndbi.push(0.0)
215	    }
216	  }
217	
218	  ndbi
219	}
   	…

235	pub fn classify_land_cover_simple(
236	  image : RemoteSensingImage,
237	) -> ClassificationResult {
238	  let w = image.width
239	  let h = image.height
240	  let total = w * h
241	  let labels : Array[Int] = []
242	
243	  let ndvi = if image.bands > 4 { calculate_ndvi(image, 2, 3) } else { [] }
244	  let ndwi = if image.bands > 4 { calculate_ndwi(image, 1, 3) } else { [] }
245	
246	  for i in 0..<total {
247	    let mut label = 10 // Unknown
248	    if ndvi.length() > 0 && ndwi.length() > 0 {
249	      let n = ndvi[i]	<-- UNCOVERED
250	      let w_idx = ndwi[i]
251	      if w_idx > 0.3 {
252	        label = 0 // Water	<-- UNCOVERED
253	      } else if n > 0.6 {
254	        label = 1 // Forest
255	      } else if n > 0.3 {
256	        label = 2 // Grassland
257	      } else if n > 0.1 {
258	        label = 3 // Cropland
259	      } else if n < -0.1 {
260	        label = 4 // Urban
261	      } else {
262	        label = 5 // Barren	<-- UNCOVERED
263	      }
264	    }
265	    labels.push(label)
266	  }
267	
268	  let class_names : Array[String] = [
269	    "Water", "Forest", "Grassland", "Cropland", "Urban", "Barren", "Wetland", "Snow",
270	    "Cloud", "Shadow", "Unknown",
271	  ]
272	  let class_colors : Array[(Int, Int, Int)] = [
273	    (0, 0, 255),
274	    (0, 128, 0),
275	    (128, 255, 0),
276	    (255, 255, 0),
277	    (255, 0, 0),
278	    (128, 128, 128),
279	    (0, 255, 255),
280	    (255, 255, 255),
281	    (200, 200, 200),
282	    (50, 50, 50),
283	    (0, 0, 0),
284	  ]
285	
286	  ClassificationResult::{
287	    width: w,
288	    height: h,
289	    labels,
290	    class_names,
291	    class_colors,
292	    num_classes: 11,
293	    overall_accuracy: 0.0,
294	    kappa_coefficient: 0.0,
295	  }
296	}
   	…

300	pub fn detect_changes_simple(
301	  image1 : RemoteSensingImage,
302	  image2 : RemoteSensingImage,
303	  threshold : Double,
304	) -> ChangeDetectionResult {
305	  let w = image1.width
306	  let h = image1.height
307	  let total = w * h
308	  let change_mask : Array[Bool] = []
309	  let change_intensity : Array[Double] = []
310	  let mut num_changed = 0
311	
312	  for i in 0..<total {
313	    let mut sum_diff = 0.0
314	    for b in 0..<image1.bands {
315	      let idx = i * image1.bands + b
316	      if idx < image1.data.length() && idx < image2.data.length() {
317	        let diff = image1.data[idx] - image2.data[idx]
318	        sum_diff += diff * diff
319	      }
320	    }
321	    let intensity = if image1.bands > 0 {
322	      (sum_diff / image1.bands.to_double()).sqrt()
323	    } else {
324	      0.0	<-- UNCOVERED
325	    }
326	    change_intensity.push(intensity)
327	    let changed = intensity > threshold
328	    change_mask.push(changed)
329	    if changed {
330	      num_changed += 1	<-- UNCOVERED
331	    }
332	  }
333	
334	  let change_percentage = if total > 0 {
335	    num_changed.to_double() / total.to_double() * 100.0
336	  } else {
337	    0.0	<-- UNCOVERED
338	  }
339	
340	  ChangeDetectionResult::{
341	    width: w,
342	    height: h,
343	    change_mask,
344	    change_percentage,
345	    num_changed_pixels: num_changed,
346	    change_intensity,
347	    threshold,
348	  }
349	}
   	…

353	pub fn calculate_texture_features(
354	  image : @types.Image,
355	  x : Int,
356	  y : Int,
357	  window_size : Int,
358	) -> TextureFeatures {
359	  let w = image.width
360	  let h = image.height
361	  let half = window_size / 2
362	  let mut sum = 0.0
363	  let mut sum_sq = 0.0
364	  let mut count = 0
365	  let histogram : Array[Int] = []
366	  for _ in 0..<256 {
367	    histogram.push(0)
368	  }
369	
370	  for dy in -half..<(half + 1) {
371	    for dx in -half..<(half + 1) {
372	      let nx = x + dx
373	      let ny = y + dy
374	      if nx >= 0 && nx < w && ny >= 0 && ny < h {
375	        let pixel = image.data[ny * w + nx].to_double()
376	        sum += pixel
377	        sum_sq += pixel * pixel
378	        count += 1
379	        histogram[pixel.to_int()] += 1
380	      }
381	    }
382	  }
383	
384	  let mean = if count > 0 { sum / count.to_double() } else { 0.0 }
385	  let variance = if count > 0 {
386	    sum_sq / count.to_double() - mean * mean
387	  } else {
388	    0.0	<-- UNCOVERED
389	  }
390	
391	  // 计算熵
392	  let mut entropy = 0.0
393	  for i in 0..<256 {
394	    if histogram[i] > 0 && count > 0 {
395	      let p = histogram[i].to_double() / count.to_double()
396	      entropy -= p * (p * 10.0)
397	    }
398	  }
399	
400	  // 简化的对比度、同质性、能量、相关性
401	  let contrast = variance
402	  let homogeneity = if variance > 0.0 { 1.0 / (1.0 + variance) } else { 1.0 }
403	  let energy = if count > 0 {
404	    let mut e = 0.0
405	    for i in 0..<256 {
406	      let p = histogram[i].to_double() / count.to_double()
407	      e += p * p
408	    }
409	    e
410	  } else {
411	    0.0	<-- UNCOVERED
412	  }
413	  let correlation = 0.5
414	  let dissimilarity = variance.sqrt()
415	
416	  TextureFeatures::{
417	    contrast,
418	    homogeneity,
419	    energy,
420	    correlation,
421	    entropy,
422	    variance,
423	    dissimilarity,
424	  }
425	}
   	…

429	pub fn calculate_spectral_signature(
430	  image : RemoteSensingImage,
431	  x : Int,
432	  y : Int,
433	) -> SpectralSignature {
434	  let reflectance : Array[Double] = []
435	  let wavelengths : Array[Double] = []
436	  let mut sum = 0.0
437	  let mut sum_sq = 0.0
438	
439	  for b in 0..<image.bands {
440	    let idx = (y * image.width + x) * image.bands + b
441	    if idx < image.data.length() {
442	      let value = image.data[idx]
443	      reflectance.push(value)
444	      wavelengths.push(image.wavelength[b])
445	      sum += value
446	      sum_sq += value * value
447	    }
448	  }
449	
450	  let mean = if image.bands > 0 { sum / image.bands.to_double() } else { 0.0 }
451	  let variance = if image.bands > 0 {
452	    sum_sq / image.bands.to_double() - mean * mean
453	  } else {
454	    0.0	<-- UNCOVERED
455	  }
456	  let std = if variance > 0.0 { variance.sqrt() } else { 0.0 }	<-- UNCOVERED
457	
458	  // 简化的吸收特征检测
459	  let absorption_features : Array[(Double, Double)] = []
460	  for b in 1..<(image.bands - 1) {
461	    if b < reflectance.length() {
462	      let prev = reflectance[b - 1]
463	      let curr = reflectance[b]
464	      let next = reflectance[b + 1]
465	      if curr < prev && curr < next {
466	        let depth = (prev + next) / 2.0 - curr	<-- UNCOVERED
467	        if depth > 0.01 {
468	          absorption_features.push((wavelengths[b], depth))	<-- UNCOVERED
469	        }
470	      }
471	    }
472	  }
473	
474	  SpectralSignature::{
475	    wavelengths,
476	    reflectance,
477	    num_bands: image.bands,
478	    mean_reflectance: mean,
479	    std_reflectance: std,
480	    absorption_features,
481	  }
482	}
   	…

486	pub fn calculate_remote_sensing_quality(
487	  image : RemoteSensingImage,
488	) -> RemoteSensingQuality {
489	  let w = image.width
490	  let h = image.height
491	  let total = w * h
492	  let mut sum = 0.0
493	  let mut sum_sq = 0.0
494	
495	  for i in 0..<total {
496	    if i < image.data.length() {
497	      let pixel = image.data[i]
498	      sum += pixel
499	      sum_sq += pixel * pixel
500	    }
501	  }
502	
503	  let mean = if total > 0 { sum / total.to_double() } else { 0.0 }
504	  let variance = if total > 0 {
505	    sum_sq / total.to_double() - mean * mean
506	  } else {
507	    0.0	<-- UNCOVERED
508	  }
509	  let std = if variance > 0.0 { variance.sqrt() } else { 1.0 }	<-- UNCOVERED
510	  let snr = if std > 0.0 { mean / std } else { 0.0 }
511	
512	  let overall_quality = if snr > 10.0 && image.cloud_cover < 10.0 {
513	    5.0	<-- UNCOVERED
514	  } else if snr > 5.0 && image.cloud_cover < 30.0 {
515	    3.0	<-- UNCOVERED
516	  } else {
517	    2.0
518	  }
519	
520	  RemoteSensingQuality::{
521	    snr,
522	    cloud_cover: image.cloud_cover,
523	    geometric_accuracy: 0.8,
524	    radiometric_accuracy: 0.85,
525	    overall_quality,
526	  }
527	}
   	…

531	pub fn remote_sensing_report_to_string(
532	  image : RemoteSensingImage,
533	  classification : ClassificationResult,
534	  changes : ChangeDetectionResult,
535	  quality : RemoteSensingQuality,
536	) -> String {
537	  let mut report = "## 遥感图像分析报告\n\n"
538	  report = report + "### 图像信息\n\n"
539	  report = report + "| 指标 | 值 |\n|------|------|\n"
540	  let type_str = if image.image_type == RemoteSensingImageType::Optical {
541	    "光学"	<-- UNCOVERED
542	  } else if image.image_type == RemoteSensingImageType::Multispectral {
543	    "多光谱"
544	  } else if image.image_type == RemoteSensingImageType::Hyperspectral {	<-- UNCOVERED
545	    "高光谱"
546	  } else if image.image_type == RemoteSensingImageType::SAR {
547	    "SAR"
548	  } else if image.image_type == RemoteSensingImageType::Thermal {
549	    "热红外"
550	  } else if image.image_type == RemoteSensingImageType::LiDAR {
551	    "激光雷达"
552	  } else {
553	    "未知"	<-- UNCOVERED
554	  }
555	  report = report + "| 类型 | " + type_str + " |\n"
556	  report = report +
557	    "| 尺寸 | " +
558	    image.width.to_string() +
559	    "x" +
560	    image.height.to_string() +
561	    " |\n"
562	  report = report + "| 波段数 | " + image.bands.to_string() + " |\n"
563	  report = report +
564	    "| 空间分辨率 | " +
565	    image.resolution.to_string() +
566	    "m |\n"
567	  report = report + "| 获取日期 | " + image.acquisition_date + " |\n"
568	  report = report + "| 云量 | " + image.cloud_cover.to_string() + "% |\n\n"
569	
570	  report = report + "### 图像质量\n\n"
571	  report = report + "| 指标 | 值 |\n|------|------|\n"
572	  report = report + "| SNR | " + quality.snr.to_string() + " dB |\n"
573	  report = report + "| 云量 | " + quality.cloud_cover.to_string() + "% |\n"
574	  report = report +
575	    "| 几何精度 | " +
576	    (quality.geometric_accuracy * 100.0).to_string() +
577	    "% |\n"
578	  report = report +
579	    "| 辐射精度 | " +
580	    (quality.radiometric_accuracy * 100.0).to_string() +
581	    "% |\n"
582	  report = report +
583	    "| 综合质量 | " +
584	    quality.overall_quality.to_string() +
585	    "/5.0 |\n\n"
586	
587	  report = report + "### 地物分类统计\n\n"
588	  report = report + "| 类别 | 颜色(RGB) |\n|------|-----------|\n"
589	  for i in 0..<classification.num_classes {
590	    if i < classification.class_names.length() &&
591	      i < classification.class_colors.length() {
592	      let color = classification.class_colors[i]
593	      report = report +
594	        "| " +
595	        classification.class_names[i] +
596	        " | " +
597	        color.0.to_string() +
598	        "," +
599	        color.1.to_string() +
600	        "," +
601	        color.2.to_string() +
602	        " |\n"
603	    }
604	  }
605	
606	  report = report + "\n### 变化检测\n\n"
607	  report = report + "| 指标 | 值 |\n|------|------|\n"
608	  report = report +
609	    "| 变化像素数 | " +
610	    changes.num_changed_pixels.to_string() +
611	    " |\n"
612	  report = report +
613	    "| 变化百分比 | " +
614	    changes.change_percentage.to_string() +
615	    "% |\n"
616	  report = report + "| 变化阈值 | " + changes.threshold.to_string() + " |\n"
617	
618	  report
619	}
   	…

3 uncovered line(s) in src\process\remote_sensing_advanced\remote_sensing_advanced.mbt:

430	pub fn remote_sensing_report_to_string(
431	  task_type : RemoteSensingTaskType,
432	  config : RemoteSensingConfig,
433	  result_description : String,
434	) -> String {
435	  let mut report = "## 遥感图像高级报告\n\n"
436	
437	  report = report + "### 任务信息\n\n"
438	  report = report + "| 指标 | 值 |\n|------|------|\n"
439	  report = report +
440	    "| 任务类型 | " +
441	    (match task_type {
442	      RemoteSensingTaskType::LandCoverClassification => "地物分类"
443	      RemoteSensingTaskType::ObjectDetection => "目标检测"	<-- UNCOVERED
444	      RemoteSensingTaskType::ChangeDetection => "变化检测"
445	      RemoteSensingTaskType::ImageFusion => "图像融合"
446	      RemoteSensingTaskType::SuperResolution => "超分辨率"
447	      RemoteSensingTaskType::TimeSeriesAnalysis => "时间序列分析"
448	      RemoteSensingTaskType::SceneClassification => "场景分类"
449	      _ => "未知"
450	    }) +
451	    " |\n"
452	  report = report +
453	    "| 图像类型 | " +
454	    (match config.image_type {
455	      RemoteSensingImageType::Optical => "光学"	<-- UNCOVERED
456	      RemoteSensingImageType::SAR => "SAR"
457	      RemoteSensingImageType::Multispectral => "多光谱"
458	      RemoteSensingImageType::Hyperspectral => "高光谱"	<-- UNCOVERED
459	      RemoteSensingImageType::Thermal => "热红外"
460	      RemoteSensingImageType::LiDAR => "激光雷达"
461	      RemoteSensingImageType::DEM => "数字高程模型"
462	      _ => "未知"
463	    }) +
464	    " |\n"
465	  report = report + "| 波段数 | " + config.num_bands.to_string() + " |\n"
466	  report = report +
467	    "| 空间分辨率 | " +
468	    config.spatial_resolution.to_string() +
469	    " m |\n"
470	  report = report +
471	    "| 时间分辨率 | " +
472	    config.temporal_resolution_days.to_string() +
473	    " 天 |\n"
474	  report = report + "| 投影 | " + config.projection + " |\n"
475	  report = report + "| 基准面 | " + config.datum + " |\n"
476	  report = report + "| 分类方法 | " + config.classification_method + " |\n"
477	  report = report +
478	    "| 检测阈值 | " +
479	    config.detection_threshold.to_string() +
480	    " |\n"
481	  report = report +
482	    "| 变化阈值 | " +
483	    config.change_threshold.to_string() +
484	    " |\n"
485	  report = report +
486	    "| NDVI | " +
487	    (if config.use_ndvi { "开启" } else { "关闭" }) +
488	    " |\n"
489	  report = report +
490	    "| NDWI | " +
491	    (if config.use_ndwi { "开启" } else { "关闭" }) +
492	    " |\n"
493	
494	  report = report + "\n### 结果\n\n"
495	  report = report + result_description + "\n"
496	
497	  report
498	}
   	…

13 uncovered line(s) in src\process\representation_learning_advanced\representation_learning_advanced.mbt:

329	pub fn train_autoencoder(
330	  data : Array[Array[Double]],
331	  config : RepresentationLearningConfig,
332	) -> AutoencoderResult {
333	  let num_samples = data.length()
334	  let original_dim = if num_samples > 0 { data[0].length() } else { 0 }
335	  let latent_dim = config.latent_dimension
336	
337	  let encoded : Array[Array[Double]] = []
338	  let reconstructed : Array[Array[Double]] = []
339	  let mut total_recon_error = 0.0
340	
341	  for sample in data {
342	    let enc = encode_simple(sample, original_dim, latent_dim)
343	    encoded.push(enc)
344	    let recon = decode_simple(enc, latent_dim, original_dim)
345	    reconstructed.push(recon)
346	
347	    // 计算重构误差
348	    let mut error = 0.0
349	    for i in 0..<original_dim {
350	      if i < sample.length() && i < recon.length() {
351	        let diff = sample[i] - recon[i]
352	        error += diff * diff
353	      }
354	    }
355	    total_recon_error += error
356	  }
357	
358	  let avg_recon_error = if num_samples > 0 {
359	    total_recon_error / num_samples.to_double()
360	  } else {
361	    0.0	<-- UNCOVERED
362	  }
363	
364	  let encoder_weights : Array[Array[Double]] = []
365	  let decoder_weights : Array[Array[Double]] = []
366	  // 简化的权重矩阵
367	  for i in 0..<latent_dim {
368	    let row : Array[Double] = []
369	    for j in 0..<original_dim {
370	      row.push((i * original_dim + j).to_double() * 0.1)
371	    }
372	    encoder_weights.push(row)
373	  }
374	  for i in 0..<original_dim {
375	    let row : Array[Double] = []
376	    for j in 0..<latent_dim {
377	      row.push((j * original_dim + i).to_double() * 0.1)
378	    }
379	    decoder_weights.push(row)
380	  }
381	
382	  AutoencoderResult::{
383	    encoded_representations: encoded,
384	    reconstructed_data: reconstructed,
385	    num_samples,
386	    original_dimension: original_dim,
387	    latent_dimension: latent_dim,
388	    reconstruction_error: avg_recon_error,
389	    training_loss: avg_recon_error,
390	    validation_loss: avg_recon_error * 1.1,
391	    num_epochs: config.num_epochs,
392	    learning_rate: config.learning_rate,
393	    autoencoder_type: config.autoencoder_type,
394	    learning_time_ms: 500.0,
395	    encoder_weights,
396	    decoder_weights,
397	  }
398	}
   	…

402	pub fn train_vae(
403	  data : Array[Array[Double]],
404	  config : RepresentationLearningConfig,
405	) -> VAEResult {
406	  let num_samples = data.length()
407	  let original_dim = if num_samples > 0 { data[0].length() } else { 0 }
408	  let latent_dim = config.latent_dimension
409	
410	  let encoded : Array[Array[Double]] = []
411	  let reconstructed : Array[Array[Double]] = []
412	  let means : Array[Array[Double]] = []
413	  let log_vars : Array[Array[Double]] = []
414	  let mut total_recon_loss = 0.0
415	  let mut total_kl = 0.0
416	
417	  for sample in data {
418	    // 编码
419	    let mean = encode_simple(sample, original_dim, latent_dim)
420	    let log_var : Array[Double] = []
421	    for _ in 0..<latent_dim {
422	      log_var.push(0.0)
423	    }
424	    means.push(mean)
425	    log_vars.push(log_var)
426	
427	    // 重参数化技巧（简化：直接使用均值）
428	    let z = mean
429	    encoded.push(z)
430	
431	    // 解码
432	    let recon = decode_simple(z, latent_dim, original_dim)
433	    reconstructed.push(recon)
434	
435	    // 计算重构损失
436	    let mut recon_loss = 0.0
437	    for i in 0..<original_dim {
438	      if i < sample.length() && i < recon.length() {
439	        let diff = sample[i] - recon[i]
440	        recon_loss += diff * diff
441	      }
442	    }
443	    total_recon_loss += recon_loss
444	
445	    // 计算KL散度
446	    let mut kl = 0.0
447	    for i in 0..<latent_dim {
448	      if i < mean.length() && i < log_var.length() {
449	        kl += -0.5 *
450	          (1.0 + log_var[i] - mean[i] * mean[i] - exp_approx_rl(log_var[i]))
451	      }
452	    }
453	    total_kl += kl
454	  }
455	
456	  let avg_recon_loss = if num_samples > 0 {
457	    total_recon_loss / num_samples.to_double()
458	  } else {
459	    0.0	<-- UNCOVERED
460	  }
461	  let avg_kl = if num_samples > 0 {
462	    total_kl / num_samples.to_double()
463	  } else {
464	    0.0	<-- UNCOVERED
465	  }
466	
467	  VAEResult::{
468	    encoded_representations: encoded,
469	    reconstructed_data: reconstructed,
470	    latent_means: means,
471	    latent_log_vars: log_vars,
472	    num_samples,
473	    original_dimension: original_dim,
474	    latent_dimension: latent_dim,
475	    reconstruction_loss: avg_recon_loss,
476	    kl_divergence: avg_kl,
477	    total_loss: avg_recon_loss + avg_kl,
478	    num_epochs: config.num_epochs,
479	    learning_rate: config.learning_rate,
480	    learning_time_ms: 800.0,
481	  }
482	}
   	…

486	fn exp_approx_rl(x : Double) -> Double {
487	  if x > 10.0 {
488	    22026.0	<-- UNCOVERED
489	  } else if x < -10.0 {
490	    0.000045	<-- UNCOVERED
491	  } else {
492	    let mut result = 1.0
493	    let mut term = 1.0
494	    for i in 1..<20 {
495	      term = term * x / i.to_double()
496	      result += term
497	    }
498	    result
499	  }
500	}
   	…

606	pub fn representation_learning_report_to_string(
607	  task_type : RepresentationLearningTaskType,
608	  config : RepresentationLearningConfig,
609	  result_description : String,
610	) -> String {
611	  let mut report = "## 表示学习高级报告\n\n"
612	
613	  report = report + "### 任务信息\n\n"
614	  report = report + "| 指标 | 值 |\n|------|------|\n"
615	  report = report +
616	    "| 任务类型 | " +
617	    (match task_type {
618	      RepresentationLearningTaskType::Autoencoder => "自编码器"
619	      RepresentationLearningTaskType::VariationalAutoencoder =>
620	        "变分自编码器"	<-- UNCOVERED
621	      RepresentationLearningTaskType::GenerativeAdversarialNetwork =>
622	        "生成对抗网络"	<-- UNCOVERED
623	      RepresentationLearningTaskType::ContrastiveLearning => "对比学习"
624	      RepresentationLearningTaskType::SelfSupervisedLearning =>
625	        "自监督学习"	<-- UNCOVERED
626	      RepresentationLearningTaskType::Pretraining => "预训练"
627	      RepresentationLearningTaskType::FineTuning => "微调"
628	      RepresentationLearningTaskType::RepresentationLearning => "表示学习"
629	      RepresentationLearningTaskType::FeatureLearning => "特征学习"
630	      _ => "未知"
631	    }) +
632	    " |\n"
633	  report = report +
634	    "| 自编码器类型 | " +
635	    (match config.autoencoder_type {
636	      AutoencoderType::Undercomplete => "欠完备"
637	      AutoencoderType::Sparse => "稀疏"	<-- UNCOVERED
638	      AutoencoderType::Denoising => "去噪"
639	      AutoencoderType::Contractive => "收缩"
640	      AutoencoderType::Convolutional => "卷积"
641	      AutoencoderType::Recurrent => "循环"
642	      AutoencoderType::Variational => "变分"
643	      AutoencoderType::Conditional => "条件"
644	      AutoencoderType::Adversarial => "对抗"
645	      _ => "未知"
646	    }) +
647	    " |\n"
648	  report = report +
649	    "| GAN类型 | " +
650	    (match config.gan_type {
651	      GANType::VanillaGAN => "Vanilla GAN"
652	      GANType::DCGAN => "DCGAN"	<-- UNCOVERED
653	      GANType::WGAN => "WGAN"
654	      GANType::WGANGP => "WGAN-GP"
655	      GANType::CycleGAN => "CycleGAN"
656	      GANType::Pix2Pix => "Pix2Pix"
657	      GANType::StyleGAN => "StyleGAN"
658	      GANType::ProGAN => "ProGAN"
659	      GANType::BigGAN => "BigGAN"
660	      GANType::ConditionalGAN => "条件GAN"
661	      _ => "未知"
662	    }) +
663	    " |\n"
664	  report = report +
665	    "| 对比学习方法 | " +
666	    (match config.contrastive_method {
667	      ContrastiveLearningMethodType::SimCLR => "SimCLR"
668	      ContrastiveLearningMethodType::MoCo => "MoCo"	<-- UNCOVERED
669	      ContrastiveLearningMethodType::BYOL => "BYOL"
670	      ContrastiveLearningMethodType::SwAV => "SwAV"
671	      ContrastiveLearningMethodType::DINO => "DINO"
672	      ContrastiveLearningMethodType::CLIP => "CLIP"
673	      ContrastiveLearningMethodType::ALIGN => "ALIGN"
674	      ContrastiveLearningMethodType::SimSiam => "SimSiam"
675	      ContrastiveLearningMethodType::BarlowTwins => "Barlow Twins"
676	      ContrastiveLearningMethodType::VICReg => "VICReg"
677	      _ => "未知"
678	    }) +
679	    " |\n"
680	  report = report +
681	    "| 自监督任务 | " +
682	    (match config.self_supervised_task {
683	      SelfSupervisedTaskType::RotationPrediction => "旋转预测"	<-- UNCOVERED
684	      SelfSupervisedTaskType::Colorization => "着色"
685	      SelfSupervisedTaskType::JigsawPuzzle => "拼图"
686	      SelfSupervisedTaskType::RelativePosition => "相对位置"
687	      SelfSupervisedTaskType::ContextPrediction => "上下文预测"
688	      SelfSupervisedTaskType::MaskedImageModeling => "掩码图像建模"
689	      SelfSupervisedTaskType::MaskedAutoencoder => "掩码自编码器"
690	      SelfSupervisedTaskType::ContrastivePredictiveCoding =>
691	        "对比预测编码"	<-- UNCOVERED
692	      SelfSupervisedTaskType::DeepCluster => "深度聚类"
693	      SelfSupervisedTaskType::SeLa => "SeLa"
694	      _ => "未知"
695	    }) +
696	    " |\n"
697	  report = report +
698	    "| 潜在维度 | " +
699	    config.latent_dimension.to_string() +
700	    " |\n"
701	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
702	  report = report + "| 训练轮数 | " + config.num_epochs.to_string() + " |\n"
703	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
704	  report = report + "| 温度 | " + config.temperature.to_string() + " |\n"
705	  report = report +
706	    "| 使用预训练 | " +
707	    (if config.use_pretrained { "开启" } else { "关闭" }) +
708	    " |\n"
709	  report = report +
710	    "| 冻结层 | " +
711	    (if config.freeze_layers { "开启" } else { "关闭" }) +
712	    " |\n"
713	
714	  report = report + "\n### 结果\n\n"
715	  report = report + result_description + "\n"
716	
717	  report
718	}
   	…

13 uncovered line(s) in src\process\retrieval\content_based_retrieval.mbt:

148	pub fn extract_color_histogram(
149	  img : @types.Image,
150	  config : ColorHistogramConfig,
151	) -> CBIRImageFeature {
152	  let w = img.width
153	  let h = img.height
154	  let total = w * h
155	  let histogram : Array[Double] = []
156	  for _ in 0..<config.num_bins {
157	    histogram.push(0.0)
158	  }
159	
160	  for i in 0..<total {
161	    let pixel = img.data[i].to_int()
162	    let bin = if config.num_bins > 0 {
163	      pixel * config.num_bins / 256
164	    } else {
165	      0	<-- UNCOVERED
166	    }
167	    if bin >= 0 && bin < config.num_bins {
168	      histogram[bin] += 1.0
169	    }
170	  }
171	
172	  // 归一化
173	  if config.normalize && total > 0 {
174	    for i in 0..<config.num_bins {
175	      histogram[i] /= total.to_double()
176	    }
177	  }
178	
179	  CBIRImageFeature::{
180	    feature_type: FeatureType::ColorHistogram,
181	    feature_vector: histogram,
182	    feature_length: config.num_bins,
183	    image_id: "",
184	    image_path: "",
185	    width: w,
186	    height: h,
187	    extraction_time_ms: 0.0,
188	  }
189	}
   	…

252	pub fn extract_hog_feature(
253	  img : @types.Image,
254	  config : HOGConfig,
255	) -> CBIRImageFeature {
256	  let w = img.width
257	  let h = img.height
258	  let cell_size = config.cell_size
259	  let num_cells_x = w / cell_size
260	  let num_cells_y = h / cell_size
261	  let num_bins = config.num_bins
262	  let feature_length = num_cells_x * num_cells_y * num_bins
263	  let feature : Array[Double] = []
264	  for _ in 0..<feature_length {
265	    feature.push(0.0)
266	  }
267	
268	  for cy in 0..<num_cells_y {
269	    for cx in 0..<num_cells_x {
270	      let cell_hist : Array[Double] = []
271	      for _ in 0..<num_bins {
272	        cell_hist.push(0.0)
273	      }
274	      for y in 0..<cell_size {
275	        for x in 0..<cell_size {
276	          let px = cx * cell_size + x
277	          let py = cy * cell_size + y
278	          if px > 0 && px < w - 1 && py > 0 && py < h - 1 {
279	            let gx = img.data[py * w + (px + 1)].to_double() -
280	              img.data[py * w + (px - 1)].to_double()
281	            let gy = img.data[(py + 1) * w + px].to_double() -
282	              img.data[(py - 1) * w + px].to_double()
283	            let magnitude = (gx * gx + gy * gy).sqrt()
284	            let mut angle = if gx != 0.0 { gy / gx * 0.1 } else { 1.5708 }
285	            if angle < 0.0 {
286	              angle += 3.14159	<-- UNCOVERED
287	            }
288	            let bin = (angle / 3.14159 * num_bins.to_double()).to_int() %
289	              num_bins
290	            cell_hist[bin] += magnitude
291	          }
292	        }
293	      }
294	      let base_idx = (cy * num_cells_x + cx) * num_bins
295	      for b in 0..<num_bins {
296	        feature[base_idx + b] = cell_hist[b]
297	      }
298	    }
299	  }
300	
301	  CBIRImageFeature::{
302	    feature_type: FeatureType::HOG,
303	    feature_vector: feature,
304	    feature_length,
305	    image_id: "",
306	    image_path: "",
307	    width: w,
308	    height: h,
309	    extraction_time_ms: 0.0,
310	  }
311	}
   	…

315	pub fn cosine_similarity_cbir(
316	  feat1 : Array[Double],
317	  feat2 : Array[Double],
318	) -> Double {
319	  let len = if feat1.length() < feat2.length() {
320	    feat1.length()	<-- UNCOVERED
321	  } else {
322	    feat2.length()
323	  }
324	  let mut dot = 0.0
325	  let mut norm1 = 0.0
326	  let mut norm2 = 0.0
327	
328	  for i in 0..<len {
329	    dot += feat1[i] * feat2[i]
330	    norm1 += feat1[i] * feat1[i]
331	    norm2 += feat2[i] * feat2[i]
332	  }
333	
334	  if norm1 > 0.0 && norm2 > 0.0 {
335	    dot / (norm1 * norm2).sqrt()
336	  } else {
337	    0.0
338	  }
339	}
   	…

343	pub fn euclidean_distance_cbir(
344	  feat1 : Array[Double],
345	  feat2 : Array[Double],
346	) -> Double {
347	  let len = if feat1.length() < feat2.length() {
348	    feat1.length()	<-- UNCOVERED
349	  } else {
350	    feat2.length()
351	  }
352	  let mut sum = 0.0
353	
354	  for i in 0..<len {
355	    let diff = feat1[i] - feat2[i]
356	    sum += diff * diff
357	  }
358	
359	  sum.sqrt()
360	}
   	…

364	pub fn manhattan_distance_cbir(
365	  feat1 : Array[Double],
366	  feat2 : Array[Double],
367	) -> Double {
368	  let len = if feat1.length() < feat2.length() {
369	    feat1.length()	<-- UNCOVERED
370	  } else {
371	    feat2.length()
372	  }
373	  let mut sum = 0.0
374	
375	  for i in 0..<len {
376	    sum += (feat1[i] - feat2[i]).abs()
377	  }
378	
379	  sum
380	}
   	…

384	pub fn histogram_intersection_cbir(
385	  feat1 : Array[Double],
386	  feat2 : Array[Double],
387	) -> Double {
388	  let len = if feat1.length() < feat2.length() {
389	    feat1.length()	<-- UNCOVERED
390	  } else {
391	    feat2.length()
392	  }
393	  let mut intersection = 0.0
394	  let mut sum1 = 0.0
395	
396	  for i in 0..<len {
397	    intersection += if feat1[i] < feat2[i] { feat1[i] } else { feat2[i] }
398	    sum1 += feat1[i]
399	  }
400	
401	  if sum1 > 0.0 {
402	    intersection / sum1
403	  } else {
404	    0.0	<-- UNCOVERED
405	  }
406	}
   	…

410	pub fn calculate_similarity(
411	  feat1 : Array[Double],
412	  feat2 : Array[Double],
413	  metric : SimilarityMetric,
414	) -> Double {
415	  if metric == SimilarityMetric::Cosine {
416	    cosine_similarity_cbir(feat1, feat2)
417	  } else if metric == SimilarityMetric::Euclidean {
418	    let dist = euclidean_distance_cbir(feat1, feat2)
419	    1.0 / (1.0 + dist)
420	  } else if metric == SimilarityMetric::Manhattan {
421	    let dist = manhattan_distance_cbir(feat1, feat2)
422	    1.0 / (1.0 + dist)
423	  } else if metric == SimilarityMetric::Intersection {
424	    histogram_intersection_cbir(feat1, feat2)
425	  } else {
426	    cosine_similarity_cbir(feat1, feat2)	<-- UNCOVERED
427	  }
428	}
   	…

461	pub fn retrieve_images(
462	  query_feature : CBIRImageFeature,
463	  index : CBIRImageIndex,
464	  num_results : Int,
465	  metric : SimilarityMetric,
466	) -> CBIRRetrievalResultList {
467	  let similarities : Array[Double] = []
468	  for i in 0..<index.num_images {
469	    let sim = calculate_similarity(
470	      query_feature.feature_vector,
471	      index.features[i].feature_vector,
472	      metric,
473	    )
474	    similarities.push(sim)
475	  }
476	
477	  // 按相似度排序（冒泡排序，降序）
478	  let indices : Array[Int] = []
479	  for i in 0..<index.num_images {
480	    indices.push(i)
481	  }
482	  for i in 0..<index.num_images {
483	    for j in 0..<(index.num_images - i - 1) {
484	      if similarities[indices[j]] < similarities[indices[j + 1]] {
485	        let temp = indices[j]	<-- UNCOVERED
486	        indices[j] = indices[j + 1]
487	        indices[j + 1] = temp
488	      }
489	    }
490	  }
491	
492	  let results : Array[CBIRRetrievalResult] = []
493	  let result_count = if num_results < index.num_images {
494	    num_results	<-- UNCOVERED
495	  } else {
496	    index.num_images
497	  }
498	  for i in 0..<result_count {
499	    let idx = indices[i]
500	    let feat = index.features[idx]
501	    results.push(CBIRRetrievalResult::{
502	      image_id: feat.image_id,
503	      image_path: feat.image_path,
504	      similarity: similarities[idx],
505	      distance: 1.0 - similarities[idx],
506	      rank: i + 1,
507	      width: feat.width,
508	      height: feat.height,
509	    })
510	  }
511	
512	  CBIRRetrievalResultList::{
513	    results,
514	    num_results: result_count,
515	    query_image_id: query_feature.image_id,
516	    retrieval_time_ms: 0.0,
517	    total_images_in_index: index.num_images,
518	  }
519	}
   	…

552	pub fn retrieval_report_to_string(
553	  results : CBIRRetrievalResultList,
554	  query_feature : CBIRImageFeature,
555	) -> String {
556	  let mut report = "## 图像检索报告\n\n"
557	  report = report + "### 查询信息\n\n"
558	  report = report + "| 指标 | 值 |\n|------|------|\n"
559	  report = report + "| 查询图像ID | " + query_feature.image_id + " |\n"
560	  let feat_type_str = if query_feature.feature_type ==
561	    FeatureType::ColorHistogram {
562	    "颜色直方图"
563	  } else if query_feature.feature_type == FeatureType::Texture {	<-- UNCOVERED
564	    "纹理"
565	  } else if query_feature.feature_type == FeatureType::Shape {
566	    "形状"
567	  } else if query_feature.feature_type == FeatureType::SIFT {
568	    "SIFT"
569	  } else if query_feature.feature_type == FeatureType::HOG {
570	    "HOG"
571	  } else if query_feature.feature_type == FeatureType::LBP {
572	    "LBP"
573	  } else if query_feature.feature_type == FeatureType::Combined {
574	    "组合特征"
575	  } else if query_feature.feature_type == FeatureType::Unknown {
576	    "未知"
577	  } else {
578	    "其他"	<-- UNCOVERED
579	  }
580	  report = report + "| 特征类型 | " + feat_type_str + " |\n"
581	  report = report +
582	    "| 特征维度 | " +
583	    query_feature.feature_length.to_string() +
584	    " |\n"
585	  report = report +
586	    "| 索引图像数 | " +
587	    results.total_images_in_index.to_string() +
588	    " |\n"
589	  report = report +
590	    "| 检索耗时 | " +
591	    results.retrieval_time_ms.to_string() +
592	    "ms |\n\n"
593	
594	  if results.num_results > 0 {
595	    report = report + "### 检索结果\n\n"
596	    report = report + "| 排名 | 图像ID | 相似度 | 距离 | 尺寸 |\n"
597	    report = report + "|------|--------|--------|------|------|\n"
598	    for result in results.results {
599	      report = report +
600	        "| " +
601	        result.rank.to_string() +
602	        " | " +
603	        result.image_id +
604	        " | " +
605	        (result.similarity * 100.0).to_string() +
606	        "% | " +
607	        result.distance.to_string() +
608	        " | " +
609	        result.width.to_string() +
610	        "x" +
611	        result.height.to_string() +
612	        " |\n"
613	    }
614	  } else {
615	    report = report + "### 检索结果\n\n未找到匹配图像。\n"	<-- UNCOVERED
616	  }
617	
618	  report
619	}
   	…

5 uncovered line(s) in src\process\retrieval\image_retrieval.mbt:

117	fn cosine_similarity(a : Array[Double], b : Array[Double]) -> Double {
118	  let len = a.length().min(b.length())
119	  let mut dot = 0.0
120	  let mut norm_a = 0.0
121	  let mut norm_b = 0.0
122	  for i in 0..<len {
123	    dot += a[i] * b[i]
124	    norm_a += a[i] * a[i]
125	    norm_b += b[i] * b[i]
126	  }
127	  let denom = norm_a.sqrt() * norm_b.sqrt()
128	  if denom < 0.000001 {
129	    return 0.0	<-- UNCOVERED
130	  }
131	  dot / denom
132	}
   	…

148	fn histogram_intersection(a : Array[Double], b : Array[Double]) -> Double {
149	  let len = a.length().min(b.length())
150	  let mut intersection = 0.0
151	  let mut sum_a = 0.0
152	  for i in 0..<len {
153	    intersection += a[i].min(b[i])
154	    sum_a += a[i]
155	  }
156	  if sum_a < 0.000001 {
157	    return 0.0	<-- UNCOVERED
158	  }
159	  intersection / sum_a
160	}
   	…

200	pub fn search_similar_images(
201	  db : ImageDatabase,
202	  query : ImageFeature,
203	  top_k? : Int = 5,
204	  method_type? : String = "cosine",
205	) -> Array[SearchResult] {
206	  let k = top_k
207	  let search_method = method_type
208	  // 计算所有相似度
209	  let scores : Array[(String, Double)] = []
210	  for f in db.features {
211	    let similarity = match search_method {
212	      "cosine" => cosine_similarity(query.features, f.features)
213	      "euclidean" => {
214	        let dist = euclidean_distance(query.features, f.features)
215	        1.0 / (1.0 + dist)
216	      }
217	      "histogram" => histogram_intersection(query.features, f.features)
218	      _ => cosine_similarity(query.features, f.features)	<-- UNCOVERED
219	    }
220	    scores.push((f.image_id, similarity))
221	  }
222	  // 简单排序（冒泡排序，按相似度降序）
223	  for i in 0..<scores.length() {
224	    for j in 0..<(scores.length() - i - 1) {
225	      if scores[j].1 < scores[j + 1].1 {
226	        let temp = scores[j]	<-- UNCOVERED
227	        scores[j] = scores[j + 1]
228	        scores[j + 1] = temp
229	      }
230	    }
231	  }
232	  // 返回 top_k
233	  let results : Array[SearchResult] = []
234	  let count = k.min(scores.length())
235	  for i in 0..<count {
236	    results.push(SearchResult::{
237	      image_id: scores[i].0,
238	      similarity: scores[i].1,
239	      rank: i + 1,
240	    })
241	  }
242	  results
243	}
   	…

247	pub fn image_similarity(
248	  img1 : @types.Image,
249	  img2 : @types.Image,
250	  method_type? : String = "cosine",
251	) -> Double {
252	  let f1 = extract_image_features(img1, "query")
253	  let f2 = extract_image_features(img2, "target")
254	  let search_method = method_type
255	  match search_method {
256	    "cosine" => cosine_similarity(f1.features, f2.features)
257	    "euclidean" => {
258	      let dist = euclidean_distance(f1.features, f2.features)
259	      1.0 / (1.0 + dist)
260	    }
261	    "histogram" => histogram_intersection(f1.features, f2.features)
262	    _ => cosine_similarity(f1.features, f2.features)	<-- UNCOVERED
263	  }
264	}
   	…

6 uncovered line(s) in src\process\robot_vision_advanced\robot_vision_advanced.mbt:

197	pub fn estimate_object_pose(
198	  object_id : Int,
199	  bounding_box : Array[Double],
200	  depth_image : Array[Double],
201	  config : RobotVisionConfig,
202	) -> ObjectPose {
203	  // 简化的位姿估计：基于边界框和深度
204	  let cx = (bounding_box[0] + bounding_box[2]) / 2.0
205	  let cy = (bounding_box[1] + bounding_box[3]) / 2.0
206	
207	  let fx = config.camera_intrinsics[0]
208	  let fy = config.camera_intrinsics[4]
209	  let px = config.camera_intrinsics[2]
210	  let py = config.camera_intrinsics[5]
211	
212	  let depth_idx = cy.to_int() * 640 + cx.to_int()
213	  let depth = if depth_idx < depth_image.length() {
214	    depth_image[depth_idx]
215	  } else {
216	    1.0	<-- UNCOVERED
217	  }
218	
219	  let x = (cx - px) * depth / fx
220	  let y = (cy - py) * depth / fy
221	  let z = depth
222	
223	  ObjectPose::{
224	    position: [x, y, z],
225	    orientation: [0.0, 0.0, 0.0, 1.0],
226	    confidence: 0.85,
227	    object_id,
228	    coordinate_frame: "camera",
229	  }
230	}
   	…

416	pub fn robot_vision_report_to_string(
417	  task_type : RobotVisionTaskType,
418	  config : RobotVisionConfig,
419	  result_description : String,
420	) -> String {
421	  let mut report = "## 机器人视觉高级报告\n\n"
422	
423	  report = report + "### 任务信息\n\n"
424	  report = report + "| 指标 | 值 |\n|------|------|\n"
425	  report = report +
426	    "| 任务类型 | " +
427	    (match task_type {
428	      RobotVisionTaskType::VisualServoing => "视觉伺服"
429	      RobotVisionTaskType::ObjectGrasping => "物体抓取"	<-- UNCOVERED
430	      RobotVisionTaskType::SLAMEnhanced => "SLAM增强"
431	      RobotVisionTaskType::Reconstruction3D => "3D重建"
432	      RobotVisionTaskType::ArmControl => "机械臂控制"
433	      RobotVisionTaskType::MobileNavigation => "移动机器人导航"
434	      RobotVisionTaskType::HumanRobotInteraction => "人机交互"
435	      _ => "未知"
436	    }) +
437	    " |\n"
438	  report = report +
439	    "| 机器人类型 | " +
440	    (match config.robot_type {
441	      RobotType::Manipulator => "机械臂"
442	      RobotType::MobileRobot => "移动机器人"	<-- UNCOVERED
443	      RobotType::Humanoid => "人形机器人"
444	      RobotType::Drone => "无人机"
445	      RobotType::Wheeled => "轮式"
446	      RobotType::Legged => "腿式"
447	      _ => "未知"
448	    }) +
449	    " |\n"
450	  report = report +
451	    "| 视觉伺服类型 | " +
452	    (match config.visual_servoing_type {
453	      VisualServoingType::PositionBased => "基于位置"	<-- UNCOVERED
454	      VisualServoingType::ImageBased => "基于图像"
455	      VisualServoingType::Hybrid => "混合"	<-- UNCOVERED
456	      _ => "未知"
457	    }) +
458	    " |\n"
459	  report = report +
460	    "| 抓取类型 | " +
461	    (match config.grasp_type {
462	      GraspType::ParallelJaw => "平行夹爪"
463	      GraspType::SuctionCup => "吸盘"	<-- UNCOVERED
464	      GraspType::MultiFinger => "多指"
465	      GraspType::Hook => "钩子"
466	      GraspType::Magnetic => "磁吸"
467	      _ => "未知"
468	    }) +
469	    " |\n"
470	  report = report + "| 关节数 | " + config.num_joints.to_string() + " |\n"
471	  report = report +
472	    "| 控制频率 | " +
473	    config.control_frequency.to_string() +
474	    " Hz |\n"
475	  report = report +
476	    "| 规划时间限制 | " +
477	    config.planning_time_limit.to_string() +
478	    " s |\n"
479	  report = report +
480	    "| 碰撞余量 | " +
481	    config.collision_margin.to_string() +
482	    " m |\n"
483	
484	  report = report + "\n### 结果\n\n"
485	  report = report + result_description + "\n"
486	
487	  report
488	}
   	…

14 uncovered line(s) in src\process\scene_understanding\scene_understanding.mbt:

150	pub fn classify_scene(
151	  img : @types.Image,
152	  config : SceneUnderstandingConfig,
153	) -> SceneClassificationResult {
154	  ignore(config)
155	  let w = img.width
156	  let h = img.height
157	  let total = w * h
158	
159	  // 计算图像统计特征
160	  let mut sum = 0.0
161	  let mut sum_sq = 0.0
162	  let mut edge_count = 0
163	  let mut _color_variation = 0.0
164	
165	  for y in 0..<h {
166	    for x in 0..<w {
167	      let idx = y * w + x
168	      let pixel = img.data[idx].to_double()
169	      sum += pixel
170	      sum_sq += pixel * pixel
171	
172	      if x > 0 && x < w - 1 && y > 0 && y < h - 1 {
173	        let gx = img.data[idx + 1].to_double() - img.data[idx - 1].to_double()
174	        let gy = img.data[idx + w].to_double() - img.data[idx - w].to_double()
175	        if (gx * gx + gy * gy).sqrt() > 20.0 {
176	          edge_count += 1	<-- UNCOVERED
177	        }
178	      }
179	    }
180	  }
181	
182	  let mean = sum / total.to_double()
183	  let variance = sum_sq / total.to_double() - mean * mean
184	  let std_dev = if variance > 0.0 { variance.sqrt() } else { 0.0 }	<-- UNCOVERED
185	  let edge_density = edge_count.to_double() / total.to_double()
186	
187	  // 简化的场景分类
188	  let mut scene_type = "indoor"
189	  let mut scene_category = "environment"
190	  let mut confidence = 0.45
191	
192	  if mean < 60.0 {
193	    scene_type = "night"
194	    scene_category = "time_of_day"
195	    confidence = 0.7
196	  } else if mean > 200.0 {
197	    scene_type = "daytime"
198	    scene_category = "time_of_day"
199	    confidence = 0.65
200	  } else if edge_density > 0.3 {
201	    scene_type = "urban"	<-- UNCOVERED
202	    scene_category = "environment"
203	    confidence = 0.6
204	  } else if edge_density < 0.1 {
205	    scene_type = "natural"
206	    scene_category = "environment"
207	    confidence = 0.55
208	  } else if std_dev > 60.0 {	<-- UNCOVERED
209	    scene_type = "outdoor"
210	    scene_category = "environment"
211	    confidence = 0.5
212	  } else {
213	    scene_type = "indoor"	<-- UNCOVERED
214	    scene_category = "environment"
215	    confidence = 0.45
216	  }
217	
218	  // Top 预测
219	  let top_predictions : Array[String] = []
220	  let top_confidences : Array[Double] = []
221	  top_predictions.push(scene_type)
222	  top_confidences.push(confidence)
223	  top_predictions.push("indoor")
224	  top_confidences.push(0.3)
225	  top_predictions.push("outdoor")
226	  top_confidences.push(0.25)
227	
228	  SceneClassificationResult::{
229	    scene_type,
230	    confidence,
231	    top_predictions,
232	    top_confidences,
233	    num_classes: 30,
234	    scene_category,
235	  }
236	}
   	…

240	pub fn generate_caption(
241	  img : @types.Image,
242	  config : SceneUnderstandingConfig,
243	) -> ImageCaptioningResult {
244	  let classification = classify_scene(img, config)
245	  let w = img.width
246	  let h = img.height
247	  let total = w * h
248	
249	  // 计算更多图像特征
250	  let mut sum = 0.0
251	  for i in 0..<total {
252	    sum += img.data[i].to_double()
253	  }
254	  let mean = sum / total.to_double()
255	
256	  // 简化的描述生成
257	  let mut caption = ""
258	  let brightness_desc = if mean < 60.0 {
259	    "dark"	<-- UNCOVERED
260	  } else if mean < 120.0 {
261	    "dimly lit"	<-- UNCOVERED
262	  } else if mean < 180.0 {
263	    "well-lit"
264	  } else {
265	    "bright"	<-- UNCOVERED
266	  }
267	
268	  caption = "A " + brightness_desc + " " + classification.scene_type + " scene."
269	
270	  let alternative_captions : Array[String] = []
271	  alternative_captions.push(
272	    "This appears to be a " + classification.scene_type + " environment.",
273	  )
274	  alternative_captions.push(
275	    "An image showing a " + classification.scene_type + " setting.",
276	  )
277	  alternative_captions.push(
278	    "A photograph of a " +
279	    brightness_desc +
280	    " " +
281	    classification.scene_type +
282	    ".",
283	  )
284	
285	  ImageCaptioningResult::{
286	    caption,
287	    confidence: classification.confidence * 0.8,
288	    alternative_captions,
289	    num_captions: 4,
290	    bleu_score: 0.3,
291	    cider_score: 0.5,
292	    generation_time_ms: 0.0,
293	  }
294	}
   	…

298	pub fn answer_visual_question(
299	  img : @types.Image,
300	  question : String,
301	  config : SceneUnderstandingConfig,
302	) -> VisualQuestionAnsweringResult {
303	  let classification = classify_scene(img, config)
304	  let w = img.width
305	  let h = img.height
306	  let total = w * h
307	
308	  // 计算亮度
309	  let mut sum = 0.0
310	  for i in 0..<total {
311	    sum += img.data[i].to_double()
312	  }
313	  let mean = sum / total.to_double()
314	
315	  // 简化的问答
316	  let mut answer = ""
317	  let mut question_type = ""
318	  let mut answer_type = ""
319	  let mut confidence = 0.0
320	
321	  if question.contains("scene") ||
322	    question.contains("environment") ||
323	    question.contains("place") {
324	    answer = classification.scene_type
325	    question_type = "scene_recognition"
326	    answer_type = "open"
327	    confidence = classification.confidence
328	  } else if question.contains("bright") ||
329	    question.contains("dark") ||
330	    question.contains("light") {
331	    if mean < 60.0 {
332	      answer = "dark"
333	    } else if mean < 120.0 {	<-- UNCOVERED
334	      answer = "dimly lit"
335	    } else if mean < 180.0 {
336	      answer = "well-lit"
337	    } else {
338	      answer = "bright"	<-- UNCOVERED
339	    }
340	    question_type = "attribute_recognition"
341	    answer_type = "open"
342	    confidence = 0.7
343	  } else if question.contains("indoor") || question.contains("outdoor") {
344	    answer = classification.scene_type	<-- UNCOVERED
345	    question_type = "binary"
346	    answer_type = "yes/no"
347	    confidence = classification.confidence
348	  } else if question.contains("color") || question.contains("colour") {
349	    if mean < 85.0 {	<-- UNCOVERED
350	      answer = "dark colors"
351	    } else if mean < 170.0 {
352	      answer = "medium colors"
353	    } else {
354	      answer = "light colors"	<-- UNCOVERED
355	    }
356	    question_type = "attribute_recognition"
357	    answer_type = "open"
358	    confidence = 0.6
359	  } else {
360	    answer = "I'm not sure about that."
361	    question_type = "unknown"
362	    answer_type = "open"
363	    confidence = 0.3
364	  }
365	
366	  let alternative_answers : Array[String] = []
367	  alternative_answers.push(classification.scene_type)
368	  alternative_answers.push("indoor")
369	  alternative_answers.push("outdoor")
370	
371	  let reasoning_steps : Array[String] = []
372	  reasoning_steps.push("Analyze image statistics")
373	  reasoning_steps.push("Classify scene type")
374	  reasoning_steps.push("Match question to answer type")
375	  reasoning_steps.push("Generate answer")
376	
377	  VisualQuestionAnsweringResult::{
378	    answer,
379	    confidence,
380	    alternative_answers,
381	    question_type,
382	    answer_type,
383	    reasoning_steps,
384	  }
385	}
   	…

461	pub fn understand_scene(
462	  img : @types.Image,
463	  config : SceneUnderstandingConfig,
464	) -> (SceneClassificationResult, ImageCaptioningResult, SceneGraph) {
465	  let classification = classify_scene(img, config)
466	  let caption = generate_caption(img, config)
467	  let scene_graph = if config.use_scene_graph {
468	    generate_scene_graph(img, config)
469	  } else {
470	    SceneGraph::{ nodes: [], relations: [], num_objects: 0, num_relations: 0 }	<-- UNCOVERED
471	  }
472	
473	  (classification, caption, scene_graph)
474	}
   	…

7 uncovered line(s) in src\process\security_surveillance_advanced\security_surveillance_advanced.mbt:

253	pub fn detect_intrusion(
254	  people : Array[DetectedPerson],
255	  intrusion_zone : Array[Int],
256	  config : SecuritySurveillanceConfig,
257	) -> Array[IntrusionEvent] {
258	  let events : Array[IntrusionEvent] = []
259	
260	  for person in people {
261	    let bbox = person.bounding_box
262	    let cx = (bbox[0] + bbox[2]) / 2
263	    let cy = (bbox[1] + bbox[3]) / 2
264	
265	    // 检查是否在入侵区域内
266	    let in_zone = cx >= intrusion_zone[0] &&
267	      cx <= intrusion_zone[2] &&
268	      cy >= intrusion_zone[1] &&
269	      cy <= intrusion_zone[3]
270	
271	    if in_zone && person.confidence > config.detection_threshold {
272	      let alert_level = if person.confidence > config.alert_threshold {
273	        AlertLevel::Critical
274	      } else if person.confidence > 0.7 {	<-- UNCOVERED
275	        AlertLevel::Warning
276	      } else {
277	        AlertLevel::Info	<-- UNCOVERED
278	      }
279	
280	      let event = IntrusionEvent::{
281	        event_id: events.length(),
282	        intrusion_type: IntrusionType::AreaEntry,
283	        timestamp: 0.0,
284	        location: [cx.to_double(), cy.to_double()],
285	        bounding_box: bbox,
286	        confidence: person.confidence,
287	        alert_level,
288	        person_id: person.person_id,
289	        is_acknowledged: false,
290	        video_clip_start: -5.0,
291	        video_clip_end: 10.0,
292	      }
293	      events.push(event)
294	    }
295	  }
296	
297	  events
298	}
   	…

302	pub fn analyze_behavior(
303	  person : DetectedPerson,
304	  config : SecuritySurveillanceConfig,
305	) -> BehaviorAnalysisResult {
306	  ignore(config)
307	  // 简化的行为分析：基于速度
308	  let speed = person.velocity[0] * person.velocity[0] +
309	    person.velocity[1] * person.velocity[1]
310	
311	  let behavior_type = if speed > 5.0 {
312	    BehaviorType::Running	<-- UNCOVERED
313	  } else if speed > 2.0 {
314	    BehaviorType::Walking
315	  } else if speed < 0.5 {
316	    BehaviorType::Suspicious
317	  } else {
318	    BehaviorType::Walking	<-- UNCOVERED
319	  }
320	
321	  let is_suspicious = behavior_type == BehaviorType::Suspicious ||
322	    behavior_type == BehaviorType::Aggressive
323	  let risk_score = if is_suspicious { 0.8 } else { 0.2 }
324	
325	  BehaviorAnalysisResult::{
326	    person_id: person.person_id,
327	    behavior_type,
328	    confidence: 0.75,
329	    timestamp: 0.0,
330	    duration: 5.0,
331	    location: [0.0, 0.0],
332	    is_suspicious,
333	    risk_score,
334	  }
335	}
   	…

410	pub fn security_surveillance_report_to_string(
411	  task_type : SecuritySurveillanceTaskType,
412	  config : SecuritySurveillanceConfig,
413	  result_description : String,
414	) -> String {
415	  let mut report = "## 安防监控高级报告\n\n"
416	
417	  report = report + "### 任务信息\n\n"
418	  report = report + "| 指标 | 值 |\n|------|------|\n"
419	  report = report +
420	    "| 任务类型 | " +
421	    (match task_type {
422	      SecuritySurveillanceTaskType::IntrusionDetection => "入侵检测"	<-- UNCOVERED
423	      SecuritySurveillanceTaskType::FaceRecognition => "人脸识别"
424	      SecuritySurveillanceTaskType::BehaviorAnalysis => "行为分析"
425	      SecuritySurveillanceTaskType::CrowdAnalysis => "人群分析"	<-- UNCOVERED
426	      SecuritySurveillanceTaskType::LicensePlateRecognition => "车牌识别"
427	      SecuritySurveillanceTaskType::AnomalyEventDetection =>
428	        "异常事件检测"	<-- UNCOVERED
429	      SecuritySurveillanceTaskType::ObjectTracking => "目标跟踪"
430	      _ => "未知"
431	    }) +
432	    " |\n"
433	  report = report +
434	    "| 检测阈值 | " +
435	    config.detection_threshold.to_string() +
436	    " |\n"
437	  report = report +
438	    "| 识别阈值 | " +
439	    config.recognition_threshold.to_string() +
440	    " |\n"
441	  report = report +
442	    "| 警报阈值 | " +
443	    config.alert_threshold.to_string() +
444	    " |\n"
445	  report = report +
446	    "| 徘徊时间 | " +
447	    config.loitering_time.to_string() +
448	    " s |\n"
449	  report = report +
450	    "| 人脸识别 | " +
451	    (if config.use_face_recognition { "开启" } else { "关闭" }) +
452	    " |\n"
453	  report = report +
454	    "| 行为分析 | " +
455	    (if config.use_behavior_analysis { "开启" } else { "关闭" }) +
456	    " |\n"
457	  report = report +
458	    "| 人群分析 | " +
459	    (if config.use_crowd_analysis { "开启" } else { "关闭" }) +
460	    " |\n"
461	  report = report +
462	    "| 自动警报 | " +
463	    (if config.auto_alert { "开启" } else { "关闭" }) +
464	    " |\n"
465	  report = report +
466	    "| 摄像头数 | " +
467	    config.num_cameras.to_string() +
468	    " |\n"
469	  report = report + "| 帧率 | " + config.fps.to_string() + " fps |\n"
470	
471	  report = report + "\n### 结果\n\n"
472	  report = report + result_description + "\n"
473	
474	  report
475	}
   	…

12 uncovered line(s) in src\process\segment\connected_components.mbt:

 20	pub fn connected_components(
 21	  img : @types.Image,
 22	  connectivity : Int,
 23	) -> (ConnectedComponentLabelImage, Array[ConnectedComponent]) raise @types.LoadError {
 24	  if connectivity != 4 && connectivity != 8 {
 25	    raise @types.LoadError::DecodeFailed("connectivity 必须为 4 或 8")
 26	  }
 27	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 28	    raise @types.LoadError::DecodeFailed(
 29	      "connected_components 仅支持 channels=1/3/4",
 30	    )
 31	  }
 32	  let w = img.width
 33	  let h = img.height
 34	  let n = w * h
 35	  // 转为二值
 36	  let binary : Array[Bool] = Array::make(n, false)
 37	  let ch = img.channels
 38	  for i in 0..<n {
 39	    binary[i] = img.data[i * ch].to_int() > 0
 40	  }
 41	  // 第一遍扫描：标记 + Union-Find
 42	  let labels : Array[Int] = Array::make(n, 0)
 43	  let parent : Array[Int] = [0] // parent[0] 是背景
 44	  let mut next_label = 1
 45	  for y in 0..<h {
 46	    for x in 0..<w {
 47	      let idx = y * w + x
 48	      if !binary[idx] {
 49	        continue
 50	      }
 51	      // 检查邻居
 52	      let mut left = 0
 53	      let mut up = 0
 54	      let mut ul = 0
 55	      let mut ur = 0
 56	      if x > 0 && binary[idx - 1] {
 57	        left = labels[idx - 1]
 58	      }
 59	      if y > 0 && binary[idx - w] {
 60	        up = labels[idx - w]
 61	      }
 62	      if connectivity == 8 {
 63	        if x > 0 && y > 0 && binary[idx - w - 1] {
 64	          ul = labels[idx - w - 1]
 65	        }
 66	        if x < w - 1 && y > 0 && binary[idx - w + 1] {
 67	          ur = labels[idx - w + 1]
 68	        }
 69	      }
 70	      // 收集非零邻居标签（用变量代替数组，避免堆分配）
 71	      let mut n1 = 0
 72	      let mut n2 = 0
 73	      let mut n3 = 0
 74	      let mut n4 = 0
 75	      let mut nc = 0
 76	      if left > 0 {
 77	        n1 = left
 78	        nc = 1
 79	      }
 80	      if up > 0 {
 81	        if nc == 0 {
 82	          n1 = up
 83	          nc = 1
 84	        } else {
 85	          n2 = up
 86	          nc = 2
 87	        }
 88	      }
 89	      if ul > 0 {
 90	        if nc == 0 {
 91	          n1 = ul
 92	          nc = 1
 93	        } else if nc == 1 {
 94	          n2 = ul
 95	          nc = 2
 96	        } else {
 97	          n3 = ul	<-- UNCOVERED
 98	          nc = 3
 99	        }
100	      }
101	      if ur > 0 {
102	        if nc == 0 {
103	          n1 = ur	<-- UNCOVERED
104	          nc = 1
105	        } else if nc == 1 {
106	          n2 = ur
107	          nc = 2
108	        } else if nc == 2 {	<-- UNCOVERED
109	          n3 = ur
110	          nc = 3
111	        } else {
112	          n4 = ur	<-- UNCOVERED
113	          nc = 4
114	        }
115	      }
116	      if nc == 0 {
117	        labels[idx] = next_label
118	        parent.push(next_label)
119	        next_label += 1
120	      } else {
121	        let mut min_label = n1
122	        if nc >= 2 && n2 < min_label {
123	          min_label = n2
124	        }
125	        if nc >= 3 && n3 < min_label {
126	          min_label = n3	<-- UNCOVERED
127	        }
128	        if nc >= 4 && n4 < min_label {
129	          min_label = n4	<-- UNCOVERED
130	        }
131	        labels[idx] = min_label
132	        union_labels(parent, min_label, n1)
133	        if nc >= 2 {
134	          union_labels(parent, min_label, n2)
135	        }
136	        if nc >= 3 {
137	          union_labels(parent, min_label, n3)	<-- UNCOVERED
138	        }
139	        if nc >= 4 {
140	          union_labels(parent, min_label, n4)	<-- UNCOVERED
141	        }
142	      }
143	    }
144	  }
145	  // 压缩路径并建立标签映射
146	  let label_map : Array[Int] = Array::make(parent.length(), 0)
147	  let mut final_count = 0
148	  for i in 1..<parent.length() {
149	    let root = find_root(parent, i)
150	    if label_map[root] == 0 {
151	      final_count += 1
152	      label_map[root] = final_count
153	    }
154	    label_map[i] = label_map[root]
155	  }
156	  // 第二遍扫描：替换为最终标签
157	  let out_labels : Array[Int] = Array::make(n, 0)
158	  for i in 0..<n {
159	    out_labels[i] = label_map[labels[i]]
160	  }
161	  // 统计连通域信息
162	  let areas : Array[Int] = Array::make(final_count + 1, 0)
163	  let sum_x : Array[Int] = Array::make(final_count + 1, 0)
164	  let sum_y : Array[Int] = Array::make(final_count + 1, 0)
165	  let min_x : Array[Int] = Array::make(final_count + 1, w)
166	  let min_y : Array[Int] = Array::make(final_count + 1, h)
167	  let max_x : Array[Int] = Array::make(final_count + 1, 0)
168	  let max_y : Array[Int] = Array::make(final_count + 1, 0)
169	  for y in 0..<h {
170	    for x in 0..<w {
171	      let label = out_labels[y * w + x]
172	      if label == 0 {
173	        continue
174	      }
175	      areas[label] += 1
176	      sum_x[label] += x
177	      sum_y[label] += y
178	      if x < min_x[label] {
179	        min_x[label] = x
180	      }
181	      if y < min_y[label] {
182	        min_y[label] = y
183	      }
184	      if x > max_x[label] {
185	        max_x[label] = x
186	      }
187	      if y > max_y[label] {
188	        max_y[label] = y
189	      }
190	    }
191	  }
192	  let components : Array[ConnectedComponent] = []
193	  for label in 1..<=final_count {
194	    let area = areas[label]
195	    let cx = if area > 0 {
196	      Float::from_int(sum_x[label]) / Float::from_int(area)
197	    } else {
198	      0.0F	<-- UNCOVERED
199	    }
200	    let cy = if area > 0 {
201	      Float::from_int(sum_y[label]) / Float::from_int(area)
202	    } else {
203	      0.0F	<-- UNCOVERED
204	    }
205	    components.push(ConnectedComponent::{
206	      label,
207	      area,
208	      bbox_x: min_x[label],
209	      bbox_y: min_y[label],
210	      bbox_w: max_x[label] - min_x[label] + 1,
211	      bbox_h: max_y[label] - min_y[label] + 1,
212	      centroid_x: cx,
213	      centroid_y: cy,
214	    })
215	  }
216	  (
217	    ConnectedComponentLabelImage::{ width: w, height: h, labels: out_labels },
218	    components,
219	  )
220	}
   	…

231	fn find_root(parent : Array[Int], x : Int) -> Int {
232	  let mut root = x
233	  while parent[root] != root {
234	    root = parent[root]
235	  }
236	  // 路径压缩
237	  let mut i = x
238	  while parent[i] != root {
239	    let next = parent[i]	<-- UNCOVERED
240	    parent[i] = root
241	    i = next
242	  }
243	  root
244	}
   	…

247	fn union_labels(parent : Array[Int], a : Int, b : Int) -> Unit {
248	  let ra = find_root(parent, a)
249	  let rb = find_root(parent, b)
250	  if ra != rb {
251	    if ra < rb {
252	      parent[rb] = ra
253	    } else {
254	      parent[ra] = rb	<-- UNCOVERED
255	    }
256	  }
257	}
   	…

8 uncovered line(s) in src\process\segment\distance_transform.mbt:

  8	pub fn distance_transform(
  9	  img : @types.Image,
 10	  distance_type? : Int = 2,
 11	) -> Array[Float] raise @types.LoadError {
 12	  if distance_type != 1 && distance_type != 2 && distance_type != 3 {
 13	    raise @types.LoadError::DecodeFailed(
 14	      "distance_type 必须为 1(L1), 2(L2), 3(Linf)",
 15	    )
 16	  }
 17	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 18	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 19	      "distance_transform 仅支持 channels=1/3/4",
 20	    )
 21	  }
 22	  let w = img.width
 23	  let h = img.height
 24	  let n = w * h
 25	  if n == 0 {
 26	    return []	<-- UNCOVERED
 27	  }
 28	  // 转为二值（非零为前景）
 29	  let binary : Array[Bool] = Array::make(n, false)
 30	  let ch = img.channels
 31	  for i in 0..<n {
 32	    binary[i] = img.data[i * ch].to_int() > 0
 33	  }
 34	  // 两遍扫描算法
 35	  let dist : Array[Float] = Array::make(n, 10000000000.0F)
 36	  // 初始化：背景为 0，前景为无穷大
 37	  for i in 0..<n {
 38	    if !binary[i] {
 39	      dist[i] = 0.0F
 40	    }
 41	  }
 42	  // 距离掩码
 43	  let (mask1, mask2) = match distance_type {
 44	    1 => ([(0, -1, 1.0F), (-1, 0, 1.0F)], [(0, 1, 1.0F), (1, 0, 1.0F)])
 45	    3 => ([(0, -1, 1.0F), (-1, 0, 1.0F)], [(0, 1, 1.0F), (1, 0, 1.0F)])
 46	    _ =>
 47	      (
 48	        [(0, -1, 1.0F), (-1, 0, 1.0F), (-1, -1, 1.414F), (-1, 1, 1.414F)],
 49	        [(0, 1, 1.0F), (1, 0, 1.0F), (1, 1, 1.414F), (1, -1, 1.414F)],
 50	      )
 51	  }
 52	  // 前向扫描（左上到右下）
 53	  // 内部区域：x >= 1 && y >= 1 && y < h - 1
 54	  for y in 1..<(h - 1) {
 55	    for x in 1..<w {
 56	      let idx = y * w + x
 57	      if !binary[idx] {
 58	        continue
 59	      }
 60	      for m in mask1 {
 61	        let (dx, dy, d) = m
 62	        let nd = dist[(y + dy) * w + (x + dx)] + d
 63	        if nd < dist[idx] {
 64	          dist[idx] = nd
 65	        }
 66	      }
 67	    }
 68	  }
 69	  // 边界行
 70	  for y in 0..<h {
 71	    if y >= 1 && y < h - 1 {
 72	      continue
 73	    }
 74	    for x in 0..<w {
 75	      let idx = y * w + x
 76	      if !binary[idx] {
 77	        continue
 78	      }
 79	      for m in mask1 {
 80	        let (dx, dy, d) = m
 81	        let nx = x + dx
 82	        let ny = y + dy
 83	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
 84	          let nd = dist[ny * w + nx] + d
 85	          if nd < dist[idx] {
 86	            dist[idx] = nd
 87	          }
 88	        }
 89	      }
 90	    }
 91	  }
 92	  // 内部行的边界列
 93	  for y in 1..<(h - 1) {
 94	    let idx = y * w
 95	    if binary[idx] {
 96	      for m in mask1 {
 97	        let (dx, dy, d) = m
 98	        let nx = dx
 99	        let ny = y + dy
100	        if nx >= 0 && ny >= 0 && ny < h {
101	          let nd = dist[ny * w + nx] + d
102	          if nd < dist[idx] {
103	            dist[idx] = nd
104	          }
105	        }
106	      }
107	    }
108	  }
109	  // 反向扫描（右下到左上）
110	  // 内部区域：x < w - 1 && y >= 1 && y < h - 1
111	  for y in 1..<(h - 1) {
112	    for x in 0..<(w - 1) {
113	      let idx = y * w + x
114	      if !binary[idx] {
115	        continue
116	      }
117	      for m in mask2 {
118	        let (dx, dy, d) = m
119	        let nd = dist[(y + dy) * w + (x + dx)] + d
120	        if nd < dist[idx] {
121	          dist[idx] = nd
122	        }
123	      }
124	    }
125	  }
126	  // 边界行
127	  for yy in 0..<h {
128	    if yy >= 1 && yy < h - 1 {
129	      continue
130	    }
131	    let y = yy
132	    for xx in 0..<w {
133	      let x = w - 1 - xx
134	      let idx = y * w + x
135	      if !binary[idx] {
136	        continue
137	      }
138	      for m in mask2 {
139	        let (dx, dy, d) = m
140	        let nx = x + dx
141	        let ny = y + dy
142	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
143	          let nd = dist[ny * w + nx] + d
144	          if nd < dist[idx] {
145	            dist[idx] = nd
146	          }
147	        }
148	      }
149	    }
150	  }
151	  // 内部行的边界列
152	  for y in 1..<(h - 1) {
153	    let x = w - 1
154	    let idx = y * w + x
155	    if binary[idx] {
156	      for m in mask2 {
157	        let (dx, dy, d) = m
158	        let nx = x + dx
159	        let ny = y + dy
160	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
161	          let nd = dist[ny * w + nx] + d
162	          if nd < dist[idx] {
163	            dist[idx] = nd
164	          }
165	        }
166	      }
167	    }
168	  }
169	  dist
170	}
   	…

174	pub fn visualize_distance_transform(
175	  dist : Array[Float],
176	  w : Int,
177	  h : Int,
178	) -> @types.Image {
179	  let n = w * h
180	  let out : Array[Byte] = Array::make(n, b'\x00')
181	  if n == 0 {
182	    return @types.Image::{	<-- UNCOVERED
183	      width: 0,
184	      height: 0,
185	      channels: 1,
186	      data: Bytes::from_array([]),	<-- UNCOVERED
187	    }
188	  }
189	  let mut max_dist = 0.0F
190	  for i in 0..<n {
191	    if dist[i] > max_dist {
192	      max_dist = dist[i]
193	    }
194	  }
195	  if max_dist > 0.0F {
196	    for i in 0..<n {
197	      let val = (dist[i] / max_dist * 255.0F).to_int()
198	      out[i] = val.clamp(min=0, max=255).to_byte()
199	    }
200	  }
201	  @types.Image::{
202	    width: w,
203	    height: h,
204	    channels: 1,
205	    data: Bytes::from_array(out),
206	  }
207	}
   	…

213	pub fn skeletonize(img : @types.Image) -> @types.Image raise @types.LoadError {
214	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
215	    raise @types.LoadError::DecodeFailed("skeletonize 仅支持 channels=1/3/4")	<-- UNCOVERED
216	  }
217	  let w = img.width
218	  let h = img.height
219	  let n = w * h
220	  if n == 0 {
221	    return img	<-- UNCOVERED
222	  }
223	  // 计算距离变换
224	  let dist = distance_transform(img, distance_type=2)
225	  // 骨架点：局部最大值
226	  let ch = img.channels
227	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
228	  for y in 1..<(h - 1) {
229	    for x in 1..<(w - 1) {
230	      let idx = y * w + x
231	      if dist[idx] == 0.0F {
232	        continue
233	      }
234	      let mut is_max = true
235	      for dy in -1..<=1 {
236	        for dx in -1..<=1 {
237	          if dx == 0 && dy == 0 {
238	            continue
239	          }
240	          if dist[(y + dy) * w + (x + dx)] > dist[idx] {
241	            is_max = false
242	            break
243	          }
244	        }
245	        if !is_max {
246	          break
247	        }
248	      }
249	      if is_max {
250	        for c in 0..<ch {
251	          if ch == 1 {
252	            out[idx] = b'\xFF'
253	          } else if c < 3 {	<-- UNCOVERED
254	            out[idx * ch + c] = b'\xFF'
255	          }
256	        }
257	        if ch == 4 {
258	          out[idx * ch + 3] = img.data[idx * ch + 3]	<-- UNCOVERED
259	        }
260	      }
261	    }
262	  }
263	  @types.Image::{
264	    width: w,
265	    height: h,
266	    channels: ch,
267	    data: Bytes::from_array(out),
268	  }
269	}
   	…

4 uncovered line(s) in src\process\segment\grabcut.mbt:

148	fn fit_gmm(
149	  r_arr : Array[Float],
150	  g_arr : Array[Float],
151	  b_arr : Array[Float],
152	  pixels : Array[Int],
153	  k : Int,
154	) -> (Array[(Float, Float, Float)], Array[(Float, Float, Float)]) {
155	  let n = pixels.length()
156	  if n == 0 {
157	    return ([], [])	<-- UNCOVERED
158	  }
159	  let actual_k = if n < k { n } else { k }
160	  // K-means 初始化
161	  let means_r : Array[Float] = Array::make(actual_k, 0.0F)
162	  let means_g : Array[Float] = Array::make(actual_k, 0.0F)
163	  let means_b : Array[Float] = Array::make(actual_k, 0.0F)
164	  let step = if n > actual_k { n / actual_k } else { 1 }
165	  for i in 0..<actual_k {
166	    let idx = pixels[(i * step).clamp(min=0, max=n - 1)]
167	    means_r[i] = r_arr[idx]
168	    means_g[i] = g_arr[idx]
169	    means_b[i] = b_arr[idx]
170	  }
171	  let labels : Array[Int] = Array::make(n, 0)
172	  // K-means 迭代
173	  for _ in 0..<10 {
174	    let mut changed = false
175	    for i in 0..<n {
176	      let idx = pixels[i]
177	      let r = r_arr[idx]
178	      let g = g_arr[idx]
179	      let b = b_arr[idx]
180	      let mut best = 0
181	      let mut best_dist = color_dist_sq_f(
182	        r,
183	        g,
184	        b,
185	        means_r[0],
186	        means_g[0],
187	        means_b[0],
188	      )
189	      for j in 1..<actual_k {
190	        let d = color_dist_sq_f(r, g, b, means_r[j], means_g[j], means_b[j])
191	        if d < best_dist {
192	          best_dist = d
193	          best = j
194	        }
195	      }
196	      if labels[i] != best {
197	        changed = true
198	        labels[i] = best
199	      }
200	    }
201	    if !changed {
202	      break
203	    }
204	    // 更新聚类中心
205	    let sum_r : Array[Float] = Array::make(actual_k, 0.0F)
206	    let sum_g : Array[Float] = Array::make(actual_k, 0.0F)
207	    let sum_b : Array[Float] = Array::make(actual_k, 0.0F)
208	    let cnt : Array[Int] = Array::make(actual_k, 0)
209	    for i in 0..<n {
210	      let idx = pixels[i]
211	      let lbl = labels[i]
212	      sum_r[lbl] += r_arr[idx]
213	      sum_g[lbl] += g_arr[idx]
214	      sum_b[lbl] += b_arr[idx]
215	      cnt[lbl] += 1
216	    }
217	    for j in 0..<actual_k {
218	      if cnt[j] > 0 {
219	        let cf = Float::from_int(cnt[j])
220	        means_r[j] = sum_r[j] / cf
221	        means_g[j] = sum_g[j] / cf
222	        means_b[j] = sum_b[j] / cf
223	      }
224	    }
225	  }
226	  // 计算方差（对角协方差）
227	  let vars_r : Array[Float] = Array::make(actual_k, 1.0F)
228	  let vars_g : Array[Float] = Array::make(actual_k, 1.0F)
229	  let vars_b : Array[Float] = Array::make(actual_k, 1.0F)
230	  let cnt : Array[Int] = Array::make(actual_k, 0)
231	  for i in 0..<n {
232	    let idx = pixels[i]
233	    let lbl = labels[i]
234	    let dr = r_arr[idx] - means_r[lbl]
235	    let dg = g_arr[idx] - means_g[lbl]
236	    let db = b_arr[idx] - means_b[lbl]
237	    vars_r[lbl] += dr * dr
238	    vars_g[lbl] += dg * dg
239	    vars_b[lbl] += db * db
240	    cnt[lbl] += 1
241	  }
242	  for j in 0..<actual_k {
243	    if cnt[j] > 0 {
244	      let cf = Float::from_int(cnt[j])
245	      vars_r[j] = (vars_r[j] / cf).clamp(min=1.0F, max=10000.0F)
246	      vars_g[j] = (vars_g[j] / cf).clamp(min=1.0F, max=10000.0F)
247	      vars_b[j] = (vars_b[j] / cf).clamp(min=1.0F, max=10000.0F)
248	    }
249	  }
250	  // 打包返回
251	  let means : Array[(Float, Float, Float)] = []
252	  let vars : Array[(Float, Float, Float)] = []
253	  for j in 0..<actual_k {
254	    means.push((means_r[j], means_g[j], means_b[j]))
255	    vars.push((vars_r[j], vars_g[j], vars_b[j]))
256	  }
257	  (means, vars)
258	}
   	…

261	fn gmm_distance(
262	  r : Float,
263	  g : Float,
264	  b : Float,
265	  means : Array[(Float, Float, Float)],
266	  vars : Array[(Float, Float, Float)],
267	) -> Float {
268	  let k = means.length()
269	  if k == 0 {
270	    return 1000000.0F	<-- UNCOVERED
271	  }
272	  // 找最近的 GMM 分量（简化：不计算完整概率，用马氏距离）
273	  let mut min_dist = 1000000.0F
274	  for j in 0..<k {
275	    let (mr, mg, mb) = means[j]
276	    let (vr, vg, vb) = vars[j]
277	    let dr = r - mr
278	    let dg = g - mg
279	    let db = b - mb
280	    // 马氏距离（对角协方差）+ log(det)
281	    let det = vr * vg * vb
282	    let log_det = @math.lnf(det)
283	    let dist = dr * dr / vr + dg * dg / vg + db * db / vb + log_det
284	    if dist < min_dist {
285	      min_dist = dist
286	    }
287	  }
288	  min_dist
289	}
   	…

294	fn smooth_term(
295	  mask : Array[Int],
296	  r_arr : Array[Float],
297	  g_arr : Array[Float],
298	  b_arr : Array[Float],
299	  x : Int,
300	  y : Int,
301	  w : Int,
302	  h : Int,
303	  label : Int,
304	  gamma : Float,
305	  two_sigma_sq : Float,
306	) -> Float {
307	  let mut energy = 0.0F
308	  let idx = y * w + x
309	  let r = r_arr[idx]
310	  let g = g_arr[idx]
311	  let b = b_arr[idx]
312	  // 4 邻域
313	  let neighbors = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]
314	  for i in 0..<4 {
315	    let (nx, ny) = neighbors[i]
316	    if nx < 0 || nx >= w || ny < 0 || ny >= h {
317	      continue	<-- UNCOVERED
318	    }
319	    let nidx = ny * w + nx
320	    let n_label = mask[nidx]
321	    // 判断邻居标签是否与当前不同
322	    let n_is_fg = n_label == gc_fgd || n_label == gc_pr_fgd
323	    let cur_is_fg = label == gc_fgd || label == gc_pr_fgd
324	    if n_is_fg != cur_is_fg {
325	      let dr = r - r_arr[nidx]
326	      let dg = g - g_arr[nidx]
327	      let db = b - b_arr[nidx]
328	      let color_dist = dr * dr + dg * dg + db * db
329	      let weight = gamma *
330	        Float::from_double(
331	          @math.exp(-color_dist.to_double() / two_sigma_sq.to_double()),
332	        )
333	      energy += weight
334	    }
335	  }
336	  energy
337	}
   	…

340	fn compute_sigma(
341	  r_arr : Array[Float],
342	  g_arr : Array[Float],
343	  b_arr : Array[Float],
344	  w : Int,
345	  h : Int,
346	) -> Float {
347	  let mut sum = 0.0F
348	  let mut count = 0
349	  for y in 0..<h {
350	    for x in 0..<(w - 1) {
351	      let idx = y * w + x
352	      let nidx = y * w + x + 1
353	      let dr = r_arr[idx] - r_arr[nidx]
354	      let dg = g_arr[idx] - g_arr[nidx]
355	      let db = b_arr[idx] - b_arr[nidx]
356	      sum += dr * dr + dg * dg + db * db
357	      count += 1
358	    }
359	  }
360	  for y in 0..<(h - 1) {
361	    for x in 0..<w {
362	      let idx = y * w + x
363	      let nidx = (y + 1) * w + x
364	      let dr = r_arr[idx] - r_arr[nidx]
365	      let dg = g_arr[idx] - g_arr[nidx]
366	      let db = b_arr[idx] - b_arr[nidx]
367	      sum += dr * dr + dg * dg + db * db
368	      count += 1
369	    }
370	  }
371	  if count == 0 {
372	    return 30.0F	<-- UNCOVERED
373	  }
374	  (sum / Float::from_int(count)).sqrt().clamp(min=1.0F, max=100.0F)
375	}
   	…

3 uncovered line(s) in src\process\segment\morphology.mbt:

 6	pub fn erode(img : @types.Image) -> @types.Image {
 7	  let ch = img.channels
 8	  let w = img.width
 9	  let h = img.height
10	  let n = w * h
11	  let out : Array[Byte] = Array::make(n * ch, b'\x00')
12	  // 预计算像素 Int 数组，避免内层循环重复类型转换
13	  let src : Array[Int] = Array::make(n * ch, 0)
14	  for i in 0..<(n * ch) {
15	    src[i] = img.data[i].to_int()
16	  }
17	  // 内部区域：x in [1, w-1), y in [1, h-1)，3x3 核完全在图像内
18	  for y in 1..<(h - 1) {
19	    for x in 1..<(w - 1) {
20	      for c in 0..<ch {
21	        let base = ((y - 1) * w + (x - 1)) * ch + c
22	        let mut min_v = src[base]
23	        if src[base + ch] < min_v {
24	          min_v = src[base + ch]
25	        }
26	        if src[base + 2 * ch] < min_v {
27	          min_v = src[base + 2 * ch]
28	        }
29	        let row1 = base + w * ch
30	        if src[row1] < min_v {
31	          min_v = src[row1]
32	        }
33	        if src[row1 + ch] < min_v {
34	          min_v = src[row1 + ch]
35	        }
36	        if src[row1 + 2 * ch] < min_v {
37	          min_v = src[row1 + 2 * ch]	<-- UNCOVERED
38	        }
39	        let row2 = base + 2 * w * ch
40	        if src[row2] < min_v {
41	          min_v = src[row2]
42	        }
43	        if src[row2 + ch] < min_v {
44	          min_v = src[row2 + ch]	<-- UNCOVERED
45	        }
46	        if src[row2 + 2 * ch] < min_v {
47	          min_v = src[row2 + 2 * ch]	<-- UNCOVERED
48	        }
49	        out[(y * w + x) * ch + c] = min_v.to_byte()
50	      }
51	    }
52	  }
53	  // 边界区域：需要 clamp
54	  for y in 0..<h {
55	    let y_inner = y >= 1 && y < h - 1
56	    for x in 0..<w {
57	      if y_inner && x >= 1 && x < w - 1 {
58	        continue
59	      }
60	      for c in 0..<ch {
61	        let mut min_v = 255
62	        for dy in -1..<=1 {
63	          for dx in -1..<=1 {
64	            let sx = clamp_i(x + dx, 0, w - 1)
65	            let sy = clamp_i(y + dy, 0, h - 1)
66	            let v = src[(sy * w + sx) * ch + c]
67	            if v < min_v {
68	              min_v = v
69	            }
70	          }
71	        }
72	        out[(y * w + x) * ch + c] = min_v.to_byte()
73	      }
74	    }
75	  }
76	  @types.Image::{
77	    width: w,
78	    height: h,
79	    channels: ch,
80	    data: Bytes::from_array(out),
81	  }
82	}
  	…

27 uncovered line(s) in src\process\segment\morphology_16f.mbt:

 92	pub fn erode_f(img : @types.ImageF) -> @types.ImageF {
 93	  let ch = img.channels
 94	  let w = img.width
 95	  let h = img.height
 96	  let n = w * h
 97	  let bpp = ch * 4
 98	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
 99	  let src : Array[Float] = Array::make(n * ch, 0.0F)
100	  for i in 0..<(n * ch) {
101	    src[i] = read_float_le_m(img.data, i * 4)
102	  }
103	  for y in 1..<(h - 1) {
104	    for x in 1..<(w - 1) {
105	      for c in 0..<ch {
106	        let base = ((y - 1) * w + (x - 1)) * ch + c
107	        let mut min_v = src[base]
108	        if src[base + ch] < min_v {
109	          min_v = src[base + ch]	<-- UNCOVERED
110	        }
111	        if src[base + 2 * ch] < min_v {
112	          min_v = src[base + 2 * ch]	<-- UNCOVERED
113	        }
114	        let row1 = base + w * ch
115	        if src[row1] < min_v {
116	          min_v = src[row1]	<-- UNCOVERED
117	        }
118	        if src[row1 + ch] < min_v {
119	          min_v = src[row1 + ch]	<-- UNCOVERED
120	        }
121	        if src[row1 + 2 * ch] < min_v {
122	          min_v = src[row1 + 2 * ch]	<-- UNCOVERED
123	        }
124	        let row2 = base + 2 * w * ch
125	        if src[row2] < min_v {
126	          min_v = src[row2]	<-- UNCOVERED
127	        }
128	        if src[row2 + ch] < min_v {
129	          min_v = src[row2 + ch]	<-- UNCOVERED
130	        }
131	        if src[row2 + 2 * ch] < min_v {
132	          min_v = src[row2 + 2 * ch]	<-- UNCOVERED
133	        }
134	        write_f32_le_m(out, (y * w + x) * bpp + c * 4, min_v)
135	      }
136	    }
137	  }
138	  for y in 0..<h {
139	    let y_inner = y >= 1 && y < h - 1
140	    for x in 0..<w {
141	      if y_inner && x >= 1 && x < w - 1 {
142	        continue
143	      }
144	      for c in 0..<ch {
145	        let mut min_v = 1.0F
146	        for dy in -1..<=1 {
147	          for dx in -1..<=1 {
148	            let sx = clamp_morph(x + dx, 0, w - 1)
149	            let sy = clamp_morph(y + dy, 0, h - 1)
150	            let v = src[(sy * w + sx) * ch + c]
151	            if v < min_v {
152	              min_v = v
153	            }
154	          }
155	        }
156	        write_f32_le_m(out, (y * w + x) * bpp + c * 4, min_v)
157	      }
158	    }
159	  }
160	  @types.ImageF::{
161	    width: w,
162	    height: h,
163	    channels: ch,
164	    data: Bytes::from_array(out),
165	  }
166	}
   	…

248	pub fn dilate_f(img : @types.ImageF) -> @types.ImageF {
249	  let ch = img.channels
250	  let w = img.width
251	  let h = img.height
252	  let n = w * h
253	  let bpp = ch * 4
254	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')
255	  let src : Array[Float] = Array::make(n * ch, 0.0F)
256	  for i in 0..<(n * ch) {
257	    src[i] = read_float_le_m(img.data, i * 4)
258	  }
259	  for y in 1..<(h - 1) {
260	    for x in 1..<(w - 1) {
261	      for c in 0..<ch {
262	        let base = ((y - 1) * w + (x - 1)) * ch + c
263	        let mut max_v = src[base]
264	        if src[base + ch] > max_v {
265	          max_v = src[base + ch]	<-- UNCOVERED
266	        }
267	        if src[base + 2 * ch] > max_v {
268	          max_v = src[base + 2 * ch]	<-- UNCOVERED
269	        }
270	        let row1 = base + w * ch
271	        if src[row1] > max_v {
272	          max_v = src[row1]	<-- UNCOVERED
273	        }
274	        if src[row1 + ch] > max_v {
275	          max_v = src[row1 + ch]	<-- UNCOVERED
276	        }
277	        if src[row1 + 2 * ch] > max_v {
278	          max_v = src[row1 + 2 * ch]	<-- UNCOVERED
279	        }
280	        let row2 = base + 2 * w * ch
281	        if src[row2] > max_v {
282	          max_v = src[row2]	<-- UNCOVERED
283	        }
284	        if src[row2 + ch] > max_v {
285	          max_v = src[row2 + ch]	<-- UNCOVERED
286	        }
287	        if src[row2 + 2 * ch] > max_v {
288	          max_v = src[row2 + 2 * ch]	<-- UNCOVERED
289	        }
290	        write_f32_le_m(out, (y * w + x) * bpp + c * 4, max_v)
291	      }
292	    }
293	  }
294	  for y in 0..<h {
295	    let y_inner = y >= 1 && y < h - 1
296	    for x in 0..<w {
297	      if y_inner && x >= 1 && x < w - 1 {
298	        continue
299	      }
300	      for c in 0..<ch {
301	        let mut max_v = 0.0F
302	        for dy in -1..<=1 {
303	          for dx in -1..<=1 {
304	            let sx = clamp_morph(x + dx, 0, w - 1)
305	            let sy = clamp_morph(y + dy, 0, h - 1)
306	            let v = src[(sy * w + sx) * ch + c]
307	            if v > max_v {
308	              max_v = v
309	            }
310	          }
311	        }
312	        write_f32_le_m(out, (y * w + x) * bpp + c * 4, max_v)
313	      }
314	    }
315	  }
316	  @types.ImageF::{
317	    width: w,
318	    height: h,
319	    channels: ch,
320	    data: Bytes::from_array(out),
321	  }
322	}
   	…

346	pub fn morph_close_f(img : @types.ImageF) -> @types.ImageF {
347	  erode_f(dilate_f(img))	<-- UNCOVERED
348	}
   	…

422	pub fn morph_tophat_f(img : @types.ImageF) -> @types.ImageF {
423	  let opened = morph_open_f(img)	<-- UNCOVERED
424	  let ch = img.channels
425	  let w = img.width
426	  let h = img.height
427	  let n = w * h
428	  let bpp = ch * 4
429	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
430	  for i in 0..<(n * ch) {
431	    let v = read_float_le_m(img.data, i * 4) -	<-- UNCOVERED
432	      read_float_le_m(opened.data, i * 4)
433	    write_f32_le_m(out, i * 4, if v < 0.0F { 0.0F } else { v })
434	  }
435	  @types.ImageF::{
436	    width: w,
437	    height: h,
438	    channels: ch,
439	    data: Bytes::from_array(out),	<-- UNCOVERED
440	  }
441	}
   	…

467	pub fn morph_blackhat_f(img : @types.ImageF) -> @types.ImageF {
468	  let closed = morph_close_f(img)	<-- UNCOVERED
469	  let ch = img.channels
470	  let w = img.width
471	  let h = img.height
472	  let n = w * h
473	  let bpp = ch * 4
474	  let out : Array[Byte] = Array::make(n * bpp, b'\x00')	<-- UNCOVERED
475	  for i in 0..<(n * ch) {
476	    let v = read_float_le_m(closed.data, i * 4) -	<-- UNCOVERED
477	      read_float_le_m(img.data, i * 4)
478	    write_f32_le_m(out, i * 4, if v < 0.0F { 0.0F } else { v })
479	  }
480	  @types.ImageF::{
481	    width: w,
482	    height: h,
483	    channels: ch,
484	    data: Bytes::from_array(out),	<-- UNCOVERED
485	  }
486	}
   	…

685	pub fn morph_open_custom_f(
686	  img : @types.ImageF,
687	  se : StructuringElement,
688	) -> @types.ImageF {
689	  dilate_custom_f(erode_custom_f(img, se), se)	<-- UNCOVERED
690	}
   	…

694	pub fn morph_close_custom_16(
695	  img : @types.Image16,
696	  se : StructuringElement,
697	) -> @types.Image16 {
698	  erode_custom_16(dilate_custom_16(img, se), se)	<-- UNCOVERED
699	}
   	…

6 uncovered line(s) in src\process\segment\segment.mbt:

133	pub fn region_growing_segment(
134	  img : @types.Image,
135	  seeds : Array[(Int, Int)],
136	  threshold? : Int = 30,
137	) -> (SegmentLabelImage, Array[SegmentRegion]) raise @types.LoadError {
138	  if img.channels != 3 && img.channels != 4 {
139	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
140	      "region_growing_segment 需要 RGB/RGBA 图像",
141	    )
142	  }
143	  let ch = img.channels
144	  let w = img.width
145	  let h = img.height
146	  let n = w * h
147	  let labels : Array[Int] = Array::make(n, 0)
148	  let regions : Array[SegmentRegion] = []
149	  let data_int : Array[Int] = Array::make(n * ch, 0)
150	  for i in 0..<(n * ch) {
151	    data_int[i] = img.data[i].to_int()
152	  }
153	  let mut current_label = 0
154	  for seed in seeds {
155	    let (sx, sy) = seed
156	    if sx < 0 || sx >= w || sy < 0 || sy >= h {
157	      continue
158	    }
159	    let seed_idx = sy * w + sx
160	    if labels[seed_idx] != 0 {
161	      continue // 已分配
162	    }
163	    current_label += 1
164	    let seed_r = data_int[seed_idx * ch]
165	    let seed_g = data_int[seed_idx * ch + 1]
166	    let seed_b = data_int[seed_idx * ch + 2]
167	    // BFS 区域生长
168	    let stack : Array[Int] = [seed_idx]
169	    labels[seed_idx] = current_label
170	    let mut area = 0
171	    let mut sum_r = 0
172	    let mut sum_g = 0
173	    let mut sum_b = 0
174	    let mut min_x = sx
175	    let mut max_x = sx
176	    let mut min_y = sy
177	    let mut max_y = sy
178	    while stack.length() > 0 {
179	      let idx = stack.remove(stack.length() - 1)
180	      let px = idx % w
181	      let py = idx / w
182	      area += 1
183	      sum_r += data_int[idx * ch]
184	      sum_g += data_int[idx * ch + 1]
185	      sum_b += data_int[idx * ch + 2]
186	      if px < min_x {
187	        min_x = px	<-- UNCOVERED
188	      }
189	      if px > max_x {
190	        max_x = px
191	      }
192	      if py < min_y {
193	        min_y = py	<-- UNCOVERED
194	      }
195	      if py > max_y {
196	        max_y = py
197	      }
198	      // 4 邻域
199	      let neighbors = [(px + 1, py), (px - 1, py), (px, py + 1), (px, py - 1)]
200	      for nb in neighbors {
201	        let (nx, ny) = nb
202	        if nx >= 0 && nx < w && ny >= 0 && ny < h {
203	          let nidx = ny * w + nx
204	          if labels[nidx] == 0 {
205	            let nr = data_int[nidx * ch]
206	            let ng = data_int[nidx * ch + 1]
207	            let nb2 = data_int[nidx * ch + 2]
208	            let dist = color_dist_sq(seed_r, seed_g, seed_b, nr, ng, nb2)
209	            if dist <= threshold * threshold {
210	              labels[nidx] = current_label
211	              stack.push(nidx)
212	            }
213	          }
214	        }
215	      }
216	    }
217	    regions.push(SegmentRegion::{
218	      label: current_label,
219	      area,
220	      mean_r: if area > 0 {
221	        sum_r / area
222	      } else {
223	        0	<-- UNCOVERED
224	      },
225	      mean_g: if area > 0 {
226	        sum_g / area
227	      } else {
228	        0	<-- UNCOVERED
229	      },
230	      mean_b: if area > 0 {
231	        sum_b / area
232	      } else {
233	        0	<-- UNCOVERED
234	      },
235	      bbox_x: min_x,
236	      bbox_y: min_y,
237	      bbox_w: max_x - min_x + 1,
238	      bbox_h: max_y - min_y + 1,
239	    })
240	  }
241	  (
242	    SegmentLabelImage::{
243	      width: w,
244	      height: h,
245	      labels,
246	      num_regions: current_label,
247	    },
248	    regions,
249	  )
250	}
   	…

29 uncovered line(s) in src\process\segment\segmentation.mbt:

 66	pub fn watershed_segment(
 67	  img : @types.Image,
 68	  config? : WatershedConfig,
 69	) -> SegmentationResult {
 70	  let cfg = match config {
 71	    Some(c) => c	<-- UNCOVERED
 72	    None => watershed_default_config()
 73	  }
 74	  let w = img.width
 75	  let h = img.height
 76	  // 1. 计算梯度
 77	  let gradient = compute_gradient(img)
 78	  // 2. 寻找局部最小值作为种子点
 79	  let labels : Array[Int] = Array::make(w * h, 0)
 80	  let mut next_label = 1
 81	  let seeds : Array[(Int, Int)] = []
 82	  for y in 1..<(h - 1) {
 83	    for x in 1..<(w - 1) {
 84	      let g = gradient[y * w + x]
 85	      if g < cfg.threshold {
 86	        // 检查是否是局部最小值
 87	        let mut is_min = true
 88	        let neighbors = [
 89	          (-1, 0),
 90	          (1, 0),
 91	          (0, -1),
 92	          (0, 1),
 93	          (-1, -1),
 94	          (1, 1),
 95	          (-1, 1),
 96	          (1, -1),
 97	        ]
 98	        for _tup0 in neighbors {
 99	          let (dx, dy) = _tup0
100	          if gradient[(y + dy) * w + (x + dx)] < g {
101	            is_min = false	<-- UNCOVERED
102	          }
103	        }
104	        if is_min && labels[y * w + x] == 0 {
105	          seeds.push((x, y))
106	          labels[y * w + x] = next_label
107	          next_label += 1
108	        }
109	      }
110	    }
111	  }
112	  // 3. 区域生长（从种子点开始，按梯度从小到大扩展）
113	  let mut changed = true
114	  while changed {
115	    changed = false
116	    for y in 0..<h {
117	      for x in 0..<w {
118	        if labels[y * w + x] == 0 && gradient[y * w + x] < cfg.threshold * 2.0 {
119	          // 检查邻域是否有已标记像素
120	          let neighbors = if cfg.connectivity == 8 {
121	            [
122	              (-1, 0),
123	              (1, 0),
124	              (0, -1),
125	              (0, 1),
126	              (-1, -1),
127	              (1, 1),
128	              (-1, 1),
129	              (1, -1),
130	            ]
131	          } else {
132	            [(-1, 0), (1, 0), (0, -1), (0, 1)]	<-- UNCOVERED
133	          }
134	          for _tup1 in neighbors {
135	            let (dx, dy) = _tup1
136	            let nx = x + dx
137	            let ny = y + dy
138	            if nx >= 0 && nx < w && ny >= 0 && ny < h && labels[ny * w + nx] > 0 {
139	              labels[y * w + x] = labels[ny * w + nx]
140	              changed = true
141	              break
142	            }
143	          }
144	        }
145	      }
146	    }
147	  }
148	  // 4. 合并小区域
149	  let region_sizes : Array[Int] = Array::make(next_label, 0)
150	  for i in 0..<(w * h) {
151	    if labels[i] > 0 {
152	      region_sizes[labels[i]] += 1
153	    }
154	  }
155	  // 小区域合并到相邻大区域
156	  for label in 1..<next_label {
157	    if region_sizes[label] < cfg.min_region_size {
158	      // 找到第一个相邻的大区域
159	      for y in 0..<h {
160	        for x in 0..<w {
161	          if labels[y * w + x] == label {
162	            let neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
163	            for _tup2 in neighbors {
164	              let (dx, dy) = _tup2
165	              let nx = x + dx
166	              let ny = y + dy
167	              if nx >= 0 &&
168	                nx < w &&
169	                ny >= 0 &&
170	                ny < h &&
171	                labels[ny * w + nx] > 0 &&
172	                labels[ny * w + nx] != label &&
173	                region_sizes[labels[ny * w + nx]] >= cfg.min_region_size {
174	                // 合并
175	                let old_label = label	<-- UNCOVERED
176	                let new_label = labels[ny * w + nx]
177	                for i in 0..<(w * h) {
178	                  if labels[i] == old_label {	<-- UNCOVERED
179	                    labels[i] = new_label
180	                  }
181	                }
182	                region_sizes[new_label] += region_sizes[old_label]
183	                region_sizes[old_label] = 0
184	                break
185	              }
186	            }
187	          }
188	        }
189	      }
190	    }
191	  }
192	  // 5. 重新编号标签
193	  let label_map : Array[Int] = Array::make(next_label, 0)
194	  let mut new_num = 1
195	  let new_sizes : Array[Int] = []
196	  for label in 1..<next_label {
197	    if region_sizes[label] > 0 {
198	      label_map[label] = new_num
199	      new_sizes.push(region_sizes[label])
200	      new_num += 1
201	    }
202	  }
203	  for i in 0..<(w * h) {
204	    if labels[i] > 0 {
205	      labels[i] = label_map[labels[i]]
206	    }
207	  }
208	  SegmentationResult::{
209	    labels,
210	    num_regions: new_num - 1,
211	    region_sizes: new_sizes,
212	  }
213	}
   	…

218	pub fn region_growing(
219	  img : @types.Image,
220	  seeds : Array[(Int, Int)],
221	  threshold? : Double = 30.0,
222	) -> SegmentationResult {
223	  let thresh = threshold
224	  let w = img.width
225	  let h = img.height
226	  let channels = img.channels
227	  let data = img.data
228	  let labels : Array[Int] = Array::make(w * h, 0)
229	  let region_sizes : Array[Int] = []
230	  let mut label = 1
231	  for _tup3 in seeds {
232	    let (sx, sy) = _tup3
233	    if sx < 0 || sx >= w || sy < 0 || sy >= h {
234	      continue	<-- UNCOVERED
235	    }
236	    if labels[sy * w + sx] > 0 {
237	      continue	<-- UNCOVERED
238	    }
239	    // 种子点颜色
240	    let seed_idx = (sy * w + sx) * channels
241	    let seed_r = data[seed_idx].to_double()
242	    let seed_g = if channels >= 2 {
243	      data[seed_idx + 1].to_double()
244	    } else {
245	      seed_r	<-- UNCOVERED
246	    }
247	    let seed_b = if channels >= 3 {
248	      data[seed_idx + 2].to_double()
249	    } else {
250	      seed_r	<-- UNCOVERED
251	    }
252	    // 区域生长（洪水填充）
253	    let stack : Array[(Int, Int)] = []
254	    stack.push((sx, sy))
255	    labels[sy * w + sx] = label
256	    let mut size = 0
257	    while stack.length() > 0 {
258	      match stack.pop() {
259	        Some((x, y)) => {
260	          size += 1
261	          let neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
262	          for _tup4 in neighbors {
263	            let (dx, dy) = _tup4
264	            let nx = x + dx
265	            let ny = y + dy
266	            if nx >= 0 &&
267	              nx < w &&
268	              ny >= 0 &&
269	              ny < h &&
270	              labels[ny * w + nx] == 0 {
271	              let idx = (ny * w + nx) * channels
272	              let r = data[idx].to_double()
273	              let g = if channels >= 2 { data[idx + 1].to_double() } else { r }
274	              let b = if channels >= 3 { data[idx + 2].to_double() } else { r }
275	              let dist = ((r - seed_r) * (r - seed_r) +
276	              (g - seed_g) * (g - seed_g) +
277	              (b - seed_b) * (b - seed_b)).sqrt()
278	              if dist < thresh {
279	                labels[ny * w + nx] = label
280	                stack.push((nx, ny))
281	              }
282	            }
283	          }
284	        }
285	        None => ignore(())	<-- UNCOVERED
286	      }
287	    }
288	    region_sizes.push(size)
289	    label += 1
290	  }
291	  SegmentationResult::{ labels, num_regions: label - 1, region_sizes }
292	}
   	…

296	pub fn threshold_otsu(img : @types.Image) -> @types.Image {
297	  let w = img.width	<-- UNCOVERED
298	  let h = img.height
299	  let channels = img.channels
300	  let data = img.data
301	  // 计算灰度直方图
302	  let histogram : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
303	  for i in 0..<(w * h) {
304	    let idx = i * channels	<-- UNCOVERED
305	    let r = data[idx].to_int()
306	    let g = if channels >= 2 { data[idx + 1].to_int() } else { r }
307	    let b = if channels >= 3 { data[idx + 2].to_int() } else { r }
308	    let gray = (0.299 * r.to_double() +
309	    0.587 * g.to_double() +
310	    0.114 * b.to_double()).to_int()
311	    histogram[gray] += 1
312	  }
313	  // Otsu 阈值计算
314	  let total = w * h
315	  let mut sum = 0
316	  for t in 0..<256 {
317	    sum += t * histogram[t]	<-- UNCOVERED
318	  }
319	  let mut sum_b = 0
320	  let mut w_b = 0
321	  let mut max_variance = 0.0
322	  let mut threshold = 128
323	  for t in 0..<256 {
324	    w_b += histogram[t]	<-- UNCOVERED
325	    if w_b == 0 {
326	      continue	<-- UNCOVERED
327	    }
328	    let w_f = total - w_b
329	    if w_f == 0 {
330	      break	<-- UNCOVERED
331	    }
332	    sum_b += t * histogram[t]
333	    let m_b = sum_b.to_double() / w_b.to_double()	<-- UNCOVERED
334	    let m_f = (sum - sum_b).to_double() / w_f.to_double()
335	    let variance = w_b.to_double() * w_f.to_double() * (m_b - m_f) * (m_b - m_f)
336	    if variance > max_variance {
337	      max_variance = variance	<-- UNCOVERED
338	      threshold = t
339	    }
340	  }
341	  // 应用阈值
342	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')	<-- UNCOVERED
343	  for i in 0..<(w * h) {
344	    let idx = i * channels	<-- UNCOVERED
345	    let r = data[idx].to_int()
346	    let g = if channels >= 2 { data[idx + 1].to_int() } else { r }
347	    let b = if channels >= 3 { data[idx + 2].to_int() } else { r }
348	    let gray = (0.299 * r.to_double() +
349	    0.587 * g.to_double() +
350	    0.114 * b.to_double()).to_int()
351	    let value = if gray > threshold { 255 } else { 0 }
352	    for c in 0..<channels {
353	      out_data[idx + c] = value.to_byte()	<-- UNCOVERED
354	    }
355	  }
356	  @types.Image::{
357	    width: w,
358	    height: h,
359	    channels,
360	    data: Bytes::from_array(out_data),	<-- UNCOVERED
361	  }
362	}
   	…

366	pub fn segmentation_to_color(
367	  result : SegmentationResult,
368	  width : Int,
369	  height : Int,
370	) -> @types.Image {
371	  let colors = [	<-- UNCOVERED
372	    (255, 0, 0),
373	    (0, 255, 0),
374	    (0, 0, 255),
375	    (255, 255, 0),
376	    (255, 0, 255),
377	    (0, 255, 255),
378	    (128, 0, 0),
379	    (0, 128, 0),
380	    (0, 0, 128),
381	    (128, 128, 0),
382	    (128, 0, 128),
383	    (0, 128, 128),
384	    (192, 192, 192),
385	    (128, 128, 128),
386	    (255, 128, 0),
387	    (255, 0, 128),
388	  ]
389	  let out_data : Array[Byte] = Array::make(width * height * 3, b'\x00')	<-- UNCOVERED
390	  for y in 0..<height {
391	    for x in 0..<width {	<-- UNCOVERED
392	      let label = result.labels[y * width + x]
393	      let idx = (y * width + x) * 3
394	      if label > 0 && label <= colors.length() {	<-- UNCOVERED
395	        let (r, g, b) = colors[label - 1]
396	        out_data[idx] = r.to_byte()
397	        out_data[idx + 1] = g.to_byte()
398	        out_data[idx + 2] = b.to_byte()
399	      } else {
400	        out_data[idx] = b'\x00'	<-- UNCOVERED
401	        out_data[idx + 1] = b'\x00'
402	        out_data[idx + 2] = b'\x00'
403	      }
404	    }
405	  }
406	  @types.Image::{
407	    width,
408	    height,
409	    channels: 3,
410	    data: Bytes::from_array(out_data),	<-- UNCOVERED
411	  }
412	}
   	…

2 uncovered line(s) in src\process\segment\segmentation_16f.mbt:

22	fn clamp_int_seg(v : Int, lo : Int, hi : Int) -> Int {
23	  if v < lo {
24	    lo	<-- UNCOVERED
25	  } else if v > hi {
26	    hi	<-- UNCOVERED
27	  } else {
28	    v
29	  }
30	}
  	…

1 uncovered line(s) in src\process\segment\slic.mbt:

13	fn sqrt_d(x : Double) -> Double {
14	  if x <= 0.0 {
15	    return 0.0	<-- UNCOVERED
16	  }
17	  let mut guess = x
18	  for _ in 0..<20 {
19	    guess = (guess + x / guess) / 2.0
20	  }
21	  guess
22	}
  	…

6 uncovered line(s) in src\process\segment\watershed.mbt:

  9	pub fn watershed(
 10	  img : @types.Image,
 11	  markers : Array[Int],
 12	) -> Array[Int] raise @types.LoadError {
 13	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 14	    raise @types.LoadError::DecodeFailed("watershed 仅支持 channels=1/3/4")
 15	  }
 16	  let w = img.width
 17	  let h = img.height
 18	  let n = w * h
 19	  if markers.length() != n {
 20	    raise @types.LoadError::DecodeFailed(
 21	      "markers 长度必须等于 width * height",
 22	    )
 23	  }
 24	  if n == 0 {
 25	    return []	<-- UNCOVERED
 26	  }
 27	  // 转灰度
 28	  let gray : Array[Int] = Array::make(n, 0)
 29	  let ch = img.channels
 30	  for i in 0..<n {
 31	    if ch == 1 {
 32	      gray[i] = img.data[i].to_int()
 33	    } else {
 34	      let r = img.data[i * ch].to_int()	<-- UNCOVERED
 35	      let g = img.data[i * ch + 1].to_int()
 36	      let b = img.data[i * ch + 2].to_int()
 37	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
 38	    }
 39	  }
 40	  // 复制 markers
 41	  let labels : Array[Int] = Array::make(n, 0)
 42	  for i in 0..<n {
 43	    labels[i] = markers[i]
 44	  }
 45	  // 沉浸式分水岭：按灰度值从低到高处理
 46	  // 创建像素索引按灰度值排序的列表
 47	  let indices : Array[Int] = Array::make(n, 0)
 48	  for i in 0..<n {
 49	    indices[i] = i
 50	  }
 51	  // 按灰度值排序（基数排序，0-255）
 52	  let sorted : Array[Array[Int]] = Array::make(256, [])
 53	  for i in 0..<256 {
 54	    sorted[i] = []
 55	  }
 56	  for i in 0..<n {
 57	    sorted[gray[i]].push(i)
 58	  }
 59	  // FIFO 队列
 60	  let wshed = -1 // 分水岭线标记
 61	  let mut current_label = 0
 62	  for i in 0..<n {
 63	    if labels[i] > current_label {
 64	      current_label = labels[i]
 65	    }
 66	  }
 67	  // 按灰度级别处理
 68	  for level in 0..<256 {
 69	    let queue : Array[Int] = []
 70	    // 将当前级别的已标记像素加入队列
 71	    for idx in sorted[level] {
 72	      // 检查邻居是否有已标记的
 73	      let x = idx % w
 74	      let y = idx / w
 75	      let mut has_labeled_neighbor = false
 76	      for dy in -1..<=1 {
 77	        for dx in -1..<=1 {
 78	          if dx == 0 && dy == 0 {
 79	            continue
 80	          }
 81	          let nx = x + dx
 82	          let ny = y + dy
 83	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
 84	            let nidx = ny * w + nx
 85	            if labels[nidx] > 0 || labels[nidx] == wshed {
 86	              has_labeled_neighbor = true
 87	              break
 88	            }
 89	          }
 90	        }
 91	        if has_labeled_neighbor {
 92	          break
 93	        }
 94	      }
 95	      if has_labeled_neighbor {
 96	        queue.push(idx)
 97	      }
 98	    }
 99	    // BFS 扩展（head 指针队列，O(1) dequeue 避免 remove(0) 的 O(n) 移压）
100	    let mut q_head = 0
101	    while q_head < queue.length() {
102	      let idx = queue[q_head]
103	      q_head += 1
104	      let x = idx % w
105	      let y = idx / w
106	      let mut label = 0
107	      let mut is_wshed = false
108	      // 查看邻居标签
109	      for dy in -1..<=1 {
110	        for dx in -1..<=1 {
111	          if dx == 0 && dy == 0 {
112	            continue
113	          }
114	          let nx = x + dx
115	          let ny = y + dy
116	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
117	            let nidx = ny * w + nx
118	            let nl = labels[nidx]
119	            if nl > 0 {
120	              if label == 0 {
121	                label = nl
122	              } else if label != nl {
123	                is_wshed = true
124	              }
125	            }
126	          }
127	        }
128	      }
129	      if is_wshed {
130	        labels[idx] = wshed
131	      } else if label > 0 {
132	        labels[idx] = label
133	        // 将同级别未标记邻居加入队列
134	        for dy in -1..<=1 {
135	          for dx in -1..<=1 {
136	            if dx == 0 && dy == 0 {
137	              continue
138	            }
139	            let nx = x + dx
140	            let ny = y + dy
141	            if nx >= 0 && nx < w && ny >= 0 && ny < h {
142	              let nidx = ny * w + nx
143	              if labels[nidx] == 0 && gray[nidx] == level {
144	                labels[nidx] = -2 // 临时标记，在队列中
145	                queue.push(nidx)
146	              }
147	            }
148	          }
149	        }
150	      }
151	    }
152	    // 清除临时标记
153	    for idx in sorted[level] {
154	      if labels[idx] == -2 {
155	        labels[idx] = 0	<-- UNCOVERED
156	      }
157	    }
158	  }
159	  labels
160	}
   	…

166	pub fn watershed_auto(
167	  img : @types.Image,
168	) -> (Array[Int], Int) raise @types.LoadError {
169	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
170	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
171	      "watershed_auto 仅支持 channels=1/3/4",
172	    )
173	  }
174	  let w = img.width
175	  let h = img.height
176	  let n = w * h
177	  if n == 0 {
178	    return ([], 0)	<-- UNCOVERED
179	  }
180	  // 转灰度
181	  let gray : Array[Int] = Array::make(n, 0)
182	  let ch = img.channels
183	  for i in 0..<n {
184	    if ch == 1 {
185	      gray[i] = img.data[i].to_int()
186	    } else {
187	      let r = img.data[i * ch].to_int()	<-- UNCOVERED
188	      let g = img.data[i * ch + 1].to_int()
189	      let b = img.data[i * ch + 2].to_int()
190	      gray[i] = (r * 299 + g * 587 + b * 114) / 1000
191	    }
192	  }
193	  // 寻找局部最小值作为种子点
194	  let markers : Array[Int] = Array::make(n, 0)
195	  let mut current_label = 0
196	  for y in 0..<h {
197	    for x in 0..<w {
198	      let idx = y * w + x
199	      let val = gray[idx]
200	      let mut is_min = true
201	      for dy in -1..<=1 {
202	        for dx in -1..<=1 {
203	          if dx == 0 && dy == 0 {
204	            continue
205	          }
206	          let nx = x + dx
207	          let ny = y + dy
208	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
209	            let nidx = ny * w + nx
210	            if gray[nidx] < val {
211	              is_min = false
212	              break
213	            }
214	          }
215	        }
216	        if !is_min {
217	          break
218	        }
219	      }
220	      if is_min && markers[idx] == 0 {
221	        current_label += 1
222	        // BFS 标记连通的等值像素（head 指针队列，避免 remove 开销）
223	        let stack : Array[Int] = [idx]
224	        let mut sp = 0
225	        markers[idx] = current_label
226	        while sp < stack.length() {
227	          let pidx = stack[sp]
228	          sp += 1
229	          let px = pidx % w
230	          let py = pidx / w
231	          for dy in -1..<=1 {
232	            for dx in -1..<=1 {
233	              if dx == 0 && dy == 0 {
234	                continue
235	              }
236	              let nx = px + dx
237	              let ny = py + dy
238	              if nx >= 0 && nx < w && ny >= 0 && ny < h {
239	                let nidx = ny * w + nx
240	                if markers[nidx] == 0 && gray[nidx] == val {
241	                  markers[nidx] = current_label
242	                  stack.push(nidx)
243	                }
244	              }
245	            }
246	          }
247	        }
248	      }
249	    }
250	  }
251	  let labels = watershed(img, markers)
252	  (labels, current_label)
253	}
   	…

3 uncovered line(s) in src\process\segmentation\image_segmentation.mbt:

432	pub fn watershed_segmentation(
433	  img : @types.Image,
434	  config : WatershedConfig,
435	) -> SemanticSegmentationResult {
436	  let w = img.width
437	  let h = img.height
438	  let total = w * h
439	
440	  // 简化版：基于梯度的区域生长
441	  let labels : Array[Int] = []
442	  for _ in 0..<total {
443	    labels.push(-1)
444	  }
445	
446	  // 找局部最小值作为种子点
447	  let mut region_id = 0
448	  for y in 1..<(h - 1) {
449	    for x in 1..<(w - 1) {
450	      let idx = y * w + x
451	      let pixel = img.data[idx].to_double()
452	      let mut is_min = true
453	      for dy in -1..<2 {
454	        for dx in -1..<2 {
455	          if dx == 0 && dy == 0 {
456	            continue
457	          }
458	          let neighbor = img.data[(y + dy) * w + (x + dx)].to_double()
459	          if neighbor < pixel {
460	            is_min = false
461	          }
462	        }
463	      }
464	      if is_min && labels[idx] == -1 {
465	        // 区域生长
466	        let queue_x : Array[Int] = [x]
467	        let queue_y : Array[Int] = [y]
468	        labels[idx] = region_id
469	        let mut queue_idx = 0
470	        while queue_idx < queue_x.length() {
471	          let cx = queue_x[queue_idx]
472	          let cy = queue_y[queue_idx]
473	          queue_idx += 1
474	          for dy in -1..<2 {
475	            for dx in -1..<2 {
476	              if dx == 0 && dy == 0 {
477	                continue
478	              }
479	              let nx = cx + dx
480	              let ny = cy + dy
481	              if nx < 0 || nx >= w || ny < 0 || ny >= h {
482	                continue
483	              }
484	              let nidx = ny * w + nx
485	              if labels[nidx] == -1 {
486	                let npixel = img.data[nidx].to_double()
487	                if npixel < pixel + 30.0 {
488	                  labels[nidx] = region_id
489	                  queue_x.push(nx)
490	                  queue_y.push(ny)
491	                }
492	              }
493	            }
494	          }
495	        }
496	        region_id += 1
497	        if region_id >= config.max_regions {
498	          break	<-- UNCOVERED
499	        }
500	      }
501	    }
502	    if region_id >= config.max_regions {
503	      break	<-- UNCOVERED
504	    }
505	  }
506	
507	  // 未标记的像素归为背景
508	  for i in 0..<total {
509	    if labels[i] == -1 {
510	      labels[i] = region_id // 背景
511	    }
512	  }
513	
514	  // 统计
515	  let pixel_counts : Array[Int] = []
516	  for _ in 0..<(region_id + 1) {
517	    pixel_counts.push(0)
518	  }
519	  for i in 0..<total {
520	    pixel_counts[labels[i]] += 1
521	  }
522	
523	  let label_info : Array[SegmentationLabel] = []
524	  for r in 0..<(region_id + 1) {
525	    let name = if r == region_id {
526	      "background"
527	    } else {
528	      "region_" + r.to_string()
529	    }
530	    label_info.push(SegmentationLabel::{
531	      id: r,
532	      name,
533	      color: (r * 37 % 256, r * 73 % 256, r * 113 % 256),
534	      pixel_count: pixel_counts[r],
535	    })
536	  }
537	
538	  SemanticSegmentationResult::{
539	    width: w,
540	    height: h,
541	    labels,
542	    label_info,
543	    num_classes: region_id + 1,
544	    inference_time_ms: 0.0,
545	  }
546	}
   	…

737	pub fn segmentation_report_to_string(
738	  result : SemanticSegmentationResult,
739	  metrics : SegmentationMetrics,
740	) -> String {
741	  let mut report = "## 图像分割报告\n\n"
742	  report = report + "### 分割结果\n\n"
743	  report = report + "| 指标 | 值 |\n|------|------|\n"
744	  report = report +
745	    "| 图像尺寸 | " +
746	    result.width.to_string() +
747	    "x" +
748	    result.height.to_string() +
749	    " |\n"
750	  report = report + "| 类别数 | " + result.num_classes.to_string() + " |\n"
751	  report = report +
752	    "| 推理耗时 | " +
753	    result.inference_time_ms.to_string() +
754	    "ms |\n\n"
755	
756	  report = report + "### 类别统计\n\n"
757	  report = report + "| ID | 名称 | 颜色(RGB) | 像素数 | 占比 |\n"
758	  report = report + "|----|------|-----------|--------|------|\n"
759	  let total = result.width * result.height
760	  for info in result.label_info {
761	    let ratio = if total > 0 {
762	      info.pixel_count.to_double() / total.to_double() * 100.0
763	    } else {
764	      0.0	<-- UNCOVERED
765	    }
766	    report = report +
767	      "| " +
768	      info.id.to_string() +
769	      " | " +
770	      info.name +
771	      " | " +
772	      info.color.0.to_string() +
773	      "," +
774	      info.color.1.to_string() +
775	      "," +
776	      info.color.2.to_string() +
777	      " | " +
778	      info.pixel_count.to_string() +
779	      " | " +
780	      ratio.to_string() +
781	      "% |\n"
782	  }
783	
784	  report = report + "\n### 评估指标\n\n"
785	  report = report + "| 指标 | 值 |\n|------|------|\n"
786	  report = report +
787	    "| Pixel Accuracy | " +
788	    (metrics.pixel_accuracy * 100.0).to_string() +
789	    "% |\n"
790	  report = report +
791	    "| Mean IoU | " +
792	    (metrics.mean_iou * 100.0).to_string() +
793	    "% |\n"
794	  report = report +
795	    "| Mean Accuracy | " +
796	    (metrics.mean_accuracy * 100.0).to_string() +
797	    "% |\n"
798	  report = report +
799	    "| Dice Coefficient | " +
800	    (metrics.dice_coefficient * 100.0).to_string() +
801	    "% |\n"
802	  report = report + "| TP | " + metrics.true_positives.to_string() + " |\n"
803	  report = report + "| FP | " + metrics.false_positives.to_string() + " |\n"
804	  report = report + "| FN | " + metrics.false_negatives.to_string() + " |\n"
805	
806	  report
807	}
   	…

9 uncovered line(s) in src\process\slam\slam_system.mbt:

147	pub fn track_features_slam(
148	  prev_img : @types.Image,
149	  curr_img : @types.Image,
150	  prev_features : Array[Array[Double]],
151	  search_radius : Int,
152	) -> FeatureTrackingResult {
153	  let w = prev_img.width
154	  let h = prev_img.height
155	  let tracked : Array[Array[Double]] = []
156	  let mut num_lost = 0
157	  let mut total_parallax = 0.0
158	
159	  for feat in prev_features {
160	    let px = feat[0].to_int()
161	    let py = feat[1].to_int()
162	    let mut best_x = px
163	    let mut best_y = py
164	    let mut best_score = 1000000.0
165	
166	    for dy in -search_radius..<(search_radius + 1) {
167	      for dx in -search_radius..<(search_radius + 1) {
168	        let nx = px + dx
169	        let ny = py + dy
170	        if nx >= 2 && nx < w - 2 && ny >= 2 && ny < h - 2 {
171	          // 计算块匹配误差（简化版：中心像素差）
172	          let prev_pixel = prev_img.data[py * w + px].to_double()
173	          let curr_pixel = curr_img.data[ny * w + nx].to_double()
174	          let diff = (prev_pixel - curr_pixel).abs()
175	          if diff < best_score {
176	            best_score = diff
177	            best_x = nx
178	            best_y = ny
179	          }
180	        }
181	      }
182	    }
183	
184	    if best_score < 50.0 {
185	      let dx = (best_x - px).to_double()
186	      let dy = (best_y - py).to_double()
187	      let parallax = (dx * dx + dy * dy).sqrt()
188	      total_parallax += parallax
189	      tracked.push([best_x.to_double(), best_y.to_double(), parallax])
190	    } else {
191	      num_lost += 1	<-- UNCOVERED
192	    }
193	  }
194	
195	  let avg_parallax = if tracked.length() > 0 {
196	    total_parallax / tracked.length().to_double()
197	  } else {
198	    0.0
199	  }
200	
201	  FeatureTrackingResult::{
202	    tracked_features: tracked,
203	    num_tracked: tracked.length(),
204	    num_lost,
205	    parallax: avg_parallax,
206	    is_keyframe: avg_parallax > 10.0 && tracked.length() > 50,
207	  }
208	}
   	…

274	pub fn detect_loop_closure(
275	  current_features : Array[Array[Double]],
276	  keyframes : Array[KeyFrame],
277	  threshold : Double,
278	) -> LoopClosureResult {
279	  let mut best_score = 0.0
280	  let mut best_index = -1
281	
282	  for i in 0..<keyframes.length() {
283	    let kf = keyframes[i]	<-- UNCOVERED
284	    // 简化的相似度：特征点数量比较
285	    let score = if kf.features.length() > 0 {	<-- UNCOVERED
286	      current_features.length().to_double() / kf.features.length().to_double()
287	    } else {
288	      0.0	<-- UNCOVERED
289	    }
290	
291	    if score > best_score && score > threshold {
292	      best_score = score	<-- UNCOVERED
293	      best_index = i
294	    }
295	  }
296	
297	  if best_index >= 0 {
298	    LoopClosureResult::{	<-- UNCOVERED
299	      is_loop_detected: true,
300	      loop_frame_index: best_index,
301	      similarity_score: best_score,
302	      transformation: [
303	        1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
304	        1.0,
305	      ],
306	      num_inliers: 50,
307	    }
308	  } else {
309	    LoopClosureResult::{
310	      is_loop_detected: false,
311	      loop_frame_index: -1,
312	      similarity_score: 0.0,
313	      transformation: [
314	        1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
315	        1.0,
316	      ],
317	      num_inliers: 0,
318	    }
319	  }
320	}
   	…

348	pub fn process_frame_slam(
349	  state : SLAMState,
350	  img : @types.Image,
351	  config : SLAMConfig,
352	) -> SLAMState {
353	  // 检测特征点
354	  let features = detect_features_slam(
355	    img,
356	    config.max_features,
357	    config.feature_threshold,
358	  )
359	
360	  // 如果有关键帧，进行特征跟踪
361	  if state.keyframes.length() > 0 {
362	    let last_kf = state.keyframes[state.keyframes.length() - 1]
363	    let tracking_result = track_features_slam(
364	      last_kf.image,
365	      img,
366	      last_kf.features,
367	      16,
368	    )
369	
370	    // 估计位姿
371	    let new_pose = estimate_camera_pose(
372	      tracking_result,
373	      config,
374	      state.current_pose,
375	    )
376	
377	    // 如果是关键帧，添加到地图
378	    if tracking_result.is_keyframe {
379	      let new_kf = KeyFrame::{
380	        pose: new_pose,
381	        image: img,
382	        features,
383	        descriptors: [],
384	        map_points: [],
385	        timestamp: new_pose.timestamp,
386	        is_loop_closure: false,
387	      }
388	
389	      // 三角化新的地图点
390	      let new_points = triangulate_points(
391	        features,
392	        last_kf.pose,
393	        new_pose,
394	        config,
395	      )
396	
397	      let new_map_points = state.map_points
398	      for p in new_points {
399	        new_map_points.push(p)
400	      }
401	
402	      let new_keyframes = state.keyframes
403	      new_keyframes.push(new_kf)
404	
405	      let new_trajectory = state.trajectory
406	      new_trajectory.push(new_pose)
407	
408	      SLAMState::{
409	        current_pose: new_pose,
410	        trajectory: new_trajectory,
411	        map_points: new_map_points,
412	        keyframes: new_keyframes,
413	        num_frames_processed: state.num_frames_processed + 1,
414	        num_lost_frames: state.num_lost_frames,
415	        is_tracking: tracking_result.num_tracked > 20,
416	        is_mapping: true,
417	        processing_time_ms: 0.0,
418	      }
419	    } else {
420	      let new_trajectory = state.trajectory	<-- UNCOVERED
421	      new_trajectory.push(new_pose)
422	
423	      SLAMState::{
424	        current_pose: new_pose,
425	        trajectory: new_trajectory,
426	        map_points: state.map_points,
427	        keyframes: state.keyframes,
428	        num_frames_processed: state.num_frames_processed + 1,
429	        num_lost_frames: if tracking_result.num_tracked < 20 {
430	          state.num_lost_frames + 1	<-- UNCOVERED
431	        } else {
432	          state.num_lost_frames	<-- UNCOVERED
433	        },
434	        is_tracking: tracking_result.num_tracked > 20,
435	        is_mapping: true,
436	        processing_time_ms: 0.0,
437	      }
438	    }
439	  } else {
440	    // 第一帧：初始化为关键帧
441	    let initial_kf = KeyFrame::{
442	      pose: state.current_pose,
443	      image: img,
444	      features,
445	      descriptors: [],
446	      map_points: [],
447	      timestamp: 0.0,
448	      is_loop_closure: false,
449	    }
450	
451	    SLAMState::{
452	      current_pose: state.current_pose,
453	      trajectory: state.trajectory,
454	      map_points: state.map_points,
455	      keyframes: [initial_kf],
456	      num_frames_processed: state.num_frames_processed + 1,
457	      num_lost_frames: state.num_lost_frames,
458	      is_tracking: true,
459	      is_mapping: true,
460	      processing_time_ms: 0.0,
461	    }
462	  }
463	}
   	…

6 uncovered line(s) in src\process\stereo3d\point_cloud_3d.mbt:

177	pub fn stereo_match(
178	  left : @types.Image,
179	  right : @types.Image,
180	  config : StereoConfig3d,
181	) -> DepthMap3d {
182	  let w = left.width
183	  let h = left.height
184	  let depths : Array[Double] = []
185	  let mut min_d = 100.0
186	  let mut max_d = 0.0
187	  let mut invalid = 0
188	
189	  for y in 0..<h {
190	    for x in 0..<w {
191	      let mut best_disparity = 0
192	      let mut best_cost = 1000000.0
193	
194	      for
195	        d in config.min_disparity..<(config.min_disparity +
196	        config.num_disparities) {
197	        if x - d < 0 {
198	          continue
199	        }
200	        let mut cost = 0.0
201	        let half_block = config.block_size / 2
202	        for by in -half_block..<(half_block + 1) {
203	          for bx in -half_block..<(half_block + 1) {
204	            let ly = y + by
205	            let lx = x + bx
206	            let rx = x + bx - d
207	            if ly >= 0 && ly < h && lx >= 0 && lx < w && rx >= 0 && rx < w {
208	              let lp = left.data[ly * w + lx].to_double()
209	              let rp = right.data[ly * w + rx].to_double()
210	              cost += (lp - rp).abs()
211	            }
212	          }
213	        }
214	        if cost < best_cost {
215	          best_cost = cost
216	          best_disparity = d
217	        }
218	      }
219	
220	      if best_disparity > 0 {
221	        let depth = 100.0 / best_disparity.to_double()	<-- UNCOVERED
222	        depths.push(depth)
223	        if depth < min_d {
224	          min_d = depth	<-- UNCOVERED
225	        }
226	        if depth > max_d {
227	          max_d = depth	<-- UNCOVERED
228	        }
229	      } else {
230	        depths.push(0.0)
231	        invalid += 1
232	      }
233	    }
234	  }
235	
236	  DepthMap3d::{
237	    width: w,
238	    height: h,
239	    depths,
240	    min_depth: min_d,
241	    max_depth: max_d,
242	    invalid_count: invalid,
243	  }
244	}
   	…

372	pub fn icp_registration(
373	  source : PointCloud3D,
374	  target : PointCloud3D,
375	  max_iterations : Int,
376	  tolerance : Double,
377	) -> RegistrationResult3d {
378	  let transformation : Array[Double] = [
379	    1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
380	  ]
381	
382	  let mut prev_error = 1000000.0
383	  let mut converged = false
384	  let mut iterations = 0
385	
386	  for iter in 0..<max_iterations {
387	    iterations = iter + 1
388	    // 简化版：计算平均误差
389	    let mut total_error = 0.0
390	    let mut _inliers = 0
391	    let sample_count = if source.num_points < 100 {
392	      source.num_points
393	    } else {
394	      100	<-- UNCOVERED
395	    }
396	    for i in 0..<sample_count {
397	      let src_idx = i * source.num_points / sample_count % source.num_points
398	      let tgt_idx = i * target.num_points / sample_count % target.num_points
399	      let sp = source.points[src_idx]
400	      let tp = target.points[tgt_idx]
401	      let dx = sp.x - tp.x
402	      let dy = sp.y - tp.y
403	      let dz = sp.z - tp.z
404	      let dist = (dx * dx + dy * dy + dz * dz).sqrt()
405	      total_error += dist
406	      if dist < tolerance * 10.0 {
407	        _inliers += 1
408	      }
409	    }
410	    let rmse = if sample_count > 0 {
411	      (total_error / sample_count.to_double()).sqrt()
412	    } else {
413	      0.0
414	    }
415	
416	    if (prev_error - rmse).abs() < tolerance {
417	      converged = true
418	      break
419	    }
420	    prev_error = rmse
421	  }
422	
423	  let fitness = if source.num_points > 0 { 0.7 } else { 0.0 }
424	
425	  RegistrationResult3d::{
426	    transformation,
427	    inliers: if source.num_points > 0 {
428	      (source.num_points.to_double() * 0.7).to_int()
429	    } else {
430	      0
431	    },
432	    rmse: prev_error,
433	    fitness,
434	    iterations,
435	    converged,
436	  }
437	}
   	…

441	pub fn estimate_normals(
442	  cloud : PointCloud3D,
443	  search_radius : Double,
444	) -> Array[(Double, Double, Double)] {
445	  let normals : Array[(Double, Double, Double)] = []
446	
447	  for i in 0..<cloud.num_points {
448	    let p = cloud.points[i]
449	    // 简化版：基于局部平面拟合
450	    let mut sum_x = 0.0
451	    let mut sum_y = 0.0
452	    let mut sum_z = 0.0
453	    let mut count = 0
454	
455	    for j in 0..<cloud.num_points {
456	      if i == j {
457	        continue
458	      }
459	      let q = cloud.points[j]
460	      let dx = p.x - q.x
461	      let dy = p.y - q.y
462	      let dz = p.z - q.z
463	      let dist = (dx * dx + dy * dy + dz * dz).sqrt()
464	      if dist < search_radius {
465	        sum_x += dx
466	        sum_y += dy
467	        sum_z += dz
468	        count += 1
469	      }
470	    }
471	
472	    if count > 0 {
473	      let nx = sum_x / count.to_double()
474	      let ny = sum_y / count.to_double()
475	      let nz = sum_z / count.to_double()
476	      let len = (nx * nx + ny * ny + nz * nz).sqrt()
477	      if len > 0 {
478	        normals.push((nx / len, ny / len, nz / len))
479	      } else {
480	        normals.push((0.0, 0.0, 1.0))
481	      }
482	    } else {
483	      normals.push((0.0, 0.0, 1.0))	<-- UNCOVERED
484	    }
485	  }
486	
487	  normals
488	}
   	…

492	pub fn depth_map_to_image(depth : DepthMap3d) -> @types.Image {
493	  let w = depth.width
494	  let h = depth.height
495	  let out : Array[Byte] = []
496	  let range = depth.max_depth - depth.min_depth
497	
498	  for i in 0..<(w * h) {
499	    let d = depth.depths[i]
500	    if d > 0.0 && range > 0.0 {
501	      let normalized = (d - depth.min_depth) / range
502	      let value = (normalized * 255.0).to_int()
503	      out.push(if value > 127 { b'\xFF' } else { b'\x00' })
504	    } else {
505	      out.push(b'\x00')	<-- UNCOVERED
506	    }
507	  }
508	
509	  @types.Image::{
510	    width: w,
511	    height: h,
512	    channels: 1,
513	    data: Bytes::from_array(out),
514	  }
515	}
   	…

4 uncovered line(s) in src\process\stereo3d\stereo_3d.mbt:

20	pub fn depth_to_colormap(
21	  depth : @types.Image,
22	  colormap? : String = "jet",
23	) -> @types.Image {
24	  let cmap = colormap
25	  let w = depth.width
26	  let h = depth.height
27	  let channels = depth.channels
28	  let out_data : Array[Byte] = Array::make(w * h * 3, b'\x00')
29	  // 找到深度范围
30	  let mut min_depth = 10000000000.0
31	  let mut max_depth = -10000000000.0
32	  for y in 0..<h {
33	    for x in 0..<w {
34	      let idx = (y * w + x) * channels
35	      let v = depth.data[idx].to_double()
36	      if v < min_depth {
37	        min_depth = v
38	      }
39	      if v > max_depth {
40	        max_depth = v
41	      }
42	    }
43	  }
44	  let range = if max_depth > min_depth { max_depth - min_depth } else { 1.0 }
45	  // 伪彩色映射
46	  for y in 0..<h {
47	    for x in 0..<w {
48	      let idx = (y * w + x) * channels
49	      let v = depth.data[idx].to_double()
50	      let normalized = (v - min_depth) / range
51	      let out_idx = (y * w + x) * 3
52	      // Jet 颜色映射（蓝 -> 青 -> 绿 -> 黄 -> 红）
53	      let (r, g, b) = if cmap == "jet" {
54	        if normalized < 0.25 {
55	          (0, (normalized * 4.0 * 255.0).to_int(), 255)
56	        } else if normalized < 0.5 {	<-- UNCOVERED
57	          (0, 255, ((0.5 - normalized) * 4.0 * 255.0).to_int())
58	        } else if normalized < 0.75 {
59	          (((normalized - 0.5) * 4.0 * 255.0).to_int(), 255, 0)
60	        } else {
61	          (255, ((1.0 - normalized) * 4.0 * 255.0).to_int(), 0)	<-- UNCOVERED
62	        }
63	      } else {
64	        // 灰度映射
65	        let gray = (normalized * 255.0).to_int()
66	        (gray, gray, gray)
67	      }
68	      out_data[out_idx] = r.min(255).max(0).to_byte()
69	      out_data[out_idx + 1] = g.min(255).max(0).to_byte()
70	      out_data[out_idx + 2] = b.min(255).max(0).to_byte()
71	    }
72	  }
73	  @types.Image::{
74	    width: w,
75	    height: h,
76	    channels: 3,
77	    data: Bytes::from_array(out_data),
78	  }
79	}
  	…

123	pub fn pointcloud_voxel_downsample(
124	  cloud : PointCloud,
125	  voxel_size? : Double = 1.0,
126	) -> PointCloud {
127	  let vs = voxel_size
128	  // 简单的体素下采样：每个体素保留第一个点
129	  let seen : Array[String] = []
130	  let points : Array[(Double, Double, Double)] = []
131	  let colors : Array[(Int, Int, Int)] = []
132	  for i in 0..<cloud.points.length() {
133	    let (x, y, z) = cloud.points[i]
134	    let vx = (x / vs).floor().to_int()
135	    let vy = (y / vs).floor().to_int()
136	    let vz = (z / vs).floor().to_int()
137	    let key = vx.to_string() + "_" + vy.to_string() + "_" + vz.to_string()
138	    let mut found = false
139	    for k in seen {
140	      if k == key {
141	        found = true
142	      }
143	    }
144	    if !found {
145	      seen.push(key)
146	      points.push((x, y, z))
147	      if i < cloud.colors.length() {
148	        colors.push(cloud.colors[i])
149	      } else {
150	        colors.push((255, 255, 255))	<-- UNCOVERED
151	      }
152	    }
153	  }
154	  PointCloud::{ points, colors, num_points: points.length() }
155	}
   	…

159	pub fn compute_disparity(
160	  left : @types.Image,
161	  right : @types.Image,
162	  max_disparity? : Int = 64,
163	  block_size? : Int = 7,
164	) -> @types.Image {
165	  let max_disp = max_disparity
166	  let bs = block_size
167	  if left.width != right.width || left.height != right.height {
168	    return left	<-- UNCOVERED
169	  }
170	  let w = left.width
171	  let h = left.height
172	  let channels = left.channels.min(right.channels)
173	  let half = bs / 2
174	  let disparity : Array[Int] = Array::make(w * h, 0)
175	  for y in half..<(h - half) {
176	    for x in half..<(w - half) {
177	      let mut best_disp = 0
178	      let mut best_cost = 10000000000.0
179	      for d in 0..<max_disp {
180	        if x - d < half {
181	          continue
182	        }
183	        // 计算 SAD（绝对差之和）
184	        let mut cost = 0.0
185	        for dy in -half..<(half + 1) {
186	          for dx in -half..<(half + 1) {
187	            let l_idx = ((y + dy) * w + (x + dx)) * channels
188	            let r_idx = ((y + dy) * w + (x + dx - d)) * channels
189	            for c in 0..<channels {
190	              let diff = (left.data[l_idx + c].to_double() -
191	              right.data[r_idx + c].to_double()).abs()
192	              cost += diff
193	            }
194	          }
195	        }
196	        if cost < best_cost {
197	          best_cost = cost
198	          best_disp = d
199	        }
200	      }
201	      disparity[y * w + x] = best_disp
202	    }
203	  }
204	  // 转换为 8 位深度图
205	  let out_data : Array[Byte] = Array::make(w * h, b'\x00')
206	  for i in 0..<(w * h) {
207	    let value = (disparity[i].to_double() / max_disp.to_double() * 255.0).to_int()
208	    out_data[i] = value.min(255).max(0).to_byte()
209	  }
210	  @types.Image::{
211	    width: w,
212	    height: h,
213	    channels: 1,
214	    data: Bytes::from_array(out_data),
215	  }
216	}
   	…

69 uncovered line(s) in src\process\stitch\image_stitch.mbt:

 93	pub fn invert_homography(h : Homography) -> Homography? {
 94	  // 计算 3x3 矩阵的逆
 95	  let a = h.h00
 96	  let b = h.h01
 97	  let c = h.h02
 98	  let d = h.h10
 99	  let e = h.h11
100	  let f = h.h12
101	  let g = h.h20
102	  let i2 = h.h21
103	  let j = h.h22
104	  // 余子式
105	  let m00 = e * j - f * i2
106	  let m01 = -(d * j - f * g)
107	  let m02 = d * i2 - e * g
108	  let m10 = -(b * j - c * i2)
109	  let m11 = a * j - c * g
110	  let m12 = -(a * i2 - b * g)
111	  let m20 = b * f - c * e
112	  let m21 = -(a * f - c * d)
113	  let m22 = a * e - b * d
114	  let det = a * m00 + b * m01 + c * m02
115	  if det.abs() < 0.000001 {
116	    return None	<-- UNCOVERED
117	  }
118	  let inv_det = 1.0 / det
119	  Some(Homography::{
120	    h00: m00 * inv_det,
121	    h01: m10 * inv_det,
122	    h02: m20 * inv_det,
123	    h10: m01 * inv_det,
124	    h11: m11 * inv_det,
125	    h12: m21 * inv_det,
126	    h20: m02 * inv_det,
127	    h21: m12 * inv_det,
128	    h22: m22 * inv_det,
129	  })
130	}
   	…

134	pub fn detect_features_simple(
135	  img : @types.Image,
136	  max_features? : Int = 500,
137	) -> Array[FeaturePoint] {
138	  let w = img.width
139	  let h = img.height
140	  let data = img.data
141	  let features : Array[FeaturePoint] = []
142	  // 简化版 FAST 角点检测
143	  let threshold = 50
144	  let circle_offsets = [
145	    (0, -3),
146	    (1, -3),
147	    (2, -2),
148	    (3, -1),
149	    (3, 0),
150	    (3, 1),
151	    (2, 2),
152	    (1, 3),
153	    (0, 3),
154	    (-1, 3),
155	    (-2, 2),
156	    (-3, 1),
157	    (-3, 0),
158	    (-3, -1),
159	    (-2, -2),
160	    (-1, -3),
161	  ]
162	  for y in 4..<(h - 4) {
163	    for x in 4..<(w - 4) {
164	      let center = data[y * w + x].to_int()
165	      // 检查连续 N 个点是否都比中心亮或暗
166	      let mut _bright_count = 0
167	      let mut _dark_count = 0
168	      let mut max_bright = 0
169	      let mut max_dark = 0
170	      let mut cur_bright = 0
171	      let mut cur_dark = 0
172	      for _tup0 in circle_offsets {
173	        let (dx, dy) = _tup0
174	        let px = data[(y + dy) * w + (x + dx)].to_int()
175	        if px > center + threshold {
176	          cur_bright += 1
177	          cur_dark = 0
178	          if cur_bright > max_bright {
179	            max_bright = cur_bright
180	          }
181	        } else if px < center - threshold {
182	          cur_dark += 1
183	          cur_bright = 0
184	          if cur_dark > max_dark {
185	            max_dark = cur_dark
186	          }
187	        } else {
188	          cur_bright = 0
189	          cur_dark = 0
190	        }
191	      }
192	      if max_bright >= 9 || max_dark >= 9 {
193	        // 计算响应值（简化版）
194	        let response = max_bright.max(max_dark).to_double()	<-- UNCOVERED
195	        features.push(FeaturePoint::{
196	          x: x.to_double(),
197	          y: y.to_double(),
198	          response,
199	          orientation: 0.0,
200	          descriptor: compute_simple_descriptor(data, w, h, x, y),	<-- UNCOVERED
201	        })
202	      }
203	    }
204	  }
205	  // 按响应值排序，取前 max_features 个
206	  let mf = max_features
207	  // 简单排序（冒泡排序的简化版，用选择排序）
208	  let sorted = sort_features_by_response(features)
209	  let result : Array[FeaturePoint] = []
210	  let count = if mf < sorted.length() { mf } else { sorted.length() }
211	  for i in 0..<count {
212	    result.push(sorted[i])	<-- UNCOVERED
213	  }
214	  result
215	}
   	…

219	fn compute_simple_descriptor(
220	  data : Bytes,
221	  w : Int,
222	  h : Int,
223	  cx : Int,
224	  cy : Int,
225	) -> Array[Double] {
226	  let grid_size = 8	<-- UNCOVERED
227	  let cell_size = 2
228	  let descriptor : Array[Double] = []
229	  for gy in 0..<grid_size {
230	    for gx in 0..<grid_size {	<-- UNCOVERED
231	      let mut sum = 0.0
232	      let mut count = 0
233	      for dy in 0..<cell_size {
234	        for dx in 0..<cell_size {	<-- UNCOVERED
235	          let px = cx - 8 + gx * cell_size + dx
236	          let py = cy - 8 + gy * cell_size + dy
237	          if px >= 0 && px < w && py >= 0 && py < h {
238	            sum += data[py * w + px].to_double() / 255.0	<-- UNCOVERED
239	            count += 1
240	          }
241	        }
242	      }
243	      descriptor.push(if count > 0 { sum / count.to_double() } else { 0.0 })	<-- UNCOVERED
244	    }
245	  }
246	  // 归一化
247	  let mut norm = 0.0
248	  for d in descriptor {
249	    norm += d * d	<-- UNCOVERED
250	  }
251	  norm = norm.sqrt()	<-- UNCOVERED
252	  if norm > 0.0 {
253	    for i in 0..<descriptor.length() {	<-- UNCOVERED
254	      descriptor[i] = descriptor[i] / norm
255	    }
256	  }
257	  descriptor
258	}
   	…

262	fn sort_features_by_response(
263	  features : Array[FeaturePoint],
264	) -> Array[FeaturePoint] {
265	  let n = features.length()
266	  let result = features.copy()
267	  for i in 0..<n {
268	    let mut max_idx = i	<-- UNCOVERED
269	    for j in (i + 1)..<n {
270	      if result[j].response > result[max_idx].response {	<-- UNCOVERED
271	        max_idx = j
272	      }
273	    }
274	    if max_idx != i {
275	      let tmp = result[i]	<-- UNCOVERED
276	      result[i] = result[max_idx]
277	      result[max_idx] = tmp
278	    }
279	  }
280	  result
281	}
   	…

285	pub fn match_features(
286	  query : Array[FeaturePoint],
287	  train : Array[FeaturePoint],
288	  ratio_threshold? : Double = 0.75,
289	) -> Array[MatchPair] {
290	  let rt = ratio_threshold
291	  let matches : Array[MatchPair] = []
292	  for i in 0..<query.length() {
293	    let mut best_dist = 1000000.0
294	    let mut second_best_dist = 1000000.0
295	    let mut best_idx = 0
296	    for j in 0..<train.length() {
297	      let dist = descriptor_distance(query[i].descriptor, train[j].descriptor)
298	      if dist < best_dist {
299	        second_best_dist = best_dist
300	        best_dist = dist
301	        best_idx = j
302	      } else if dist < second_best_dist {	<-- UNCOVERED
303	        second_best_dist = dist
304	      }
305	    }
306	    // Lowe 比率测试
307	    if best_dist < rt * second_best_dist {
308	      matches.push(MatchPair::{
309	        query_idx: i,
310	        train_idx: best_idx,
311	        distance: best_dist,
312	      })
313	    }
314	  }
315	  matches
316	}
   	…

320	fn descriptor_distance(d1 : Array[Double], d2 : Array[Double]) -> Double {
321	  let n = if d1.length() < d2.length() { d1.length() } else { d2.length() }	<-- UNCOVERED
322	  let mut sum = 0.0
323	  for i in 0..<n {
324	    let diff = d1[i] - d2[i]
325	    sum += diff * diff
326	  }
327	  sum.sqrt()
328	}
   	…

332	pub fn estimate_homography_ransac(
333	  matches : Array[MatchPair],
334	  query_points : Array[FeaturePoint],
335	  train_points : Array[FeaturePoint],
336	  iterations? : Int = 1000,
337	  threshold? : Double = 3.0,
338	) -> (Homography?, Array[MatchPair]) {
339	  let iter = iterations
340	  let thresh = threshold
341	  if matches.length() < 4 {
342	    return (None, [])
343	  }
344	  let mut best_h : Homography? = None
345	  let mut best_inliers : Array[MatchPair] = []
346	  let mut best_inlier_count = 0
347	  // RANSAC 迭代
348	  for _ in 0..<iter {
349	    // 随机选择 4 个匹配对
350	    let sample = random_sample(matches, 4)	<-- UNCOVERED
351	    if sample.length() < 4 {
352	      continue
353	    }
354	    // 从 4 对点估计单应性矩阵
355	    let src_pts : Array[(Double, Double)] = []
356	    let dst_pts : Array[(Double, Double)] = []
357	    for m in sample {
358	      src_pts.push((query_points[m.query_idx].x, query_points[m.query_idx].y))	<-- UNCOVERED
359	      dst_pts.push((train_points[m.train_idx].x, train_points[m.train_idx].y))
360	    }
361	    let h = compute_homography_dlt(src_pts, dst_pts)	<-- UNCOVERED
362	    match h {
363	      Some(homography) => {
364	        // 计算内点
365	        let inliers : Array[MatchPair] = []
366	        for m in matches {
367	          let (qx, qy) = (	<-- UNCOVERED
368	            query_points[m.query_idx].x,
369	            query_points[m.query_idx].y,
370	          )
371	          let (tx, ty) = (
372	            train_points[m.train_idx].x,
373	            train_points[m.train_idx].y,
374	          )
375	          let (px, py) = apply_homography(homography, qx, qy)	<-- UNCOVERED
376	          let dist = ((px - tx) * (px - tx) + (py - ty) * (py - ty)).sqrt()
377	          if dist < thresh {
378	            inliers.push(m)	<-- UNCOVERED
379	          }
380	        }
381	        if inliers.length() > best_inlier_count {	<-- UNCOVERED
382	          best_inlier_count = inliers.length()
383	          best_h = Some(homography)
384	          best_inliers = inliers
385	        }
386	      }
387	      None => ignore(())	<-- UNCOVERED
388	    }
389	  }
390	  (best_h, best_inliers)
391	}
   	…

395	fn random_sample(matches : Array[MatchPair], count : Int) -> Array[MatchPair] {
396	  let n = matches.length()	<-- UNCOVERED
397	  if n < count {
398	    return matches	<-- UNCOVERED
399	  }
400	  let indices : Array[Int] = []
401	  let result : Array[MatchPair] = []
402	  let mut i = 0
403	  while i < count && indices.length() < n {	<-- UNCOVERED
404	    // 简单伪随机（基于索引的哈希）
405	    let idx = (i * 1103515245 % n + indices.length() * 7) % n	<-- UNCOVERED
406	    let mut duplicate = false
407	    for j in indices {
408	      if j == idx {	<-- UNCOVERED
409	        duplicate = true
410	      }
411	    }
412	    if !duplicate {
413	      indices.push(idx)	<-- UNCOVERED
414	      result.push(matches[idx])
415	    }
416	    i += 1
417	  }
418	  result
419	}
   	…

423	fn compute_homography_dlt(
424	  src : Array[(Double, Double)],
425	  dst : Array[(Double, Double)],
426	) -> Homography? {
427	  if src.length() < 4 || dst.length() < 4 {	<-- UNCOVERED
428	    return None
429	  }
430	  // 简化版：使用前4对点，通过解析方法求解
431	  // 对于 4 对点，可以直接求解 8 个未知数（h22=1）
432	  // 这里使用简化的最小二乘法
433	  let (x1, y1) = src[0]
434	  let (u1, v1) = dst[0]
435	  let (x2, _y2) = src[1]
436	  let (u2, _v2) = dst[1]
437	  let (_x3, y3) = src[2]
438	  let (_u3, v3) = dst[2]
439	  let (_x4, _y4) = src[3]
440	  let (_u4, _v4) = dst[3]
441	  // 构建 8x8 线性方程组（简化版，直接求解）
442	  // 这里使用近似方法：假设 h22 = 1，求解其他 8 个参数
443	  // 由于完整的 DLT 比较复杂，这里使用简化的仿射变换近似
444	  // 计算仿射变换参数（6个参数）作为单应性的近似
445	  let sx = (u2 - u1) / (x2 - x1 + 0.0001)
446	  let sy = (v3 - v1) / (y3 - y1 + 0.0001)
447	  let tx = u1 - sx * x1
448	  let ty = v1 - sy * y1
449	  Some(Homography::{
450	    h00: sx,
451	    h01: 0.0,
452	    h02: tx,
453	    h10: 0.0,
454	    h11: sy,
455	    h12: ty,
456	    h20: 0.0,
457	    h21: 0.0,
458	    h22: 1.0,
459	  })
460	}
   	…

464	pub fn stitch_images(
465	  img1 : @types.Image,
466	  img2 : @types.Image,
467	  config? : StitchConfig,
468	) -> @types.Image? {
469	  let cfg = match config {
470	    Some(c) => c	<-- UNCOVERED
471	    None => stitch_default_config()
472	  }
473	  // 1. 检测特征
474	  let features1 = detect_features_simple(img1, max_features=cfg.max_features)
475	  let features2 = detect_features_simple(img2, max_features=cfg.max_features)
476	  if features1.length() < 4 || features2.length() < 4 {
477	    return None
478	  }
479	  // 2. 特征匹配
480	  let matches = match_features(	<-- UNCOVERED
481	    features1,
482	    features2,
483	    ratio_threshold=cfg.ratio_threshold,
484	  )
485	  if matches.length() < cfg.min_inliers {	<-- UNCOVERED
486	    return None
487	  }
488	  // 3. RANSAC 估计单应性矩阵
489	  let (h, inliers) = estimate_homography_ransac(	<-- UNCOVERED
490	    matches,
491	    features1,
492	    features2,
493	    iterations=cfg.ransac_iterations,
494	    threshold=cfg.ransac_threshold,
495	  )
496	  match h {	<-- UNCOVERED
497	    Some(homography) => {
498	      if inliers.length() < cfg.min_inliers {
499	        return None
500	      }
501	      // 4. 计算输出图像尺寸
502	      let (out_w, out_h, offset_x, offset_y) = compute_output_size(	<-- UNCOVERED
503	        img1, img2, homography,
504	      )
505	      // 5. 变换 img2 到输出坐标系
506	      let warped = warp_image(	<-- UNCOVERED
507	        img2, homography, out_w, out_h, offset_x, offset_y,
508	      )
509	      // 6. 将 img1 放置到输出图像
510	      let result = blend_images(	<-- UNCOVERED
511	        img1,
512	        warped,
513	        offset_x,
514	        offset_y,
515	        cfg.blend_width,
516	      )
517	      Some(result)
518	    }
519	    None => None	<-- UNCOVERED
520	  }
521	}
   	…

525	fn compute_output_size(
526	  img1 : @types.Image,
527	  img2 : @types.Image,
528	  h : Homography,
529	) -> (Int, Int, Int, Int) {
530	  let w1 = img1.width	<-- UNCOVERED
531	  let h1 = img1.height
532	  let w2 = img2.width
533	  let h2 = img2.height
534	  // 变换 img2 的四个角点
535	  let corners = [
536	    (0.0, 0.0),
537	    (w2.to_double(), 0.0),	<-- UNCOVERED
538	    (w2.to_double(), h2.to_double()),
539	    (0.0, h2.to_double()),
540	  ]
541	  let mut min_x = 0.0
542	  let mut max_x = w1.to_double()	<-- UNCOVERED
543	  let mut min_y = 0.0
544	  let mut max_y = h1.to_double()	<-- UNCOVERED
545	  for _tup1 in corners {
546	    let (x, y) = _tup1	<-- UNCOVERED
547	    let (nx, ny) = apply_homography(h, x, y)
548	    if nx < min_x {
549	      min_x = nx	<-- UNCOVERED
550	    }
551	    if nx > max_x {
552	      max_x = nx	<-- UNCOVERED
553	    }
554	    if ny < min_y {
555	      min_y = ny	<-- UNCOVERED
556	    }
557	    if ny > max_y {
558	      max_y = ny	<-- UNCOVERED
559	    }
560	  }
561	  let offset_x = (-min_x).to_int()	<-- UNCOVERED
562	  let offset_y = (-min_y).to_int()
563	  let out_w = (max_x - min_x).to_int() + 1
564	  let out_h = (max_y - min_y).to_int() + 1
565	  (out_w, out_h, offset_x, offset_y)
566	}
   	…

570	fn warp_image(
571	  img : @types.Image,
572	  h : Homography,
573	  out_w : Int,
574	  out_h : Int,
575	  offset_x : Int,
576	  offset_y : Int,
577	) -> @types.Image {
578	  let w = img.width	<-- UNCOVERED
579	  let hgt = img.height
580	  let data = img.data
581	  let channels = img.channels
582	  let inv_h = invert_homography(h)	<-- UNCOVERED
583	  let out_data : Array[Byte] = Array::make(out_w * out_h * channels, b'\x00')
584	  match inv_h {
585	    Some(inv) =>
586	      for y in 0..<out_h {	<-- UNCOVERED
587	        for x in 0..<out_w {
588	          let src_x = x.to_double() - offset_x.to_double()
589	          let src_y = y.to_double() - offset_y.to_double()
590	          let (orig_x, orig_y) = apply_homography(inv, src_x, src_y)
591	          let ix = orig_x.to_int()
592	          let iy = orig_y.to_int()
593	          if ix >= 0 && ix < w && iy >= 0 && iy < hgt {
594	            for c in 0..<channels {	<-- UNCOVERED
595	              out_data[(y * out_w + x) * channels + c] = data[(iy * w + ix) *
596	                channels +
597	                c]
598	            }
599	          }
600	        }
601	      }
602	    None => ignore(())	<-- UNCOVERED
603	  }
604	  @types.Image::{
605	    width: out_w,
606	    height: out_h,
607	    channels,
608	    data: Bytes::from_array(out_data),	<-- UNCOVERED
609	  }
610	}
   	…

614	fn blend_images(
615	  img1 : @types.Image,
616	  img2 : @types.Image,
617	  offset_x : Int,
618	  offset_y : Int,
619	  blend_width : Int,
620	) -> @types.Image {
621	  let w = img2.width	<-- UNCOVERED
622	  let h = img2.height
623	  let channels = img2.channels
624	  let data1 = img1.data
625	  let data2 = img2.data
626	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')	<-- UNCOVERED
627	  let w1 = img1.width
628	  let h1 = img1.height
629	  for y in 0..<h {
630	    for x in 0..<w {	<-- UNCOVERED
631	      // 检查 img1 是否覆盖该像素
632	      let x1 = x - offset_x	<-- UNCOVERED
633	      let y1 = y - offset_y
634	      let in_img1 = x1 >= 0 && x1 < w1 && y1 >= 0 && y1 < h1
635	      // 检查 img2 是否覆盖该像素
636	      let in_img2 = data2[(y * w + x) * channels].to_int() > 0 ||	<-- UNCOVERED
637	        data2[(y * w + x) * channels + 1].to_int() > 0 ||
638	        data2[(y * w + x) * channels + 2].to_int() > 0
639	      if in_img1 && in_img2 {
640	        // 重叠区域：加权融合
641	        // 计算到 img1 右边缘的距离
642	        let dist_to_edge = (w1 - x1).to_double()	<-- UNCOVERED
643	        let alpha = if dist_to_edge < blend_width.to_double() {
644	          dist_to_edge / blend_width.to_double()
645	        } else {
646	          1.0	<-- UNCOVERED
647	        }
648	        for c in 0..<channels {
649	          let v1 = data1[(y1 * w1 + x1) * channels + c].to_int()	<-- UNCOVERED
650	          let v2 = data2[(y * w + x) * channels + c].to_int()
651	          let blended = (v1.to_double() * alpha + v2.to_double() * (1.0 - alpha)).to_int()
652	          out_data[(y * w + x) * channels + c] = blended.to_byte()
653	        }
654	      } else if in_img1 {	<-- UNCOVERED
655	        // 只有 img1
656	        for c in 0..<channels {	<-- UNCOVERED
657	          out_data[(y * w + x) * channels + c] = data1[(y1 * w1 + x1) * channels +
658	            c]
659	        }
660	      } else {
661	        // 只有 img2
662	        for c in 0..<channels {	<-- UNCOVERED
663	          out_data[(y * w + x) * channels + c] = data2[(y * w + x) * channels +
664	            c]
665	        }
666	      }
667	    }
668	  }
669	  @types.Image::{
670	    width: w,
671	    height: h,
672	    channels,
673	    data: Bytes::from_array(out_data),	<-- UNCOVERED
674	  }
675	}
   	…

16 uncovered line(s) in src\process\style_transfer\style_transfer_enhancement.mbt:

182	pub fn color_transfer(
183	  content : @types.Image,
184	  style : @types.Image,
185	  strength : Double,
186	) -> @types.Image {
187	  let content_stats = calculate_color_statistics(content)
188	  let style_stats = calculate_color_statistics(style)
189	
190	  let w = content.width
191	  let h = content.height
192	  let total = w * h
193	  let out : Array[Byte] = []
194	
195	  for i in 0..<total {
196	    let pixel = content.data[i].to_double()
197	    // 标准化
198	    let normalized = if content_stats.std_r > 0.0 {
199	      (pixel - content_stats.mean_r) / content_stats.std_r
200	    } else {
201	      0.0	<-- UNCOVERED
202	    }
203	    // 转移到风格分布
204	    let transferred = normalized * style_stats.std_r + style_stats.mean_r
205	    // 混合
206	    let blended = pixel * (1.0 - strength) + transferred * strength
207	    let clamped = if blended < 0.0 {
208	      0.0	<-- UNCOVERED
209	    } else if blended > 255.0 {
210	      255.0	<-- UNCOVERED
211	    } else {
212	      blended
213	    }
214	    out.push(clamped.to_int().to_byte())
215	  }
216	
217	  @types.Image::{
218	    width: w,
219	    height: h,
220	    channels: content.channels,
221	    data: Bytes::from_array(out),
222	  }
223	}
   	…

302	pub fn cartoonize(img : @types.Image, strength : Double) -> @types.Image {
303	  let w = img.width
304	  let h = img.height
305	  let _ = w * h
306	  let out : Array[Byte] = []
307	
308	  // 简化：边缘检测 + 色彩量化
309	  for y in 0..<h {
310	    for x in 0..<w {
311	      let idx = y * w + x
312	      let center = img.data[idx].to_double()
313	
314	      // 边缘检测（Sobel 简化）
315	      let mut gx = 0.0
316	      let mut gy = 0.0
317	      if x > 0 && x < w - 1 && y > 0 && y < h - 1 {
318	        gx = img.data[(y + 1) * w + (x - 1)].to_double() +
319	          2.0 * img.data[y * w + (x - 1)].to_double() +
320	          img.data[(y - 1) * w + (x - 1)].to_double() -
321	          img.data[(y + 1) * w + (x + 1)].to_double() -
322	          2.0 * img.data[y * w + (x + 1)].to_double() -
323	          img.data[(y - 1) * w + (x + 1)].to_double()
324	        gy = img.data[(y - 1) * w + (x + 1)].to_double() +
325	          2.0 * img.data[(y - 1) * w + x].to_double() +
326	          img.data[(y - 1) * w + (x - 1)].to_double() -
327	          img.data[(y + 1) * w + (x + 1)].to_double() -
328	          2.0 * img.data[(y + 1) * w + x].to_double() -
329	          img.data[(y + 1) * w + (x - 1)].to_double()
330	      }
331	      let edge = (gx * gx + gy * gy).sqrt()
332	
333	      // 色彩量化（减少色阶）
334	      let levels = 8.0
335	      let quantized = (center / 255.0 * levels).to_int().to_double() /
336	        levels *
337	        255.0
338	
339	      // 混合：边缘变暗，量化色彩
340	      let edge_factor = if edge > 30.0 { strength } else { 0.0 }
341	      let result = quantized * (1.0 - edge_factor) + 0.0 * edge_factor
342	      let clamped = if result < 0.0 {
343	        0.0	<-- UNCOVERED
344	      } else if result > 255.0 {
345	        255.0	<-- UNCOVERED
346	      } else {
347	        result
348	      }
349	      out.push(clamped.to_int().to_byte())
350	    }
351	  }
352	
353	  @types.Image::{
354	    width: w,
355	    height: h,
356	    channels: img.channels,
357	    data: Bytes::from_array(out),
358	  }
359	}
   	…

363	pub fn oil_painting(
364	  img : @types.Image,
365	  radius : Int,
366	  intensity_levels : Int,
367	) -> @types.Image {
368	  let w = img.width
369	  let h = img.height
370	  let _ = w * h
371	  let out : Array[Byte] = []
372	
373	  for y in 0..<h {
374	    for x in 0..<w {
375	      // 统计邻域内的强度直方图
376	      let histogram : Array[Int] = []
377	      let sum_histogram : Array[Double] = []
378	      for _ in 0..<intensity_levels {
379	        histogram.push(0)
380	        sum_histogram.push(0.0)
381	      }
382	
383	      for dy in -radius..<(radius + 1) {
384	        for dx in -radius..<(radius + 1) {
385	          let nx = x + dx
386	          let ny = y + dy
387	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
388	            let pixel = img.data[ny * w + nx].to_double()
389	            let level = (pixel / 255.0 * intensity_levels.to_double()).to_int()
390	            let clamped_level = if level < 0 {
391	              0	<-- UNCOVERED
392	            } else if level >= intensity_levels {
393	              intensity_levels - 1	<-- UNCOVERED
394	            } else {
395	              level
396	            }
397	            histogram[clamped_level] += 1
398	            sum_histogram[clamped_level] += pixel
399	          }
400	        }
401	      }
402	
403	      // 找到最频繁的强度级别
404	      let mut max_count = 0
405	      let mut max_level = 0
406	      for i in 0..<intensity_levels {
407	        if histogram[i] > max_count {
408	          max_count = histogram[i]
409	          max_level = i
410	        }
411	      }
412	
413	      // 输出该级别的平均值
414	      let result = if max_count > 0 {
415	        sum_histogram[max_level] / max_count.to_double()
416	      } else {
417	        img.data[y * w + x].to_double()	<-- UNCOVERED
418	      }
419	      let clamped = if result < 0.0 {
420	        0.0	<-- UNCOVERED
421	      } else if result > 255.0 {
422	        255.0	<-- UNCOVERED
423	      } else {
424	        result
425	      }
426	      out.push(clamped.to_int().to_byte())
427	    }
428	  }
429	
430	  @types.Image::{
431	    width: w,
432	    height: h,
433	    channels: img.channels,
434	    data: Bytes::from_array(out),
435	  }
436	}
   	…

440	pub fn sketch_effect(img : @types.Image, strength : Double) -> @types.Image {
441	  let w = img.width
442	  let h = img.height
443	  let _ = w * h
444	  let out : Array[Byte] = []
445	
446	  for y in 0..<h {
447	    for x in 0..<w {
448	      let idx = y * w + x
449	      let _ = img.data[idx].to_double()
450	
451	      // 边缘检测
452	      let mut gx = 0.0
453	      let mut gy = 0.0
454	      if x > 0 && x < w - 1 && y > 0 && y < h - 1 {
455	        gx = img.data[(y + 1) * w + (x - 1)].to_double() +
456	          2.0 * img.data[y * w + (x - 1)].to_double() +
457	          img.data[(y - 1) * w + (x - 1)].to_double() -
458	          img.data[(y + 1) * w + (x + 1)].to_double() -
459	          2.0 * img.data[y * w + (x + 1)].to_double() -
460	          img.data[(y - 1) * w + (x + 1)].to_double()
461	        gy = img.data[(y - 1) * w + (x + 1)].to_double() +
462	          2.0 * img.data[(y - 1) * w + x].to_double() +
463	          img.data[(y - 1) * w + (x - 1)].to_double() -
464	          img.data[(y + 1) * w + (x + 1)].to_double() -
465	          2.0 * img.data[(y + 1) * w + x].to_double() -
466	          img.data[(y + 1) * w + (x - 1)].to_double()
467	      }
468	      let edge = (gx * gx + gy * gy).sqrt()
469	
470	      // 反色：边缘为黑色，背景为白色
471	      let sketch_value = 255.0 - edge * strength
472	      let clamped = if sketch_value < 0.0 {
473	        0.0	<-- UNCOVERED
474	      } else if sketch_value > 255.0 {
475	        255.0	<-- UNCOVERED
476	      } else {
477	        sketch_value
478	      }
479	      out.push(clamped.to_int().to_byte())
480	    }
481	  }
482	
483	  @types.Image::{
484	    width: w,
485	    height: h,
486	    channels: img.channels,
487	    data: Bytes::from_array(out),
488	  }
489	}
   	…

493	pub fn style_transfer(
494	  content : @types.Image,
495	  style : @types.Image,
496	  config : StyleTransferConfig,
497	) -> StyleTransferResult {
498	  let result = if config.algorithm == StyleTransferAlgorithm::ColorTransfer {
499	    color_transfer(content, style, config.style_strength)
500	  } else if config.algorithm == StyleTransferAlgorithm::Cartoon {
501	    cartoonize(content, config.style_strength)
502	  } else if config.algorithm == StyleTransferAlgorithm::OilPainting {
503	    oil_painting(content, 3, 8)
504	  } else if config.algorithm == StyleTransferAlgorithm::Sketch {	<-- UNCOVERED
505	    sketch_effect(content, config.style_strength)
506	  } else {
507	    color_transfer(content, style, config.style_strength)	<-- UNCOVERED
508	  }
509	
510	  StyleTransferResult::{
511	    image: result,
512	    content_image_path: "",
513	    style_image_path: "",
514	    algorithm: config.algorithm,
515	    style_strength: config.style_strength,
516	    content_strength: config.content_strength,
517	    processing_time_ms: 0.0,
518	    iterations: config.iterations,
519	  }
520	}
   	…

524	pub fn style_transfer_report_to_string(
525	  result : StyleTransferResult,
526	  content_stats : ColorStatistics,
527	  style_stats : ColorStatistics,
528	  texture : StyleTransferTextureFeatures,
529	) -> String {
530	  let mut report = "## 图像风格迁移报告\n\n"
531	  report = report + "### 处理信息\n\n"
532	  report = report + "| 指标 | 值 |\n|------|------|\n"
533	  let algo_str = if result.algorithm == StyleTransferAlgorithm::ColorTransfer {
534	    "颜色转移"
535	  } else if result.algorithm == StyleTransferAlgorithm::TextureSynthesis {	<-- UNCOVERED
536	    "纹理合成"
537	  } else if result.algorithm == StyleTransferAlgorithm::NeuralStyle {
538	    "神经风格"
539	  } else if result.algorithm == StyleTransferAlgorithm::FastStyle {
540	    "快速风格"
541	  } else if result.algorithm == StyleTransferAlgorithm::CycleGAN {
542	    "CycleGAN"
543	  } else if result.algorithm == StyleTransferAlgorithm::AdaIN {
544	    "AdaIN"
545	  } else if result.algorithm == StyleTransferAlgorithm::Cartoon {
546	    "卡通化"
547	  } else if result.algorithm == StyleTransferAlgorithm::OilPainting {
548	    "油画"
549	  } else if result.algorithm == StyleTransferAlgorithm::Watercolor {
550	    "水彩"
551	  } else if result.algorithm == StyleTransferAlgorithm::Sketch {
552	    "素描"
553	  } else {
554	    "未知"	<-- UNCOVERED
555	  }
556	  report = report + "| 算法 | " + algo_str + " |\n"
557	  report = report +
558	    "| 风格强度 | " +
559	    result.style_strength.to_string() +
560	    " |\n"
561	  report = report +
562	    "| 内容强度 | " +
563	    result.content_strength.to_string() +
564	    " |\n"
565	  report = report + "| 迭代次数 | " + result.iterations.to_string() + " |\n"
566	  report = report +
567	    "| 处理耗时 | " +
568	    result.processing_time_ms.to_string() +
569	    "ms |\n\n"
570	
571	  report = report + "### 颜色统计\n\n"
572	  report = report +
573	    "| 指标 | 内容图 | 风格图 |\n|------|--------|--------|\n"
574	  report = report +
575	    "| 均值 | " +
576	    content_stats.mean_r.to_string() +
577	    " | " +
578	    style_stats.mean_r.to_string() +
579	    " |\n"
580	  report = report +
581	    "| 标准差 | " +
582	    content_stats.std_r.to_string() +
583	    " | " +
584	    style_stats.std_r.to_string() +
585	    " |\n"
586	  report = report +
587	    "| 最小值 | " +
588	    content_stats.min_r.to_string() +
589	    " | " +
590	    style_stats.min_r.to_string() +
591	    " |\n"
592	  report = report +
593	    "| 最大值 | " +
594	    content_stats.max_r.to_string() +
595	    " | " +
596	    style_stats.max_r.to_string() +
597	    " |\n\n"
598	
599	  report = report + "### 纹理特征\n\n"
600	  report = report + "| 指标 | 值 |\n|------|------|\n"
601	  report = report + "| 对比度 | " + texture.contrast.to_string() + " |\n"
602	  report = report + "| 同质性 | " + texture.homogeneity.to_string() + " |\n"
603	  report = report + "| 能量 | " + texture.energy.to_string() + " |\n"
604	  report = report + "| 相关性 | " + texture.correlation.to_string() + " |\n"
605	  report = report + "| 熵 | " + texture.entropy.to_string() + " |\n"
606	  report = report + "| 粗糙度 | " + texture.coarseness.to_string() + " |\n"
607	
608	  report
609	}
   	…

6 uncovered line(s) in src\process\stylize\stylize.mbt:

 31	pub fn oil_paint(
 32	  img : @types.Image,
 33	  radius? : Int = 3,
 34	  intensity? : Int = 20,
 35	) -> @types.Image {
 36	  let r = radius
 37	  let levels = intensity
 38	  let w = img.width
 39	  let h = img.height
 40	  let channels = img.channels
 41	  let data = img.data
 42	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
 43	  for y in 0..<h {
 44	    for x in 0..<w {
 45	      // 统计邻域内各颜色的出现频率
 46	      let bins : Array[Int] = Array::make(levels * levels * levels, 0)
 47	      let bin_r : Array[Int] = Array::make(levels * levels * levels, 0)
 48	      let bin_g : Array[Int] = Array::make(levels * levels * levels, 0)
 49	      let bin_b : Array[Int] = Array::make(levels * levels * levels, 0)
 50	      for dy in -r..<(r + 1) {
 51	        for dx in -r..<(r + 1) {
 52	          let nx = x + dx
 53	          let ny = y + dy
 54	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
 55	            let idx = (ny * w + nx) * channels
 56	            let red = data[idx].to_int()
 57	            let green = if channels >= 2 { data[idx + 1].to_int() } else { red }
 58	            let blue = if channels >= 3 { data[idx + 2].to_int() } else { red }
 59	            // 量化到 levels 级
 60	            let r_bin = (red * levels / 256).min(levels - 1)
 61	            let g_bin = (green * levels / 256).min(levels - 1)
 62	            let b_bin = (blue * levels / 256).min(levels - 1)
 63	            let bin_idx = r_bin * levels * levels + g_bin * levels + b_bin
 64	            bins[bin_idx] += 1
 65	            bin_r[bin_idx] += red
 66	            bin_g[bin_idx] += green
 67	            bin_b[bin_idx] += blue
 68	          }
 69	        }
 70	      }
 71	      // 找出现频率最高的颜色
 72	      let mut max_count = 0
 73	      let mut max_bin = 0
 74	      for i in 0..<(levels * levels * levels) {
 75	        if bins[i] > max_count {
 76	          max_count = bins[i]
 77	          max_bin = i
 78	        }
 79	      }
 80	      // 取该 bin 的平均颜色
 81	      let out_idx = (y * w + x) * channels
 82	      if max_count > 0 {
 83	        out_data[out_idx] = (bin_r[max_bin] / max_count).to_byte()
 84	        if channels >= 2 {
 85	          out_data[out_idx + 1] = (bin_g[max_bin] / max_count).to_byte()
 86	        }
 87	        if channels >= 3 {
 88	          out_data[out_idx + 2] = (bin_b[max_bin] / max_count).to_byte()
 89	        }
 90	        if channels >= 4 {
 91	          out_data[out_idx + 3] = data[out_idx + 3]	<-- UNCOVERED
 92	        }
 93	      } else {
 94	        for c in 0..<channels {	<-- UNCOVERED
 95	          out_data[out_idx + c] = data[out_idx + c]
 96	        }
 97	      }
 98	    }
 99	  }
100	  @types.Image::{
101	    width: w,
102	    height: h,
103	    channels,
104	    data: Bytes::from_array(out_data),
105	  }
106	}
   	…

111	pub fn sketch(img : @types.Image, blend? : Double = 0.7) -> @types.Image {
112	  let b = blend
113	  let w = img.width
114	  let h = img.height
115	  let channels = img.channels
116	  let data = img.data
117	  // 1. 转换为灰度
118	  let gray : Array[Byte] = []
119	  for i in 0..<(w * h) {
120	    let idx = i * channels
121	    let r = data[idx].to_double()
122	    let g = if channels >= 2 { data[idx + 1].to_double() } else { r }
123	    let bl = if channels >= 3 { data[idx + 2].to_double() } else { r }
124	    let gray_val = (0.299 * r + 0.587 * g + 0.114 * bl).to_int().min(255).max(0)
125	    gray.push(gray_val.to_byte())
126	  }
127	  // 2. 反色
128	  let inverted : Array[Byte] = []
129	  for i in 0..<(w * h) {
130	    inverted.push((255 - gray[i].to_int()).to_byte())
131	  }
132	  // 3. 对反色图进行高斯模糊（简化版：均值模糊）
133	  let blurred = simple_blur(inverted, w, h, 5)
134	  // 4. 颜色减淡混合：result = base / (1 - blend)
135	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
136	  for i in 0..<(w * h) {
137	    let base = gray[i].to_double()
138	    let blur = blurred[i].to_double()
139	    // 颜色减淡：result = base * 255 / (255 - blur)
140	    let mut result = if blur < 255.0 {
141	      base * 255.0 / (255.0 - blur)
142	    } else {
143	      255.0	<-- UNCOVERED
144	    }
145	    result = result.min(255.0).max(0.0)
146	    // 与原图混合
147	    let final_val = (result * b + base * (1.0 - b)).to_int().min(255).max(0)
148	    let out_idx = i * channels
149	    out_data[out_idx] = final_val.to_byte()
150	    if channels >= 2 {
151	      out_data[out_idx + 1] = final_val.to_byte()
152	    }
153	    if channels >= 3 {
154	      out_data[out_idx + 2] = final_val.to_byte()
155	    }
156	    if channels >= 4 {
157	      out_data[out_idx + 3] = data[out_idx + 3]	<-- UNCOVERED
158	    }
159	  }
160	  @types.Image::{
161	    width: w,
162	    height: h,
163	    channels,
164	    data: Bytes::from_array(out_data),
165	  }
166	}
   	…

170	fn simple_blur(
171	  input : Array[Byte],
172	  w : Int,
173	  h : Int,
174	  radius : Int,
175	) -> Array[Byte] {
176	  let output : Array[Byte] = []
177	  for y in 0..<h {
178	    for x in 0..<w {
179	      let mut sum = 0
180	      let mut count = 0
181	      for dy in -radius..<(radius + 1) {
182	        for dx in -radius..<(radius + 1) {
183	          let nx = x + dx
184	          let ny = y + dy
185	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
186	            sum += input[ny * w + nx].to_int()
187	            count += 1
188	          }
189	        }
190	      }
191	      output.push(
192	        if count > 0 {
193	          (sum / count).to_byte()
194	        } else {
195	          input[y * w + x]	<-- UNCOVERED
196	        },
197	      )
198	    }
199	  }
200	  output
201	}
   	…

245	pub fn cartoon(
246	  img : @types.Image,
247	  levels? : Int = 8,
248	  edge_threshold? : Double = 30.0,
249	) -> @types.Image {
250	  let lv = levels
251	  let et = edge_threshold
252	  let w = img.width
253	  let h = img.height
254	  let channels = img.channels
255	  let data = img.data
256	  // 1. 颜色量化
257	  let quantized : Array[Byte] = Array::make(w * h * channels, b'\x00')
258	  for i in 0..<(w * h) {
259	    for c in 0..<channels {
260	      let idx = i * channels + c
261	      let val = data[idx].to_int()
262	      let q = val * lv / 256 * (256 / lv)
263	      quantized[idx] = q.min(255).to_byte()
264	    }
265	  }
266	  // 2. 边缘检测（Sobel，基于灰度）
267	  let edges : Array[Double] = Array::make(w * h, 0.0)
268	  for y in 1..<(h - 1) {
269	    for x in 1..<(w - 1) {
270	      let _ = (y * w + x) * channels
271	      // 灰度
272	      let gray = (px, py) => {
273	        let i = (py * w + px) * channels
274	        let r = data[i].to_double()
275	        let g = if channels >= 2 { data[i + 1].to_double() } else { r }
276	        let b = if channels >= 3 { data[i + 2].to_double() } else { r }
277	        0.299 * r + 0.587 * g + 0.114 * b
278	      }
279	      let gx = -gray(x - 1, y - 1) -
280	        2.0 * gray(x - 1, y) -
281	        gray(x - 1, y + 1) +
282	        gray(x + 1, y - 1) +
283	        2.0 * gray(x + 1, y) +
284	        gray(x + 1, y + 1)
285	      let gy = -gray(x - 1, y - 1) -
286	        2.0 * gray(x, y - 1) -
287	        gray(x + 1, y - 1) +
288	        gray(x - 1, y + 1) +
289	        2.0 * gray(x, y + 1) +
290	        gray(x + 1, y + 1)
291	      edges[y * w + x] = (gx * gx + gy * gy).sqrt()
292	    }
293	  }
294	  // 3. 合成：量化颜色 + 黑色边缘
295	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
296	  for i in 0..<(w * h) {
297	    let idx = i * channels
298	    if edges[i] > et {
299	      // 边缘：黑色
300	      for c in 0..<channels {
301	        out_data[idx + c] = b'\x00'
302	      }
303	      if channels >= 4 {
304	        out_data[idx + 3] = quantized[idx + 3]	<-- UNCOVERED
305	      }
306	    } else {
307	      // 非边缘：量化颜色
308	      for c in 0..<channels {
309	        out_data[idx + c] = quantized[idx + c]
310	      }
311	    }
312	  }
313	  @types.Image::{
314	    width: w,
315	    height: h,
316	    channels,
317	    data: Bytes::from_array(out_data),
318	  }
319	}
   	…

9 uncovered line(s) in src\process\superres\super_resolution.mbt:

45	fn lanczos_kernel(x : Double, a : Int) -> Double {
46	  let a_d = a.to_double()
47	  let abs_x = x.abs()
48	  if abs_x < 0.0001 {
49	    1.0	<-- UNCOVERED
50	  } else if abs_x < a_d {
51	    let pi_x = 3.141592653589793 * x
52	    let pi_x_a = pi_x / a_d
53	    @math.sin(pi_x) * @math.sin(pi_x_a) / (pi_x * pi_x_a)
54	  } else {
55	    0.0
56	  }
57	}
  	…

 61	pub fn super_resolution_bicubic(
 62	  img : @types.Image,
 63	  scale : Double,
 64	) -> @types.Image {
 65	  let w = img.width
 66	  let h = img.height
 67	  let channels = img.channels
 68	  let data = img.data
 69	  let out_w = (w.to_double() * scale).to_int()
 70	  let out_h = (h.to_double() * scale).to_int()
 71	  let out_data : Array[Byte] = Array::make(out_w * out_h * channels, b'\x00')
 72	  for y in 0..<out_h {
 73	    for x in 0..<out_w {
 74	      // 反向映射到原图坐标
 75	      let src_x = (x.to_double() + 0.5) / scale - 0.5
 76	      let src_y = (y.to_double() + 0.5) / scale - 0.5
 77	      let x0 = src_x.to_int()
 78	      let y0 = src_y.to_int()
 79	      for c in 0..<channels {
 80	        let mut sum = 0.0
 81	        let mut weight_sum = 0.0
 82	        // 4x4 邻域
 83	        for dy in -1..<3 {
 84	          for dx in -1..<3 {
 85	            let px = x0 + dx
 86	            let py = y0 + dy
 87	            if px >= 0 && px < w && py >= 0 && py < h {
 88	              let wx = bicubic_kernel(src_x - px.to_double())
 89	              let wy = bicubic_kernel(src_y - py.to_double())
 90	              let weight = wx * wy
 91	              sum += data[(py * w + px) * channels + c].to_double() * weight
 92	              weight_sum += weight
 93	            }
 94	          }
 95	        }
 96	        let value = if weight_sum > 0.0 { sum / weight_sum } else { 0.0 }
 97	        let clamped = if value < 0.0 {
 98	          0.0
 99	        } else if value > 255.0 {
100	          255.0	<-- UNCOVERED
101	        } else {
102	          value
103	        }
104	        out_data[(y * out_w + x) * channels + c] = clamped.to_int().to_byte()
105	      }
106	    }
107	  }
108	  @types.Image::{
109	    width: out_w,
110	    height: out_h,
111	    channels,
112	    data: Bytes::from_array(out_data),
113	  }
114	}
   	…

118	pub fn super_resolution_lanczos(
119	  img : @types.Image,
120	  scale : Double,
121	  a? : Int = 3,
122	) -> @types.Image {
123	  let lanczos_a = a
124	  let w = img.width
125	  let h = img.height
126	  let channels = img.channels
127	  let data = img.data
128	  let out_w = (w.to_double() * scale).to_int()
129	  let out_h = (h.to_double() * scale).to_int()
130	  let out_data : Array[Byte] = Array::make(out_w * out_h * channels, b'\x00')
131	  let radius = lanczos_a
132	  for y in 0..<out_h {
133	    for x in 0..<out_w {
134	      let src_x = (x.to_double() + 0.5) / scale - 0.5
135	      let src_y = (y.to_double() + 0.5) / scale - 0.5
136	      let x0 = src_x.to_int()
137	      let y0 = src_y.to_int()
138	      for c in 0..<channels {
139	        let mut sum = 0.0
140	        let mut weight_sum = 0.0
141	        for dy in -radius..<(radius + 1) {
142	          for dx in -radius..<(radius + 1) {
143	            let px = x0 + dx
144	            let py = y0 + dy
145	            if px >= 0 && px < w && py >= 0 && py < h {
146	              let wx = lanczos_kernel(src_x - px.to_double(), lanczos_a)
147	              let wy = lanczos_kernel(src_y - py.to_double(), lanczos_a)
148	              let weight = wx * wy
149	              sum += data[(py * w + px) * channels + c].to_double() * weight
150	              weight_sum += weight
151	            }
152	          }
153	        }
154	        let value = if weight_sum > 0.0 { sum / weight_sum } else { 0.0 }
155	        let clamped = if value < 0.0 {
156	          0.0
157	        } else if value > 255.0 {
158	          255.0	<-- UNCOVERED
159	        } else {
160	          value
161	        }
162	        out_data[(y * out_w + x) * channels + c] = clamped.to_int().to_byte()
163	      }
164	    }
165	  }
166	  @types.Image::{
167	    width: out_w,
168	    height: out_h,
169	    channels,
170	    data: Bytes::from_array(out_data),
171	  }
172	}
   	…

176	pub fn super_resolution_edge_preserving(
177	  img : @types.Image,
178	  scale : Double,
179	  edge_threshold? : Double = 30.0,
180	  sharpen_amount? : Double = 0.5,
181	) -> @types.Image {
182	  let et = edge_threshold
183	  let sa = sharpen_amount
184	  // 第一步：双三次插值
185	  let upscaled = super_resolution_bicubic(img, scale)
186	  // 第二步：边缘检测（Sobel）
187	  let edges = detect_edges_sobel(upscaled)
188	  // 第三步：在边缘区域进行锐化
189	  let w = upscaled.width
190	  let h = upscaled.height
191	  let channels = upscaled.channels
192	  let data = upscaled.data
193	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
194	  for y in 0..<h {
195	    for x in 0..<w {
196	      for c in 0..<channels {
197	        let idx = (y * w + x) * channels + c
198	        let edge_strength = edges[y * w + x]
199	        if edge_strength > et && x > 0 && x < w - 1 && y > 0 && y < h - 1 {
200	          // 锐化：中心像素加权，减去邻域模糊
201	          let center = data[idx].to_double()	<-- UNCOVERED
202	          let mut neighbor_sum = 0.0
203	          let neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
204	          for _tup0 in neighbors {
205	            let (dx, dy) = _tup0	<-- UNCOVERED
206	            neighbor_sum += data[((y + dy) * w + (x + dx)) * channels + c].to_double()
207	          }
208	          let sharpened = center * (1.0 + sa) - neighbor_sum * sa / 4.0
209	          let clamped = if sharpened < 0.0 {
210	            0.0	<-- UNCOVERED
211	          } else if sharpened > 255.0 {
212	            255.0
213	          } else {
214	            sharpened	<-- UNCOVERED
215	          }
216	          out_data[idx] = clamped.to_int().to_byte()	<-- UNCOVERED
217	        } else {
218	          out_data[idx] = data[idx]
219	        }
220	      }
221	    }
222	  }
223	  @types.Image::{
224	    width: w,
225	    height: h,
226	    channels,
227	    data: Bytes::from_array(out_data),
228	  }
229	}
   	…

233	fn detect_edges_sobel(img : @types.Image) -> Array[Double] {
234	  let w = img.width
235	  let h = img.height
236	  let data = img.data
237	  let channels = img.channels
238	  let edges : Array[Double] = Array::make(w * h, 0.0)
239	  // 转换为灰度
240	  let gray : Array[Double] = []
241	  for i in 0..<(w * h) {
242	    if channels >= 3 {
243	      let r = data[i * channels].to_double()	<-- UNCOVERED
244	      let g = data[i * channels + 1].to_double()
245	      let b = data[i * channels + 2].to_double()
246	      gray.push(0.299 * r + 0.587 * g + 0.114 * b)
247	    } else {
248	      gray.push(data[i * channels].to_double())
249	    }
250	  }
251	  // Sobel 算子
252	  let gx_kernel = [
253	    (-1, -1, -1),
254	    (0, -1, 0),
255	    (1, -1, 1),
256	    (-1, 0, -2),
257	    (0, 0, 0),
258	    (1, 0, 2),
259	    (-1, 1, -1),
260	    (0, 1, 0),
261	    (1, 1, 1),
262	  ]
263	  let gy_kernel = [
264	    (-1, -1, -1),
265	    (0, -1, -2),
266	    (1, -1, -1),
267	    (-1, 0, 0),
268	    (0, 0, 0),
269	    (1, 0, 0),
270	    (-1, 1, 1),
271	    (0, 1, 2),
272	    (1, 1, 1),
273	  ]
274	  for y in 1..<(h - 1) {
275	    for x in 1..<(w - 1) {
276	      let mut gx = 0.0
277	      let mut gy = 0.0
278	      for i in 0..<9 {
279	        let (dx, dy, weight) = gx_kernel[i]
280	        gx += gray[(y + dy) * w + (x + dx)] * weight.to_double()
281	      }
282	      for i in 0..<9 {
283	        let (dx, dy, weight) = gy_kernel[i]
284	        gy += gray[(y + dy) * w + (x + dx)] * weight.to_double()
285	      }
286	      edges[y * w + x] = (gx * gx + gy * gy).sqrt()
287	    }
288	  }
289	  edges
290	}
   	…

31 uncovered line(s) in src\process\superres\super_resolution_enhancement.mbt:

 99	pub fn nearest_neighbor_interpolate(
100	  img : @types.Image,
101	  scale_factor : Double,
102	) -> @types.Image {
103	  let w = img.width
104	  let h = img.height
105	  let new_w = (w.to_double() * scale_factor).to_int()
106	  let new_h = (h.to_double() * scale_factor).to_int()
107	  let out : Array[Byte] = []
108	
109	  for y in 0..<new_h {
110	    for x in 0..<new_w {
111	      let src_x = (x.to_double() / scale_factor).to_int()
112	      let src_y = (y.to_double() / scale_factor).to_int()
113	      let clamped_x = if src_x < 0 {
114	        0	<-- UNCOVERED
115	      } else if src_x >= w {
116	        w - 1	<-- UNCOVERED
117	      } else {
118	        src_x
119	      }
120	      let clamped_y = if src_y < 0 {
121	        0	<-- UNCOVERED
122	      } else if src_y >= h {
123	        h - 1	<-- UNCOVERED
124	      } else {
125	        src_y
126	      }
127	      out.push(img.data[clamped_y * w + clamped_x])
128	    }
129	  }
130	
131	  @types.Image::{
132	    width: new_w,
133	    height: new_h,
134	    channels: img.channels,
135	    data: Bytes::from_array(out),
136	  }
137	}
   	…

141	pub fn bilinear_interpolate(
142	  img : @types.Image,
143	  scale_factor : Double,
144	) -> @types.Image {
145	  let w = img.width
146	  let h = img.height
147	  let new_w = (w.to_double() * scale_factor).to_int()
148	  let new_h = (h.to_double() * scale_factor).to_int()
149	  let out : Array[Byte] = []
150	
151	  for y in 0..<new_h {
152	    for x in 0..<new_w {
153	      let src_x = x.to_double() / scale_factor
154	      let src_y = y.to_double() / scale_factor
155	      let x0 = src_x.to_int()
156	      let y0 = src_y.to_int()
157	      let x1 = x0 + 1
158	      let y1 = y0 + 1
159	      let fx = src_x - x0.to_double()
160	      let fy = src_y - y0.to_double()
161	
162	      let cx0 = if x0 < 0 { 0 } else if x0 >= w { w - 1 } else { x0 }	<-- UNCOVERED
163	      let cy0 = if y0 < 0 { 0 } else if y0 >= h { h - 1 } else { y0 }
164	      let cx1 = if x1 < 0 { 0 } else if x1 >= w { w - 1 } else { x1 }
165	      let cy1 = if y1 < 0 { 0 } else if y1 >= h { h - 1 } else { y1 }
166	
167	      let p00 = img.data[cy0 * w + cx0].to_double()
168	      let p01 = img.data[cy0 * w + cx1].to_double()
169	      let p10 = img.data[cy1 * w + cx0].to_double()
170	      let p11 = img.data[cy1 * w + cx1].to_double()
171	
172	      let top = p00 * (1.0 - fx) + p01 * fx
173	      let bottom = p10 * (1.0 - fx) + p11 * fx
174	      let value = top * (1.0 - fy) + bottom * fy
175	      let clamped = if value < 0.0 {
176	        0.0	<-- UNCOVERED
177	      } else if value > 255.0 {
178	        255.0	<-- UNCOVERED
179	      } else {
180	        value
181	      }
182	      out.push(clamped.to_int().to_byte())
183	    }
184	  }
185	
186	  @types.Image::{
187	    width: new_w,
188	    height: new_h,
189	    channels: img.channels,
190	    data: Bytes::from_array(out),
191	  }
192	}
   	…

196	pub fn bicubic_interpolate(
197	  img : @types.Image,
198	  scale_factor : Double,
199	) -> @types.Image {
200	  let w = img.width
201	  let h = img.height
202	  let new_w = (w.to_double() * scale_factor).to_int()
203	  let new_h = (h.to_double() * scale_factor).to_int()
204	  let out : Array[Byte] = []
205	
206	  for y in 0..<new_h {
207	    for x in 0..<new_w {
208	      let src_x = x.to_double() / scale_factor
209	      let src_y = y.to_double() / scale_factor
210	      let x0 = src_x.to_int() - 1
211	      let y0 = src_y.to_int() - 1
212	      let fx = src_x - src_x.to_int().to_double()
213	      let fy = src_y - src_y.to_int().to_double()
214	
215	      // 简化为双线性（双三次需要更多计算）
216	      let cx0 = if x0 < 0 { 0 } else if x0 >= w { w - 1 } else { x0 }
217	      let cy0 = if y0 < 0 { 0 } else if y0 >= h { h - 1 } else { y0 }
218	      let cx1 = if cx0 + 1 >= w { w - 1 } else { cx0 + 1 }
219	      let cy1 = if cy0 + 1 >= h { h - 1 } else { cy0 + 1 }
220	
221	      let p00 = img.data[cy0 * w + cx0].to_double()
222	      let p01 = img.data[cy0 * w + cx1].to_double()
223	      let p10 = img.data[cy1 * w + cx0].to_double()
224	      let p11 = img.data[cy1 * w + cx1].to_double()
225	
226	      let top = p00 * (1.0 - fx) + p01 * fx
227	      let bottom = p10 * (1.0 - fx) + p11 * fx
228	      let value = top * (1.0 - fy) + bottom * fy
229	      let clamped = if value < 0.0 {
230	        0.0	<-- UNCOVERED
231	      } else if value > 255.0 {
232	        255.0	<-- UNCOVERED
233	      } else {
234	        value
235	      }
236	      out.push(clamped.to_int().to_byte())
237	    }
238	  }
239	
240	  @types.Image::{
241	    width: new_w,
242	    height: new_h,
243	    channels: img.channels,
244	    data: Bytes::from_array(out),
245	  }
246	}
   	…

250	pub fn sharpen_image(img : @types.Image, strength : Double) -> @types.Image {
251	  let w = img.width
252	  let h = img.height
253	  let out : Array[Byte] = []
254	
255	  // 简化的锐化：中心加权
256	  for y in 0..<h {
257	    for x in 0..<w {
258	      let center = img.data[y * w + x].to_double()
259	      let mut sum = 0.0
260	      let mut count = 0
261	      for dy in -1..<2 {
262	        for dx in -1..<2 {
263	          if dx == 0 && dy == 0 {
264	            continue
265	          }
266	          let nx = x + dx
267	          let ny = y + dy
268	          if nx >= 0 && nx < w && ny >= 0 && ny < h {
269	            sum += img.data[ny * w + nx].to_double()
270	            count += 1
271	          }
272	        }
273	      }
274	      let avg = if count > 0 { sum / count.to_double() } else { center }
275	      let sharpened = center + strength * (center - avg)
276	      let clamped = if sharpened < 0.0 {
277	        0.0
278	      } else if sharpened > 255.0 {
279	        255.0	<-- UNCOVERED
280	      } else {
281	        sharpened
282	      }
283	      out.push(clamped.to_int().to_byte())
284	    }
285	  }
286	
287	  @types.Image::{
288	    width: w,
289	    height: h,
290	    channels: img.channels,
291	    data: Bytes::from_array(out),
292	  }
293	}
   	…

297	pub fn denoise_image(img : @types.Image, strength : Double) -> @types.Image {
298	  let w = img.width
299	  let h = img.height
300	  let out : Array[Byte] = []
301	  let kernel_size = if strength > 0.5 { 3 } else { 1 }
302	
303	  for y in 0..<h {
304	    for x in 0..<w {
305	      if kernel_size <= 1 {
306	        out.push(img.data[y * w + x])
307	      } else {
308	        let mut sum = 0.0	<-- UNCOVERED
309	        let mut count = 0
310	        for dy in -1..<2 {
311	          for dx in -1..<2 {	<-- UNCOVERED
312	            let nx = x + dx
313	            let ny = y + dy
314	            if nx >= 0 && nx < w && ny >= 0 && ny < h {
315	              sum += img.data[ny * w + nx].to_double()	<-- UNCOVERED
316	              count += 1
317	            }
318	          }
319	        }
320	        let avg = if count > 0 {
321	          sum / count.to_double()	<-- UNCOVERED
322	        } else {
323	          img.data[y * w + x].to_double()	<-- UNCOVERED
324	        }
325	        let original = img.data[y * w + x].to_double()	<-- UNCOVERED
326	        let blended = original * (1.0 - strength) + avg * strength
327	        out.push(blended.to_int().to_byte())	<-- UNCOVERED
328	      }
329	    }
330	  }
331	
332	  @types.Image::{
333	    width: w,
334	    height: h,
335	    channels: img.channels,
336	    data: Bytes::from_array(out),
337	  }
338	}
   	…

342	pub fn super_resolve(
343	  img : @types.Image,
344	  config : InterpolationConfig,
345	) -> SuperResolutionResult {
346	  let mut result = if config.algorithm ==
347	    SuperResolutionAlgorithm::NearestNeighbor {
348	    nearest_neighbor_interpolate(img, config.scale_factor)
349	  } else if config.algorithm == SuperResolutionAlgorithm::Bilinear {
350	    bilinear_interpolate(img, config.scale_factor)
351	  } else if config.algorithm == SuperResolutionAlgorithm::Bicubic {
352	    bicubic_interpolate(img, config.scale_factor)
353	  } else {
354	    bicubic_interpolate(img, config.scale_factor)	<-- UNCOVERED
355	  }
356	
357	  // 去噪
358	  if config.denoise_strength > 0.0 {
359	    result = denoise_image(result, config.denoise_strength)
360	  }
361	
362	  // 锐化
363	  if config.sharpening_strength > 0.0 {
364	    result = sharpen_image(result, config.sharpening_strength)
365	  }
366	
367	  SuperResolutionResult::{
368	    image: result,
369	    original_width: img.width,
370	    original_height: img.height,
371	    output_width: result.width,
372	    output_height: result.height,
373	    scale_factor: config.scale_factor,
374	    algorithm: config.algorithm,
375	    processing_time_ms: 0.0,
376	    psnr: 0.0,
377	    ssim: 0.0,
378	  }
379	}
   	…

409	pub fn calculate_ssim_sr(
410	  original : @types.Image,
411	  processed : @types.Image,
412	) -> Double {
413	  let w = original.width
414	  let h = original.height
415	  let total = w * h
416	  let mut sum1 = 0.0
417	  let mut sum2 = 0.0
418	  let mut sum12 = 0.0
419	  let mut sum1_sq = 0.0
420	  let mut sum2_sq = 0.0
421	
422	  for i in 0..<total {
423	    let p1 = original.data[i].to_double()
424	    let p2 = processed.data[i].to_double()
425	    sum1 += p1
426	    sum2 += p2
427	    sum12 += p1 * p2
428	    sum1_sq += p1 * p1
429	    sum2_sq += p2 * p2
430	  }
431	
432	  let mean1 = sum1 / total.to_double()
433	  let mean2 = sum2 / total.to_double()
434	  let var1 = sum1_sq / total.to_double() - mean1 * mean1
435	  let var2 = sum2_sq / total.to_double() - mean2 * mean2
436	  let cov12 = sum12 / total.to_double() - mean1 * mean2
437	
438	  let c1 = 6.5025
439	  let c2 = 58.5225
440	  let numerator = (2.0 * mean1 * mean2 + c1) * (2.0 * cov12 + c2)
441	  let denominator = (mean1 * mean1 + mean2 * mean2 + c1) * (var1 + var2 + c2)
442	
443	  if denominator > 0.0 {
444	    numerator / denominator
445	  } else {
446	    1.0	<-- UNCOVERED
447	  }
448	}
   	…

452	pub fn calculate_image_quality_metrics(
453	  original : @types.Image,
454	  processed : @types.Image,
455	) -> ImageQualityMetrics {
456	  let psnr = calculate_psnr_sr(original, processed)
457	  let ssim = calculate_ssim_sr(original, processed)
458	
459	  let w = original.width
460	  let h = original.height
461	  let total = w * h
462	  let mut mse = 0.0
463	  let mut mae = 0.0
464	  let mut sum1 = 0.0
465	  let mut sum2 = 0.0
466	  let mut sum12 = 0.0
467	
468	  for i in 0..<total {
469	    let p1 = original.data[i].to_double()
470	    let p2 = processed.data[i].to_double()
471	    let diff = p1 - p2
472	    mse += diff * diff
473	    mae += diff.abs()
474	    sum1 += p1
475	    sum2 += p2
476	    sum12 += p1 * p2
477	  }
478	  mse /= total.to_double()
479	  mae /= total.to_double()
480	
481	  let mean1 = sum1 / total.to_double()
482	  let mean2 = sum2 / total.to_double()
483	  let cov = sum12 / total.to_double() - mean1 * mean2
484	  let var1 = if total > 0 {
485	    let mut v = 0.0
486	    for i in 0..<total {
487	      let p = original.data[i].to_double()
488	      v += (p - mean1) * (p - mean1)
489	    }
490	    v / total.to_double()
491	  } else {
492	    0.0	<-- UNCOVERED
493	  }
494	  let var2 = if total > 0 {
495	    let mut v = 0.0
496	    for i in 0..<total {
497	      let p = processed.data[i].to_double()
498	      v += (p - mean2) * (p - mean2)
499	    }
500	    v / total.to_double()
501	  } else {
502	    0.0	<-- UNCOVERED
503	  }
504	  let correlation = if var1 > 0.0 && var2 > 0.0 {
505	    cov / (var1 * var2).sqrt()
506	  } else {
507	    0.0
508	  }
509	
510	  // 清晰度（基于梯度）
511	  let mut sharpness = 0.0
512	  for y in 1..<(h - 1) {
513	    for x in 1..<(w - 1) {
514	      let idx = y * w + x
515	      let gx = processed.data[idx + 1].to_double() -
516	        processed.data[idx - 1].to_double()
517	      let gy = processed.data[idx + w].to_double() -
518	        processed.data[idx - w].to_double()
519	      sharpness += (gx * gx + gy * gy).sqrt()
520	    }
521	  }
522	  sharpness /= total.to_double()
523	
524	  let noise_level = mse.sqrt()
525	  let overall_quality = if psnr > 40.0 && ssim > 0.95 {
526	    5.0
527	  } else if psnr > 30.0 && ssim > 0.9 {
528	    4.0	<-- UNCOVERED
529	  } else if psnr > 20.0 && ssim > 0.8 {
530	    3.0	<-- UNCOVERED
531	  } else {
532	    2.0
533	  }
534	
535	  ImageQualityMetrics::{
536	    psnr,
537	    ssim,
538	    mse,
539	    mae,
540	    correlation,
541	    sharpness,
542	    noise_level,
543	    overall_quality,
544	  }
545	}
   	…

549	pub fn enhance_image(
550	  img : @types.Image,
551	  config : EnhancementConfig,
552	) -> @types.Image {
553	  let w = img.width
554	  let h = img.height
555	  let total = w * h
556	  let out : Array[Byte] = []
557	
558	  for i in 0..<total {
559	    let mut pixel = img.data[i].to_double()
560	
561	    // 亮度
562	    pixel += config.brightness * 255.0
563	
564	    // 对比度
565	    pixel = (pixel - 128.0) * config.contrast + 128.0
566	
567	    // Gamma
568	    if config.gamma != 1.0 && config.gamma > 0.0 {
569	      pixel = 255.0 * (pixel / 255.0) * (1.0 / config.gamma)	<-- UNCOVERED
570	    }
571	
572	    let clamped = if pixel < 0.0 {
573	      0.0
574	    } else if pixel > 255.0 {
575	      255.0	<-- UNCOVERED
576	    } else {
577	      pixel
578	    }
579	    out.push(clamped.to_int().to_byte())
580	  }
581	
582	  let mut result = @types.Image::{
583	    width: w,
584	    height: h,
585	    channels: img.channels,
586	    data: Bytes::from_array(out),
587	  }
588	
589	  // 去噪
590	  if config.denoise > 0.0 {
591	    result = denoise_image(result, config.denoise)	<-- UNCOVERED
592	  }
593	
594	  // 锐化
595	  if config.sharpness > 0.0 {
596	    result = sharpen_image(result, config.sharpness)	<-- UNCOVERED
597	  }
598	
599	  result
600	}
   	…

604	pub fn super_resolution_report_to_string(
605	  result : SuperResolutionResult,
606	  metrics : ImageQualityMetrics,
607	) -> String {
608	  let mut report = "## 图像超分辨率报告\n\n"
609	  report = report + "### 处理信息\n\n"
610	  report = report + "| 指标 | 值 |\n|------|------|\n"
611	  report = report +
612	    "| 原始尺寸 | " +
613	    result.original_width.to_string() +
614	    "x" +
615	    result.original_height.to_string() +
616	    " |\n"
617	  report = report +
618	    "| 输出尺寸 | " +
619	    result.output_width.to_string() +
620	    "x" +
621	    result.output_height.to_string() +
622	    " |\n"
623	  report = report +
624	    "| 缩放因子 | " +
625	    result.scale_factor.to_string() +
626	    "x |\n"
627	  let algo_str = if result.algorithm ==
628	    SuperResolutionAlgorithm::NearestNeighbor {
629	    "最近邻"	<-- UNCOVERED
630	  } else if result.algorithm == SuperResolutionAlgorithm::Bilinear {
631	    "双线性"	<-- UNCOVERED
632	  } else if result.algorithm == SuperResolutionAlgorithm::Bicubic {
633	    "双三次"
634	  } else if result.algorithm == SuperResolutionAlgorithm::Lanczos {	<-- UNCOVERED
635	    "Lanczos"
636	  } else if result.algorithm == SuperResolutionAlgorithm::EdgeDirected {
637	    "边缘导向"
638	  } else if result.algorithm == SuperResolutionAlgorithm::ExampleBased {
639	    "基于示例"
640	  } else if result.algorithm == SuperResolutionAlgorithm::DeepLearning {
641	    "深度学习"
642	  } else {
643	    "未知"	<-- UNCOVERED
644	  }
645	  report = report + "| 算法 | " + algo_str + " |\n"
646	  report = report +
647	    "| 处理耗时 | " +
648	    result.processing_time_ms.to_string() +
649	    "ms |\n\n"
650	
651	  report = report + "### 质量评估\n\n"
652	  report = report + "| 指标 | 值 |\n|------|------|\n"
653	  report = report + "| PSNR | " + metrics.psnr.to_string() + " dB |\n"
654	  report = report + "| SSIM | " + metrics.ssim.to_string() + " |\n"
655	  report = report + "| MSE | " + metrics.mse.to_string() + " |\n"
656	  report = report + "| MAE | " + metrics.mae.to_string() + " |\n"
657	  report = report + "| 相关性 | " + metrics.correlation.to_string() + " |\n"
658	  report = report + "| 清晰度 | " + metrics.sharpness.to_string() + " |\n"
659	  report = report +
660	    "| 噪声水平 | " +
661	    metrics.noise_level.to_string() +
662	    " |\n"
663	  report = report +
664	    "| 综合质量 | " +
665	    metrics.overall_quality.to_string() +
666	    "/5.0 |\n"
667	
668	  report
669	}
   	…

12 uncovered line(s) in src\process\transfer_learning_advanced\transfer_learning_advanced.mbt:

310	pub fn load_pretrained_model(
311	  model_type : PretrainedModelType,
312	  num_layers : Int,
313	  layer_size : Int,
314	) -> PretrainingResult {
315	  let weights = init_pretrained_weights(num_layers, layer_size)
316	
317	  let architecture = match model_type {
318	    PretrainedModelType::ResNet => "ResNet50"
319	    PretrainedModelType::VGG => "VGG16"	<-- UNCOVERED
320	    PretrainedModelType::Inception => "InceptionV3"
321	    PretrainedModelType::EfficientNet => "EfficientNetB0"
322	    PretrainedModelType::VisionTransformer => "ViT-Base"
323	    PretrainedModelType::SwinTransformer => "Swin-Base"
324	    PretrainedModelType::ConvNeXt => "ConvNeXt-Base"
325	    PretrainedModelType::MobileNet => "MobileNetV2"
326	    PretrainedModelType::DenseNet => "DenseNet121"
327	    PretrainedModelType::AlexNet => "AlexNet"
328	    _ => "Unknown"
329	  }
330	
331	  PretrainingResult::{
332	    pretrained_weights: weights,
333	    num_layers,
334	    num_parameters: num_layers * layer_size,
335	    pretraining_loss: 0.1,
336	    pretraining_accuracy: 0.95,
337	    num_epochs: 100,
338	    batch_size: 256,
339	    learning_rate: 0.1,
340	    pretraining_time_ms: 10000.0,
341	    model_architecture: architecture,
342	    dataset_size: 1280000,
343	    pretrained_model_type: model_type,
344	  }
345	}
   	…

494	pub fn few_shot_learning(
495	  support_data : Array[Array[Double]],
496	  query_data : Array[Array[Double]],
497	  config : TransferLearningConfig,
498	) -> FewShotLearningResult {
499	  ignore(query_data)
500	  let num_classes = config.num_way
501	  let num_support = config.num_way * config.num_shot
502	
503	  // 计算原型（简化版）
504	  let prototypes : Array[Array[Double]] = []
505	  for c in 0..<num_classes {
506	    let proto : Array[Double] = []
507	    let dim = if support_data.length() > 0 {
508	      support_data[0].length()
509	    } else {
510	      1	<-- UNCOVERED
511	    }
512	    for d in 0..<dim {
513	      let mut sum = 0.0
514	      for s in 0..<config.num_shot {
515	        let idx = c * config.num_shot + s
516	        if idx < support_data.length() && d < support_data[idx].length() {
517	          sum += support_data[idx][d]
518	        }
519	      }
520	      proto.push(sum / config.num_shot.to_double())
521	    }
522	    prototypes.push(proto)
523	  }
524	
525	  FewShotLearningResult::{
526	    learned_representations: prototypes,
527	    num_way: config.num_way,
528	    num_shot: config.num_shot,
529	    num_query: config.num_query,
530	    training_accuracy: 0.7,
531	    validation_accuracy: 0.68,
532	    test_accuracy: 0.65,
533	    confidence_interval: 0.05,
534	    few_shot_method: config.few_shot_method,
535	    num_episodes: config.num_episodes,
536	    learning_rate: config.learning_rate,
537	    learning_time_ms: 6000.0,
538	    num_classes,
539	    num_support_samples: num_support,
540	  }
541	}
   	…

545	pub fn knowledge_distillation(
546	  teacher_weights : Array[Array[Double]],
547	  student_num_layers : Int,
548	  layer_size : Int,
549	  config : TransferLearningConfig,
550	) -> KnowledgeDistillationResult {
551	  let student_weights = init_pretrained_weights(student_num_layers, layer_size)
552	
553	  let teacher_params = {
554	    let mut total = 0
555	    for layer in teacher_weights {
556	      total += layer.length()
557	    }
558	    total
559	  }
560	  let student_params = student_num_layers * layer_size
561	  let compression_ratio = if student_params > 0 {
562	    teacher_params.to_double() / student_params.to_double()
563	  } else {
564	    1.0	<-- UNCOVERED
565	  }
566	
567	  KnowledgeDistillationResult::{
568	    student_weights,
569	    teacher_weights,
570	    student_accuracy: 0.82,
571	    teacher_accuracy: 0.9,
572	    student_loss: 0.25,
573	    distillation_loss: 0.1,
574	    total_loss: 0.35,
575	    temperature: config.temperature,
576	    alpha: config.alpha,
577	    num_epochs: config.num_epochs,
578	    learning_rate: config.learning_rate,
579	    distillation_time_ms: 7000.0,
580	    model_compression_ratio: compression_ratio,
581	    accuracy_gap: 0.08,
582	  }
583	}
   	…

632	pub fn transfer_learning_report_to_string(
633	  task_type : TransferLearningTaskType,
634	  config : TransferLearningConfig,
635	  result_description : String,
636	) -> String {
637	  let mut report = "## 迁移学习高级报告\n\n"
638	
639	  report = report + "### 任务信息\n\n"
640	  report = report + "| 指标 | 值 |\n|------|------|\n"
641	  report = report +
642	    "| 任务类型 | " +
643	    (match task_type {
644	      TransferLearningTaskType::Pretraining => "预训练"	<-- UNCOVERED
645	      TransferLearningTaskType::FineTuning => "微调"
646	      TransferLearningTaskType::FeatureExtraction => "特征提取"	<-- UNCOVERED
647	      TransferLearningTaskType::DomainAdaptation => "域适应"
648	      TransferLearningTaskType::MultiTaskLearning => "多任务学习"
649	      TransferLearningTaskType::LifelongLearning => "终身学习"
650	      TransferLearningTaskType::ZeroShotLearning => "零样本学习"
651	      TransferLearningTaskType::FewShotLearning => "少样本学习"
652	      TransferLearningTaskType::MetaLearning => "元学习"
653	      TransferLearningTaskType::KnowledgeDistillation => "知识蒸馏"
654	      _ => "未知"
655	    }) +
656	    " |\n"
657	  report = report +
658	    "| 预训练模型 | " +
659	    (match config.pretrained_model {
660	      PretrainedModelType::ResNet => "ResNet"
661	      PretrainedModelType::VGG => "VGG"	<-- UNCOVERED
662	      PretrainedModelType::Inception => "Inception"
663	      PretrainedModelType::EfficientNet => "EfficientNet"
664	      PretrainedModelType::VisionTransformer => "Vision Transformer"
665	      PretrainedModelType::SwinTransformer => "Swin Transformer"
666	      PretrainedModelType::ConvNeXt => "ConvNeXt"
667	      PretrainedModelType::MobileNet => "MobileNet"
668	      PretrainedModelType::DenseNet => "DenseNet"
669	      PretrainedModelType::AlexNet => "AlexNet"
670	      _ => "未知"
671	    }) +
672	    " |\n"
673	  report = report +
674	    "| 微调策略 | " +
675	    (match config.fine_tuning_strategy {
676	      FineTuningStrategyType::FullFineTuning => "全量微调"	<-- UNCOVERED
677	      FineTuningStrategyType::PartialFineTuning => "部分微调"
678	      FineTuningStrategyType::LayerWiseFineTuning => "逐层微调"	<-- UNCOVERED
679	      FineTuningStrategyType::DiscriminativeLearningRate => "判别式学习率"
680	      FineTuningStrategyType::GradualUnfreezing => "逐步解冻"
681	      FineTuningStrategyType::FreezeBackbone => "冻结骨干"
682	      FineTuningStrategyType::AdapterTuning => "Adapter微调"
683	      FineTuningStrategyType::LoRA => "LoRA"
684	      FineTuningStrategyType::PromptTuning => "提示微调"
685	      FineTuningStrategyType::PrefixTuning => "前缀微调"
686	      _ => "未知"
687	    }) +
688	    " |\n"
689	  report = report +
690	    "| 域适应方法 | " +
691	    (match config.domain_adaptation_method {
692	      DomainAdaptationMethodType::DANN => "DANN"
693	      DomainAdaptationMethodType::CDAN => "CDAN"	<-- UNCOVERED
694	      DomainAdaptationMethodType::MMD => "MMD"
695	      DomainAdaptationMethodType::CORAL => "CORAL"
696	      DomainAdaptationMethodType::ADDA => "ADDA"
697	      DomainAdaptationMethodType::CycleGAN => "CycleGAN"
698	      DomainAdaptationMethodType::MCD => "MCD"
699	      DomainAdaptationMethodType::M3SDA => "M3SDA"
700	      DomainAdaptationMethodType::SHOT => "SHOT"
701	      DomainAdaptationMethodType::FixMatch => "FixMatch"
702	      _ => "未知"
703	    }) +
704	    " |\n"
705	  report = report +
706	    "| 多任务方法 | " +
707	    (match config.multi_task_method {
708	      MultiTaskLearningMethodType::HardParameterSharing => "硬参数共享"
709	      MultiTaskLearningMethodType::SoftParameterSharing => "软参数共享"	<-- UNCOVERED
710	      MultiTaskLearningMethodType::CrossStitch => "CrossStitch"
711	      MultiTaskLearningMethodType::TensorFactorization => "张量分解"
712	      MultiTaskLearningMethodType::MMoE => "MMoE"
713	      MultiTaskLearningMethodType::PLE => "PLE"
714	      MultiTaskLearningMethodType::SharedBottom => "共享底层"
715	      MultiTaskLearningMethodType::MultiGateMixtureOfExperts =>
716	        "多门混合专家"	<-- UNCOVERED
717	      _ => "未知"
718	    }) +
719	    " |\n"
720	  report = report +
721	    "| 少样本方法 | " +
722	    (match config.few_shot_method {
723	      FewShotLearningMethodType::PrototypicalNetwork => "原型网络"
724	      FewShotLearningMethodType::MatchingNetwork => "匹配网络"	<-- UNCOVERED
725	      FewShotLearningMethodType::RelationNetwork => "关系网络"
726	      FewShotLearningMethodType::MAML => "MAML"
727	      FewShotLearningMethodType::Reptile => "Reptile"
728	      FewShotLearningMethodType::MetaSGD => "Meta-SGD"
729	      FewShotLearningMethodType::TADAM => "TADAM"
730	      FewShotLearningMethodType::FEAT => "FEAT"
731	      FewShotLearningMethodType::Baseline => "Baseline"
732	      FewShotLearningMethodType::BaselinePlus => "Baseline++"
733	      _ => "未知"
734	    }) +
735	    " |\n"
736	  report = report +
737	    "| 冻结层数 | " +
738	    config.num_frozen_layers.to_string() +
739	    " |\n"
740	  report = report + "| 学习率 | " + config.learning_rate.to_string() + " |\n"
741	  report = report + "| 训练轮数 | " + config.num_epochs.to_string() + " |\n"
742	  report = report + "| 批次大小 | " + config.batch_size.to_string() + " |\n"
743	  report = report + "| 温度 | " + config.temperature.to_string() + " |\n"
744	  report = report + "| N-way | " + config.num_way.to_string() + " |\n"
745	  report = report + "| K-shot | " + config.num_shot.to_string() + " |\n"
746	  report = report +
747	    "| 使用预训练 | " +
748	    (if config.use_pretrained { "开启" } else { "关闭" }) +
749	    " |\n"
750	
751	  report = report + "\n### 结果\n\n"
752	  report = report + result_description + "\n"
753	
754	  report
755	}
   	…

4 uncovered line(s) in src\process\transform\draw.mbt:

102	fn put_pixel(
103	  out : Array[Byte],
104	  w : Int,
105	  h : Int,
106	  ch : Int,
107	  x : Int,
108	  y : Int,
109	  color : Array[Byte],
110	) -> Unit {
111	  if x < 0 || x >= w || y < 0 || y >= h {
112	    return	<-- UNCOVERED
113	  }
114	  let off = (y * w + x) * ch
115	  for c in 0..<ch {
116	    if c < color.length() {
117	      out[off + c] = color[c]
118	    }
119	  }
120	}
   	…

304	pub fn draw_polygon(
305	  img : @types.Image,
306	  points : Array[(Int, Int)],
307	  color : Array[Byte],
308	  thickness? : Int = 1,
309	  fill? : Bool = false,
310	) -> @types.Image {
311	  let w = img.width
312	  let h = img.height
313	  let ch = img.channels
314	  let out : Array[Byte] = []
315	  for i in 0..<img.data.length() {
316	    out.push(img.data[i])
317	  }
318	  let n = points.length()
319	  if n < 2 {
320	    return @types.Image::{	<-- UNCOVERED
321	      width: w,
322	      height: h,
323	      channels: ch,
324	      data: Bytes::from_array(out),	<-- UNCOVERED
325	    }
326	  }
327	  // 画轮廓
328	  let mut result = @types.Image::{
329	    width: w,
330	    height: h,
331	    channels: ch,
332	    data: Bytes::from_array(out),
333	  }
334	  for i in 0..<n {
335	    let (x1, y1) = points[i]
336	    let (x2, y2) = points[(i + 1) % n]
337	    result = draw_line(result, x1, y1, x2, y2, color, thickness~)
338	  }
339	  if fill {
340	    // 扫描线填充
341	    let fill_out : Array[Byte] = []
342	    for i in 0..<result.data.length() {
343	      fill_out.push(result.data[i])
344	    }
345	    let mut y_min = h
346	    let mut y_max = 0
347	    for i in 0..<n {
348	      let (_, py) = points[i]
349	      if py < y_min {
350	        y_min = py
351	      }
352	      if py > y_max {
353	        y_max = py
354	      }
355	    }
356	    for y in y_min..<=y_max {
357	      // 求交点
358	      let xs : Array[Int] = []
359	      for i in 0..<n {
360	        let (x1, y1) = points[i]
361	        let (x2, y2) = points[(i + 1) % n]
362	        if y1 == y2 {
363	          continue
364	        }
365	        if y < y1 && y < y2 {
366	          continue	<-- UNCOVERED
367	        }
368	        if y >= y1 && y >= y2 {
369	          continue
370	        }
371	        let x = x1 + (y - y1) * (x2 - x1) / (y2 - y1)
372	        xs.push(x)
373	      }
374	      // 排序交点
375	      xs.sort()
376	      // 填充相邻对
377	      let mut j = 0
378	      while j + 1 < xs.length() {
379	        for x in xs[j]..<=xs[j + 1] {
380	          put_pixel(fill_out, w, h, ch, x, y, color)
381	        }
382	        j += 2
383	      }
384	    }
385	    return @types.Image::{
386	      width: w,
387	      height: h,
388	      channels: ch,
389	      data: Bytes::from_array(fill_out),
390	    }
391	  }
392	  result
393	}
   	…

9 uncovered line(s) in src\process\transform\draw_16f.mbt:

22	fn clamp_int_draw(v : Int, lo : Int, hi : Int) -> Int {
23	  if v < lo {
24	    lo	<-- UNCOVERED
25	  } else if v > hi {
26	    hi	<-- UNCOVERED
27	  } else {
28	    v
29	  }
30	}
  	…

33	fn set_pixel_16(
34	  buf : Array[Byte],
35	  x : Int,
36	  y : Int,
37	  w : Int,
38	  ch : Int,
39	  color : Array[Int],
40	) -> Unit {
41	  if x < 0 || x >= w || y < 0 {
42	    return	<-- UNCOVERED
43	  }
44	  let idx = (y * w + x) * ch * 2
45	  for c in 0..<ch {
46	    if c < color.length() {
47	      write_u16_le_draw(buf, idx + c * 2, clamp_int_draw(color[c], 0, 65535))
48	    }
49	  }
50	}
  	…

53	fn set_pixel_f(
54	  buf : Array[Byte],
55	  x : Int,
56	  y : Int,
57	  w : Int,
58	  ch : Int,
59	  color : Array[Float],
60	) -> Unit {
61	  if x < 0 || x >= w || y < 0 {
62	    return	<-- UNCOVERED
63	  }
64	  let idx = (y * w + x) * ch * 4
65	  for c in 0..<ch {
66	    if c < color.length() {
67	      write_f32_le_draw(buf, idx + c * 4, color[c])
68	    }
69	  }
70	}
  	…

231	pub fn draw_polygon_16(
232	  img : @types.Image16,
233	  points : Array[(Int, Int)],
234	  color : Array[Int],
235	  thickness? : Int = 1,
236	) -> @types.Image16 {
237	  let mut result = img
238	  let n = points.length()
239	  if n < 2 {
240	    return result	<-- UNCOVERED
241	  }
242	  for i in 0..<n {
243	    let (x1, y1) = points[i]
244	    let (x2, y2) = points[(i + 1) % n]
245	    result = draw_line_16(result, x1, y1, x2, y2, color, thickness~)
246	  }
247	  result
248	}
   	…

307	pub fn draw_rectangle_f(
308	  img : @types.ImageF,
309	  x : Int,
310	  y : Int,
311	  rw : Int,
312	  rh : Int,
313	  color : Array[Float],
314	  thickness? : Int = 1,
315	  fill? : Bool = false,
316	) -> @types.ImageF {
317	  let w = img.width
318	  let h = img.height
319	  let ch = img.channels
320	  let buf : Array[Byte] = img.data.to_array()
321	  if fill {
322	    for py in y..<(y + rh) {	<-- UNCOVERED
323	      for px in x..<(x + rw) {
324	        set_pixel_f(buf, px, py, w, ch, color)
325	      }
326	    }
327	  } else {
328	    let t = if thickness < 1 { 1 } else { thickness }
329	    for px in x..<(x + rw) {
330	      for ty in 0..<t {
331	        set_pixel_f(buf, px, y + ty, w, ch, color)
332	        set_pixel_f(buf, px, y + rh - 1 - ty, w, ch, color)
333	      }
334	    }
335	    for py in y..<(y + rh) {
336	      for tx in 0..<t {
337	        set_pixel_f(buf, x + tx, py, w, ch, color)
338	        set_pixel_f(buf, x + rw - 1 - tx, py, w, ch, color)
339	      }
340	    }
341	  }
342	  @types.ImageF::{
343	    width: w,
344	    height: h,
345	    channels: ch,
346	    data: Bytes::from_array(buf),
347	  }
348	}
   	…

352	pub fn draw_circle_f(
353	  img : @types.ImageF,
354	  cx : Int,
355	  cy : Int,
356	  r : Int,
357	  color : Array[Float],
358	  thickness? : Int = 1,
359	  fill? : Bool = false,
360	) -> @types.ImageF {
361	  let w = img.width
362	  let h = img.height
363	  let ch = img.channels
364	  let buf : Array[Byte] = img.data.to_array()
365	  if fill {
366	    for y in (cy - r)..<=(cy + r) {	<-- UNCOVERED
367	      for x in (cx - r)..<=(cx + r) {
368	        let dx = x - cx
369	        let dy = y - cy
370	        if dx * dx + dy * dy <= r * r {
371	          set_pixel_f(buf, x, y, w, ch, color)	<-- UNCOVERED
372	        }
373	      }
374	    }
375	  } else {
376	    let _ = thickness
377	    let mut x = r
378	    let mut y = 0
379	    let mut err = 0
380	    while x >= y {
381	      set_pixel_f(buf, cx + x, cy + y, w, ch, color)
382	      set_pixel_f(buf, cx + y, cy + x, w, ch, color)
383	      set_pixel_f(buf, cx - y, cy + x, w, ch, color)
384	      set_pixel_f(buf, cx - x, cy + y, w, ch, color)
385	      set_pixel_f(buf, cx - x, cy - y, w, ch, color)
386	      set_pixel_f(buf, cx - y, cy - x, w, ch, color)
387	      set_pixel_f(buf, cx + y, cy - x, w, ch, color)
388	      set_pixel_f(buf, cx + x, cy - y, w, ch, color)
389	      y += 1
390	      if err <= 0 {
391	        err += 2 * y + 1
392	      }
393	      if err > 0 {
394	        x -= 1
395	        err -= 2 * x + 1
396	      }
397	    }
398	  }
399	  @types.ImageF::{
400	    width: w,
401	    height: h,
402	    channels: ch,
403	    data: Bytes::from_array(buf),
404	  }
405	}
   	…

409	pub fn draw_polygon_f(
410	  img : @types.ImageF,
411	  points : Array[(Int, Int)],
412	  color : Array[Float],
413	  thickness? : Int = 1,
414	) -> @types.ImageF {
415	  let mut result = img
416	  let n = points.length()
417	  if n < 2 {
418	    return result	<-- UNCOVERED
419	  }
420	  for i in 0..<n {
421	    let (x1, y1) = points[i]
422	    let (x2, y2) = points[(i + 1) % n]
423	    result = draw_line_f(result, x1, y1, x2, y2, color, thickness~)
424	  }
425	  result
426	}
   	…

2 uncovered line(s) in src\process\transform\geometry.mbt:

143	pub fn get_affine_transform(
144	  src : Array[(Float, Float)],
145	  dst : Array[(Float, Float)],
146	) -> AffineMatrix? {
147	  if src.length() != 3 || dst.length() != 3 {
148	    return None	<-- UNCOVERED
149	  }
150	  // 6×6 线性方程组
151	  let a : Array[Array[Float]] = Array::make(6, [])
152	  let b : Array[Float] = Array::make(6, 0.0F)
153	  for i in 0..<3 {
154	    let (sx, sy) = src[i]
155	    let (dx, dy) = dst[i]
156	    a[i * 2] = [sx, sy, 1.0F, 0.0F, 0.0F, 0.0F]
157	    b[i * 2] = dx
158	    a[i * 2 + 1] = [0.0F, 0.0F, 0.0F, sx, sy, 1.0F]
159	    b[i * 2 + 1] = dy
160	  }
161	  let sol = solve_linear_system(a, b, 6)
162	  match sol {
163	    None => None	<-- UNCOVERED
164	    Some(v) =>
165	      Some(AffineMatrix::{
166	        a: v[0],
167	        b: v[1],
168	        c: v[2],
169	        d: v[3],
170	        e: v[4],
171	        f: v[5],
172	      })
173	  }
174	}
   	…

1 uncovered line(s) in src\process\transform\geometry_16f.mbt:

37	fn clamp_int_geo(v : Int, lo : Int, hi : Int) -> Int {
38	  if v < lo {
39	    lo	<-- UNCOVERED
40	  } else if v > hi {
41	    hi
42	  } else {
43	    v
44	  }
45	}
  	…

9 uncovered line(s) in src\process\transform\multi_band_blend.mbt:

 91	fn get_w_from_n(n : Int, w : Int, h : Int) -> Int {
 92	  if h == 0 {
 93	    return 0	<-- UNCOVERED
 94	  }
 95	  let ratio = n / h
 96	  if ratio > 0 {
 97	    ratio
 98	  } else {
 99	    w
100	  }
101	}
   	…

185	fn collapse_laplacian_pyramid_f(
186	  pyramid : Array[Array[Float]],
187	  w : Int,
188	  h : Int,
189	) -> Array[Float] {
190	  if pyramid.length() == 0 {
191	    return Array::make(w * h, 0.0F)	<-- UNCOVERED
192	  }
193	  // 从最粗层开始重建
194	  let mut current = pyramid[pyramid.length() - 1]
195	  let mut cw = 1
196	  let mut ch = 1
197	  // 推算最粗层尺寸
198	  let mut tw = w
199	  let mut th = h
200	  let sizes : Array[(Int, Int)] = [(tw, th)]
201	  for _l in 1..<pyramid.length() {
202	    tw = tw / 2
203	    th = th / 2
204	    if tw == 0 {
205	      tw = 1	<-- UNCOVERED
206	    }
207	    if th == 0 {
208	      th = 1	<-- UNCOVERED
209	    }
210	    sizes.push((tw, th))
211	  }
212	  let (lcw, lch) = sizes[sizes.length() - 1]
213	  cw = lcw
214	  ch = lch
215	  // 从倒数第二层开始向上重建
216	  for k in 0..<(pyramid.length() - 1) {
217	    let l = pyramid.length() - 2 - k
218	    let (nw, nh) = sizes[l]
219	    let upsampled = upsample_f(current, cw, ch, nw, nh)
220	    current = Array::make(nw * nh, 0.0F)
221	    for i in 0..<(nw * nh) {
222	      current[i] = pyramid[l][i] + upsampled[i]
223	    }
224	    cw = nw
225	    ch = nh
226	  }
227	  current
228	}
   	…

234	pub fn multi_band_blend_16(
235	  img_a : @types.Image16,
236	  img_b : @types.Image16,
237	  mask : @types.Image16,
238	  num_bands? : Int = 5,
239	) -> @types.Image16 raise @types.LoadError {
240	  if img_a.width != img_b.width || img_a.height != img_b.height {
241	    raise @types.LoadError::DecodeFailed("img_a 和 img_b 尺寸需相同")	<-- UNCOVERED
242	  }
243	  if mask.width != img_a.width || mask.height != img_a.height {
244	    raise @types.LoadError::DecodeFailed("mask 尺寸需与输入图像相同")	<-- UNCOVERED
245	  }
246	  let w = img_a.width
247	  let h = img_a.height
248	  let ch = img_a.channels
249	  let n = w * h
250	  let mask_f : Array[Float] = Array::make(n, 0.0F)
251	  for i in 0..<n {
252	    let v = mask.data[i * 2].to_int() | (mask.data[i * 2 + 1].to_int() << 8)
253	    mask_f[i] = Float::from_int(v) / 65535.0F
254	  }
255	  let a_f : Array[Float] = Array::make(n * ch, 0.0F)
256	  let b_f : Array[Float] = Array::make(n * ch, 0.0F)
257	  for i in 0..<(n * ch) {
258	    let va = img_a.data[i * 2].to_int() | (img_a.data[i * 2 + 1].to_int() << 8)
259	    let vb = img_b.data[i * 2].to_int() | (img_b.data[i * 2 + 1].to_int() << 8)
260	    a_f[i] = Float::from_int(va)
261	    b_f[i] = Float::from_int(vb)
262	  }
263	  let levels = if num_bands > 0 { num_bands } else { 5 }
264	  let mask_pyr : Array[Array[Float]] = [mask_f]
265	  for _l in 1..<levels {
266	    let prev = mask_pyr[mask_pyr.length() - 1]
267	    let pw = get_w_from_n(prev.length(), w, h)
268	    let ph = prev.length() / pw
269	    mask_pyr.push(blur_downsample(prev, pw, ph))
270	  }
271	  let out : Array[Float] = Array::make(n * ch, 0.0F)
272	  for c in 0..<ch {
273	    let a_chan : Array[Float] = Array::make(n, 0.0F)
274	    let b_chan : Array[Float] = Array::make(n, 0.0F)
275	    for i in 0..<n {
276	      a_chan[i] = a_f[i * ch + c]
277	      b_chan[i] = b_f[i * ch + c]
278	    }
279	    let a_pyr = build_laplacian_pyramid_f(a_chan, w, h, levels)
280	    let b_pyr = build_laplacian_pyramid_f(b_chan, w, h, levels)
281	    let blended : Array[Array[Float]] = []
282	    for l in 0..<a_pyr.length() {
283	      let a_l = a_pyr[l]
284	      let b_l = b_pyr[l]
285	      let m_l = mask_pyr[l]
286	      let blended_l : Array[Float] = Array::make(a_l.length(), 0.0F)
287	      for i in 0..<a_l.length() {
288	        let m = if i < m_l.length() { m_l[i] } else { 0.5F }
289	        blended_l[i] = m * a_l[i] + (1.0F - m) * b_l[i]
290	      }
291	      blended.push(blended_l)
292	    }
293	    let reconstructed = collapse_laplacian_pyramid_f(blended, w, h)
294	    for i in 0..<n {
295	      out[i * ch + c] = reconstructed[i]
296	    }
297	  }
298	  let out_bytes : Array[Byte] = Array::make(n * ch * 2, b'\x00')
299	  for i in 0..<(n * ch) {
300	    let v = (out[i] + 0.5F).to_int()
301	    let clamped = if v < 0 { 0 } else if v > 65535 { 65535 } else { v }	<-- UNCOVERED
302	    out_bytes[i * 2] = (clamped & 0xFF).to_byte()
303	    out_bytes[i * 2 + 1] = ((clamped >> 8) & 0xFF).to_byte()
304	  }
305	  @types.Image16::{
306	    width: w,
307	    height: h,
308	    channels: ch,
309	    data: Bytes::from_array(out_bytes),
310	  }
311	}
   	…

317	pub fn multi_band_blend_f(
318	  img_a : @types.ImageF,
319	  img_b : @types.ImageF,
320	  mask : @types.ImageF,
321	  num_bands? : Int = 5,
322	) -> @types.ImageF raise @types.LoadError {
323	  if img_a.width != img_b.width || img_a.height != img_b.height {
324	    raise @types.LoadError::DecodeFailed("img_a 和 img_b 尺寸需相同")	<-- UNCOVERED
325	  }
326	  if mask.width != img_a.width || mask.height != img_a.height {
327	    raise @types.LoadError::DecodeFailed("mask 尺寸需与输入图像相同")	<-- UNCOVERED
328	  }
329	  let w = img_a.width
330	  let h = img_a.height
331	  let ch = img_a.channels
332	  let n = w * h
333	  let mask_f : Array[Float] = Array::make(n, 0.0F)
334	  for i in 0..<n {
335	    let bits = mask.data[i * 4].to_int() |
336	      (mask.data[i * 4 + 1].to_int() << 8) |
337	      (mask.data[i * 4 + 2].to_int() << 16) |
338	      (mask.data[i * 4 + 3].to_int() << 24)
339	    mask_f[i] = Float::reinterpret_from_int(bits)
340	  }
341	  let a_f : Array[Float] = Array::make(n * ch, 0.0F)
342	  let b_f : Array[Float] = Array::make(n * ch, 0.0F)
343	  for i in 0..<(n * ch) {
344	    let bits_a = img_a.data[i * 4].to_int() |
345	      (img_a.data[i * 4 + 1].to_int() << 8) |
346	      (img_a.data[i * 4 + 2].to_int() << 16) |
347	      (img_a.data[i * 4 + 3].to_int() << 24)
348	    let bits_b = img_b.data[i * 4].to_int() |
349	      (img_b.data[i * 4 + 1].to_int() << 8) |
350	      (img_b.data[i * 4 + 2].to_int() << 16) |
351	      (img_b.data[i * 4 + 3].to_int() << 24)
352	    a_f[i] = Float::reinterpret_from_int(bits_a)
353	    b_f[i] = Float::reinterpret_from_int(bits_b)
354	  }
355	  let levels = if num_bands > 0 { num_bands } else { 5 }
356	  let mask_pyr : Array[Array[Float]] = [mask_f]
357	  for _l in 1..<levels {
358	    let prev = mask_pyr[mask_pyr.length() - 1]
359	    let pw = get_w_from_n(prev.length(), w, h)
360	    let ph = prev.length() / pw
361	    mask_pyr.push(blur_downsample(prev, pw, ph))
362	  }
363	  let out : Array[Float] = Array::make(n * ch, 0.0F)
364	  for c in 0..<ch {
365	    let a_chan : Array[Float] = Array::make(n, 0.0F)
366	    let b_chan : Array[Float] = Array::make(n, 0.0F)
367	    for i in 0..<n {
368	      a_chan[i] = a_f[i * ch + c]
369	      b_chan[i] = b_f[i * ch + c]
370	    }
371	    let a_pyr = build_laplacian_pyramid_f(a_chan, w, h, levels)
372	    let b_pyr = build_laplacian_pyramid_f(b_chan, w, h, levels)
373	    let blended : Array[Array[Float]] = []
374	    for l in 0..<a_pyr.length() {
375	      let a_l = a_pyr[l]
376	      let b_l = b_pyr[l]
377	      let m_l = mask_pyr[l]
378	      let blended_l : Array[Float] = Array::make(a_l.length(), 0.0F)
379	      for i in 0..<a_l.length() {
380	        let m = if i < m_l.length() { m_l[i] } else { 0.5F }
381	        blended_l[i] = m * a_l[i] + (1.0F - m) * b_l[i]
382	      }
383	      blended.push(blended_l)
384	    }
385	    let reconstructed = collapse_laplacian_pyramid_f(blended, w, h)
386	    for i in 0..<n {
387	      out[i * ch + c] = reconstructed[i]
388	    }
389	  }
390	  let out_bytes : Array[Byte] = Array::make(n * ch * 4, b'\x00')
391	  for i in 0..<(n * ch) {
392	    let bits = out[i].reinterpret_as_int()
393	    out_bytes[i * 4] = (bits & 0xFF).to_byte()
394	    out_bytes[i * 4 + 1] = ((bits >> 8) & 0xFF).to_byte()
395	    out_bytes[i * 4 + 2] = ((bits >> 16) & 0xFF).to_byte()
396	    out_bytes[i * 4 + 3] = ((bits >> 24) & 0xFF).to_byte()
397	  }
398	  @types.ImageF::{
399	    width: w,
400	    height: h,
401	    channels: ch,
402	    data: Bytes::from_array(out_bytes),
403	  }
404	}
   	…

4 uncovered line(s) in src\process\transform\perspective.mbt:

19	fn solve_linear_system(
20	  a : Array[Array[Float]],
21	  b : Array[Float],
22	  n : Int,
23	) -> Array[Float]? {
24	  let mat : Array[Array[Float]] = Array::make(n, [])
25	  for i in 0..<n {
26	    mat[i] = Array::make(n + 1, 0.0F)
27	    for j in 0..<n {
28	      mat[i][j] = a[i][j]
29	    }
30	    mat[i][n] = b[i]
31	  }
32	  // 前向消元
33	  for col in 0..<n {
34	    // 选主元
35	    let mut pivot = col
36	    for row in (col + 1)..<n {
37	      if mat[row][col].abs() > mat[pivot][col].abs() {
38	        pivot = row
39	      }
40	    }
41	    if mat[pivot][col].abs() < 0.0000000001F {
42	      return None	<-- UNCOVERED
43	    }
44	    if pivot != col {
45	      let tmp = mat[pivot]
46	      mat[pivot] = mat[col]
47	      mat[col] = tmp
48	    }
49	    // 消元
50	    for row in (col + 1)..<n {
51	      let factor = mat[row][col] / mat[col][col]
52	      for j in col..<=n {
53	        mat[row][j] -= factor * mat[col][j]
54	      }
55	    }
56	  }
57	  // 回代
58	  let x : Array[Float] = Array::make(n, 0.0F)
59	  for k in 0..<n {
60	    let i = n - 1 - k
61	    let mut sum = mat[i][n]
62	    for j in (i + 1)..<n {
63	      sum -= mat[i][j] * x[j]
64	    }
65	    x[i] = sum / mat[i][i]
66	  }
67	  Some(x)
68	}
  	…

 73	pub fn get_perspective_transform(
 74	  src : Array[(Float, Float)],
 75	  dst : Array[(Float, Float)],
 76	) -> PerspectiveMatrix? {
 77	  if src.length() != 4 || dst.length() != 4 {
 78	    return None	<-- UNCOVERED
 79	  }
 80	  // 构建 8×8 线性方程组
 81	  let a : Array[Array[Float]] = Array::make(8, [])
 82	  let b : Array[Float] = Array::make(8, 0.0F)
 83	  for i in 0..<4 {
 84	    let (sx, sy) = src[i]
 85	    let (dx, dy) = dst[i]
 86	    // 方程 1: m00*sx + m01*sy + m02 - m20*sx*dx - m21*sy*dx = dx
 87	    a[i * 2] = [sx, sy, 1.0F, 0.0F, 0.0F, 0.0F, -sx * dx, -sy * dx]
 88	    b[i * 2] = dx
 89	    // 方程 2: m10*sx + m11*sy + m12 - m20*sx*dy - m21*sy*dy = dy
 90	    a[i * 2 + 1] = [0.0F, 0.0F, 0.0F, sx, sy, 1.0F, -sx * dy, -sy * dy]
 91	    b[i * 2 + 1] = dy
 92	  }
 93	  let sol = solve_linear_system(a, b, 8)
 94	  match sol {
 95	    None => None	<-- UNCOVERED
 96	    Some(v) =>
 97	      Some(PerspectiveMatrix::{
 98	        m00: v[0],
 99	        m01: v[1],
100	        m02: v[2],
101	        m10: v[3],
102	        m11: v[4],
103	        m12: v[5],
104	        m20: v[6],
105	        m21: v[7],
106	        m22: 1.0F,
107	      })
108	  }
109	}
   	…

114	pub fn warp_perspective(
115	  img : @types.Image,
116	  matrix : PerspectiveMatrix,
117	  new_w : Int,
118	  new_h : Int,
119	) -> @types.Image {
120	  let ch = img.channels
121	  let w = img.width
122	  let h = img.height
123	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
124	  for i in 0..<(w * h * ch) {
125	    data_int[i] = img.data[i].to_int()
126	  }
127	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
128	  for dy in 0..<new_h {
129	    let fdx = 0.0F
130	    let fdy = Float::from_int(dy)
131	    for dx in 0..<new_w {
132	      let fdx2 = if dx == 0 { fdx } else { Float::from_int(dx) }
133	      // dst→src: 求逆映射。这里假设 matrix 是 src→dst，需要求逆。
134	      // 简化：直接用 matrix 做 dst→src 映射（前向映射的逆）
135	      // src_x = (m00*dx + m01*dy + m02) / (m20*dx + m21*dy + m22)
136	      // src_y = (m10*dx + m11*dy + m12) / (m20*dx + m21*dy + m22)
137	      let denom = matrix.m20 * fdx2 + matrix.m21 * fdy + matrix.m22
138	      if denom.abs() < 0.0000000001F {
139	        continue	<-- UNCOVERED
140	      }
141	      let sx = (matrix.m00 * fdx2 + matrix.m01 * fdy + matrix.m02) / denom
142	      let sy = (matrix.m10 * fdx2 + matrix.m11 * fdy + matrix.m12) / denom
143	      for ci in 0..<ch {
144	        let val = persp_sample_bilinear(data_int, w, h, ch, ci, sx, sy)
145	        out[(dy * new_w + dx) * ch + ci] = val.to_byte()
146	      }
147	    }
148	  }
149	  @types.Image::{
150	    width: new_w,
151	    height: new_h,
152	    channels: ch,
153	    data: Bytes::from_array(out),
154	  }
155	}
   	…

4 uncovered line(s) in src\process\transform\pyramid.mbt:

 51	pub fn pyr_up(img : @types.Image) -> @types.Image raise @types.LoadError {
 52	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
 53	    raise @types.LoadError::DecodeFailed("pyr_up 仅支持 channels=1/3/4")	<-- UNCOVERED
 54	  }
 55	  let ch = img.channels
 56	  let w = img.width
 57	  let h = img.height
 58	  let new_w = w * 2
 59	  let new_h = h * 2
 60	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
 61	  for i in 0..<(w * h * ch) {
 62	    data_int[i] = img.data[i].to_int()
 63	  }
 64	  // 预计算 x 方向插值参数
 65	  let x0_arr : Array[Int] = Array::make(new_w, 0)
 66	  let x1_arr : Array[Int] = Array::make(new_w, 0)
 67	  let wx_arr : Array[Float] = Array::make(new_w, 0.0F)
 68	  for x in 0..<new_w {
 69	    let fx = (Float::from_int(x) - 0.5F) / 2.0F
 70	    let x0 = fx.to_int().clamp(min=0, max=w - 1)
 71	    x0_arr[x] = x0
 72	    x1_arr[x] = (x0 + 1).clamp(min=0, max=w - 1)
 73	    wx_arr[x] = fx - Float::from_int(x0)
 74	  }
 75	  // 预计算 y 方向插值参数
 76	  let y0_arr : Array[Int] = Array::make(new_h, 0)
 77	  let y1_arr : Array[Int] = Array::make(new_h, 0)
 78	  let wy_arr : Array[Float] = Array::make(new_h, 0.0F)
 79	  for y in 0..<new_h {
 80	    let fy = (Float::from_int(y) - 0.5F) / 2.0F
 81	    let y0 = fy.to_int().clamp(min=0, max=h - 1)
 82	    y0_arr[y] = y0
 83	    y1_arr[y] = (y0 + 1).clamp(min=0, max=h - 1)
 84	    wy_arr[y] = fy - Float::from_int(y0)
 85	  }
 86	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
 87	  for y in 0..<new_h {
 88	    let y0 = y0_arr[y]
 89	    let y1 = y1_arr[y]
 90	    let wy = wy_arr[y]
 91	    let wy_inv = 1.0F - wy
 92	    let row0 = y0 * w
 93	    let row1 = y1 * w
 94	    for x in 0..<new_w {
 95	      let x0 = x0_arr[x]
 96	      let x1 = x1_arr[x]
 97	      let wx = wx_arr[x]
 98	      let wx_inv = 1.0F - wx
 99	      for c in 0..<ch {
100	        let v00 = Float::from_int(data_int[(row0 + x0) * ch + c])
101	        let v01 = Float::from_int(data_int[(row0 + x1) * ch + c])
102	        let v10 = Float::from_int(data_int[(row1 + x0) * ch + c])
103	        let v11 = Float::from_int(data_int[(row1 + x1) * ch + c])
104	        let top = v00 * wx_inv + v01 * wx
105	        let bot = v10 * wx_inv + v11 * wx
106	        let v = top * wy_inv + bot * wy
107	        let clamped = if v < 0.0F {
108	          0	<-- UNCOVERED
109	        } else if v > 255.0F {
110	          255	<-- UNCOVERED
111	        } else {
112	          v.to_int()
113	        }
114	        out[(y * new_w + x) * ch + c] = clamped.to_byte()
115	      }
116	    }
117	  }
118	  @types.Image::{
119	    width: new_w,
120	    height: new_h,
121	    channels: ch,
122	    data: Bytes::from_array(out),
123	  }
124	}
   	…

164	fn laplacian_diff(a : @types.Image, b : @types.Image) -> @types.Image {
165	  let ch = a.channels
166	  let w = a.width
167	  let h = a.height
168	  let a_int : Array[Int] = Array::make(w * h * ch, 0)
169	  for i in 0..<(w * h * ch) {
170	    a_int[i] = a.data[i].to_int()
171	  }
172	  let bw = b.width
173	  let bh = b.height
174	  let b_int : Array[Int] = Array::make(bw * bh * ch, 0)
175	  for i in 0..<(bw * bh * ch) {
176	    b_int[i] = b.data[i].to_int()
177	  }
178	  let out : Array[Byte] = Array::make(w * h * ch, b'\x00')
179	  for y in 0..<h {
180	    let by = y * bh / h
181	    for x in 0..<w {
182	      let bx = x * bw / w
183	      for c in 0..<ch {
184	        let va = a_int[(y * w + x) * ch + c]
185	        let vb = b_int[(by * bw + bx) * ch + c]
186	        let diff = va - vb + 128
187	        let clamped = if diff < 0 { 0 } else if diff > 255 { 255 } else { diff }	<-- UNCOVERED
188	        out[(y * w + x) * ch + c] = clamped.to_byte()
189	      }
190	    }
191	  }
192	  @types.Image::{
193	    width: w,
194	    height: h,
195	    channels: ch,
196	    data: Bytes::from_array(out),
197	  }
198	}
   	…

13 uncovered line(s) in src\process\transform\pyramid_16f.mbt:

 5	fn clamp_i_p(v : Int, lo : Int, hi : Int) -> Int {
 6	  if v < lo {
 7	    lo	<-- UNCOVERED
 8	  } else if v > hi {
 9	    hi
10	  } else {
11	    v
12	  }
13	}
  	…

49	pub fn pyr_down_16(
50	  img : @types.Image16,
51	) -> @types.Image16 raise @types.LoadError {
52	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
53	    raise @types.LoadError::DecodeFailed("pyr_down_16 仅支持 channels=1/3/4")	<-- UNCOVERED
54	  }
55	  let ch = img.channels
56	  let w = img.width
57	  let h = img.height
58	  let new_w = w / 2
59	  let new_h = h / 2
60	  if new_w == 0 || new_h == 0 {
61	    raise @types.LoadError::DecodeFailed("图像太小，无法下采样")	<-- UNCOVERED
62	  }
63	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
64	  for i in 0..<(w * h * ch) {
65	    data_int[i] = read_u16_p(img.data, i * 2)
66	  }
67	  let out : Array[Byte] = Array::make(new_w * new_h * ch * 2, b'\x00')
68	  for y in 0..<new_h {
69	    for x in 0..<new_w {
70	      for c in 0..<ch {
71	        let mut sum = 0
72	        let mut count = 0
73	        for dy in 0..<2 {
74	          for dx in 0..<2 {
75	            let sx = x * 2 + dx
76	            let sy = y * 2 + dy
77	            if sx < w && sy < h {
78	              sum += data_int[(sy * w + sx) * ch + c]
79	              count += 1
80	            }
81	          }
82	        }
83	        write_u16_p(out, ((y * new_w + x) * ch + c) * 2, sum / count)
84	      }
85	    }
86	  }
87	  @types.Image16::{
88	    width: new_w,
89	    height: new_h,
90	    channels: ch,
91	    data: Bytes::from_array(out),
92	  }
93	}
  	…

 99	pub fn pyr_down_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
100	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
101	    raise @types.LoadError::DecodeFailed("pyr_down_f 仅支持 channels=1/3/4")	<-- UNCOVERED
102	  }
103	  let ch = img.channels
104	  let w = img.width
105	  let h = img.height
106	  let new_w = w / 2
107	  let new_h = h / 2
108	  if new_w == 0 || new_h == 0 {
109	    raise @types.LoadError::DecodeFailed("图像太小，无法下采样")	<-- UNCOVERED
110	  }
111	  let data_f : Array[Float] = Array::make(w * h * ch, 0.0F)
112	  for i in 0..<(w * h * ch) {
113	    data_f[i] = read_f32_p(img.data, i * 4)
114	  }
115	  let out : Array[Byte] = Array::make(new_w * new_h * ch * 4, b'\x00')
116	  for y in 0..<new_h {
117	    for x in 0..<new_w {
118	      for c in 0..<ch {
119	        let mut sum = 0.0F
120	        let mut count = 0
121	        for dy in 0..<2 {
122	          for dx in 0..<2 {
123	            let sx = x * 2 + dx
124	            let sy = y * 2 + dy
125	            if sx < w && sy < h {
126	              sum += data_f[(sy * w + sx) * ch + c]
127	              count += 1
128	            }
129	          }
130	        }
131	        write_f32_p(
132	          out,
133	          ((y * new_w + x) * ch + c) * 4,
134	          sum / Float::from_int(count),
135	        )
136	      }
137	    }
138	  }
139	  @types.ImageF::{
140	    width: new_w,
141	    height: new_h,
142	    channels: ch,
143	    data: Bytes::from_array(out),
144	  }
145	}
   	…

151	pub fn pyr_up_16(img : @types.Image16) -> @types.Image16 raise @types.LoadError {
152	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
153	    raise @types.LoadError::DecodeFailed("pyr_up_16 仅支持 channels=1/3/4")	<-- UNCOVERED
154	  }
155	  let ch = img.channels
156	  let w = img.width
157	  let h = img.height
158	  let new_w = w * 2
159	  let new_h = h * 2
160	  let data_int : Array[Int] = Array::make(w * h * ch, 0)
161	  for i in 0..<(w * h * ch) {
162	    data_int[i] = read_u16_p(img.data, i * 2)
163	  }
164	  let x0_arr : Array[Int] = Array::make(new_w, 0)
165	  let x1_arr : Array[Int] = Array::make(new_w, 0)
166	  let wx_arr : Array[Float] = Array::make(new_w, 0.0F)
167	  for x in 0..<new_w {
168	    let fx = (Float::from_int(x) - 0.5F) / 2.0F
169	    let x0 = clamp_i_p(fx.to_int(), 0, w - 1)
170	    x0_arr[x] = x0
171	    x1_arr[x] = clamp_i_p(x0 + 1, 0, w - 1)
172	    wx_arr[x] = fx - Float::from_int(x0)
173	  }
174	  let y0_arr : Array[Int] = Array::make(new_h, 0)
175	  let y1_arr : Array[Int] = Array::make(new_h, 0)
176	  let wy_arr : Array[Float] = Array::make(new_h, 0.0F)
177	  for y in 0..<new_h {
178	    let fy = (Float::from_int(y) - 0.5F) / 2.0F
179	    let y0 = clamp_i_p(fy.to_int(), 0, h - 1)
180	    y0_arr[y] = y0
181	    y1_arr[y] = clamp_i_p(y0 + 1, 0, h - 1)
182	    wy_arr[y] = fy - Float::from_int(y0)
183	  }
184	  let out : Array[Byte] = Array::make(new_w * new_h * ch * 2, b'\x00')
185	  for y in 0..<new_h {
186	    let y0 = y0_arr[y]
187	    let y1 = y1_arr[y]
188	    let wy = wy_arr[y]
189	    let wy_inv = 1.0F - wy
190	    let row0 = y0 * w
191	    let row1 = y1 * w
192	    for x in 0..<new_w {
193	      let x0 = x0_arr[x]
194	      let x1 = x1_arr[x]
195	      let wx = wx_arr[x]
196	      let wx_inv = 1.0F - wx
197	      for c in 0..<ch {
198	        let v00 = Float::from_int(data_int[(row0 + x0) * ch + c])
199	        let v01 = Float::from_int(data_int[(row0 + x1) * ch + c])
200	        let v10 = Float::from_int(data_int[(row1 + x0) * ch + c])
201	        let v11 = Float::from_int(data_int[(row1 + x1) * ch + c])
202	        let top = v00 * wx_inv + v01 * wx
203	        let bot = v10 * wx_inv + v11 * wx
204	        let v = top * wy_inv + bot * wy
205	        let clamped = if v < 0.0F {
206	          0	<-- UNCOVERED
207	        } else if v > 65535.0F {
208	          65535	<-- UNCOVERED
209	        } else {
210	          (v + 0.5F).to_int()
211	        }
212	        write_u16_p(out, ((y * new_w + x) * ch + c) * 2, clamped)
213	      }
214	    }
215	  }
216	  @types.Image16::{
217	    width: new_w,
218	    height: new_h,
219	    channels: ch,
220	    data: Bytes::from_array(out),
221	  }
222	}
   	…

228	pub fn pyr_up_f(img : @types.ImageF) -> @types.ImageF raise @types.LoadError {
229	  if img.channels != 1 && img.channels != 3 && img.channels != 4 {
230	    raise @types.LoadError::DecodeFailed("pyr_up_f 仅支持 channels=1/3/4")	<-- UNCOVERED
231	  }
232	  let ch = img.channels
233	  let w = img.width
234	  let h = img.height
235	  let new_w = w * 2
236	  let new_h = h * 2
237	  let data_f : Array[Float] = Array::make(w * h * ch, 0.0F)
238	  for i in 0..<(w * h * ch) {
239	    data_f[i] = read_f32_p(img.data, i * 4)
240	  }
241	  let x0_arr : Array[Int] = Array::make(new_w, 0)
242	  let x1_arr : Array[Int] = Array::make(new_w, 0)
243	  let wx_arr : Array[Float] = Array::make(new_w, 0.0F)
244	  for x in 0..<new_w {
245	    let fx = (Float::from_int(x) - 0.5F) / 2.0F
246	    let x0 = clamp_i_p(fx.to_int(), 0, w - 1)
247	    x0_arr[x] = x0
248	    x1_arr[x] = clamp_i_p(x0 + 1, 0, w - 1)
249	    wx_arr[x] = fx - Float::from_int(x0)
250	  }
251	  let y0_arr : Array[Int] = Array::make(new_h, 0)
252	  let y1_arr : Array[Int] = Array::make(new_h, 0)
253	  let wy_arr : Array[Float] = Array::make(new_h, 0.0F)
254	  for y in 0..<new_h {
255	    let fy = (Float::from_int(y) - 0.5F) / 2.0F
256	    let y0 = clamp_i_p(fy.to_int(), 0, h - 1)
257	    y0_arr[y] = y0
258	    y1_arr[y] = clamp_i_p(y0 + 1, 0, h - 1)
259	    wy_arr[y] = fy - Float::from_int(y0)
260	  }
261	  let out : Array[Byte] = Array::make(new_w * new_h * ch * 4, b'\x00')
262	  for y in 0..<new_h {
263	    let y0 = y0_arr[y]
264	    let y1 = y1_arr[y]
265	    let wy = wy_arr[y]
266	    let wy_inv = 1.0F - wy
267	    let row0 = y0 * w
268	    let row1 = y1 * w
269	    for x in 0..<new_w {
270	      let x0 = x0_arr[x]
271	      let x1 = x1_arr[x]
272	      let wx = wx_arr[x]
273	      let wx_inv = 1.0F - wx
274	      for c in 0..<ch {
275	        let v00 = data_f[(row0 + x0) * ch + c]
276	        let v01 = data_f[(row0 + x1) * ch + c]
277	        let v10 = data_f[(row1 + x0) * ch + c]
278	        let v11 = data_f[(row1 + x1) * ch + c]
279	        let top = v00 * wx_inv + v01 * wx
280	        let bot = v10 * wx_inv + v11 * wx
281	        let v = top * wy_inv + bot * wy
282	        write_f32_p(out, ((y * new_w + x) * ch + c) * 4, v)
283	      }
284	    }
285	  }
286	  @types.ImageF::{
287	    width: new_w,
288	    height: new_h,
289	    channels: ch,
290	    data: Bytes::from_array(out),
291	  }
292	}
   	…

297	fn laplacian_diff_p16(a : @types.Image16, b : @types.Image16) -> @types.Image16 {
298	  let ch = a.channels
299	  let w = a.width
300	  let h = a.height
301	  let a_int : Array[Int] = Array::make(w * h * ch, 0)
302	  for i in 0..<(w * h * ch) {
303	    a_int[i] = read_u16_p(a.data, i * 2)
304	  }
305	  let bw = b.width
306	  let bh = b.height
307	  let b_int : Array[Int] = Array::make(bw * bh * ch, 0)
308	  for i in 0..<(bw * bh * ch) {
309	    b_int[i] = read_u16_p(b.data, i * 2)
310	  }
311	  let out : Array[Byte] = Array::make(w * h * ch * 2, b'\x00')
312	  for y in 0..<h {
313	    let by = y * bh / h
314	    for x in 0..<w {
315	      let bx = x * bw / w
316	      for c in 0..<ch {
317	        let va = a_int[(y * w + x) * ch + c]
318	        let vb = b_int[(by * bw + bx) * ch + c]
319	        let diff = va - vb + 32768
320	        let clamped = if diff < 0 {
321	          0	<-- UNCOVERED
322	        } else if diff > 65535 {
323	          65535	<-- UNCOVERED
324	        } else {
325	          diff
326	        }
327	        write_u16_p(out, ((y * w + x) * ch + c) * 2, clamped)
328	      }
329	    }
330	  }
331	  @types.Image16::{
332	    width: w,
333	    height: h,
334	    channels: ch,
335	    data: Bytes::from_array(out),
336	  }
337	}
   	…

381	pub fn build_gaussian_pyramid_16(
382	  img : @types.Image16,
383	  levels : Int,
384	) -> Array[@types.Image16] raise @types.LoadError {
385	  let result : Array[@types.Image16] = [img]
386	  let mut current = img
387	  let max_levels = if levels == 0 { 100 } else { levels }
388	  for _ in 1..<max_levels {
389	    if current.width < 2 || current.height < 2 {
390	      break	<-- UNCOVERED
391	    }
392	    current = pyr_down_16(current)
393	    result.push(current)
394	  }
395	  result
396	}
   	…

402	pub fn build_gaussian_pyramid_f(
403	  img : @types.ImageF,
404	  levels : Int,
405	) -> Array[@types.ImageF] raise @types.LoadError {
406	  let result : Array[@types.ImageF] = [img]
407	  let mut current = img
408	  let max_levels = if levels == 0 { 100 } else { levels }
409	  for _ in 1..<max_levels {
410	    if current.width < 2 || current.height < 2 {
411	      break	<-- UNCOVERED
412	    }
413	    current = pyr_down_f(current)
414	    result.push(current)
415	  }
416	  result
417	}
   	…

4 uncovered line(s) in src\process\transform\resize_cover.mbt:

 7	pub fn resize_to_cover(
 8	  img : @types.Image,
 9	  target_w : Int,
10	  target_h : Int,
11	) -> @types.Image raise @types.LoadError {
12	  if target_w <= 0 || target_h <= 0 {
13	    raise @types.LoadError::DecodeFailed("目标尺寸必须为正数")	<-- UNCOVERED
14	  }
15	  let scale_w = Float::from_int(target_w) / Float::from_int(img.width)
16	  let scale_h = Float::from_int(target_h) / Float::from_int(img.height)
17	  let scale = if scale_w > scale_h { scale_w } else { scale_h }
18	  let scaled_w = (Float::from_int(img.width) * scale).to_int()
19	  let scaled_h = (Float::from_int(img.height) * scale).to_int()
20	  let scaled = @pure_util.resize_pure(img, scaled_w, scaled_h)
21	  let x = (scaled_w - target_w) / 2
22	  let y = (scaled_h - target_h) / 2
23	  crop(scaled, x, y, target_w, target_h)
24	}
  	…

28	pub fn resize_to_contain(
29	  img : @types.Image,
30	  target_w : Int,
31	  target_h : Int,
32	  fill_color : Array[Byte],
33	) -> @types.Image raise @types.LoadError {
34	  if target_w <= 0 || target_h <= 0 {
35	    raise @types.LoadError::DecodeFailed("目标尺寸必须为正数")	<-- UNCOVERED
36	  }
37	  let scale_w = Float::from_int(target_w) / Float::from_int(img.width)
38	  let scale_h = Float::from_int(target_h) / Float::from_int(img.height)
39	  let scale = if scale_w < scale_h { scale_w } else { scale_h }
40	  let scaled_w = (Float::from_int(img.width) * scale).to_int()
41	  let scaled_h = (Float::from_int(img.height) * scale).to_int()
42	  let scaled = @pure_util.resize_pure(img, scaled_w, scaled_h)
43	  let px = (target_w - scaled_w) / 2
44	  let py = (target_h - scaled_h) / 2
45	  pad_image(scaled, px, py, fill_color)
46	}
  	…

50	fn pad_image(
51	  img : @types.Image,
52	  px : Int,
53	  py : Int,
54	  pad_color : Array[Byte],
55	) -> @types.Image raise @types.LoadError {
56	  if px < 0 || py < 0 {
57	    raise @types.LoadError::DecodeFailed("pad 填充必须非负")	<-- UNCOVERED
58	  }
59	  let ch = img.channels
60	  let new_w = img.width + 2 * px
61	  let new_h = img.height + 2 * py
62	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
63	  let color_len = pad_color.length()
64	  if color_len == 0 {
65	    raise @types.LoadError::DecodeFailed("pad_color 不能为空")	<-- UNCOVERED
66	  }
67	  // 填充
68	  for y in 0..<new_h {
69	    for x in 0..<new_w {
70	      let dst_off = (y * new_w + x) * ch
71	      if x >= px && x < px + img.width && y >= py && y < py + img.height {
72	        // 拷贝原始像素
73	        let src_off = ((y - py) * img.width + (x - px)) * ch
74	        for c in 0..<ch {
75	          out[dst_off + c] = img.data[src_off + c]
76	        }
77	      } else {
78	        // 填充颜色
79	        for c in 0..<ch {
80	          out[dst_off + c] = pad_color[c % color_len]
81	        }
82	      }
83	    }
84	  }
85	  @types.Image::{
86	    width: new_w,
87	    height: new_h,
88	    channels: ch,
89	    data: Bytes::from_array(out),
90	  }
91	}
  	…

12 uncovered line(s) in src\process\transform\seam_carving.mbt:

37	pub fn find_vertical_seam(
38	  energy : Array[Double],
39	  w : Int,
40	  h : Int,
41	) -> Array[Int] {
42	  let m = Array::make(w * h, 0.0)
43	  for j in 0..<w {
44	    m[j] = energy[j]
45	  }
46	  for i in 1..<h {
47	    for j in 0..<w {
48	      let mut min_prev = m[(i - 1) * w + j]
49	      if j > 0 {
50	        let v = m[(i - 1) * w + j - 1]
51	        if v < min_prev {
52	          min_prev = v
53	        }
54	      }
55	      if j < w - 1 {
56	        let v = m[(i - 1) * w + j + 1]
57	        if v < min_prev {
58	          min_prev = v
59	        }
60	      }
61	      m[i * w + j] = energy[i * w + j] + min_prev
62	    }
63	  }
64	  let seam = Array::make(h, 0)
65	  let mut min_val = m[(h - 1) * w]
66	  let mut min_j = 0
67	  for j in 1..<w {
68	    let v = m[(h - 1) * w + j]
69	    if v < min_val {
70	      min_val = v	<-- UNCOVERED
71	      min_j = j
72	    }
73	  }
74	  seam[h - 1] = min_j
75	  for i in 1..<h {
76	    let row = h - i
77	    let prev_j = seam[row]
78	    let mut best_j = prev_j
79	    let mut best_val = m[(row - 1) * w + prev_j]
80	    if prev_j > 0 {
81	      let v = m[(row - 1) * w + prev_j - 1]	<-- UNCOVERED
82	      if v < best_val {
83	        best_val = v	<-- UNCOVERED
84	        best_j = prev_j - 1
85	      }
86	    }
87	    if prev_j < w - 1 {
88	      let v = m[(row - 1) * w + prev_j + 1]
89	      if v < best_val {
90	        best_val = v	<-- UNCOVERED
91	        best_j = prev_j + 1
92	      }
93	    }
94	    seam[row - 1] = best_j
95	  }
96	  seam
97	}
  	…

101	pub fn remove_vertical_seam(
102	  img : @types.Image,
103	  seam : Array[Int],
104	) -> @types.Image {
105	  let w = img.width
106	  let h = img.height
107	  let ch = img.channels
108	  let new_w = w - 1
109	  let out : Array[Byte] = Array::make(new_w * h * ch, b'\x00')
110	  for y in 0..<h {
111	    let seam_x = seam[y]
112	    let dst_row = y * new_w * ch
113	    let src_row = y * w * ch
114	    for x in 0..<seam_x {
115	      for c in 0..<ch {	<-- UNCOVERED
116	        out[dst_row + x * ch + c] = img.data[src_row + x * ch + c]
117	      }
118	    }
119	    for x in seam_x..<new_w {
120	      for c in 0..<ch {
121	        out[dst_row + x * ch + c] = img.data[src_row + (x + 1) * ch + c]
122	      }
123	    }
124	  }
125	  @types.Image::{
126	    width: new_w,
127	    height: h,
128	    channels: ch,
129	    data: Bytes::from_array(out),
130	  }
131	}
   	…

135	pub fn find_horizontal_seam(
136	  energy : Array[Double],
137	  w : Int,
138	  h : Int,
139	) -> Array[Int] {
140	  let m = Array::make(w * h, 0.0)
141	  for i in 0..<h {
142	    for j in 0..<w {
143	      m[i * w + j] = energy[i * w + j]
144	    }
145	  }
146	  for j in 1..<w {
147	    for i in 0..<h {
148	      let mut min_prev = m[i * w + j - 1]
149	      if i > 0 {
150	        let v = m[(i - 1) * w + j - 1]
151	        if v < min_prev {
152	          min_prev = v
153	        }
154	      }
155	      if i < h - 1 {
156	        let v = m[(i + 1) * w + j - 1]
157	        if v < min_prev {
158	          min_prev = v
159	        }
160	      }
161	      m[i * w + j] = energy[i * w + j] + min_prev
162	    }
163	  }
164	  let seam = Array::make(w, 0)
165	  let mut min_val = m[w - 1]
166	  let mut min_i = 0
167	  for i in 1..<h {
168	    let v = m[i * w + w - 1]
169	    if v < min_val {
170	      min_val = v	<-- UNCOVERED
171	      min_i = i
172	    }
173	  }
174	  seam[w - 1] = min_i
175	  for j in 1..<w {
176	    let col = w - j
177	    let prev_i = seam[col]
178	    let mut best_i = prev_i
179	    let mut best_val = m[prev_i * w + col - 1]
180	    if prev_i > 0 {
181	      let v = m[(prev_i - 1) * w + col - 1]	<-- UNCOVERED
182	      if v < best_val {
183	        best_val = v	<-- UNCOVERED
184	        best_i = prev_i - 1
185	      }
186	    }
187	    if prev_i < h - 1 {
188	      let v = m[(prev_i + 1) * w + col - 1]
189	      if v < best_val {
190	        best_val = v	<-- UNCOVERED
191	        best_i = prev_i + 1
192	      }
193	    }
194	    seam[col - 1] = best_i
195	  }
196	  seam
197	}
   	…

201	pub fn remove_horizontal_seam(
202	  img : @types.Image,
203	  seam : Array[Int],
204	) -> @types.Image {
205	  let w = img.width
206	  let h = img.height
207	  let ch = img.channels
208	  let new_h = h - 1
209	  let out : Array[Byte] = Array::make(w * new_h * ch, b'\x00')
210	  for x in 0..<w {
211	    let seam_y = seam[x]
212	    for y in 0..<seam_y {
213	      for c in 0..<ch {	<-- UNCOVERED
214	        out[(y * w + x) * ch + c] = img.data[(y * w + x) * ch + c]
215	      }
216	    }
217	    for y in seam_y..<new_h {
218	      for c in 0..<ch {
219	        out[(y * w + x) * ch + c] = img.data[((y + 1) * w + x) * ch + c]
220	      }
221	    }
222	  }
223	  @types.Image::{
224	    width: w,
225	    height: new_h,
226	    channels: ch,
227	    data: Bytes::from_array(out),
228	  }
229	}
   	…

233	fn insert_vertical_seam(img : @types.Image, seam : Array[Int]) -> @types.Image {
234	  let w = img.width
235	  let h = img.height
236	  let ch = img.channels
237	  let new_w = w + 1
238	  let out : Array[Byte] = Array::make(new_w * h * ch, b'\x00')
239	  for y in 0..<h {
240	    let seam_x = seam[y]
241	    let dst_row = y * new_w * ch
242	    let src_row = y * w * ch
243	    for x in 0..<seam_x {
244	      for c in 0..<ch {	<-- UNCOVERED
245	        out[dst_row + x * ch + c] = img.data[src_row + x * ch + c]
246	      }
247	    }
248	    for c in 0..<ch {
249	      out[dst_row + seam_x * ch + c] = img.data[src_row + seam_x * ch + c]
250	    }
251	    for x in seam_x..<w {
252	      for c in 0..<ch {
253	        out[dst_row + (x + 1) * ch + c] = img.data[src_row + x * ch + c]
254	      }
255	    }
256	  }
257	  @types.Image::{
258	    width: new_w,
259	    height: h,
260	    channels: ch,
261	    data: Bytes::from_array(out),
262	  }
263	}
   	…

267	fn insert_horizontal_seam(
268	  img : @types.Image,
269	  seam : Array[Int],
270	) -> @types.Image {
271	  let w = img.width
272	  let h = img.height
273	  let ch = img.channels
274	  let new_h = h + 1
275	  let out : Array[Byte] = Array::make(w * new_h * ch, b'\x00')
276	  for x in 0..<w {
277	    let seam_y = seam[x]
278	    for y in 0..<seam_y {
279	      for c in 0..<ch {	<-- UNCOVERED
280	        out[(y * w + x) * ch + c] = img.data[(y * w + x) * ch + c]
281	      }
282	    }
283	    for c in 0..<ch {
284	      out[(seam_y * w + x) * ch + c] = img.data[(seam_y * w + x) * ch + c]
285	    }
286	    for y in seam_y..<h {
287	      for c in 0..<ch {
288	        out[((y + 1) * w + x) * ch + c] = img.data[(y * w + x) * ch + c]
289	      }
290	    }
291	  }
292	  @types.Image::{
293	    width: w,
294	    height: new_h,
295	    channels: ch,
296	    data: Bytes::from_array(out),
297	  }
298	}
   	…

40 uncovered line(s) in src\process\transform\seam_carving_16f.mbt:

 87	pub fn remove_vertical_seam_16(
 88	  img : @types.Image16,
 89	  seam : Array[Int],
 90	) -> @types.Image16 {
 91	  let w = img.width
 92	  let h = img.height
 93	  let ch = img.channels
 94	  let new_w = w - 1
 95	  let out : Array[Byte] = Array::make(new_w * h * ch * 2, b'\x00')
 96	  for y in 0..<h {
 97	    let seam_x = seam[y]
 98	    for x in 0..<seam_x {
 99	      for c in 0..<ch {	<-- UNCOVERED
100	        out[((y * new_w + x) * ch + c) * 2] = img.data[((y * w + x) * ch + c) *
101	          2]
102	        out[((y * new_w + x) * ch + c) * 2 + 1] = img.data[(
103	            (y * w + x) * ch + c
104	          ) *
105	          2 +
106	          1]
107	      }
108	    }
109	    for x in seam_x..<new_w {
110	      for c in 0..<ch {
111	        out[((y * new_w + x) * ch + c) * 2] = img.data[(
112	            (y * w + x + 1) * ch + c
113	          ) *
114	          2]
115	        out[((y * new_w + x) * ch + c) * 2 + 1] = img.data[(
116	            (y * w + x + 1) * ch + c
117	          ) *
118	          2 +
119	          1]
120	      }
121	    }
122	  }
123	  @types.Image16::{
124	    width: new_w,
125	    height: h,
126	    channels: ch,
127	    data: Bytes::from_array(out),
128	  }
129	}
   	…

133	pub fn remove_vertical_seam_f(
134	  img : @types.ImageF,
135	  seam : Array[Int],
136	) -> @types.ImageF {
137	  let w = img.width
138	  let h = img.height
139	  let ch = img.channels
140	  let new_w = w - 1
141	  let out : Array[Byte] = Array::make(new_w * h * ch * 4, b'\x00')
142	  for y in 0..<h {
143	    let seam_x = seam[y]
144	    for x in 0..<seam_x {
145	      for c in 0..<ch {	<-- UNCOVERED
146	        for b in 0..<4 {
147	          out[((y * new_w + x) * ch + c) * 4 + b] = img.data[(
148	              (y * w + x) * ch + c
149	            ) *
150	            4 +
151	            b]
152	        }
153	      }
154	    }
155	    for x in seam_x..<new_w {
156	      for c in 0..<ch {
157	        for b in 0..<4 {
158	          out[((y * new_w + x) * ch + c) * 4 + b] = img.data[(
159	              (y * w + x + 1) * ch + c
160	            ) *
161	            4 +
162	            b]
163	        }
164	      }
165	    }
166	  }
167	  @types.ImageF::{
168	    width: new_w,
169	    height: h,
170	    channels: ch,
171	    data: Bytes::from_array(out),
172	  }
173	}
   	…

177	pub fn remove_horizontal_seam_16(
178	  img : @types.Image16,
179	  seam : Array[Int],
180	) -> @types.Image16 {
181	  let w = img.width
182	  let h = img.height
183	  let ch = img.channels
184	  let new_h = h - 1
185	  let out : Array[Byte] = Array::make(w * new_h * ch * 2, b'\x00')
186	  for x in 0..<w {
187	    let seam_y = seam[x]
188	    for y in 0..<seam_y {
189	      for c in 0..<ch {	<-- UNCOVERED
190	        out[((y * w + x) * ch + c) * 2] = img.data[((y * w + x) * ch + c) * 2]
191	        out[((y * w + x) * ch + c) * 2 + 1] = img.data[((y * w + x) * ch + c) *
192	          2 +
193	          1]
194	      }
195	    }
196	    for y in seam_y..<new_h {
197	      for c in 0..<ch {
198	        out[((y * w + x) * ch + c) * 2] = img.data[(((y + 1) * w + x) * ch + c) *
199	          2]
200	        out[((y * w + x) * ch + c) * 2 + 1] = img.data[(
201	            ((y + 1) * w + x) * ch + c
202	          ) *
203	          2 +
204	          1]
205	      }
206	    }
207	  }
208	  @types.Image16::{
209	    width: w,
210	    height: new_h,
211	    channels: ch,
212	    data: Bytes::from_array(out),
213	  }
214	}
   	…

218	pub fn remove_horizontal_seam_f(
219	  img : @types.ImageF,
220	  seam : Array[Int],
221	) -> @types.ImageF {
222	  let w = img.width
223	  let h = img.height
224	  let ch = img.channels
225	  let new_h = h - 1
226	  let out : Array[Byte] = Array::make(w * new_h * ch * 4, b'\x00')
227	  for x in 0..<w {
228	    let seam_y = seam[x]
229	    for y in 0..<seam_y {
230	      for c in 0..<ch {	<-- UNCOVERED
231	        for b in 0..<4 {
232	          out[((y * w + x) * ch + c) * 4 + b] = img.data[((y * w + x) * ch + c) *
233	            4 +
234	            b]
235	        }
236	      }
237	    }
238	    for y in seam_y..<new_h {
239	      for c in 0..<ch {
240	        for b in 0..<4 {
241	          out[((y * w + x) * ch + c) * 4 + b] = img.data[(
242	              ((y + 1) * w + x) * ch + c
243	            ) *
244	            4 +
245	            b]
246	        }
247	      }
248	    }
249	  }
250	  @types.ImageF::{
251	    width: w,
252	    height: new_h,
253	    channels: ch,
254	    data: Bytes::from_array(out),
255	  }
256	}
   	…

260	fn insert_vertical_seam_16(
261	  img : @types.Image16,
262	  seam : Array[Int],
263	) -> @types.Image16 {
264	  let w = img.width	<-- UNCOVERED
265	  let h = img.height
266	  let ch = img.channels
267	  let new_w = w + 1
268	  let out : Array[Byte] = Array::make(new_w * h * ch * 2, b'\x00')	<-- UNCOVERED
269	  for y in 0..<h {
270	    let seam_x = seam[y]	<-- UNCOVERED
271	    for x in 0..<seam_x {
272	      for c in 0..<ch {	<-- UNCOVERED
273	        out[((y * new_w + x) * ch + c) * 2] = img.data[((y * w + x) * ch + c) *
274	          2]
275	        out[((y * new_w + x) * ch + c) * 2 + 1] = img.data[(
276	            (y * w + x) * ch + c
277	          ) *
278	          2 +
279	          1]
280	      }
281	    }
282	    for c in 0..<ch {
283	      out[((y * new_w + seam_x) * ch + c) * 2] = img.data[(	<-- UNCOVERED
284	          (y * w + seam_x) * ch + c
285	        ) *
286	        2]
287	      out[((y * new_w + seam_x) * ch + c) * 2 + 1] = img.data[(
288	          (y * w + seam_x) * ch + c
289	        ) *
290	        2 +
291	        1]
292	    }
293	    for x in seam_x..<w {
294	      for c in 0..<ch {	<-- UNCOVERED
295	        out[((y * new_w + x + 1) * ch + c) * 2] = img.data[(
296	            (y * w + x) * ch + c
297	          ) *
298	          2]
299	        out[((y * new_w + x + 1) * ch + c) * 2 + 1] = img.data[(
300	            (y * w + x) * ch + c
301	          ) *
302	          2 +
303	          1]
304	      }
305	    }
306	  }
307	  @types.Image16::{
308	    width: new_w,
309	    height: h,
310	    channels: ch,
311	    data: Bytes::from_array(out),	<-- UNCOVERED
312	  }
313	}
   	…

317	fn insert_vertical_seam_f(
318	  img : @types.ImageF,
319	  seam : Array[Int],
320	) -> @types.ImageF {
321	  let w = img.width	<-- UNCOVERED
322	  let h = img.height
323	  let ch = img.channels
324	  let new_w = w + 1
325	  let out : Array[Byte] = Array::make(new_w * h * ch * 4, b'\x00')	<-- UNCOVERED
326	  for y in 0..<h {
327	    let seam_x = seam[y]	<-- UNCOVERED
328	    for x in 0..<seam_x {
329	      for c in 0..<ch {	<-- UNCOVERED
330	        for b in 0..<4 {
331	          out[((y * new_w + x) * ch + c) * 4 + b] = img.data[(
332	              (y * w + x) * ch + c
333	            ) *
334	            4 +
335	            b]
336	        }
337	      }
338	    }
339	    for c in 0..<ch {
340	      for b in 0..<4 {	<-- UNCOVERED
341	        out[((y * new_w + seam_x) * ch + c) * 4 + b] = img.data[(
342	            (y * w + seam_x) * ch + c
343	          ) *
344	          4 +
345	          b]
346	      }
347	    }
348	    for x in seam_x..<w {
349	      for c in 0..<ch {	<-- UNCOVERED
350	        for b in 0..<4 {
351	          out[((y * new_w + x + 1) * ch + c) * 4 + b] = img.data[(
352	              (y * w + x) * ch + c
353	            ) *
354	            4 +
355	            b]
356	        }
357	      }
358	    }
359	  }
360	  @types.ImageF::{
361	    width: new_w,
362	    height: h,
363	    channels: ch,
364	    data: Bytes::from_array(out),	<-- UNCOVERED
365	  }
366	}
   	…

370	fn insert_horizontal_seam_16(
371	  img : @types.Image16,
372	  seam : Array[Int],
373	) -> @types.Image16 {
374	  let w = img.width	<-- UNCOVERED
375	  let h = img.height
376	  let ch = img.channels
377	  let new_h = h + 1
378	  let out : Array[Byte] = Array::make(w * new_h * ch * 2, b'\x00')	<-- UNCOVERED
379	  for x in 0..<w {
380	    let seam_y = seam[x]	<-- UNCOVERED
381	    for y in 0..<seam_y {
382	      for c in 0..<ch {	<-- UNCOVERED
383	        out[((y * w + x) * ch + c) * 2] = img.data[((y * w + x) * ch + c) * 2]
384	        out[((y * w + x) * ch + c) * 2 + 1] = img.data[((y * w + x) * ch + c) *
385	          2 +
386	          1]
387	      }
388	    }
389	    for c in 0..<ch {
390	      out[((seam_y * w + x) * ch + c) * 2] = img.data[(	<-- UNCOVERED
391	          (seam_y * w + x) * ch + c
392	        ) *
393	        2]
394	      out[((seam_y * w + x) * ch + c) * 2 + 1] = img.data[(
395	          (seam_y * w + x) * ch + c
396	        ) *
397	        2 +
398	        1]
399	    }
400	    for y in seam_y..<h {
401	      for c in 0..<ch {	<-- UNCOVERED
402	        out[(((y + 1) * w + x) * ch + c) * 2] = img.data[((y * w + x) * ch + c) *
403	          2]
404	        out[(((y + 1) * w + x) * ch + c) * 2 + 1] = img.data[(
405	            (y * w + x) * ch + c
406	          ) *
407	          2 +
408	          1]
409	      }
410	    }
411	  }
412	  @types.Image16::{
413	    width: w,
414	    height: new_h,
415	    channels: ch,
416	    data: Bytes::from_array(out),	<-- UNCOVERED
417	  }
418	}
   	…

422	fn insert_horizontal_seam_f(
423	  img : @types.ImageF,
424	  seam : Array[Int],
425	) -> @types.ImageF {
426	  let w = img.width	<-- UNCOVERED
427	  let h = img.height
428	  let ch = img.channels
429	  let new_h = h + 1
430	  let out : Array[Byte] = Array::make(w * new_h * ch * 4, b'\x00')	<-- UNCOVERED
431	  for x in 0..<w {
432	    let seam_y = seam[x]	<-- UNCOVERED
433	    for y in 0..<seam_y {
434	      for c in 0..<ch {	<-- UNCOVERED
435	        for b in 0..<4 {
436	          out[((y * w + x) * ch + c) * 4 + b] = img.data[((y * w + x) * ch + c) *
437	            4 +
438	            b]
439	        }
440	      }
441	    }
442	    for c in 0..<ch {
443	      for b in 0..<4 {	<-- UNCOVERED
444	        out[((seam_y * w + x) * ch + c) * 4 + b] = img.data[(
445	            (seam_y * w + x) * ch + c
446	          ) *
447	          4 +
448	          b]
449	      }
450	    }
451	    for y in seam_y..<h {
452	      for c in 0..<ch {	<-- UNCOVERED
453	        for b in 0..<4 {
454	          out[(((y + 1) * w + x) * ch + c) * 4 + b] = img.data[(
455	              (y * w + x) * ch + c
456	            ) *
457	            4 +
458	            b]
459	        }
460	      }
461	    }
462	  }
463	  @types.ImageF::{
464	    width: w,
465	    height: new_h,
466	    channels: ch,
467	    data: Bytes::from_array(out),	<-- UNCOVERED
468	  }
469	}
   	…

473	pub fn seam_carve_resize_16(
474	  img : @types.Image16,
475	  new_w : Int,
476	  new_h : Int,
477	) -> @types.Image16 raise @types.LoadError {
478	  if new_w <= 0 || new_h <= 0 {
479	    raise @types.LoadError::DecodeFailed("目标尺寸必须为正数")	<-- UNCOVERED
480	  }
481	  if new_w > img.width * 2 || new_h > img.height * 2 {
482	    raise @types.LoadError::DecodeFailed("放大不能超过原尺寸的 2 倍")	<-- UNCOVERED
483	  }
484	  let mut result = img
485	  while result.width > new_w {
486	    let energy = compute_energy_16(result)
487	    let seam = find_vertical_seam(energy, result.width, result.height)
488	    result = remove_vertical_seam_16(result, seam)
489	  }
490	  while result.width < new_w {
491	    let energy = compute_energy_16(result)	<-- UNCOVERED
492	    let seam = find_vertical_seam(energy, result.width, result.height)
493	    result = insert_vertical_seam_16(result, seam)
494	  }
495	  while result.height > new_h {
496	    let energy = compute_energy_16(result)
497	    let seam = find_horizontal_seam(energy, result.width, result.height)
498	    result = remove_horizontal_seam_16(result, seam)
499	  }
500	  while result.height < new_h {
501	    let energy = compute_energy_16(result)	<-- UNCOVERED
502	    let seam = find_horizontal_seam(energy, result.width, result.height)
503	    result = insert_horizontal_seam_16(result, seam)
504	  }
505	  result
506	}
   	…

510	pub fn seam_carve_resize_f(
511	  img : @types.ImageF,
512	  new_w : Int,
513	  new_h : Int,
514	) -> @types.ImageF raise @types.LoadError {
515	  if new_w <= 0 || new_h <= 0 {
516	    raise @types.LoadError::DecodeFailed("目标尺寸必须为正数")	<-- UNCOVERED
517	  }
518	  if new_w > img.width * 2 || new_h > img.height * 2 {
519	    raise @types.LoadError::DecodeFailed("放大不能超过原尺寸的 2 倍")	<-- UNCOVERED
520	  }
521	  let mut result = img
522	  while result.width > new_w {
523	    let energy = compute_energy_f(result)
524	    let seam = find_vertical_seam(energy, result.width, result.height)
525	    result = remove_vertical_seam_f(result, seam)
526	  }
527	  while result.width < new_w {
528	    let energy = compute_energy_f(result)	<-- UNCOVERED
529	    let seam = find_vertical_seam(energy, result.width, result.height)
530	    result = insert_vertical_seam_f(result, seam)
531	  }
532	  while result.height > new_h {
533	    let energy = compute_energy_f(result)
534	    let seam = find_horizontal_seam(energy, result.width, result.height)
535	    result = remove_horizontal_seam_f(result, seam)
536	  }
537	  while result.height < new_h {
538	    let energy = compute_energy_f(result)	<-- UNCOVERED
539	    let seam = find_horizontal_seam(energy, result.width, result.height)
540	    result = insert_horizontal_seam_f(result, seam)
541	  }
542	  result
543	}
   	…

6 uncovered line(s) in src\process\transform\transform.mbt:

197	pub fn crop_f(
198	  img : @types.ImageF,
199	  x : Int,
200	  y : Int,
201	  w : Int,
202	  h : Int,
203	) -> @types.ImageF raise @types.LoadError {
204	  if x < 0 ||	<-- UNCOVERED
205	    y < 0 ||
206	    w <= 0 ||
207	    h <= 0 ||
208	    x + w > img.width ||
209	    y + h > img.height {
210	    raise @types.LoadError::DecodeFailed("crop 区域越界")	<-- UNCOVERED
211	  }
212	  let ch = img.channels
213	  let out : Array[Byte] = Array::make(w * h * ch * 4, b'\x00')	<-- UNCOVERED
214	  for row in 0..<h {
215	    let src_off = ((y + row) * img.width + x) * ch * 4	<-- UNCOVERED
216	    let dst_off = row * w * ch * 4
217	    for i in 0..<(w * ch * 4) {
218	      out[dst_off + i] = img.data[src_off + i]	<-- UNCOVERED
219	    }
220	  }
221	  @types.ImageF::{
222	    width: w,
223	    height: h,
224	    channels: ch,
225	    data: Bytes::from_array(out),	<-- UNCOVERED
226	  }
227	}
   	…

4 uncovered line(s) in src\process\video\video_processing.mbt:

16	pub fn video_frames_new(
17	  frames : Array[@types.Image],
18	  fps? : Double = 30.0,
19	) -> VideoFrames {
20	  if frames.length() == 0 {
21	    return VideoFrames::{ frames: [], width: 0, height: 0, channels: 1, fps }	<-- UNCOVERED
22	  }
23	  let first = frames[0]
24	  VideoFrames::{
25	    frames,
26	    width: first.width,
27	    height: first.height,
28	    channels: first.channels,
29	    fps,
30	  }
31	}
  	…

52	pub fn interpolate_frames(
53	  frame1 : @types.Image,
54	  frame2 : @types.Image,
55	  num_frames? : Int = 1,
56	) -> Array[@types.Image] {
57	  let n = num_frames
58	  if frame1.width != frame2.width || frame1.height != frame2.height {
59	    return []	<-- UNCOVERED
60	  }
61	  let w = frame1.width
62	  let h = frame1.height
63	  let channels = frame1.channels.min(frame2.channels)
64	  let result : Array[@types.Image] = []
65	  for i in 1..<(n + 1) {
66	    let alpha = i.to_double() / (n + 1).to_double()
67	    let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
68	    for y in 0..<h {
69	      for x in 0..<w {
70	        let idx = (y * w + x) * channels
71	        for c in 0..<channels {
72	          let v1 = frame1.data[idx + c].to_double()
73	          let v2 = frame2.data[idx + c].to_double()
74	          let interpolated = v1 * (1.0 - alpha) + v2 * alpha
75	          out_data[idx + c] = interpolated
76	            .min(255.0)
77	            .max(0.0)
78	            .to_int()
79	            .to_byte()
80	        }
81	      }
82	    }
83	    result.push(@types.Image::{
84	      width: w,
85	      height: h,
86	      channels,
87	      data: Bytes::from_array(out_data),
88	    })
89	  }
90	  result
91	}
  	…

254	pub fn video_frame_average(video : VideoFrames) -> @types.Image {
255	  let frames = video.frames
256	  if frames.length() == 0 {
257	    return @types.Image::{	<-- UNCOVERED
258	      width: 1,
259	      height: 1,
260	      channels: 1,
261	      data: Bytes::from_array([b'\x00']),	<-- UNCOVERED
262	    }
263	  }
264	  let w = video.width
265	  let h = video.height
266	  let channels = video.channels
267	  let n = frames.length().to_double()
268	  let sum : Array[Double] = Array::make(w * h * channels, 0.0)
269	  for frame in frames {
270	    for i in 0..<(w * h * channels) {
271	      sum[i] += frame.data[i].to_double()
272	    }
273	  }
274	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
275	  for i in 0..<(w * h * channels) {
276	    out_data[i] = (sum[i] / n).min(255.0).max(0.0).to_int().to_byte()
277	  }
278	  @types.Image::{
279	    width: w,
280	    height: h,
281	    channels,
282	    data: Bytes::from_array(out_data),
283	  }
284	}
   	…

16 uncovered line(s) in src\process\video\video_tracking.mbt:

142	pub fn compute_frame_difference(
143	  frame1 : @types.Image,
144	  frame2 : @types.Image,
145	  threshold? : Double = 30.0,
146	) -> Double {
147	  let w = frame1.width
148	  let h = frame1.height
149	  let mut motion_pixels = 0
150	  let total = w * h
151	
152	  for y in 0..<h {
153	    for x in 0..<w {
154	      let idx = y * w + x
155	      let p1 = frame1.data[idx].to_int()
156	      let p2 = frame2.data[idx].to_int()
157	      let diff = (p1 - p2).abs().to_double()
158	      if diff > threshold {
159	        motion_pixels += 1
160	      }
161	    }
162	  }
163	
164	  if total > 0 {
165	    motion_pixels.to_double() / total.to_double()
166	  } else {
167	    0.0	<-- UNCOVERED
168	  }
169	}
   	…

173	pub fn compute_optical_flow(
174	  frame1 : @types.Image,
175	  frame2 : @types.Image,
176	  block_size? : Int = 8,
177	  search_range? : Int = 4,
178	) -> OpticalFlowField {
179	  let w = frame1.width
180	  let h = frame1.height
181	  let vectors : Array[OpticalFlowVector] = []
182	  let mut total_magnitude = 0.0
183	  let mut max_mag = 0.0
184	  let mut sum_dx = 0.0
185	  let mut sum_dy = 0.0
186	  let mut count = 0
187	
188	  for by in 0..<(h / block_size) {
189	    for bx in 0..<(w / block_size) {
190	      let mut best_dx = 0
191	      let mut best_dy = 0
192	      let mut best_sad = 1000000.0
193	
194	      // 搜索最佳匹配
195	      for dy in -search_range..<(search_range + 1) {
196	        for dx in -search_range..<(search_range + 1) {
197	          let mut sad = 0.0
198	          let mut valid = true
199	
200	          for py in 0..<block_size {
201	            for px in 0..<block_size {
202	              let x1 = bx * block_size + px
203	              let y1 = by * block_size + py
204	              let x2 = x1 + dx
205	              let y2 = y1 + dy
206	
207	              if x1 >= 0 &&
208	                x1 < w &&
209	                y1 >= 0 &&
210	                y1 < h &&
211	                x2 >= 0 &&
212	                x2 < w &&
213	                y2 >= 0 &&
214	                y2 < h {
215	                sad += (frame1.data[y1 * w + x1].to_int() -
216	                  frame2.data[y2 * w + x2].to_int())
217	                  .abs()
218	                  .to_double()
219	              } else {
220	                valid = false
221	              }
222	            }
223	          }
224	
225	          if valid && sad < best_sad {
226	            best_sad = sad
227	            best_dx = dx
228	            best_dy = dy
229	          }
230	        }
231	      }
232	
233	      let magnitude = (best_dx.to_double() * best_dx.to_double() +
234	      best_dy.to_double() * best_dy.to_double()).sqrt()
235	      let angle = if best_dx != 0 {
236	        best_dy.to_double() / best_dx.to_double() * 57.2958
237	      } else {
238	        0.0
239	      }
240	
241	      vectors.push(OpticalFlowVector::{
242	        x: best_dx.to_double(),
243	        y: best_dy.to_double(),
244	        magnitude,
245	        angle,
246	      })
247	
248	      total_magnitude += magnitude
249	      if magnitude > max_mag {
250	        max_mag = magnitude
251	      }
252	      sum_dx += best_dx.to_double()
253	      sum_dy += best_dy.to_double()
254	      count += 1
255	    }
256	  }
257	
258	  let avg_mag = if count > 0 {
259	    total_magnitude / count.to_double()
260	  } else {
261	    0.0	<-- UNCOVERED
262	  }
263	  let motion_dir = if sum_dx != 0 { sum_dy / sum_dx * 57.2958 } else { 0.0 }
264	
265	  OpticalFlowField::{
266	    width: w / block_size,
267	    height: h / block_size,
268	    vectors,
269	    average_magnitude: avg_mag,
270	    max_magnitude: max_mag,
271	    motion_direction: motion_dir,
272	  }
273	}
   	…

341	pub fn update_tracking(
342	  obj : TrackedObject,
343	  frame : @types.Image,
344	) -> TrackedObject {
345	  let w = frame.width
346	  let h = frame.height
347	  let search_radius = 20
348	
349	  let mut best_x = obj.x
350	  let mut best_y = obj.y
351	  let mut best_score = -1.0
352	
353	  // 在目标周围搜索最佳匹配
354	  for dy in -search_radius..<(search_radius + 1) {
355	    for dx in -search_radius..<(search_radius + 1) {
356	      let new_x = obj.x + dx
357	      let new_y = obj.y + dy
358	
359	      // 计算当前位置的颜色直方图
360	      let current_hist : Array[Double] = []
361	      for _ in 0..<16 {
362	        current_hist.push(0.0)
363	      }
364	      let mut total = 0
365	
366	      for py in 0..<obj.height {
367	        for px in 0..<obj.width {
368	          let x = new_x + px
369	          let y = new_y + py
370	          if x >= 0 && x < w && y >= 0 && y < h {
371	            let pixel = frame.data[y * w + x].to_int()
372	            let bin = pixel / 16
373	            if bin < 16 {
374	              current_hist[bin] = current_hist[bin] + 1.0
375	            }
376	            total += 1
377	          }
378	        }
379	      }
380	
381	      if total > 0 {
382	        // 归一化
383	        for i in 0..<16 {
384	          current_hist[i] = current_hist[i] / total.to_double()
385	        }
386	
387	        // 计算直方图交集（相似度）
388	        let mut score = 0.0
389	        for i in 0..<16 {
390	          score += if current_hist[i] < obj.color_histogram[i] {
391	            current_hist[i]
392	          } else {
393	            obj.color_histogram[i]
394	          }
395	        }
396	
397	        if score > best_score {
398	          best_score = score
399	          best_x = new_x
400	          best_y = new_y
401	        }
402	      }
403	    }
404	  }
405	
406	  // 更新速度
407	  let new_center_x = (best_x + obj.width / 2).to_double()
408	  let new_center_y = (best_y + obj.height / 2).to_double()
409	  let velocity_x = new_center_x - obj.center_x
410	  let velocity_y = new_center_y - obj.center_y
411	
412	  // 更新轨迹
413	  let mut new_trajectory = obj.trajectory
414	  new_trajectory.push((best_x + obj.width / 2, best_y + obj.height / 2))
415	  if new_trajectory.length() > 100 {
416	    // 保留最近 100 个点
417	    let trimmed : Array[(Int, Int)] = []	<-- UNCOVERED
418	    for i in (new_trajectory.length() - 100)..<new_trajectory.length() {
419	      trimmed.push(new_trajectory[i])
420	    }
421	    new_trajectory = trimmed
422	  }
423	
424	  TrackedObject::{
425	    id: obj.id,
426	    label: obj.label,
427	    x: best_x,
428	    y: best_y,
429	    width: obj.width,
430	    height: obj.height,
431	    center_x: new_center_x,
432	    center_y: new_center_y,
433	    velocity_x,
434	    velocity_y,
435	    confidence: best_score,
436	    age: obj.age + 1,
437	    visible_count: obj.visible_count + 1,
438	    invisible_count: 0,
439	    color_histogram: obj.color_histogram,
440	    trajectory: new_trajectory,
441	  }
442	}
   	…

446	pub fn recognize_action(
447	  frames : Array[@types.Image],
448	  start_frame : Int,
449	  end_frame : Int,
450	) -> ActionRecognitionResult {
451	  if frames.length() < 2 || end_frame <= start_frame {
452	    return ActionRecognitionResult::{	<-- UNCOVERED
453	      action_type: ActionType::Unknown,
454	      confidence: 0.0,
455	      start_frame,
456	      end_frame,
457	      duration_frames: 0,
458	      motion_energy: 0.0,
459	      direction: 0.0,
460	      key_points: [],
461	    }
462	  }
463	
464	  // 计算运动能量序列
465	  let mut total_energy = 0.0
466	  let mut energy_count = 0
467	  let mut max_energy = 0.0
468	  let mut motion_dx = 0.0
469	  let mut motion_dy = 0.0
470	
471	  for i in start_frame..<(end_frame - 1) {
472	    if i + 1 < frames.length() {
473	      let energy = compute_frame_difference(frames[i], frames[i + 1])
474	      total_energy += energy
475	      energy_count += 1
476	      if energy > max_energy {
477	        max_energy = energy
478	      }
479	
480	      // 计算运动方向（简化版）
481	      let _ = compute_optical_flow(
482	        frames[i],
483	        frames[i + 1],
484	        block_size=16,
485	        search_range=2,
486	      )
487	      motion_dx += 1.0
488	      motion_dy += 0.0
489	    }
490	  }
491	
492	  let avg_energy = if energy_count > 0 {
493	    total_energy / energy_count.to_double()
494	  } else {
495	    0.0	<-- UNCOVERED
496	  }
497	  let direction = if motion_dx != 0 {
498	    motion_dy / motion_dx * 57.2958
499	  } else {
500	    0.0	<-- UNCOVERED
501	  }
502	
503	  // 根据运动特征分类动作
504	  let (action_type, confidence) = if avg_energy < 0.01 {
505	    (ActionType::Standing, 0.7)	<-- UNCOVERED
506	  } else if avg_energy < 0.05 {
507	    (ActionType::Walking, 0.6)	<-- UNCOVERED
508	  } else if avg_energy < 0.1 {
509	    (ActionType::Running, 0.6)	<-- UNCOVERED
510	  } else if max_energy > 0.2 && avg_energy > 0.05 {
511	    (ActionType::Jumping, 0.5)
512	  } else if avg_energy > 0.15 {	<-- UNCOVERED
513	    (ActionType::Waving, 0.5)
514	  } else {
515	    (ActionType::Unknown, 0.3)	<-- UNCOVERED
516	  }
517	
518	  let key_points : Array[(Int, Int)] = []
519	
520	  ActionRecognitionResult::{
521	    action_type,
522	    confidence,
523	    start_frame,
524	    end_frame,
525	    duration_frames: end_frame - start_frame,
526	    motion_energy: avg_energy,
527	    direction,
528	    key_points,
529	  }
530	}
   	…

560	pub fn extract_keyframes(
561	  frames : Array[@types.Image],
562	  fps : Double,
563	  method_type? : String = "auto",
564	) -> Array[Keyframe] {
565	  ignore(method_type)
566	  let keyframes : Array[Keyframe] = []
567	
568	  if frames.length() == 0 {
569	    return keyframes	<-- UNCOVERED
570	  }
571	
572	  // 第一帧总是关键帧
573	  keyframes.push(Keyframe::{
574	    frame_index: 0,
575	    timestamp_ms: 0.0,
576	    keyframe_type: KeyframeType::UniformSampling,
577	    score: 1.0,
578	    thumbnail: frames[0],
579	  })
580	
581	  // 检测场景切换
582	  for i in 1..<frames.length() {
583	    let (is_change, score) = detect_scene_change(frames[i - 1], frames[i])
584	    if is_change {
585	      keyframes.push(Keyframe::{
586	        frame_index: i,
587	        timestamp_ms: i.to_double() * 1000.0 / fps,
588	        keyframe_type: KeyframeType::SceneChange,
589	        score,
590	        thumbnail: frames[i],
591	      })
592	    }
593	  }
594	
595	  // 均匀采样（如果关键帧太少）
596	  let min_keyframes = if frames.length() / 30 > 5 {
597	    frames.length() / 30	<-- UNCOVERED
598	  } else {
599	    5
600	  }
601	  if keyframes.length() < min_keyframes {
602	    let interval = frames.length() / min_keyframes
603	    for i in 1..<min_keyframes {
604	      let idx = i * interval
605	      let mut exists = false
606	      for kf in keyframes {
607	        if kf.frame_index == idx {
608	          exists = true
609	        }
610	      }
611	      if !exists && idx < frames.length() {
612	        keyframes.push(Keyframe::{
613	          frame_index: idx,
614	          timestamp_ms: idx.to_double() * 1000.0 / fps,
615	          keyframe_type: KeyframeType::UniformSampling,
616	          score: 0.5,
617	          thumbnail: frames[idx],
618	        })
619	      }
620	    }
621	  }
622	
623	  keyframes
624	}
   	…

628	pub fn generate_video_summary(
629	  frames : Array[@types.Image],
630	  fps : Double,
631	) -> VideoSummary {
632	  let total_frames = frames.length()
633	  let duration_ms = if fps > 0 {
634	    total_frames.to_double() * 1000.0 / fps
635	  } else {
636	    0.0	<-- UNCOVERED
637	  }
638	
639	  // 提取关键帧
640	  let keyframes = extract_keyframes(frames, fps)
641	
642	  // 计算运动能量序列
643	  let motion_peaks : Array[Int] = []
644	  let scene_changes : Array[Int] = []
645	  let mut total_motion = 0.0
646	  let mut motion_count = 0
647	
648	  for i in 1..<total_frames {
649	    let energy = compute_frame_difference(frames[i - 1], frames[i])
650	    total_motion += energy
651	    motion_count += 1
652	
653	    if energy > 0.15 {
654	      motion_peaks.push(i)
655	    }
656	
657	    let (is_change, _) = detect_scene_change(frames[i - 1], frames[i])
658	    if is_change {
659	      scene_changes.push(i)
660	    }
661	  }
662	
663	  let avg_motion = if motion_count > 0 {
664	    total_motion / motion_count.to_double()
665	  } else {
666	    0.0	<-- UNCOVERED
667	  }
668	
669	  // 生成摘要文本
670	  let summary_text = "视频共 " +
671	    total_frames.to_string() +
672	    " 帧，时长 " +
673	    (duration_ms / 1000.0).to_string() +
674	    " 秒。" +
675	    "检测到 " +
676	    scene_changes.length().to_string() +
677	    " 个场景切换，" +
678	    motion_peaks.length().to_string() +
679	    " 个运动峰值。" +
680	    "平均运动能量 " +
681	    avg_motion.to_string() +
682	    "。" +
683	    "提取 " +
684	    keyframes.length().to_string() +
685	    " 个关键帧。"
686	
687	  VideoSummary::{
688	    total_frames,
689	    duration_ms,
690	    keyframes,
691	    num_keyframes: keyframes.length(),
692	    average_motion: avg_motion,
693	    motion_peaks,
694	    scene_changes,
695	    summary_text,
696	  }
697	}
   	…

701	pub fn stabilize_video(
702	  frames : Array[@types.Image],
703	  config : VideoStabilizationConfig,
704	) -> Array[@types.Image] {
705	  if frames.length() < 2 {
706	    return frames	<-- UNCOVERED
707	  }
708	
709	  let stabilized : Array[@types.Image] = []
710	  stabilized.push(frames[0])
711	
712	  // 计算帧间偏移
713	  let offsets_x : Array[Double] = []
714	  let offsets_y : Array[Double] = []
715	  offsets_x.push(0.0)
716	  offsets_y.push(0.0)
717	
718	  for i in 1..<frames.length() {
719	    let flow = compute_optical_flow(
720	      frames[i - 1],
721	      frames[i],
722	      block_size=32,
723	      search_range=4,
724	    )
725	    // 平均光流作为全局偏移
726	    let mut avg_dx = 0.0
727	    let mut avg_dy = 0.0
728	    for v in flow.vectors {
729	      avg_dx += v.x
730	      avg_dy += v.y
731	    }
732	    if flow.vectors.length() > 0 {
733	      avg_dx = avg_dx / flow.vectors.length().to_double()
734	      avg_dy = avg_dy / flow.vectors.length().to_double()
735	    }
736	    offsets_x.push(avg_dx)
737	    offsets_y.push(avg_dy)
738	  }
739	
740	  // 平滑偏移
741	  let smoothed_x : Array[Double] = []
742	  let smoothed_y : Array[Double] = []
743	  let radius = config.smoothing_radius
744	
745	  for i in 0..<frames.length() {
746	    let mut sum_x = 0.0
747	    let mut sum_y = 0.0
748	    let mut count = 0
749	    for j in (i - radius)..<(i + radius + 1) {
750	      if j >= 0 && j < frames.length() {
751	        sum_x += offsets_x[j]
752	        sum_y += offsets_y[j]
753	        count += 1
754	      }
755	    }
756	    smoothed_x.push(if count > 0 { sum_x / count.to_double() } else { 0.0 })
757	    smoothed_y.push(if count > 0 { sum_y / count.to_double() } else { 0.0 })
758	  }
759	
760	  // 应用稳定（简化版：直接返回原帧，实际应进行仿射变换）
761	  for i in 1..<frames.length() {
762	    stabilized.push(frames[i])
763	  }
764	
765	  stabilized
766	}
   	…

15 uncovered line(s) in src\process\video_processing_advanced\video_processing_advanced.mbt:

204	pub fn enhance_video(
205	  frames : Array[VideoFrame],
206	  config : VideoProcessingConfig,
207	) -> VideoEnhancementResult {
208	  let enhanced : Array[VideoFrame] = []
209	
210	  for frame in frames {
211	    let enhanced_data : Array[Double] = []
212	    for pixel in frame.frame_data {
213	      let mut value = pixel + config.brightness_adjustment
214	      value = value * (1.0 + config.contrast_adjustment)
215	      if value < 0.0 {
216	        value = 0.0	<-- UNCOVERED
217	      }
218	      if value > 255.0 {
219	        value = 255.0	<-- UNCOVERED
220	      }
221	      enhanced_data.push(value)
222	    }
223	
224	    let enhanced_frame = VideoFrame::{
225	      frame_data: enhanced_data,
226	      width: frame.width,
227	      height: frame.height,
228	      num_channels: frame.num_channels,
229	      frame_index: frame.frame_index,
230	      timestamp_ms: frame.timestamp_ms,
231	      frame_type: frame.frame_type,
232	      quality_score: frame.quality_score * 1.2,
233	      motion_vectors: frame.motion_vectors,
234	    }
235	    enhanced.push(enhanced_frame)
236	  }
237	
238	  VideoEnhancementResult::{
239	    enhanced_frames: enhanced,
240	    num_frames: enhanced.length(),
241	    enhancement_method: "basic",
242	    brightness_improvement: config.brightness_adjustment.abs(),
243	    contrast_improvement: config.contrast_adjustment.abs(),
244	    sharpness_improvement: config.sharpen_strength,
245	    noise_reduction: config.denoise_strength,
246	    quality_score: 0.85,
247	    processing_time_ms: frames.length().to_double() * 10.0,
248	  }
249	}
   	…

253	pub fn video_super_resolution(
254	  frames : Array[VideoFrame],
255	  config : VideoProcessingConfig,
256	) -> VideoSuperResolutionResult {
257	  let sr_frames : Array[VideoFrame] = []
258	  let scale = config.sr_scale_factor
259	
260	  for frame in frames {
261	    let sr_width = (frame.width.to_double() * scale).to_int()
262	    let sr_height = (frame.height.to_double() * scale).to_int()
263	    let sr_data : Array[Double] = []
264	
265	    // 简化的双线性插值
266	    for y in 0..<sr_height {
267	      for x in 0..<sr_width {
268	        let src_x = x.to_double() / scale
269	        let src_y = y.to_double() / scale
270	        let x0 = src_x.to_int()
271	        let y0 = src_y.to_int()
272	        let x1 = if x0 + 1 < frame.width { x0 + 1 } else { x0 }
273	        let y1 = if y0 + 1 < frame.height { y0 + 1 } else { y0 }
274	        let fx = src_x - x0.to_double()
275	        let fy = src_y - y0.to_double()
276	
277	        let idx00 = y0 * frame.width + x0
278	        let idx01 = y0 * frame.width + x1
279	        let idx10 = y1 * frame.width + x0
280	        let idx11 = y1 * frame.width + x1
281	
282	        let v00 = if idx00 < frame.frame_data.length() {
283	          frame.frame_data[idx00]
284	        } else {
285	          0.0	<-- UNCOVERED
286	        }
287	        let v01 = if idx01 < frame.frame_data.length() {
288	          frame.frame_data[idx01]
289	        } else {
290	          0.0	<-- UNCOVERED
291	        }
292	        let v10 = if idx10 < frame.frame_data.length() {
293	          frame.frame_data[idx10]
294	        } else {
295	          0.0	<-- UNCOVERED
296	        }
297	        let v11 = if idx11 < frame.frame_data.length() {
298	          frame.frame_data[idx11]
299	        } else {
300	          0.0	<-- UNCOVERED
301	        }
302	
303	        let value = v00 * (1.0 - fx) * (1.0 - fy) +
304	          v01 * fx * (1.0 - fy) +
305	          v10 * (1.0 - fx) * fy +
306	          v11 * fx * fy
307	        sr_data.push(value)
308	      }
309	    }
310	
311	    let sr_frame = VideoFrame::{
312	      frame_data: sr_data,
313	      width: sr_width,
314	      height: sr_height,
315	      num_channels: frame.num_channels,
316	      frame_index: frame.frame_index,
317	      timestamp_ms: frame.timestamp_ms,
318	      frame_type: frame.frame_type,
319	      quality_score: frame.quality_score,
320	      motion_vectors: [],
321	    }
322	    sr_frames.push(sr_frame)
323	  }
324	
325	  VideoSuperResolutionResult::{
326	    sr_frames,
327	    original_width: if frames.length() > 0 {
328	      frames[0].width
329	    } else {
330	      0	<-- UNCOVERED
331	    },
332	    original_height: if frames.length() > 0 {
333	      frames[0].height
334	    } else {
335	      0	<-- UNCOVERED
336	    },
337	    sr_width: if frames.length() > 0 {
338	      (frames[0].width.to_double() * scale).to_int()
339	    } else {
340	      0	<-- UNCOVERED
341	    },
342	    sr_height: if frames.length() > 0 {
343	      (frames[0].height.to_double() * scale).to_int()
344	    } else {
345	      0	<-- UNCOVERED
346	    },
347	    scale_factor: scale,
348	    sr_method: config.sr_method,
349	    psnr: 32.0,
350	    ssim: 0.9,
351	    processing_time_ms: frames.length().to_double() * 50.0,
352	  }
353	}
   	…

357	pub fn summarize_video(
358	  frames : Array[VideoFrame],
359	  config : VideoProcessingConfig,
360	) -> VideoSummarizationResult {
361	  let keyframes : Array[VideoFrame] = []
362	  let keyframe_indices : Array[Int] = []
363	  let importance_scores : Array[Double] = []
364	
365	  let mut state = 42
366	  let num_keyframes = if config.max_keyframes < frames.length() {
367	    config.max_keyframes	<-- UNCOVERED
368	  } else {
369	    frames.length()
370	  }
371	
372	  for _ in 0..<num_keyframes {
373	    state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
374	    let idx = state % frames.length()
375	    keyframes.push(frames[idx])
376	    keyframe_indices.push(idx)
377	    state = ((state * 11035 + 12345).reinterpret_as_uint() % 1000000).reinterpret_as_int()
378	    let score = (state % 1000).to_double() / 1000.0
379	    importance_scores.push(score)
380	  }
381	
382	  let original_duration = if frames.length() > 0 {
383	    frames[frames.length() - 1].timestamp_ms
384	  } else {
385	    0.0	<-- UNCOVERED
386	  }
387	  let summary_duration = original_duration *
388	    num_keyframes.to_double() /
389	    frames.length().to_double()
390	
391	  VideoSummarizationResult::{
392	    keyframes,
393	    num_keyframes,
394	    summary_duration_ms: summary_duration,
395	    original_duration_ms: original_duration,
396	    compression_ratio: frames.length().to_double() / num_keyframes.to_double(),
397	    keyframe_indices,
398	    importance_scores,
399	    summarization_method: "uniform",
400	  }
401	}
   	…

405	pub fn video_processing_report_to_string(
406	  task_type : VideoProcessingTaskType,
407	  config : VideoProcessingConfig,
408	  result_description : String,
409	) -> String {
410	  let mut report = "## 视频处理高级报告\n\n"
411	
412	  report = report + "### 任务信息\n\n"
413	  report = report + "| 指标 | 值 |\n|------|------|\n"
414	  report = report +
415	    "| 任务类型 | " +
416	    (match task_type {
417	      VideoProcessingTaskType::VideoEncoding => "视频编码"	<-- UNCOVERED
418	      VideoProcessingTaskType::VideoDecoding => "视频解码"
419	      VideoProcessingTaskType::VideoEnhancement => "视频增强"
420	      VideoProcessingTaskType::VideoSuperResolution => "视频超分辨率"	<-- UNCOVERED
421	      VideoProcessingTaskType::VideoDenoising => "视频去噪"
422	      VideoProcessingTaskType::VideoStabilization => "视频稳定"
423	      VideoProcessingTaskType::VideoSummarization => "视频摘要"
424	      _ => "未知"
425	    }) +
426	    " |\n"
427	  report = report +
428	    "| 编码格式 | " +
429	    (match config.codec {
430	      VideoCodecType::H264 => "H.264"
431	      VideoCodecType::H265 => "H.265/HEVC"	<-- UNCOVERED
432	      VideoCodecType::VP8 => "VP8"
433	      VideoCodecType::VP9 => "VP9"
434	      VideoCodecType::AV1 => "AV1"
435	      _ => "未知"
436	    }) +
437	    " |\n"
438	  report = report +
439	    "| 目标帧率 | " +
440	    config.target_fps.to_string() +
441	    " fps |\n"
442	  report = report +
443	    "| 目标码率 | " +
444	    config.target_bitrate_kbps.to_string() +
445	    " kbps |\n"
446	  report = report +
447	    "| 目标分辨率 | " +
448	    config.target_width.to_string() +
449	    "x" +
450	    config.target_height.to_string() +
451	    " |\n"
452	  report = report + "| 质量 | " + config.quality.to_string() + " |\n"
453	  report = report +
454	    "| 去噪强度 | " +
455	    config.denoise_strength.to_string() +
456	    " |\n"
457	  report = report +
458	    "| 锐化强度 | " +
459	    config.sharpen_strength.to_string() +
460	    " |\n"
461	  report = report +
462	    "| 超分倍率 | " +
463	    config.sr_scale_factor.to_string() +
464	    "x |\n"
465	  report = report +
466	    "| 最大关键帧 | " +
467	    config.max_keyframes.to_string() +
468	    " |\n"
469	
470	  report = report + "\n### 结果\n\n"
471	  report = report + result_description + "\n"
472	
473	  report
474	}
   	…

1 uncovered line(s) in src\process\vision_3d_advanced\vision_3d_advanced.mbt:

420	pub fn vision_3d_report_to_string(
421	  task_type : Vision3DTaskType,
422	  config : Vision3DConfig,
423	  result_description : String,
424	) -> String {
425	  let mut report = "## 3D视觉高级报告\n\n"
426	
427	  report = report + "### 任务信息\n\n"
428	  report = report + "| 指标 | 值 |\n|------|------|\n"
429	  report = report +
430	    "| 任务类型 | " +
431	    (match task_type {
432	      Vision3DTaskType::Reconstruction3D => "三维重建"
433	      Vision3DTaskType::PointCloudProcessing => "点云处理"	<-- UNCOVERED
434	      Vision3DTaskType::StereoMatching => "立体匹配"
435	      Vision3DTaskType::DepthEstimation => "深度估计"
436	      Vision3DTaskType::PoseEstimation => "位姿估计"
437	      Vision3DTaskType::SLAM => "SLAM"
438	      Vision3DTaskType::ObjectTracking3D => "3D目标跟踪"
439	      _ => "未知"
440	    }) +
441	    " |\n"
442	  report = report +
443	    "| 立体基线 | " +
444	    config.stereo_baseline.to_string() +
445	    " m |\n"
446	  report = report + "| 焦距 | " + config.focal_length.to_string() + " px |\n"
447	  report = report +
448	    "| 最小深度 | " +
449	    config.min_depth.to_string() +
450	    " m |\n"
451	  report = report +
452	    "| 最大深度 | " +
453	    config.max_depth.to_string() +
454	    " m |\n"
455	  report = report +
456	    "| 视差范围 | " +
457	    config.disparity_range.to_string() +
458	    " |\n"
459	  report = report + "| 匹配方法 | " + config.matching_method + " |\n"
460	  report = report + "| 重建方法 | " + config.reconstruction_method + " |\n"
461	  report = report +
462	    "| 体素大小 | " +
463	    config.voxel_size.to_string() +
464	    " m |\n"
465	  report = report +
466	    "| 使用颜色 | " +
467	    (if config.use_color { "开启" } else { "关闭" }) +
468	    " |\n"
469	  report = report +
470	    "| 使用法线 | " +
471	    (if config.use_normals { "开启" } else { "关闭" }) +
472	    " |\n"
473	
474	  report = report + "\n### 结果\n\n"
475	  report = report + result_description + "\n"
476	
477	  report
478	}
   	…

11 uncovered line(s) in src\process\vlm_multimodal\vlm_multimodal.mbt:

218	pub fn compute_image_text_similarity(
219	  image : @types.Image,
220	  text : String,
221	  config : VLMConfig,
222	) -> VisionTextEmbedding {
223	  let w = image.width
224	  let h = image.height
225	  let total = w * h
226	
227	  // 简化的图像特征提取：统计特征
228	  let mut sum = 0.0
229	  let mut sum_sq = 0.0
230	  for i in 0..<total {
231	    let v = image.data[i].to_double()
232	    sum += v
233	    sum_sq += v * v
234	  }
235	  let mean = sum / total.to_double()
236	  let variance = sum_sq / total.to_double() - mean * mean
237	  let std_dev = if variance > 0.0 { variance.sqrt() } else { 0.0 }
238	
239	  // 简化的文本特征：字符统计
240	  let text_len = text.length().to_double()
241	  let text_hash = text.length().to_double()
242	
243	  // 生成嵌入向量
244	  let image_embedding : Array[Double] = []
245	  let text_embedding : Array[Double] = []
246	  for i in 0..<config.projection_dim {
247	    let img_val = mean * 0.01 + std_dev * 0.005 + (i % 10).to_double() * 0.001
248	    let txt_val = text_len * 0.01 +
249	      text_hash * 0.0001 +
250	      (i % 10).to_double() * 0.001
251	    image_embedding.push(img_val)
252	    text_embedding.push(txt_val)
253	  }
254	
255	  // 计算余弦相似度
256	  let mut dot = 0.0
257	  let mut norm_img = 0.0
258	  let mut norm_txt = 0.0
259	  for i in 0..<config.projection_dim {
260	    dot += image_embedding[i] * text_embedding[i]
261	    norm_img += image_embedding[i] * image_embedding[i]
262	    norm_txt += text_embedding[i] * text_embedding[i]
263	  }
264	  let similarity = if norm_img > 0.0 && norm_txt > 0.0 {
265	    dot / (norm_img.sqrt() * norm_txt.sqrt())
266	  } else {
267	    0.0	<-- UNCOVERED
268	  }
269	
270	  VisionTextEmbedding::{
271	    image_embedding,
272	    text_embedding,
273	    similarity,
274	    projection_dim: config.projection_dim,
275	  }
276	}
   	…

280	pub fn fuse_modalities(
281	  image_features : Array[Double],
282	  text_features : Array[Double],
283	  config : FusionConfig,
284	) -> FusionResult {
285	  let dim = if image_features.length() > text_features.length() {
286	    image_features.length()
287	  } else {
288	    text_features.length()
289	  }
290	
291	  let fused : Array[Double] = []
292	  let weights : Array[Double] = []
293	
294	  for i in 0..<dim {
295	    let img_val = if i < image_features.length() {
296	      image_features[i]
297	    } else {
298	      0.0	<-- UNCOVERED
299	    }
300	    let txt_val = if i < text_features.length() {
301	      text_features[i]
302	    } else {
303	      0.0
304	    }
305	
306	    let fused_val = match config.strategy {
307	      MultimodalFusionStrategy::EarlyFusion => (img_val + txt_val) / 2.0	<-- UNCOVERED
308	      MultimodalFusionStrategy::LateFusion => img_val * 0.6 + txt_val * 0.4
309	      MultimodalFusionStrategy::HybridFusion =>
310	        (img_val + txt_val) * 0.5 + img_val * 0.25	<-- UNCOVERED
311	      MultimodalFusionStrategy::CrossAttention => img_val * 0.7 + txt_val * 0.3
312	      MultimodalFusionStrategy::TensorFusion => img_val * txt_val
313	      MultimodalFusionStrategy::GatedFusion => img_val * 0.5 + txt_val * 0.5	<-- UNCOVERED
314	      MultimodalFusionStrategy::Unknown => (img_val + txt_val) / 2.0
315	    }
316	
317	    fused.push(fused_val)
318	    weights.push(0.5)
319	  }
320	
321	  FusionResult::{
322	    fused_representation: fused,
323	    modality_weights: weights,
324	    fusion_strategy: config.strategy,
325	    hidden_dim: dim,
326	  }
327	}
   	…

407	pub fn image_to_text(
408	  image : @types.Image,
409	  config : GenerativeAIConfig,
410	) -> GenerativeAIResult {
411	  ignore(config)
412	  let w = image.width
413	  let h = image.height
414	  let total = w * h
415	
416	  // 简化的图像描述生成
417	  let mut sum = 0.0
418	  for i in 0..<total {
419	    sum += image.data[i].to_double()
420	  }
421	  let mean = sum / total.to_double()
422	
423	  let brightness = if mean < 60.0 {
424	    "dark"
425	  } else if mean < 120.0 {
426	    "dimly lit"	<-- UNCOVERED
427	  } else if mean < 180.0 {
428	    "well-lit"	<-- UNCOVERED
429	  } else {
430	    "bright"
431	  }
432	
433	  let caption = "A " +
434	    brightness +
435	    " image with " +
436	    w.to_string() +
437	    "x" +
438	    h.to_string() +
439	    " resolution."
440	
441	  GenerativeAIResult::{
442	    task_type: GenerativeAITaskType::ImageToText,
443	    image,
444	    text: caption,
445	    confidence: 0.6,
446	    generation_time_ms: 0.0,
447	    num_iterations: 1,
448	  }
449	}
   	…

453	pub fn vlm_report_to_string(
454	  embedding : VisionTextEmbedding,
455	  model_type : VisionLanguageModelType,
456	) -> String {
457	  let mut report = "## 视觉语言模型报告\n\n"
458	  report = report + "### 模型信息\n\n"
459	  report = report + "| 指标 | 值 |\n|------|------|\n"
460	  report = report +
461	    "| 模型类型 | " +
462	    (match model_type {
463	      VisionLanguageModelType::CLIP => "CLIP"
464	      VisionLanguageModelType::BLIP => "BLIP"	<-- UNCOVERED
465	      VisionLanguageModelType::ALIGN => "ALIGN"
466	      VisionLanguageModelType::LLaVA => "LLaVA"
467	      VisionLanguageModelType::QwenVL => "Qwen-VL"	<-- UNCOVERED
468	      VisionLanguageModelType::InternVL => "InternVL"
469	      _ => "未知"
470	    }) +
471	    " |\n"
472	  report = report +
473	    "| 投影维度 | " +
474	    embedding.projection_dim.to_string() +
475	    " |\n"
476	  report = report +
477	    "| 图文相似度 | " +
478	    (embedding.similarity * 100.0).to_string() +
479	    "% |\n"
480	
481	  report
482	}
   	…

486	pub fn fusion_report_to_string(result : FusionResult) -> String {
487	  let mut report = "## 多模态融合报告\n\n"
488	  report = report + "### 融合信息\n\n"
489	  report = report + "| 指标 | 值 |\n|------|------|\n"
490	  report = report +
491	    "| 融合策略 | " +
492	    (match result.fusion_strategy {
493	      MultimodalFusionStrategy::EarlyFusion => "早期融合"	<-- UNCOVERED
494	      MultimodalFusionStrategy::LateFusion => "晚期融合"
495	      MultimodalFusionStrategy::HybridFusion => "混合融合"
496	      MultimodalFusionStrategy::CrossAttention => "交叉注意力"
497	      MultimodalFusionStrategy::TensorFusion => "张量融合"	<-- UNCOVERED
498	      MultimodalFusionStrategy::GatedFusion => "门控融合"
499	      _ => "未知"
500	    }) +
501	    " |\n"
502	  report = report + "| 隐藏维度 | " + result.hidden_dim.to_string() + " |\n"
503	  report = report +
504	    "| 模态数量 | " +
505	    result.modality_weights.length().to_string() +
506	    " |\n"
507	
508	  report
509	}
   	…

5 uncovered line(s) in src\process\watermark\digital_watermark.mbt:

30	fn generate_random_positions(
31	  count : Int,
32	  total_pixels : Int,
33	  key : Int,
34	) -> Array[Int] {
35	  let positions : Array[Int] = []
36	  let mut seed = key
37	  let mut i = 0
38	  while i < count {
39	    let (next, value) = lcg_next(seed)
40	    seed = next
41	    let pos = value % total_pixels
42	    // 检查是否重复
43	    let mut duplicate = false
44	    for p in positions {
45	      if p == pos {
46	        duplicate = true	<-- UNCOVERED
47	      }
48	    }
49	    if !duplicate {
50	      positions.push(pos)
51	      i += 1
52	    }
53	  }
54	  positions
55	}
  	…

 60	pub fn embed_watermark_lsb(
 61	  cover : @types.Image,
 62	  watermark : @types.Image,
 63	  key? : Int = 42,
 64	) -> @types.Image {
 65	  let _ = key
 66	  if cover.width != watermark.width || cover.height != watermark.height {
 67	    return cover	<-- UNCOVERED
 68	  }
 69	  let w = cover.width
 70	  let h = cover.height
 71	  let channels = cover.channels
 72	  let out_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
 73	  // 复制原图
 74	  for i in 0..<(w * h * channels) {
 75	    out_data[i] = cover.data[i]
 76	  }
 77	  // 嵌入水印到红色通道的最低有效位
 78	  for y in 0..<h {
 79	    for x in 0..<w {
 80	      let idx = (y * w + x) * channels
 81	      let watermark_idx = y * w + x
 82	      let watermark_val = if watermark.channels >= 1 {
 83	        watermark.data[watermark_idx * watermark.channels].to_int()
 84	      } else {
 85	        0	<-- UNCOVERED
 86	      }
 87	      let bit = if watermark_val > 128 { 1 } else { 0 }
 88	      // 修改最低有效位
 89	      let original = out_data[idx].to_int()
 90	      let modified = if bit == 1 { original | 1 } else { original & 254 }
 91	      out_data[idx] = modified.to_byte()
 92	    }
 93	  }
 94	  @types.Image::{
 95	    width: w,
 96	    height: h,
 97	    channels,
 98	    data: Bytes::from_array(out_data),
 99	  }
100	}
   	…

199	pub fn watermark_psnr(
200	  original : @types.Image,
201	  watermarked : @types.Image,
202	) -> Double {
203	  if original.width != watermarked.width ||
204	    original.height != watermarked.height {
205	    return -1.0	<-- UNCOVERED
206	  }
207	  let w = original.width
208	  let h = original.height
209	  let channels = original.channels.min(watermarked.channels)
210	  let mut mse = 0.0
211	  let mut count = 0
212	  for y in 0..<h {
213	    for x in 0..<w {
214	      let idx = (y * w + x) * channels
215	      for c in 0..<channels {
216	        let diff = original.data[idx + c].to_double() -
217	          watermarked.data[idx + c].to_double()
218	        mse += diff * diff
219	        count += 1
220	      }
221	    }
222	  }
223	  if count > 0 {
224	    mse = mse / count.to_double()
225	  }
226	  if mse < 0.000001 {
227	    return 100.0
228	  }
229	  10.0 * @math.log10(255.0 * 255.0 / mse)
230	}
   	…

234	pub fn watermark_robustness_test(
235	  watermarked : @types.Image,
236	  original_watermark : @types.Image,
237	  noise_level? : Double = 10.0,
238	) -> Double {
239	  let noise = noise_level
240	  let w = watermarked.width
241	  let h = watermarked.height
242	  let channels = watermarked.channels
243	  // 添加高斯噪声（简化版）
244	  let noisy_data : Array[Byte] = Array::make(w * h * channels, b'\x00')
245	  let mut seed = 12345
246	  for i in 0..<(w * h * channels) {
247	    let (next, value) = lcg_next(seed)
248	    seed = next
249	    let noise_val = (value.to_double() / 1073741824.0 - 0.5) * 2.0 * noise
250	    let original = watermarked.data[i].to_double()
251	    let noisy = (original + noise_val).min(255.0).max(0.0)
252	    noisy_data[i] = noisy.to_int().to_byte()
253	  }
254	  let noisy_img = @types.Image::{
255	    width: w,
256	    height: h,
257	    channels,
258	    data: Bytes::from_array(noisy_data),
259	  }
260	  // 提取水印
261	  let extracted = extract_watermark_lsb(noisy_img)
262	  // 计算相似度
263	  let mut match_count = 0
264	  let total = w * h
265	  for i in 0..<total {
266	    let orig = if original_watermark.channels >= 1 {
267	      original_watermark.data[i * original_watermark.channels].to_int()
268	    } else {
269	      0	<-- UNCOVERED
270	    }
271	    let extr = extracted.data[i].to_int()
272	    if (orig > 128 && extr > 128) || (orig <= 128 && extr <= 128) {
273	      match_count += 1
274	    }
275	  }
276	  match_count.to_double() / total.to_double()
277	}
   	…

8 uncovered line(s) in src\pure\codec\apng_codec.mbt:

 8	fn apng_chunk_type(data : Bytes, offset : Int) -> String {
 9	  let buf : Array[Byte] = [
10	    data[offset],
11	    data[offset + 1],
12	    data[offset + 2],
13	    data[offset + 3],
14	  ]
15	  @encoding/utf8.decode(Bytes::from_array(buf)) catch {
16	    _ => "????"	<-- UNCOVERED
17	  }
18	}
  	…

122	pub fn decode_apng_pure(
123	  data : Bytes,
124	) -> @types.PngAnimation raise @types.LoadError {
125	  let chunks = apng_parse_chunks(data)
126	  // 查找 IHDR
127	  let mut ihdr_data : Bytes = b""
128	  let mut found_ihdr = false
129	  for i in 0..<chunks.length() {
130	    if chunks[i].chunk_type == "IHDR" {
131	      ihdr_data = apng_extract_chunk_data(data, chunks[i])
132	      found_ihdr = true
133	      break
134	    }
135	  }
136	  if !found_ihdr {
137	    raise @types.LoadError::DecodeFailed("PNG/APNG 缺少 IHDR")
138	  }
139	  // 查找 acTL
140	  let mut has_actl = false
141	  for i in 0..<chunks.length() {
142	    if chunks[i].chunk_type == "acTL" {
143	      has_actl = true
144	      break
145	    }
146	  }
147	  if !has_actl {
148	    // 普通 PNG，当作单帧动画
149	    let img = decode_png_pure(data)
150	    return @types.PngAnimation::{ frames: [img], delays: [0] }
151	  }
152	  // APNG: 收集所有帧
153	  let frames : Array[@types.Image] = []
154	  let delays : Array[Int] = []
155	  // 收集 IDAT 数据（第一帧可能用 IDAT）
156	  let mut idat_data : Bytes = b""
157	  let mut found_idat = false
158	  for i in 0..<chunks.length() {
159	    if chunks[i].chunk_type == "IDAT" {
160	      let chunk_data = apng_extract_chunk_data(data, chunks[i])
161	      if !found_idat {
162	        idat_data = chunk_data
163	        found_idat = true
164	      } else {
165	        // 合并多个 IDAT
166	        let merged : Array[Byte] = []	<-- UNCOVERED
167	        for j in 0..<idat_data.length() {
168	          merged.push(idat_data[j])
169	        }
170	        for j in 0..<chunk_data.length() {	<-- UNCOVERED
171	          merged.push(chunk_data[j])
172	        }
173	        idat_data = Bytes::from_array(merged)	<-- UNCOVERED
174	      }
175	    }
176	  }
177	  // 遍历 chunk，处理 fcTL + IDAT/fdAT
178	  let mut i = 0
179	  let mut first_frame_done = false
180	  while i < chunks.length() {
181	    if chunks[i].chunk_type == "fcTL" {
182	      // 解析 fcTL
183	      let fctl_data = apng_extract_chunk_data(data, chunks[i])
184	      // delay_num (2 bytes at offset 20), delay_den (2 bytes at offset 22)
185	      let delay_num = read_u16_be(fctl_data, 20)
186	      let delay_den = read_u16_be(fctl_data, 22)
187	      let delay_ms = if delay_den == 0 {
188	        0	<-- UNCOVERED
189	      } else {
190	        delay_num * 1000 / delay_den
191	      }
192	      // 查找对应的帧数据（IDAT 或 fdAT）
193	      let mut frame_idat : Bytes = b""
194	      let mut found_frame_data = false
195	      let mut j = i + 1
196	      while j < chunks.length() {
197	        if chunks[j].chunk_type == "IDAT" && !first_frame_done {
198	          frame_idat = idat_data
199	          found_frame_data = true
200	          first_frame_done = true
201	          break
202	        } else if chunks[j].chunk_type == "fdAT" {
203	          // fdAT: 前 4 字节是 seq_num，后面是 IDAT 数据
204	          let fdat_data = apng_extract_chunk_data(data, chunks[j])
205	          let fdat_idat : Array[Byte] = []
206	          for k in 4..<fdat_data.length() {
207	            fdat_idat.push(fdat_data[k])
208	          }
209	          frame_idat = Bytes::from_array(fdat_idat)
210	          found_frame_data = true
211	          break
212	        } else if chunks[j].chunk_type == "fcTL" {	<-- UNCOVERED
213	          // 遇到下一个 fcTL，停止
214	          break	<-- UNCOVERED
215	        }
216	        j += 1
217	      }
218	      if found_frame_data {
219	        let png = build_standalone_png(ihdr_data, frame_idat)
220	        let img = decode_png_pure(png)
221	        frames.push(img)
222	        delays.push(delay_ms)
223	      }
224	    }
225	    i += 1
226	  }
227	  if frames.length() == 0 {
228	    raise @types.LoadError::DecodeFailed("APNG 无有效帧")	<-- UNCOVERED
229	  }
230	  @types.PngAnimation::{ frames, delays }
231	}
   	…

7 uncovered line(s) in src\pure\codec\avif_decode.mbt:

24	pub fn is_avif_format(data : Bytes) -> Bool {
25	  let len = data.length()
26	  if len < 12 {
27	    return false
28	  }
29	  // 检查 ftyp box: bytes 4-7 = "ftyp"
30	  if data[4] != b'f' || data[5] != b't' || data[6] != b'y' || data[7] != b'p' {
31	    return false	<-- UNCOVERED
32	  }
33	  // 检查 brand: bytes 8-11 = "avif" or "avis"
34	  let b8 = data[8]
35	  let b9 = data[9]
36	  let b10 = data[10]
37	  let b11 = data[11]
38	  // "avif" = 0x61 0x76 0x69 0x66
39	  if b8 == b'a' && b9 == b'v' && b10 == b'i' && b11 == b'f' {
40	    return true
41	  }
42	  // "avis" = 0x61 0x76 0x69 0x73
43	  if b8 == b'a' && b9 == b'v' && b10 == b'i' && b11 == b's' {
44	    return true
45	  }
46	  false
47	}
  	…

 54	pub fn avif_info(data : Bytes) -> AvifInfo raise @types.LoadError {
 55	  if !is_avif_format(data) {
 56	    raise @types.LoadError::UnsupportedFormat("Not an AVIF file")
 57	  }
 58	  let len = data.length()
 59	  let mut offset = 0
 60	  let mut width = 0
 61	  let mut height = 0
 62	  let mut bit_depth = 8
 63	  let mut has_alpha = false
 64	  // 遍历 ISOBMFF boxes
 65	  while offset + 8 <= len {
 66	    let box_size = (data[offset].to_int() << 24) |
 67	      (data[offset + 1].to_int() << 16) |
 68	      (data[offset + 2].to_int() << 8) |
 69	      data[offset + 3].to_int()
 70	    if box_size < 8 || offset + box_size > len {
 71	      break	<-- UNCOVERED
 72	    }
 73	    let box_type_0 = data[offset + 4]
 74	    let box_type_1 = data[offset + 5]
 75	    let box_type_2 = data[offset + 6]
 76	    let box_type_3 = data[offset + 7]
 77	    // 检查 ispe box (ImageSpatialExtentsProperty)
 78	    // box type "ispe" = 0x69 0x73 0x70 0x65
 79	    if box_type_0 == b'i' &&
 80	      box_type_1 == b's' &&
 81	      box_type_2 == b'p' &&
 82	      box_type_3 == b'e' {
 83	      // ispe box: full_box_header(8) + version(4) + width(4) + height(4)
 84	      if offset + 20 <= len {	<-- UNCOVERED
 85	        width = (data[offset + 12].to_int() << 24) |
 86	          (data[offset + 13].to_int() << 16) |
 87	          (data[offset + 14].to_int() << 8) |
 88	          data[offset + 15].to_int()
 89	        height = (data[offset + 16].to_int() << 24) |
 90	          (data[offset + 17].to_int() << 16) |
 91	          (data[offset + 18].to_int() << 8) |
 92	          data[offset + 19].to_int()
 93	      }
 94	    }
 95	    // 检查 auxC box (AuxiliaryTypeProperty) for alpha
 96	    // box type "auxC" = 0x61 0x75 0x78 0x43
 97	    if box_type_0 == b'a' &&
 98	      box_type_1 == b'u' &&
 99	      box_type_2 == b'x' &&
100	      box_type_3 == b'C' {
101	      has_alpha = true	<-- UNCOVERED
102	    }
103	    // 检查 pixi box (PixelInformationProperty) for bit depth
104	    // box type "pixi" = 0x70 0x69 0x78 0x69
105	    if box_type_0 == b'p' &&
106	      box_type_1 == b'i' &&
107	      box_type_2 == b'x' &&
108	      box_type_3 == b'i' {
109	      // pixi box: full_box_header(8) + version(4) + num_channels(1) + bit_depths(...)
110	      if offset + 13 <= len {	<-- UNCOVERED
111	        let num_ch = data[offset + 12].to_int()
112	        if num_ch > 0 && offset + 13 + num_ch <= len {
113	          bit_depth = data[offset + 13].to_int()	<-- UNCOVERED
114	        }
115	      }
116	    }
117	    offset += box_size
118	  }
119	  if width == 0 || height == 0 {
120	    raise @types.LoadError::DecodeFailed(
121	      "AVIF: could not determine image dimensions",
122	    )
123	  }
124	  let num_channels = if has_alpha { 4 } else { 3 }	<-- UNCOVERED
125	  AvifInfo::{ width, height, bit_depth, num_channels, has_alpha }
126	}
   	…

1 uncovered line(s) in src\pure\codec\byte_io.mbt:

71	fn is_png_signature(data : Bytes, offset : Int) -> Bool {
72	  if offset + 8 > data.length() {
73	    return false	<-- UNCOVERED
74	  }
75	  data[offset].to_int() == 0x89 &&
76	  data[offset + 1].to_int() == 0x50 &&
77	  data[offset + 2].to_int() == 0x4E &&
78	  data[offset + 3].to_int() == 0x47 &&
79	  data[offset + 4].to_int() == 0x0D &&
80	  data[offset + 5].to_int() == 0x0A &&
81	  data[offset + 6].to_int() == 0x1A &&
82	  data[offset + 7].to_int() == 0x0A
83	}
  	…

13 uncovered line(s) in src\pure\codec\gif_animation_decode.mbt:

58	fn gif_skip_sub_blocks(
59	  data : Bytes,
60	  pos : Int,
61	  len : Int,
62	) -> Int raise @types.LoadError {
63	  let mut p = pos
64	  while p < len {
65	    let sub_len = data[p].to_int()
66	    p += 1
67	    if sub_len == 0 {
68	      break
69	    }
70	    if p + sub_len > len {
71	      raise @types.LoadError::DecodeFailed("GIF 子块长度越界")	<-- UNCOVERED
72	    }
73	    p += sub_len
74	  }
75	  p
76	}
  	…

 80	fn gif_apply_disposal(
 81	  disposal : Int,
 82	  canvas : Array[Byte],
 83	  prev_canvas : Array[Byte],
 84	  left : Int,
 85	  top : Int,
 86	  img_width : Int,
 87	  img_height : Int,
 88	  screen_width : Int,
 89	  screen_height : Int,
 90	  bg_r : Int,
 91	  bg_g : Int,
 92	  bg_b : Int,
 93	) -> Unit {
 94	  match disposal {
 95	    2 =>
 96	      // restore to background
 97	      for y in 0..<img_height {	<-- UNCOVERED
 98	        for x in 0..<img_width {
 99	          let cx = left + x
100	          let cy = top + y
101	          if cx >= screen_width || cy >= screen_height {
102	            continue	<-- UNCOVERED
103	          }
104	          let canvas_off = (cy * screen_width + cx) * 3
105	          canvas[canvas_off] = bg_r.to_byte()	<-- UNCOVERED
106	          canvas[canvas_off + 1] = bg_g.to_byte()
107	          canvas[canvas_off + 2] = bg_b.to_byte()
108	        }
109	      }
110	    3 =>
111	      // restore to previous
112	      for i in 0..<(screen_width * screen_height * 3) {	<-- UNCOVERED
113	        canvas[i] = prev_canvas[i]
114	      }
115	    _ => ()
116	  }
117	}
   	…

121	fn gif_write_frame_pixels(
122	  indices : Array[Int],
123	  color_table : Array[(Int, Int, Int)],
124	  left : Int,
125	  top : Int,
126	  img_width : Int,
127	  img_height : Int,
128	  screen_width : Int,
129	  screen_height : Int,
130	  gce_transparent : Int,
131	  canvas : Array[Byte],
132	) -> Unit raise @types.LoadError {
133	  for y in 0..<img_height {
134	    for x in 0..<img_width {
135	      let idx = indices[y * img_width + x]
136	      let cx = left + x
137	      let cy = top + y
138	      if cx >= screen_width || cy >= screen_height {
139	        continue	<-- UNCOVERED
140	      }
141	      if gce_transparent >= 0 && idx == gce_transparent {
142	        continue	<-- UNCOVERED
143	      }
144	      if idx >= color_table.length() {
145	        raise @types.LoadError::DecodeFailed("GIF 颜色表索引越界")
146	      }
147	      let (r, g, b) = color_table[idx]
148	      let canvas_off = (cy * screen_width + cx) * 3
149	      canvas[canvas_off] = r.to_byte()
150	      canvas[canvas_off + 1] = g.to_byte()
151	      canvas[canvas_off + 2] = b.to_byte()
152	    }
153	  }
154	}
   	…

160	pub fn decode_gif_animation_pure(
161	  data : Bytes,
162	) -> @types.GifAnimation raise @types.LoadError {
163	  let len = data.length()
164	  if len < 13 {
165	    raise @types.LoadError::DecodeFailed(
166	      "GIF 数据过短：无法读取完整 header",
167	    )
168	  }
169	  if data[0].to_int() != 0x47 ||
170	    data[1].to_int() != 0x49 ||
171	    data[2].to_int() != 0x46 ||
172	    data[3].to_int() != 0x38 ||
173	    (data[4].to_int() != 0x39 && data[4].to_int() != 0x37) ||
174	    data[5].to_int() != 0x61 {
175	    raise @types.LoadError::DecodeFailed(
176	      "GIF signature 错误，非 \"GIF87a\"/\"GIF89a\"",
177	    )
178	  }
179	  let screen_width = read_u16_le(data, 6)
180	  let screen_height = read_u16_le(data, 8)
181	  @types.check_dims(screen_width, screen_height, 3)
182	  let packed = data[10].to_int()
183	  let has_gct = (packed & 0x80) != 0
184	  let gct_size = 1 << ((packed & 0x07) + 1)
185	  let bg_idx = data[11].to_int()
186	  let mut pos = 13
187	  let gct : Array[(Int, Int, Int)] = if has_gct {
188	    let (table, new_pos) = gif_read_color_table(data, pos, gct_size, len)
189	    pos = new_pos
190	    table
191	  } else {
192	    []
193	  }
194	  let (bg_r, bg_g, bg_b) = if bg_idx < gct.length() {
195	    gct[bg_idx]
196	  } else {
197	    (0, 0, 0)
198	  }
199	  let frames : Array[@types.Image] = []
200	  let delays : Array[Int] = []
201	  let canvas : Array[Byte] = Array::make(
202	    screen_width * screen_height * 3,
203	    bg_r.to_byte(),
204	  )
205	  for i in 0..<(screen_width * screen_height) {
206	    canvas[i * 3] = bg_r.to_byte()
207	    canvas[i * 3 + 1] = bg_g.to_byte()
208	    canvas[i * 3 + 2] = bg_b.to_byte()
209	  }
210	  let prev_canvas : Array[Byte] = Array::make(
211	    screen_width * screen_height * 3,
212	    b'\x00',
213	  )
214	  let mut gce_delay = 0
215	  let mut gce_transparent = -1
216	  let mut gce_disposal = 0
217	  while pos < len {
218	    let block_type = data[pos].to_int()
219	    pos += 1
220	    match block_type {
221	      0x2C => {
222	        // Image Descriptor
223	        if pos + 9 > len {
224	          raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
225	            "GIF 数据过短：无法读取 Image Descriptor",
226	          )
227	        }
228	        let left = read_u16_le(data, pos)
229	        let top = read_u16_le(data, pos + 2)
230	        let img_width = read_u16_le(data, pos + 4)
231	        let img_height = read_u16_le(data, pos + 6)
232	        let img_packed = data[pos + 8].to_int()
233	        pos += 9
234	        let has_lct = (img_packed & 0x80) != 0
235	        let is_interlaced = (img_packed & 0x40) != 0
236	        let lct_size_val = 1 << ((img_packed & 0x07) + 1)
237	        if is_interlaced {
238	          raise @types.LoadError::DecodeFailed("GIF interlace 不支持")
239	        }
240	        let lct : Array[(Int, Int, Int)] = if has_lct {
241	          let (table, new_pos) = gif_read_color_table(
242	            data, pos, lct_size_val, len,
243	          )
244	          pos = new_pos
245	          table
246	        } else {
247	          []
248	        }
249	        if pos >= len {
250	          raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
251	            "GIF 数据过短：无法读取 LZW min code size",
252	          )
253	        }
254	        let lzw_min_code_size = data[pos].to_int()
255	        pos += 1
256	        if lzw_min_code_size < 2 || lzw_min_code_size > 8 {
257	          raise @types.LoadError::DecodeFailed(
258	            "GIF LZW minimum code size 无效",
259	          )
260	        }
261	        let (lzw_data, new_pos) = gif_read_sub_blocks(data, pos, len)
262	        pos = new_pos
263	        let indices = lzw_decode(lzw_data, lzw_min_code_size)
264	        let color_table = if lct.length() > 0 { lct } else { gct }
265	        if color_table.length() == 0 {
266	          raise @types.LoadError::DecodeFailed("GIF 无颜色表可用")	<-- UNCOVERED
267	        }
268	        for i in 0..<(screen_width * screen_height * 3) {
269	          prev_canvas[i] = canvas[i]
270	        }
271	        gif_write_frame_pixels(
272	          indices, color_table, left, top, img_width, img_height, screen_width, screen_height,
273	          gce_transparent, canvas,
274	        )
275	        let frame_data : Array[Byte] = Array::make(
276	          screen_width * screen_height * 3,
277	          b'\x00',
278	        )
279	        for i in 0..<(screen_width * screen_height * 3) {
280	          frame_data[i] = canvas[i]
281	        }
282	        frames.push(@types.Image::{
283	          width: screen_width,
284	          height: screen_height,
285	          channels: 3,
286	          data: Bytes::from_array(frame_data),
287	        })
288	        delays.push(gce_delay)
289	        gif_apply_disposal(
290	          gce_disposal, canvas, prev_canvas, left, top, img_width, img_height, screen_width,
291	          screen_height, bg_r, bg_g, bg_b,
292	        )
293	        gce_delay = 0
294	        gce_transparent = -1
295	        gce_disposal = 0
296	      }
297	      0x21 => {
298	        // Extension
299	        if pos >= len {
300	          raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
301	            "GIF 数据过短：无法读取 Extension label",
302	          )
303	        }
304	        let label = data[pos].to_int()
305	        pos += 1
306	        if label == 0xF9 && pos + 6 <= len {
307	          // Graphics Control Extension
308	          let _block_size = data[pos].to_int()
309	          let gce_packed = data[pos + 1].to_int()
310	          gce_delay = read_u16_le(data, pos + 2) * 10
311	          gce_transparent = if (gce_packed & 0x01) != 0 {
312	            data[pos + 4].to_int()	<-- UNCOVERED
313	          } else {
314	            -1
315	          }
316	          gce_disposal = (gce_packed >> 2) & 0x07
317	          pos += 6
318	        } else if label == 0x01 {
319	          // Plain Text Extension
320	          if pos + 8 > len {
321	            raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
322	              "GIF 数据过短：无法读取 Plain Text Extension header",
323	            )
324	          }
325	          pos += 8
326	          pos = gif_skip_sub_blocks(data, pos, len)
327	        } else {
328	          pos = gif_skip_sub_blocks(data, pos, len)
329	        }
330	      }
331	      0x3B => break
332	      _ => raise @types.LoadError::DecodeFailed("GIF 未知 block type")
333	    }
334	  }
335	  if frames.length() == 0 {
336	    raise @types.LoadError::DecodeFailed("GIF 无 Image Separator")
337	  }
338	  @types.GifAnimation::{ frames, delays }
339	}
   	…

4 uncovered line(s) in src\pure\codec\gif_decode.mbt:

 9	fn lzw_decode(
10	  data : Array[Byte],
11	  min_code_size : Int,
12	) -> Array[Int] raise @types.LoadError {
13	  let clear_code = 1 << min_code_size
14	  let end_code = clear_code + 1
15	  let mut code_width = min_code_size + 1
16	  let mut dict : Array[Array[Int]] = []
17	  for i in 0..<clear_code {
18	    dict.push([i])
19	  }
20	  dict.push([])
21	  dict.push([])
22	  let output : Array[Int] = []
23	  let mut bit_buffer = 0
24	  let mut bit_count = 0
25	  let mut data_pos = 0
26	  let mut prev : Int = -1
27	  let data_len = data.length()
28	  let mut done = false
29	  while !done {
30	    while bit_count < code_width {
31	      if data_pos >= data_len {
32	        done = true
33	        break
34	      }
35	      bit_buffer = bit_buffer | (data[data_pos].to_int() << bit_count)
36	      bit_count += 8
37	      data_pos += 1
38	    }
39	    if done {
40	      break
41	    }
42	    let code = bit_buffer & ((1 << code_width) - 1)
43	    bit_buffer = bit_buffer >> code_width
44	    bit_count -= code_width
45	    if code == clear_code {
46	      dict = []
47	      for i in 0..<clear_code {
48	        dict.push([i])
49	      }
50	      dict.push([])
51	      dict.push([])
52	      code_width = min_code_size + 1
53	      prev = -1
54	    } else if code == end_code {
55	      done = true
56	    } else {
57	      let entry : Array[Int] = if code < dict.length() {
58	        dict[code]
59	      } else if code == dict.length() && prev >= 0 {
60	        let prev_entry = dict[prev]	<-- UNCOVERED
61	        let new_entry : Array[Int] = []
62	        for v in prev_entry {
63	          new_entry.push(v)	<-- UNCOVERED
64	        }
65	        new_entry.push(prev_entry[0])	<-- UNCOVERED
66	        new_entry
67	      } else {
68	        raise @types.LoadError::DecodeFailed(
69	          "GIF LZW 解码错误：code 超出字典范围",
70	        )
71	      }
72	      for v in entry {
73	        output.push(v)
74	      }
75	      if prev >= 0 {
76	        let prev_entry = dict[prev]
77	        let new_entry : Array[Int] = []
78	        for v in prev_entry {
79	          new_entry.push(v)
80	        }
81	        new_entry.push(entry[0])
82	        dict.push(new_entry)
83	        if dict.length() == 1 << code_width && code_width < 12 {
84	          code_width += 1
85	        }
86	      }
87	      prev = code
88	    }
89	  }
90	  output
91	}
  	…

 98	pub fn decode_gif_pure(data : Bytes) -> @types.Image raise @types.LoadError {
 99	  let anim = decode_gif_animation_pure(data)
100	  if anim.frames.length() == 0 {
101	    raise @types.LoadError::DecodeFailed("GIF 无有效帧")	<-- UNCOVERED
102	  }
103	  anim.frames[0]
104	}
   	…

2 uncovered line(s) in src\pure\codec\gif_encode.mbt:

 36	fn lzw_compress(indices : Array[Int], min_code_size : Int) -> Bytes {
 37	  let clear_code = 1 << min_code_size
 38	  let end_code = clear_code + 1
 39	  let code_table : Array[(Int, Int)] = [] // (prefix_code, pixel_value) pairs
 40	  // 初始化 code table
 41	  for i in 0..<clear_code {
 42	    code_table.push((-1, i))
 43	  }
 44	  code_table.push((-1, -1)) // clear code
 45	  code_table.push((-1, -1)) // end code
 46	  let next_code = Ref(clear_code + 2)
 47	  let code_size = Ref(min_code_size + 1)
 48	  let max_code = Ref((1 << code_size.val) - 1)
 49	  // 输出位流
 50	  let bit_buffer = Ref(0)
 51	  let bit_count = Ref(0)
 52	  let output : Array[Byte] = []
 53	  fn emit_code(code : Int) -> Unit {
 54	    bit_buffer.val = bit_buffer.val | (code << bit_count.val)
 55	    bit_count.val += code_size.val
 56	    while bit_count.val >= 8 {
 57	      output.push((bit_buffer.val & 0xFF).to_byte())
 58	      bit_buffer.val = bit_buffer.val >> 8
 59	      bit_count.val -= 8
 60	    }
 61	  }
 62	  emit_code(clear_code)
 63	  if indices.length() > 0 {
 64	    let prefix = Ref(indices[0])
 65	    for i in 1..<indices.length() {
 66	      let pixel = indices[i]
 67	      // 查找 (prefix, pixel) 在 code table 中
 68	      let found = Ref(-1)
 69	      for j in 0..<code_table.length() {
 70	        let (p, v) = code_table[j]
 71	        if p == prefix.val && v == pixel {
 72	          found.val = j
 73	          break
 74	        }
 75	      }
 76	      if found.val >= 0 {
 77	        prefix.val = found.val
 78	      } else {
 79	        emit_code(prefix.val)
 80	        if next_code.val < 4096 {
 81	          code_table.push((prefix.val, pixel))
 82	          if next_code.val > max_code.val && code_size.val < 12 {
 83	            code_size.val += 1	<-- UNCOVERED
 84	            max_code.val = (1 << code_size.val) - 1
 85	          }
 86	          next_code.val += 1
 87	        }
 88	        prefix.val = pixel
 89	      }
 90	    }
 91	    emit_code(prefix.val)
 92	  }
 93	  emit_code(end_code)
 94	  // 刷新剩余位
 95	  if bit_count.val > 0 {
 96	    output.push((bit_buffer.val & 0xFF).to_byte())
 97	  }
 98	  // 包装为 sub-blocks (每块最多 255 字节)
 99	  let result : Array[Byte] = []
100	  let pos = Ref(0)
101	  while pos.val < output.length() {
102	    let block_size = if output.length() - pos.val > 255 {
103	      255	<-- UNCOVERED
104	    } else {
105	      output.length() - pos.val
106	    }
107	    result.push(block_size.to_byte())
108	    for i in 0..<block_size {
109	      result.push(output[pos.val + i])
110	    }
111	    pos.val += block_size
112	  }
113	  result.push(b'\x00') // block terminator
114	  Bytes::from_array(result)
115	}
   	…

11 uncovered line(s) in src\pure\codec\hdr_decode.mbt:

27	fn float_to_rgbe(r : Float, g : Float, b : Float) -> (Byte, Byte, Byte, Byte) {
28	  let max_v = if r > g { r } else { g }
29	  let max_v = if max_v > b { max_v } else { b }
30	  if max_v < 0.0000000001F {
31	    return (b'\x00', b'\x00', b'\x00', b'\x00')
32	  }
33	  let log_max = @math.lnf(max_v) / @math.lnf(2.0F)
34	  let exp = log_max.to_int() + 128
35	  let exp = if exp < 0 { 0 } else if exp > 255 { 255 } else { exp }	<-- UNCOVERED
36	  let scale = @math.powf(2.0F, Float::from_int(exp - 128 - 8))
37	  let ri = (r * scale + 0.5F).to_int().clamp(min=0, max=255)
38	  let gi = (g * scale + 0.5F).to_int().clamp(min=0, max=255)
39	  let bi = (b * scale + 0.5F).to_int().clamp(min=0, max=255)
40	  (ri.to_byte(), gi.to_byte(), bi.to_byte(), exp.to_byte())
41	}
  	…

 45	fn parse_hdr_header(data : Bytes) -> (Int, Int, Int)? {
 46	  let len = data.length()
 47	  // 检查 magic "#?"
 48	  if len < 2 || data[0].to_int() != 0x23 || data[1].to_int() != 0x3F {
 49	    return None
 50	  }
 51	  // 跳过 magic 行和注释行
 52	  let mut pos = 2
 53	  // 跳过到第一个换行
 54	  while pos < len && data[pos].to_int() != 0x0A {
 55	    pos += 1
 56	  }
 57	  pos += 1
 58	  // 跳过 header 行（非尺寸行），尺寸行以 '-' 或 '+' 开头
 59	  while pos < len {
 60	    let b = data[pos].to_int()
 61	    if b == 0x2D || b == 0x2B {
 62	      // '-' 或 '+'，尺寸行开始
 63	      break
 64	    }
 65	    // 跳过当前行到行尾
 66	    while pos < len && data[pos].to_int() != 0x0A {
 67	      pos += 1
 68	    }
 69	    pos += 1
 70	  }
 71	  // 解析尺寸行: "-Y HEIGHT +X WIDTH\n" 或 "+X WIDTH -Y HEIGHT\n"
 72	  let mut sign = 1
 73	  let mut current_val = 0
 74	  let mut has_digit = false
 75	  let vals : Array[Int] = []
 76	  let signs : Array[Int] = []
 77	  while pos < len && data[pos].to_int() != 0x0A {
 78	    let b = data[pos].to_int()
 79	    if b == 0x2D {
 80	      // '-'
 81	      sign = -1
 82	    } else if b == 0x2B {
 83	      // '+'
 84	      sign = 1
 85	    } else if b >= 0x30 && b <= 0x39 {
 86	      // digit
 87	      current_val = current_val * 10 + (b - 0x30)
 88	      has_digit = true
 89	    } else if has_digit {
 90	      vals.push(current_val * sign)
 91	      signs.push(sign)
 92	      current_val = 0
 93	      has_digit = false
 94	      sign = 1
 95	    }
 96	    pos += 1
 97	  }
 98	  if has_digit {
 99	    vals.push(current_val * sign)
100	    signs.push(sign)
101	  }
102	  pos += 1
103	  // vals 应该有 2 个值，一个是 Y（height），一个是 X（width）
104	  if vals.length() < 2 {
105	    return None
106	  }
107	  let (width, height) = if signs[0] < 0 && signs[1] > 0 {
108	    (vals[1].abs(), vals[0].abs())
109	  } else if signs[0] > 0 && signs[1] < 0 {	<-- UNCOVERED
110	    (vals[0].abs(), vals[1].abs())
111	  } else {
112	    (vals[0].abs(), vals[1].abs())	<-- UNCOVERED
113	  }
114	  if width <= 0 || height <= 0 {
115	    return None	<-- UNCOVERED
116	  }
117	  Some((width, height, pos))
118	}
   	…

122	fn rle_decode_scanline_channel(
123	  data : Bytes,
124	  pos : Int,
125	  len : Int,
126	  scanline_len : Int,
127	) -> (Array[Byte], Int)? {
128	  let out : Array[Byte] = []
129	  let mut p = pos
130	  while out.length() < scanline_len {
131	    if p >= len {
132	      return None	<-- UNCOVERED
133	    }
134	    let code = data[p].to_int()
135	    p += 1
136	    if code > 128 {
137	      // RLE: 重复
138	      let count = code - 128
139	      if p >= len {
140	        return None	<-- UNCOVERED
141	      }
142	      let val = data[p]
143	      p += 1
144	      for _ in 0..<count {
145	        out.push(val)
146	      }
147	    } else {
148	      // 非 RLE: 逐个读取
149	      let count = code
150	      if p + count > len {
151	        return None	<-- UNCOVERED
152	      }
153	      for i in 0..<count {
154	        out.push(data[p + i])
155	      }
156	      p += count
157	    }
158	  }
159	  if out.length() != scanline_len {
160	    return None	<-- UNCOVERED
161	  }
162	  Some((out, p))
163	}
   	…

168	pub fn decode_hdr_pure(data : Bytes) -> @types.ImageF raise @types.LoadError {
169	  let (width, height, data_start) = match parse_hdr_header(data) {
170	    Some(v) => v
171	    None => raise @types.LoadError::DecodeFailed("invalid HDR header")
172	  }
173	  let len = data.length()
174	  @types.check_dims(width, height, 3)
175	  let pixel_count = width * height
176	  // RGBE 数据：每像素 4 字节
177	  let rgbe : Array[Byte] = Array::make(pixel_count * 4, b'\x00')
178	  let mut pos = data_start
179	  // 检查是否为 RLE 编码
180	  let is_rle = pos + 4 <= len &&
181	    data[pos].to_int() == 2 &&
182	    data[pos + 1].to_int() == 2 &&
183	    data[pos + 2].to_int() * 256 + data[pos + 3].to_int() == width
184	  if is_rle {
185	    // RLE 解码：每行 4 通道分别 RLE 编码
186	    for y in 0..<height {
187	      // 检查行头 [2, 2, hi, lo]
188	      if pos + 4 > len || data[pos].to_int() != 2 || data[pos + 1].to_int() != 2 {
189	        raise @types.LoadError::DecodeFailed("invalid RLE scanline header")	<-- UNCOVERED
190	      }
191	      let scanline_w = data[pos + 2].to_int() * 256 + data[pos + 3].to_int()
192	      pos += 4
193	      if scanline_w != width {
194	        raise @types.LoadError::DecodeFailed("scanline width mismatch")	<-- UNCOVERED
195	      }
196	      // 4 通道分别解码
197	      for c in 0..<4 {
198	        let (channel_data, new_pos) = match
199	          rle_decode_scanline_channel(data, pos, len, width) {
200	          Some(v) => v
201	          None => raise @types.LoadError::DecodeFailed("RLE decode failed")	<-- UNCOVERED
202	        }
203	        pos = new_pos
204	        for x in 0..<width {
205	          rgbe[(y * width + x) * 4 + c] = channel_data[x]
206	        }
207	      }
208	    }
209	  } else {
210	    // 未压缩：逐像素读取 RGBE
211	    if pos + pixel_count * 4 > len {
212	      raise @types.LoadError::DecodeFailed("insufficient uncompressed HDR data")
213	    }
214	    for i in 0..<(pixel_count * 4) {
215	      rgbe[i] = data[pos + i]
216	    }
217	  }
218	  // RGBE → float
219	  let float_data : Array[Byte] = Array::make(pixel_count * 3 * 4, b'\x00')
220	  for i in 0..<pixel_count {
221	    let (r, g, b) = rgbe_to_float(
222	      rgbe[i * 4],
223	      rgbe[i * 4 + 1],
224	      rgbe[i * 4 + 2],
225	      rgbe[i * 4 + 3],
226	    )
227	    // 写入 little-endian float
228	    let r_bytes = r.to_le_bytes()
229	    let g_bytes = g.to_le_bytes()
230	    let b_bytes = b.to_le_bytes()
231	    float_data[i * 12] = r_bytes[0]
232	    float_data[i * 12 + 1] = r_bytes[1]
233	    float_data[i * 12 + 2] = r_bytes[2]
234	    float_data[i * 12 + 3] = r_bytes[3]
235	    float_data[i * 12 + 4] = g_bytes[0]
236	    float_data[i * 12 + 5] = g_bytes[1]
237	    float_data[i * 12 + 6] = g_bytes[2]
238	    float_data[i * 12 + 7] = g_bytes[3]
239	    float_data[i * 12 + 8] = b_bytes[0]
240	    float_data[i * 12 + 9] = b_bytes[1]
241	    float_data[i * 12 + 10] = b_bytes[2]
242	    float_data[i * 12 + 11] = b_bytes[3]
243	  }
244	  @types.ImageF::{
245	    width,
246	    height,
247	    channels: 3,
248	    data: Bytes::from_array(float_data),
249	  }
250	}
   	…

1 uncovered line(s) in src\pure\codec\hdr_encode.mbt:

 7	fn rle_encode_scanline_channel(
 8	  channel_data : Array[Byte],
 9	  scanline_len : Int,
10	) -> Array[Byte] {
11	  let out : Array[Byte] = []
12	  let mut pos = 0
13	  while pos < scanline_len {
14	    // 查找重复 run
15	    let mut run_len = 1
16	    while pos + run_len < scanline_len &&
17	          channel_data[pos + run_len] == channel_data[pos] &&
18	          run_len < 127 {
19	      run_len += 1
20	    }
21	    if run_len >= 4 {
22	      // RLE 编码
23	      out.push((128 + run_len).to_byte())
24	      out.push(channel_data[pos])
25	      pos += run_len
26	    } else {
27	      // 查找非重复 run
28	      let mut lit_len = 1
29	      while pos + lit_len < scanline_len && lit_len < 128 {
30	        // 检查是否开始新的重复 run
31	        let mut next_run = 0
32	        while pos + lit_len + next_run < scanline_len &&
33	              channel_data[pos + lit_len + next_run] ==
34	              channel_data[pos + lit_len] &&
35	              next_run < 3 {
36	          next_run += 1
37	        }
38	        if next_run >= 4 {
39	          break	<-- UNCOVERED
40	        }
41	        lit_len += 1
42	      }
43	      // 非重复编码
44	      out.push(lit_len.to_byte())
45	      for i in 0..<lit_len {
46	        out.push(channel_data[pos + i])
47	      }
48	      pos += lit_len
49	    }
50	  }
51	  out
52	}
  	…

7 uncovered line(s) in src\pure\codec\heif_decode.mbt:

24	pub fn is_heif_format(data : Bytes) -> Bool {
25	  let len = data.length()
26	  if len < 12 {
27	    return false
28	  }
29	  // 检查 ftyp box: bytes 4-7 = "ftyp"
30	  if data[4] != b'f' || data[5] != b't' || data[6] != b'y' || data[7] != b'p' {
31	    return false	<-- UNCOVERED
32	  }
33	  let b8 = data[8]
34	  let b9 = data[9]
35	  let b10 = data[10]
36	  let b11 = data[11]
37	  // "heic" = 0x68 0x65 0x69 0x63
38	  if b8 == b'h' && b9 == b'e' && b10 == b'i' && b11 == b'c' {
39	    return true
40	  }
41	  // "heix" = 0x68 0x65 0x69 0x78
42	  if b8 == b'h' && b9 == b'e' && b10 == b'i' && b11 == b'x' {
43	    return true
44	  }
45	  // "hevc" = 0x68 0x65 0x76 0x63
46	  if b8 == b'h' && b9 == b'e' && b10 == b'v' && b11 == b'c' {
47	    return true
48	  }
49	  // "mif1" = 0x6D 0x69 0x66 0x31
50	  if b8 == b'm' && b9 == b'i' && b10 == b'f' && b11 == b'1' {
51	    return true
52	  }
53	  false
54	}
  	…

 60	pub fn heif_info(data : Bytes) -> HeifInfo raise @types.LoadError {
 61	  if !is_heif_format(data) {
 62	    raise @types.LoadError::UnsupportedFormat("Not a HEIF file")
 63	  }
 64	  let len = data.length()
 65	  let mut offset = 0
 66	  let mut width = 0
 67	  let mut height = 0
 68	  let mut bit_depth = 8
 69	  let mut has_alpha = false
 70	  // 遍历 ISOBMFF boxes
 71	  while offset + 8 <= len {
 72	    let box_size = (data[offset].to_int() << 24) |
 73	      (data[offset + 1].to_int() << 16) |
 74	      (data[offset + 2].to_int() << 8) |
 75	      data[offset + 3].to_int()
 76	    if box_size < 8 || offset + box_size > len {
 77	      break	<-- UNCOVERED
 78	    }
 79	    let box_type_0 = data[offset + 4]
 80	    let box_type_1 = data[offset + 5]
 81	    let box_type_2 = data[offset + 6]
 82	    let box_type_3 = data[offset + 7]
 83	    // 检查 ispe box (ImageSpatialExtentsProperty)
 84	    if box_type_0 == b'i' &&
 85	      box_type_1 == b's' &&
 86	      box_type_2 == b'p' &&
 87	      box_type_3 == b'e' {
 88	      if offset + 20 <= len {	<-- UNCOVERED
 89	        width = (data[offset + 12].to_int() << 24) |
 90	          (data[offset + 13].to_int() << 16) |
 91	          (data[offset + 14].to_int() << 8) |
 92	          data[offset + 15].to_int()
 93	        height = (data[offset + 16].to_int() << 24) |
 94	          (data[offset + 17].to_int() << 16) |
 95	          (data[offset + 18].to_int() << 8) |
 96	          data[offset + 19].to_int()
 97	      }
 98	    }
 99	    // 检查 auxC box for alpha
100	    if box_type_0 == b'a' &&
101	      box_type_1 == b'u' &&
102	      box_type_2 == b'x' &&
103	      box_type_3 == b'C' {
104	      has_alpha = true	<-- UNCOVERED
105	    }
106	    // 检查 pixi box for bit depth
107	    if box_type_0 == b'p' &&
108	      box_type_1 == b'i' &&
109	      box_type_2 == b'x' &&
110	      box_type_3 == b'i' {
111	      if offset + 13 <= len {	<-- UNCOVERED
112	        let num_ch = data[offset + 12].to_int()
113	        if num_ch > 0 && offset + 13 + num_ch <= len {
114	          bit_depth = data[offset + 13].to_int()	<-- UNCOVERED
115	        }
116	      }
117	    }
118	    offset += box_size
119	  }
120	  if width == 0 || height == 0 {
121	    raise @types.LoadError::DecodeFailed(
122	      "HEIF: could not determine image dimensions",
123	    )
124	  }
125	  let num_channels = if has_alpha { 4 } else { 3 }	<-- UNCOVERED
126	  HeifInfo::{ width, height, bit_depth, num_channels, has_alpha }
127	}
   	…

8 uncovered line(s) in src\pure\codec\icns_codec.mbt:

20	fn icns_type_to_string(data : Bytes, offset : Int) -> String {
21	  let buf : Array[Byte] = [
22	    data[offset],
23	    data[offset + 1],
24	    data[offset + 2],
25	    data[offset + 3],
26	  ]
27	  @encoding/utf8.decode(Bytes::from_array(buf)) catch {
28	    _ => "????"	<-- UNCOVERED
29	  }
30	}
  	…

34	fn icns_icon_info(type_code : String) -> (Int, Bool) {
35	  match type_code {
36	    "ic07" => (128, true) // 128x128 PNG
37	    "ic08" => (256, true) // 256x256 PNG	<-- UNCOVERED
38	    "ic09" => (512, true) // 512x512 PNG
39	    "ic10" => (1024, true) // 1024x1024 PNG
40	    "ic11" => (16, true) // 16x16 PNG
41	    "ic12" => (32, true) // 32x32 PNG
42	    "ic13" => (64, true) // 64x64 PNG	<-- UNCOVERED
43	    "ic14" => (256, true) // 256x256 PNG
44	    "ic04" => (16, false) // 16x16 ARGB
45	    "ic05" => (32, false) // 32x32 ARGB	<-- UNCOVERED
46	    "icp4" => (16, false) // 16x16 ARGB
47	    "icp5" => (32, false) // 32x32 ARGB
48	    "icp6" => (64, false) // 64x64 ARGB
49	    "icp7" => (128, false) // 128x128 ARGB
50	    "icp8" => (256, false) // 256x256 ARGB
51	    "icp9" => (512, false) // 512x512 ARGB
52	    _ => (0, false)
53	  }
54	}
  	…

58	fn icns_size_to_type(size : Int) -> String {
59	  match size {
60	    16 => "ic11"
61	    32 => "ic12"
62	    64 => "ic13"
63	    128 => "ic07"
64	    256 => "ic08"	<-- UNCOVERED
65	    512 => "ic09"
66	    1024 => "ic10"
67	    _ => "ic08" // 默认用 256x256
68	  }
69	}
  	…

 73	fn parse_icns(data : Bytes) -> Array[IcnsChunk] raise @types.LoadError {
 74	  let len = data.length()
 75	  if len < 8 {
 76	    raise @types.LoadError::DecodeFailed("ICNS 数据过短")
 77	  }
 78	  // 检查 magic "icns"
 79	  if data[0].to_int() != 0x69 ||
 80	    data[1].to_int() != 0x63 ||
 81	    data[2].to_int() != 0x6E ||
 82	    data[3].to_int() != 0x73 {
 83	    raise @types.LoadError::DecodeFailed("ICNS magic 不匹配")
 84	  }
 85	  let file_size = read_u32_be(data, 4)
 86	  let chunks : Array[IcnsChunk] = []
 87	  let mut pos = 8
 88	  let end = if file_size < len { file_size } else { len }
 89	  while pos + 8 <= end {
 90	    let type_code = icns_type_to_string(data, pos)
 91	    let chunk_len = read_u32_be(data, pos + 4)
 92	    if chunk_len < 8 || pos + chunk_len > end {
 93	      break	<-- UNCOVERED
 94	    }
 95	    let data_offset = pos + 8
 96	    let data_size = chunk_len - 8
 97	    let (icon_size, is_png) = icns_icon_info(type_code)
 98	    chunks.push(IcnsChunk::{ data_offset, data_size, icon_size, is_png })
 99	    pos += chunk_len
100	  }
101	  if chunks.length() == 0 {
102	    raise @types.LoadError::DecodeFailed("ICNS 无图标 chunk")
103	  }
104	  chunks
105	}
   	…

109	fn decode_icns_argb(
110	  data : Bytes,
111	  offset : Int,
112	  size : Int,
113	) -> @types.Image raise @types.LoadError {
114	  let len = data.length()
115	  let pixel_count = size * size
116	  if offset + pixel_count * 4 > len {
117	    raise @types.LoadError::DecodeFailed("ICNS ARGB 数据不完整")	<-- UNCOVERED
118	  }
119	  let pixels : Array[Byte] = Array::make(pixel_count * 4, b'\x00')
120	  for i in 0..<pixel_count {
121	    let src = offset + i * 4
122	    let a = data[src].to_int()
123	    let r = data[src + 1].to_int()
124	    let g = data[src + 2].to_int()
125	    let b = data[src + 3].to_int()
126	    let dst = i * 4
127	    pixels[dst] = r.to_byte()
128	    pixels[dst + 1] = g.to_byte()
129	    pixels[dst + 2] = b.to_byte()
130	    pixels[dst + 3] = a.to_byte()
131	  }
132	  @types.Image::{
133	    width: size,
134	    height: size,
135	    channels: 4,
136	    data: Bytes::from_array(pixels),
137	  }
138	}
   	…

143	pub fn decode_icns_pure(data : Bytes) -> @types.Image raise @types.LoadError {
144	  let chunks = parse_icns(data)
145	  // 选择面积最大的图标 chunk
146	  let mut best_idx = 0
147	  let mut best_area = 0
148	  for i in 0..<chunks.length() {
149	    let area = chunks[i].icon_size * chunks[i].icon_size
150	    if area > best_area {
151	      best_area = area
152	      best_idx = i
153	    }
154	  }
155	  let chunk = chunks[best_idx]
156	  if chunk.icon_size == 0 {
157	    raise @types.LoadError::DecodeFailed("ICNS 无有效图标 chunk")	<-- UNCOVERED
158	  }
159	  if chunk.is_png {
160	    // PNG 子图像
161	    let sub : Array[Byte] = []
162	    for i in 0..<chunk.data_size {
163	      sub.push(data[chunk.data_offset + i])
164	    }
165	    decode_png_pure(Bytes::from_array(sub))
166	  } else {
167	    // ARGB 原始数据
168	    decode_icns_argb(data, chunk.data_offset, chunk.icon_size)
169	  }
170	}
   	…

17 uncovered line(s) in src\pure\codec\ico_codec.mbt:

 77	fn decode_ico_bmp_subimage(
 78	  data : Bytes,
 79	  offset : Int,
 80	  byte_size : Int,
 81	) -> @types.Image raise @types.LoadError {
 82	  let len = data.length()
 83	  if offset + 40 > len {
 84	    raise @types.LoadError::DecodeFailed("ICO BMP 子图像数据不完整")	<-- UNCOVERED
 85	  }
 86	  let dib_size = read_u32_le(data, offset)
 87	  if dib_size < 40 {
 88	    raise @types.LoadError::DecodeFailed("ICO BMP DIB 头过短")
 89	  }
 90	  let width = read_u32_le(data, offset + 4)
 91	  let height2 = read_u32_le(data, offset + 8)
 92	  let bpp = read_u16_le(data, offset + 14)
 93	  let compression = read_u32_le(data, offset + 16)
 94	  if compression != 0 {
 95	    raise @types.LoadError::DecodeFailed("ICO BMP 子图像不支持压缩")
 96	  }
 97	  let height = height2 / 2
 98	  if width <= 0 || height <= 0 {
 99	    raise @types.LoadError::DecodeFailed("ICO BMP 子图像尺寸无效")
100	  }
101	  let abs_w = if width < 0 { -width } else { width }
102	  let abs_h = if height < 0 { -height } else { height }
103	  let color_offset = offset + dib_size
104	  let bytes_per_pixel = bpp / 8
105	  let row_size = (abs_w * bytes_per_pixel + 3) / 4 * 4
106	  // AND 掩码：1 bit/pixel，每行 4 字节对齐
107	  let and_row_size = (abs_w + 31) / 32 * 4
108	  let out_channels = 4
109	  let pixel_count = abs_w * abs_h
110	  let pixels : Array[Byte] = Array::make(pixel_count * out_channels, b'\x00')
111	  // 色彩数据自下而上
112	  for file_row in 0..<abs_h {
113	    let out_row = abs_h - 1 - file_row
114	    let row_start = color_offset + file_row * row_size
115	    for x in 0..<abs_w {
116	      let px_off = row_start + x * bytes_per_pixel
117	      if px_off + bytes_per_pixel > len {
118	        raise @types.LoadError::DecodeFailed("ICO BMP 像素数据越界")
119	      }
120	      let b = data[px_off].to_int()
121	      let g = data[px_off + 1].to_int()
122	      let r = data[px_off + 2].to_int()
123	      let a = if bpp == 32 { data[px_off + 3].to_int() } else { 255 }
124	      let out_idx = (out_row * abs_w + x) * out_channels
125	      pixels[out_idx] = r.to_byte()
126	      pixels[out_idx + 1] = g.to_byte()
127	      pixels[out_idx + 2] = b.to_byte()
128	      pixels[out_idx + 3] = a.to_byte()
129	    }
130	  }
131	  // 处理 AND 掩码（1-bit 透明度），仅对非 32-bit 有效
132	  if bpp != 32 {
133	    let and_offset = color_offset + abs_h * row_size	<-- UNCOVERED
134	    for file_row in 0..<abs_h {
135	      let out_row = abs_h - 1 - file_row	<-- UNCOVERED
136	      let and_row_start = and_offset + file_row * and_row_size
137	      for x in 0..<abs_w {
138	        let byte_idx = x / 8	<-- UNCOVERED
139	        let bit_idx = 7 - x % 8
140	        if and_row_start + byte_idx < len {
141	          let mask_byte = data[and_row_start + byte_idx].to_int()	<-- UNCOVERED
142	          if ((mask_byte >> bit_idx) & 1) == 1 {
143	            let out_idx = (out_row * abs_w + x) * out_channels	<-- UNCOVERED
144	            pixels[out_idx + 3] = b'\x00'
145	          }
146	        }
147	      }
148	    }
149	  }
150	  let _ = byte_size
151	  @types.Image::{
152	    width: abs_w,
153	    height: abs_h,
154	    channels: out_channels,
155	    data: Bytes::from_array(pixels),
156	  }
157	}
   	…

162	pub fn decode_ico_pure(data : Bytes) -> @types.Image raise @types.LoadError {
163	  let (icon_type, entries) = parse_ico_header(data)
164	  if icon_type != 1 {
165	    raise @types.LoadError::DecodeFailed("ICO type 应为 1")	<-- UNCOVERED
166	  }
167	  let best = select_best_entry(entries)
168	  let entry = entries[best]
169	  let len = data.length()
170	  if entry.offset + entry.byte_size > len {
171	    raise @types.LoadError::DecodeFailed("ICO 子图像数据越界")
172	  }
173	  if is_png_signature(data, entry.offset) {
174	    // PNG 子图像：提取子字节流并委托 PNG 解码器
175	    let sub : Array[Byte] = []	<-- UNCOVERED
176	    for i in 0..<entry.byte_size {
177	      sub.push(data[entry.offset + i])	<-- UNCOVERED
178	    }
179	    decode_png_pure(Bytes::from_array(sub))	<-- UNCOVERED
180	  } else {
181	    decode_ico_bmp_subimage(data, entry.offset, entry.byte_size)
182	  }
183	}
   	…

187	pub fn decode_cur_pure(data : Bytes) -> @types.Image raise @types.LoadError {
188	  let (icon_type, entries) = parse_ico_header(data)
189	  if icon_type != 2 {
190	    raise @types.LoadError::DecodeFailed("CUR type 应为 2")	<-- UNCOVERED
191	  }
192	  let best = select_best_entry(entries)
193	  let entry = entries[best]
194	  let len = data.length()
195	  if entry.offset + entry.byte_size > len {
196	    raise @types.LoadError::DecodeFailed("CUR 子图像数据越界")	<-- UNCOVERED
197	  }
198	  if is_png_signature(data, entry.offset) {
199	    let sub : Array[Byte] = []	<-- UNCOVERED
200	    for i in 0..<entry.byte_size {
201	      sub.push(data[entry.offset + i])	<-- UNCOVERED
202	    }
203	    decode_png_pure(Bytes::from_array(sub))	<-- UNCOVERED
204	  } else {
205	    decode_ico_bmp_subimage(data, entry.offset, entry.byte_size)
206	  }
207	}
   	…

212	fn encode_ico_bmp_subimage(img : @types.Image) -> (Array[Byte], Int, Int) {
213	  let width = img.width
214	  let height = img.height
215	  let channels = img.channels
216	  let data = img.data
217	  let out : Array[Byte] = []
218	  // BITMAPINFOHEADER (40 bytes)
219	  write_u32_le(out, 40) // biSize
220	  write_u32_le(out, width) // biWidth
221	  write_u32_le(out, height * 2) // biHeight = 2*h (XOR + AND)
222	  write_u16_le(out, 1) // biPlanes
223	  write_u16_le(out, 32) // biBitCount
224	  write_u32_le(out, 0) // biCompression = BI_RGB
225	  let xor_size = width * height * 4
226	  write_u32_le(out, xor_size) // biSizeImage
227	  write_u32_le(out, 0) // biXPelsPerMeter
228	  write_u32_le(out, 0) // biYPelsPerMeter
229	  write_u32_le(out, 0) // biClrUsed
230	  write_u32_le(out, 0) // biClrImportant
231	  // XOR 掩码：32-bit BGRA，自下而上
232	  for y in 0..<height {
233	    let ry = height - 1 - y
234	    let row_start = ry * width * channels
235	    for x in 0..<width {
236	      let idx = row_start + x * channels
237	      let r = data[idx].to_int()
238	      let g = if channels >= 2 { data[idx + 1].to_int() } else { r }
239	      let b = if channels >= 3 { data[idx + 2].to_int() } else { r }
240	      let a = if channels >= 4 { data[idx + 3].to_int() } else { 255 }
241	      out.push(b.to_byte())
242	      out.push(g.to_byte())
243	      out.push(r.to_byte())
244	      out.push(a.to_byte())
245	    }
246	  }
247	  // AND 掩码：1-bit/pixel，自下而上，每行 4 字节对齐
248	  let and_row_bytes = (width + 31) / 32 * 4
249	  for y in 0..<height {
250	    let ry = height - 1 - y
251	    let row_start = ry * width * channels
252	    let and_row : Array[Byte] = Array::make(and_row_bytes, b'\x00')
253	    for x in 0..<width {
254	      let idx = row_start + x * channels
255	      let a = if channels >= 4 { data[idx + 3].to_int() } else { 255 }
256	      // 透明像素 AND 掩码位置 1，不透明位置 0
257	      if a == 0 {
258	        let byte_idx = x / 8	<-- UNCOVERED
259	        let bit_idx = 7 - x % 8
260	        and_row[byte_idx] = (and_row[byte_idx].to_int() | (1 << bit_idx)).to_byte()	<-- UNCOVERED
261	      }
262	    }
263	    for i in 0..<and_row_bytes {
264	      out.push(and_row[i])
265	    }
266	  }
267	  (out, width, height)
268	}
   	…

16 uncovered line(s) in src\pure\codec\jpeg_decode.mbt:

63	fn jpeg_read_bit(r : JpegBitReader) -> Int raise @types.LoadError {
64	  if r.bit_cnt == 0 {
65	    if r.pos >= r.len {
66	      raise @types.LoadError::DecodeFailed("JPEG 数据意外结束")	<-- UNCOVERED
67	    }
68	    let b = r.data[r.pos].to_int()
69	    r.pos += 1
70	    if b == 0xFF {
71	      if r.pos >= r.len {
72	        raise @types.LoadError::DecodeFailed("JPEG 数据意外结束于 0xFF")	<-- UNCOVERED
73	      }
74	      let next = r.data[r.pos].to_int()
75	      r.pos += 1
76	      if next == 0x00 {
77	        r.bit_buf = 0xFF
78	      } else if next >= 0xD0 && next <= 0xD7 {
79	        r.bit_buf = 0	<-- UNCOVERED
80	      } else {
81	        raise @types.LoadError::DecodeFailed("JPEG 无效标记 0xFF")
82	      }
83	    } else {
84	      r.bit_buf = b
85	    }
86	    r.bit_cnt = 8
87	  }
88	  let bit = (r.bit_buf >> 7) & 1
89	  r.bit_buf = (r.bit_buf & 0x7F) << 1
90	  r.bit_cnt -= 1
91	  bit
92	}
  	…

191	fn jpeg_decode_block(
192	  r : JpegBitReader,
193	  dc_table : JpegHuffmanTable,
194	  ac_table : JpegHuffmanTable,
195	  quant : Array[Int],
196	  prev_dc : Ref[Int],
197	  zz : Array[Int],
198	) -> Array[Int] raise @types.LoadError {
199	  let block = Array::make(64, 0)
200	  let s = jpeg_decode_huffman(r, dc_table)
201	  let diff = if s == 0 {
202	    0
203	  } else {
204	    let bits = jpeg_read_bits(r, s)
205	    jpeg_extend(bits, s)
206	  }
207	  block[0] = prev_dc.val + diff
208	  prev_dc.val = block[0]
209	  let mut k = 1
210	  while k < 64 {
211	    let rs = jpeg_decode_huffman(r, ac_table)
212	    let run = (rs >> 4) & 0x0F
213	    let size = rs & 0x0F
214	    if size == 0 {
215	      if run == 15 {
216	        k += 16
217	        continue
218	      } else {
219	        break
220	      }
221	    }
222	    k += run
223	    if k >= 64 {
224	      break	<-- UNCOVERED
225	    }
226	    let bits = jpeg_read_bits(r, size)
227	    block[zz[k]] = jpeg_extend(bits, size)
228	    k += 1
229	  }
230	  for i in 0..<64 {
231	    block[i] = block[i] * quant[i]
232	  }
233	  block
234	}
   	…

270	fn jpeg_parse_header(data : Bytes) -> JpegHeader raise @types.LoadError {
271	  let len = data.length()
272	  if len < 2 || data[0].to_int() != 0xFF || data[1].to_int() != 0xD8 {
273	    raise @types.LoadError::DecodeFailed("JPEG SOI marker 错误")
274	  }
275	  let mut pos = 2
276	  let mut width = 0
277	  let mut height = 0
278	  let quant_tables : Array[Array[Int]] = Array::make(4, Array::make(64, 0))
279	  let huff_dc : Array[JpegHuffmanTable] = Array::make(4, empty_huffman_table())
280	  let huff_ac : Array[JpegHuffmanTable] = Array::make(4, empty_huffman_table())
281	  let mut num_components = 3
282	  let component_ids : Array[Int] = Array::make(4, 0)
283	  let component_h : Array[Int] = Array::make(4, 1)
284	  let component_v : Array[Int] = Array::make(4, 1)
285	  let component_qt : Array[Int] = Array::make(4, 0)
286	  let mut restart_interval = 0
287	  let scan_comp_idx : Array[Int] = Array::make(4, 0)
288	  let scan_dc_table : Array[Int] = Array::make(4, 0)
289	  let scan_ac_table : Array[Int] = Array::make(4, 0)
290	  let mut scan_ns = 0
291	  let mut entropy_start = 0
292	  let zz = zigzag()
293	  while pos < len {
294	    if data[pos].to_int() != 0xFF {
295	      pos += 1
296	      continue
297	    }
298	    let marker = data[pos + 1].to_int()
299	    pos += 2
300	    match marker {
301	      0xC0 => {
302	        // SOF0: length(2) + precision(1) + height(2) + width(2) + num_components(1)
303	        if pos + 9 > len {
304	          raise @types.LoadError::DecodeFailed("JPEG SOF0 过短")	<-- UNCOVERED
305	        }
306	        let _precision = data[pos + 2].to_int()
307	        height = data[pos + 3].to_int() * 256 + data[pos + 4].to_int()
308	        width = data[pos + 5].to_int() * 256 + data[pos + 6].to_int()
309	        num_components = data[pos + 7].to_int()
310	        pos += 8
311	        for i in 0..<num_components {
312	          if pos + 3 > len {
313	            raise @types.LoadError::DecodeFailed("JPEG SOF0 component 过短")
314	          }
315	          component_ids[i] = data[pos].to_int()
316	          let hv = data[pos + 1].to_int()
317	          component_h[i] = (hv >> 4) & 0x0F
318	          component_v[i] = hv & 0x0F
319	          component_qt[i] = data[pos + 2].to_int()
320	          pos += 3
321	        }
322	      }
323	      0xDB => {
324	        // DQT
325	        let seg_len = data[pos].to_int() * 256 + data[pos + 1].to_int()
326	        let seg_end = pos + seg_len
327	        pos += 2
328	        while pos < seg_end {
329	          let pq = data[pos].to_int()
330	          let precision = pq >> 4
331	          let tq = pq & 0x0F
332	          pos += 1
333	          let step = if precision == 0 { 1 } else { 2 }
334	          if tq < 4 {
335	            for i in 0..<64 {
336	              let v = if precision == 0 {
337	                data[pos].to_int()
338	              } else {
339	                data[pos].to_int() * 256 + data[pos + 1].to_int()	<-- UNCOVERED
340	              }
341	              quant_tables[tq][zz[i]] = v
342	              pos += step
343	            }
344	          } else {
345	            pos += 64 * step	<-- UNCOVERED
346	          }
347	        }
348	      }
349	      0xC4 => {
350	        // DHT
351	        let seg_len = data[pos].to_int() * 256 + data[pos + 1].to_int()
352	        let seg_end = pos + seg_len
353	        pos += 2
354	        while pos < seg_end {
355	          let tc_th = data[pos].to_int()
356	          let tc = (tc_th >> 4) & 0x0F
357	          let th = tc_th & 0x0F
358	          pos += 1
359	          let counts = Array::make(16, 0)
360	          for i in 0..<16 {
361	            counts[i] = data[pos + i].to_int()
362	          }
363	          pos += 16
364	          let total = {
365	            let mut s = 0
366	            for c in counts {
367	              s += c
368	            }
369	            s
370	          }
371	          let values = Array::make(total, 0)
372	          for i in 0..<total {
373	            values[i] = data[pos + i].to_int()
374	          }
375	          pos += total
376	          if th < 4 {
377	            let table = build_jpeg_huffman(counts, values)
378	            if tc == 0 {
379	              huff_dc[th] = table
380	            } else {
381	              huff_ac[th] = table
382	            }
383	          }
384	        }
385	      }
386	      0xDD => {	<-- UNCOVERED
387	        // DRI
388	        let seg_len = data[pos].to_int() * 256 + data[pos + 1].to_int()	<-- UNCOVERED
389	        restart_interval = data[pos + 2].to_int() * 256 + data[pos + 3].to_int()
390	        pos += seg_len
391	      }
392	      0xDA => {
393	        // SOS
394	        let seg_len = data[pos].to_int() * 256 + data[pos + 1].to_int()
395	        scan_ns = data[pos + 2].to_int()
396	        let mut p = pos + 3
397	        for i in 0..<scan_ns {
398	          let cs = data[p].to_int()
399	          let td_ta = data[p + 1].to_int()
400	          let mut cidx = 0
401	          for j in 0..<num_components {
402	            if component_ids[j] == cs {
403	              cidx = j
404	              break
405	            }
406	          }
407	          scan_comp_idx[i] = cidx
408	          scan_dc_table[i] = (td_ta >> 4) & 0x0F
409	          scan_ac_table[i] = td_ta & 0x0F
410	          p += 2
411	        }
412	        pos += seg_len
413	        entropy_start = pos
414	        break
415	      }
416	      0xD9 => break	<-- UNCOVERED
417	      _ =>
418	        if pos + 2 <= len {
419	          let seg_len = data[pos].to_int() * 256 + data[pos + 1].to_int()
420	          pos += seg_len
421	        }
422	    }
423	  }
424	  JpegHeader::{
425	    width,
426	    height,
427	    num_components,
428	    quant_tables,
429	    huff_dc,
430	    huff_ac,
431	    component_h,
432	    component_v,
433	    component_qt,
434	    restart_interval,
435	    scan_comp_idx,
436	    scan_dc_table,
437	    scan_ac_table,
438	    scan_ns,
439	    entropy_start,
440	  }
441	}
   	…

445	fn jpeg_write_mcu_pixels(
446	  header : JpegHeader,
447	  comp_data : Array[Array[Int]],
448	  mx : Int,
449	  my : Int,
450	  max_h : Int,
451	  max_v : Int,
452	  mcu_width : Int,
453	  mcu_height : Int,
454	  out_channels : Int,
455	  rgb_data : Array[Byte],
456	) -> Unit {
457	  let width = header.width
458	  let height = header.height
459	  let num_components = header.num_components
460	  let scan_comp_idx = header.scan_comp_idx
461	  let component_h = header.component_h
462	  let component_v = header.component_v
463	  for py in 0..<mcu_height {
464	    for px in 0..<mcu_width {
465	      let ox = mx * mcu_width + px
466	      let oy = my * mcu_height + py
467	      if ox >= width || oy >= height {
468	        continue
469	      }
470	      if num_components == 1 {
471	        let ci = scan_comp_idx[0]	<-- UNCOVERED
472	        let hi = component_h[ci]
473	        let vi = component_v[ci]
474	        let cx = px * hi / max_h
475	        let cy = py * vi / max_v
476	        let bx = cx / 8
477	        let by = cy / 8
478	        let ix = by * hi * 64 + bx * 64 + cy % 8 * 8 + cx % 8
479	        let y_val = comp_data[ci][ix]
480	        let oidx = oy * width * out_channels + ox * out_channels
481	        let yb = y_val.to_byte()	<-- UNCOVERED
482	        rgb_data[oidx] = yb
483	        rgb_data[oidx + 1] = yb
484	        rgb_data[oidx + 2] = yb
485	      } else {
486	        let y_ci = scan_comp_idx[0]
487	        let cb_ci = scan_comp_idx[1]
488	        let cr_ci = scan_comp_idx[2]
489	        let y_hi = component_h[y_ci]
490	        let y_vi = component_v[y_ci]
491	        let ycx = px * y_hi / max_h
492	        let ycy = py * y_vi / max_v
493	        let ybx = ycx / 8
494	        let yby = ycy / 8
495	        let y_idx = yby * y_hi * 64 + ybx * 64 + ycy % 8 * 8 + ycx % 8
496	        let y_val = comp_data[y_ci][y_idx]
497	        let cb_hi = component_h[cb_ci]
498	        let cb_vi = component_v[cb_ci]
499	        let cbcx = px * cb_hi / max_h
500	        let cbcy = py * cb_vi / max_v
501	        let cbbx = cbcx / 8
502	        let cbby = cbcy / 8
503	        let cb_idx = cbby * cb_hi * 64 + cbbx * 64 + cbcy % 8 * 8 + cbcx % 8
504	        let cb_val = comp_data[cb_ci][cb_idx]
505	        let cr_hi = component_h[cr_ci]
506	        let cr_vi = component_v[cr_ci]
507	        let crcx = px * cr_hi / max_h
508	        let crcy = py * cr_vi / max_v
509	        let crbx = crcx / 8
510	        let crby = crcy / 8
511	        let cr_idx = crby * cr_hi * 64 + crbx * 64 + crcy % 8 * 8 + crcx % 8
512	        let cr_val = comp_data[cr_ci][cr_idx]
513	        let (r, g, b) = ycbcr_to_rgb(y_val, cb_val, cr_val)
514	        let oidx = oy * width * out_channels + ox * out_channels
515	        rgb_data[oidx] = r.to_byte()
516	        rgb_data[oidx + 1] = g.to_byte()
517	        rgb_data[oidx + 2] = b.to_byte()
518	      }
519	    }
520	  }
521	}
   	…

525	fn jpeg_decode_scan(
526	  header : JpegHeader,
527	  data : Bytes,
528	) -> @types.Image raise @types.LoadError {
529	  let width = header.width
530	  let height = header.height
531	  let num_components = header.num_components
532	  let zz = zigzag()
533	  let reader = JpegBitReader::{
534	    data,
535	    len: data.length(),
536	    pos: header.entropy_start,
537	    bit_buf: 0,
538	    bit_cnt: 0,
539	  }
540	  let mut max_h = 1
541	  let mut max_v = 1
542	  for i in 0..<num_components {
543	    if header.component_h[i] > max_h {
544	      max_h = header.component_h[i]	<-- UNCOVERED
545	    }
546	    if header.component_v[i] > max_v {
547	      max_v = header.component_v[i]	<-- UNCOVERED
548	    }
549	  }
550	  let mcu_width = max_h * 8
551	  let mcu_height = max_v * 8
552	  let mcus_x = (width + mcu_width - 1) / mcu_width
553	  let mcus_y = (height + mcu_height - 1) / mcu_height
554	  let dc_pred : Array[Ref[Int]] = []
555	  for _ in 0..<num_components {
556	    dc_pred.push(Ref(0))
557	  }
558	  let out_channels = 3
559	  let rgb_data : Array[Byte] = Array::make(
560	    width * height * out_channels,
561	    b'\x00',
562	  )
563	  let mut mcu_count = 0
564	  for _my in 0..<mcus_y {
565	    for _mx in 0..<mcus_x {
566	      let my = _my
567	      let mx = _mx
568	      if header.restart_interval > 0 &&
569	        mcu_count > 0 &&
570	        mcu_count % header.restart_interval == 0 {
571	        for i in 0..<num_components {	<-- UNCOVERED
572	          dc_pred[i].val = 0
573	        }
574	        reader.bit_cnt = 0
575	        while reader.pos < reader.len - 1 {
576	          if reader.data[reader.pos].to_int() == 0xFF &&	<-- UNCOVERED
577	            reader.data[reader.pos + 1].to_int() >= 0xD0 &&
578	            reader.data[reader.pos + 1].to_int() <= 0xD7 {
579	            reader.pos += 2
580	            break
581	          }
582	          reader.pos += 1
583	        }
584	      }
585	      let comp_data : Array[Array[Int]] = []
586	      for _ in 0..<num_components {
587	        comp_data.push(Array::make(256, 0))
588	      }
589	      for si in 0..<header.scan_ns {
590	        let ci = header.scan_comp_idx[si]
591	        let hi = header.component_h[ci]
592	        let vi = header.component_v[ci]
593	        let nblocks = hi * vi
594	        let dc_tab = header.huff_dc[header.scan_dc_table[si]]
595	        let ac_tab = header.huff_ac[header.scan_ac_table[si]]
596	        let qt = header.quant_tables[header.component_qt[ci]]
597	        for bi in 0..<nblocks {
598	          let block = jpeg_decode_block(
599	            reader,
600	            dc_tab,
601	            ac_tab,
602	            qt,
603	            dc_pred[ci],
604	            zz,
605	          )
606	          let spatial = idct_8x8(block)
607	          let bx = bi % hi
608	          let by = bi / hi
609	          for py in 0..<8 {
610	            for px in 0..<8 {
611	              let idx = by * hi * 64 + bx * 64 + py * 8 + px
612	              comp_data[ci][idx] = spatial[py * 8 + px]
613	            }
614	          }
615	        }
616	      }
617	      jpeg_write_mcu_pixels(
618	        header, comp_data, mx, my, max_h, max_v, mcu_width, mcu_height, out_channels,
619	        rgb_data,
620	      )
621	      mcu_count += 1
622	    }
623	  }
624	  @types.Image::{
625	    width,
626	    height,
627	    channels: out_channels,
628	    data: Bytes::from_array(rgb_data),
629	  }
630	}
   	…

1 uncovered line(s) in src\pure\codec\jpeg_encode.mbt:

63	fn jpeg_flush_bits(w : JpegBitWriter) -> Unit {
64	  if w.bit_cnt > 0 {
65	    w.bit_buf = w.bit_buf << (8 - w.bit_cnt)
66	    let b = w.bit_buf & 0xFF
67	    w.out.push(b.to_byte())
68	    if b == 0xFF {
69	      w.out.push(b'\x00')	<-- UNCOVERED
70	    }
71	    w.bit_buf = 0
72	    w.bit_cnt = 0
73	  }
74	}
  	…

2 uncovered line(s) in src\pure\codec\jpegxl_decode.mbt:

 85	pub fn jpegxl_info(data : Bytes) -> JpegXlInfo raise @types.LoadError {
 86	  if !is_jpegxl_format(data) {
 87	    raise @types.LoadError::UnsupportedFormat("Not a JPEG-XL file")
 88	  }
 89	  // JPEG-XL 头部解析非常复杂，需要解析 VarInt、SizeHeader 等
 90	  // 此处返回默认值，完整解析需 JPEG-XL 解码器
 91	  // 裸码流格式: 0xFF 0x0A + signature byte
 92	  // ISOBMFF 格式: 需要先跳过 jxl signature box，找到 codestream box
 93	  let _len = data.length()	<-- UNCOVERED
 94	  // 返回默认信息（完整解析需要 JPEG-XL 解码器）
 95	  JpegXlInfo::{
 96	    width: 0,
 97	    height: 0,
 98	    bit_depth: 8,
 99	    num_channels: 3,
100	    has_alpha: false,
101	    is_lossless: false,
102	  }
103	}
   	…

110	pub fn jpegxl_decode(data : Bytes) -> @types.Image raise @types.LoadError {
111	  if !is_jpegxl_format(data) {
112	    raise @types.LoadError::UnsupportedFormat("Not a JPEG-XL file")	<-- UNCOVERED
113	  }
114	  raise @types.LoadError::UnsupportedFormat(
115	    "JPEG-XL decode not yet implemented (JPEG-XL decoder required)",
116	  )
117	}
   	…

13 uncovered line(s) in src\pure\codec\png_decode.mbt:

20	fn paeth_predict(a : Int, b : Int, c : Int) -> Int {
21	  let p = a + b - c	<-- UNCOVERED
22	  let pa = (p - a).abs()
23	  let pb = (p - b).abs()
24	  let pc = (p - c).abs()
25	  if pa <= pb && pa <= pc {
26	    a	<-- UNCOVERED
27	  } else if pb <= pc {
28	    b
29	  } else {
30	    c	<-- UNCOVERED
31	  }
32	}
  	…

 39	pub fn decode_png_pure(
 40	  data : Bytes,
 41	  req_channels? : Int? = None,
 42	) -> @types.Image raise @types.LoadError {
 43	  let len = data.length()
 44	  // 验证 PNG signature
 45	  if len < 8 || !is_png_signature(data, 0) {
 46	    raise @types.LoadError::DecodeFailed("PNG signature 错误")
 47	  }
 48	  let mut pos = 8
 49	  // 解析 chunks
 50	  let mut width = 0
 51	  let mut height = 0
 52	  let mut bit_depth = 0
 53	  let mut color_type = 0
 54	  let mut interlace = 0
 55	  let palette : Array[(Int, Int, Int)] = []
 56	  let idat_data : Array[Byte] = []
 57	  let mut found_ihdr = false
 58	  let mut found_iend = false
 59	  while pos < len && !found_iend {
 60	    if pos + 8 > len {
 61	      raise @types.LoadError::DecodeFailed("PNG chunk 头部过短")
 62	    }
 63	    let chunk_len = read_u32_be(data, pos)
 64	    if chunk_len < 0 {
 65	      raise @types.LoadError::DecodeFailed("PNG chunk 长度无效")
 66	    }
 67	    let chunk_key = read_u32_be(data, pos + 4)
 68	    pos += 8
 69	    if pos + chunk_len > len {
 70	      raise @types.LoadError::DecodeFailed("PNG chunk 数据越界")
 71	    }
 72	    match chunk_key {
 73	      0x49484452 => {
 74	        // IHDR
 75	        if chunk_len < 13 {
 76	          raise @types.LoadError::DecodeFailed("PNG IHDR 过短")	<-- UNCOVERED
 77	        }
 78	        width = read_u32_be(data, pos)
 79	        height = read_u32_be(data, pos + 4)
 80	        bit_depth = data[pos + 8].to_int()
 81	        color_type = data[pos + 9].to_int()
 82	        interlace = data[pos + 12].to_int()
 83	        found_ihdr = true
 84	      }
 85	      0x504C5445 => {	<-- UNCOVERED
 86	        // PLTE
 87	        let num_colors = chunk_len / 3
 88	        for i in 0..<num_colors {
 89	          palette.push(	<-- UNCOVERED
 90	            (
 91	              data[pos + i * 3].to_int(),	<-- UNCOVERED
 92	              data[pos + i * 3 + 1].to_int(),
 93	              data[pos + i * 3 + 2].to_int(),
 94	            ),
 95	          )
 96	        }
 97	      }
 98	      0x49444154 =>
 99	        // IDAT
100	        for i in 0..<chunk_len {
101	          idat_data.push(data[pos + i])
102	        }
103	      0x49454E44 =>
104	        // IEND
105	        found_iend = true
106	      _ => ()
107	    }
108	    pos += chunk_len + 4 // skip data + CRC
109	  }
110	  if !found_ihdr {
111	    raise @types.LoadError::DecodeFailed("PNG 无 IHDR chunk")
112	  }
113	  if width <= 0 || height <= 0 {
114	    raise @types.LoadError::DecodeFailed("PNG 尺寸无效")
115	  }
116	  if interlace != 0 {
117	    raise @types.LoadError::DecodeFailed("PNG interlace 不支持")
118	  }
119	  if bit_depth != 8 {
120	    raise @types.LoadError::DecodeFailed("PNG 仅支持 8-bit bit depth")
121	  }
122	  let ch = match png_channels(color_type) {
123	    Some(c) => c
124	    None => raise @types.LoadError::DecodeFailed("PNG 无效 color type")
125	  }
126	  @types.check_dims(width, height, ch)
127	  // zlib inflate IDAT 数据
128	  let raw = @util.zlib_inflate(Bytes::from_array(idat_data))
129	  // unfilter scanlines
130	  let bpp = ch // bytes per pixel (8-bit)
131	  let stride = width * bpp
132	  let unfiltered : Array[Byte] = Array::make(height * stride, b'\x00')
133	  let mut raw_pos = 0
134	  for y in 0..<height {
135	    if raw_pos >= raw.length() {
136	      raise @types.LoadError::DecodeFailed("PNG 解压数据过短")	<-- UNCOVERED
137	    }
138	    let filter_type = raw[raw_pos].to_int()
139	    raw_pos += 1
140	    for x in 0..<stride {
141	      if raw_pos >= raw.length() {
142	        raise @types.LoadError::DecodeFailed("PNG 解压数据过短")	<-- UNCOVERED
143	      }
144	      let raw_byte = raw[raw_pos].to_int()
145	      raw_pos += 1
146	      let a = if x >= bpp {
147	        unfiltered[y * stride + x - bpp].to_int()
148	      } else {
149	        0
150	      }
151	      let b = if y > 0 { unfiltered[(y - 1) * stride + x].to_int() } else { 0 }
152	      let c = if x >= bpp && y > 0 {
153	        unfiltered[(y - 1) * stride + x - bpp].to_int()
154	      } else {
155	        0
156	      }
157	      let val = match filter_type {
158	        0 => raw_byte // None
159	        1 => (raw_byte + a) & 0xFF // Sub
160	        2 => (raw_byte + b) & 0xFF // Up
161	        3 => (raw_byte + (a + b) / 2) & 0xFF // Average	<-- UNCOVERED
162	        4 => (raw_byte + paeth_predict(a, b, c)) & 0xFF // Paeth
163	        _ => raise @types.LoadError::DecodeFailed("PNG 无效 filter type")
164	      }
165	      unfiltered[y * stride + x] = val.to_byte()
166	    }
167	  }
168	  // 转换为 Image（保留 alpha，根据 req_channels 决定输出通道数）
169	  let pixel_count = width * height
170	  let out_ch = match req_channels {
171	    Some(n) => n
172	    None => ch // 默认保留原色（含 alpha）
173	  }
174	  if color_type == 3 && palette.length() == 0 {	<-- UNCOVERED
175	    raise @types.LoadError::DecodeFailed("PNG palette 缺失")
176	  }
177	  let out_data : Array[Byte] = Array::make(pixel_count * out_ch, b'\x00')
178	  for i in 0..<pixel_count {
179	    let (r, g, b, a) = match color_type {
180	      3 => {	<-- UNCOVERED
181	        // palette
182	        let idx = unfiltered[i].to_int()	<-- UNCOVERED
183	        if idx >= palette.length() {
184	          raise @types.LoadError::DecodeFailed("PNG palette 索引越界")
185	        }
186	        let (r, g, b) = palette[idx]
187	        (r, g, b, 255)
188	      }
189	      0 => {
190	        // gray
191	        let v = unfiltered[i].to_int()
192	        (v, v, v, 255)
193	      }
194	      2 =>
195	        // RGB
196	        (
197	          unfiltered[i * 3].to_int(),
198	          unfiltered[i * 3 + 1].to_int(),
199	          unfiltered[i * 3 + 2].to_int(),
200	          255,
201	        )
202	      4 => {
203	        // gray + alpha
204	        let v = unfiltered[i * 2].to_int()
205	        let a = unfiltered[i * 2 + 1].to_int()
206	        (v, v, v, a)
207	      }
208	      _ =>
209	        // RGBA
210	        (
211	          unfiltered[i * 4].to_int(),
212	          unfiltered[i * 4 + 1].to_int(),
213	          unfiltered[i * 4 + 2].to_int(),
214	          unfiltered[i * 4 + 3].to_int(),
215	        )
216	    }
217	    let (or, og, ob, oa) = match out_ch {
218	      1 => {
219	        let gray = (r * 299 + g * 587 + b * 114) / 1000
220	        (gray, gray, gray, 255)
221	      }
222	      2 => {
223	        let gray = (r * 299 + g * 587 + b * 114) / 1000
224	        (gray, gray, gray, a)
225	      }
226	      3 => (r, g, b, 255)
227	      _ => (r, g, b, a)
228	    }
229	    match out_ch {
230	      1 => out_data[i] = or.to_byte()
231	      2 => {
232	        out_data[i * 2] = or.to_byte()
233	        out_data[i * 2 + 1] = oa.to_byte()
234	      }
235	      3 => {
236	        out_data[i * 3] = or.to_byte()
237	        out_data[i * 3 + 1] = og.to_byte()
238	        out_data[i * 3 + 2] = ob.to_byte()
239	      }
240	      _ => {
241	        out_data[i * 4] = or.to_byte()
242	        out_data[i * 4 + 1] = og.to_byte()
243	        out_data[i * 4 + 2] = ob.to_byte()
244	        out_data[i * 4 + 3] = oa.to_byte()
245	      }
246	    }
247	  }
248	  @types.Image::{
249	    width,
250	    height,
251	    channels: out_ch,
252	    data: Bytes::from_array(out_data),
253	  }
254	}
   	…

15 uncovered line(s) in src\pure\codec\png_decode_16.mbt:

 10	pub fn decode_png_16_pure(
 11	  data : Bytes,
 12	  req_channels? : Int? = None,
 13	) -> @types.Image16 raise @types.LoadError {
 14	  match req_channels {
 15	    Some(n) =>
 16	      if n < 1 || n > 4 {
 17	        raise @types.LoadError::DecodeFailed("req_channels 必须为 1-4")
 18	      }
 19	    None => ()
 20	  }
 21	  let len = data.length()
 22	  if len < 8 || !is_png_signature(data, 0) {
 23	    raise @types.LoadError::DecodeFailed("PNG signature 错误")
 24	  }
 25	  let mut pos = 8
 26	  let mut width = 0
 27	  let mut height = 0
 28	  let mut bit_depth = 0
 29	  let mut color_type = 0
 30	  let mut interlace = 0
 31	  let palette : Array[(Int, Int, Int)] = []
 32	  let idat_data : Array[Byte] = []
 33	  let mut found_ihdr = false
 34	  let mut found_iend = false
 35	  while pos < len && !found_iend {
 36	    if pos + 8 > len {
 37	      raise @types.LoadError::DecodeFailed("PNG chunk 头部过短")
 38	    }
 39	    let chunk_len = read_u32_be(data, pos)
 40	    let chunk_key = read_u32_be(data, pos + 4)
 41	    pos += 8
 42	    if pos + chunk_len > len {
 43	      raise @types.LoadError::DecodeFailed("PNG chunk 数据越界")
 44	    }
 45	    match chunk_key {
 46	      0x49484452 => {
 47	        if chunk_len < 13 {
 48	          raise @types.LoadError::DecodeFailed("PNG IHDR 过短")
 49	        }
 50	        width = read_u32_be(data, pos)
 51	        height = read_u32_be(data, pos + 4)
 52	        bit_depth = data[pos + 8].to_int()
 53	        color_type = data[pos + 9].to_int()
 54	        interlace = data[pos + 12].to_int()
 55	        found_ihdr = true
 56	      }
 57	      0x504C5445 => {	<-- UNCOVERED
 58	        let num_colors = chunk_len / 3
 59	        for i in 0..<num_colors {
 60	          palette.push(	<-- UNCOVERED
 61	            (
 62	              data[pos + i * 3].to_int(),	<-- UNCOVERED
 63	              data[pos + i * 3 + 1].to_int(),
 64	              data[pos + i * 3 + 2].to_int(),
 65	            ),
 66	          )
 67	        }
 68	      }
 69	      0x49444154 =>
 70	        for i in 0..<chunk_len {
 71	          idat_data.push(data[pos + i])
 72	        }
 73	      0x49454E44 => found_iend = true
 74	      _ => ()
 75	    }
 76	    pos += chunk_len + 4
 77	  }
 78	  if !found_ihdr {
 79	    raise @types.LoadError::DecodeFailed("PNG 无 IHDR chunk")
 80	  }
 81	  if width <= 0 || height <= 0 {
 82	    raise @types.LoadError::DecodeFailed("PNG 尺寸无效")
 83	  }
 84	  if interlace != 0 {
 85	    raise @types.LoadError::DecodeFailed("PNG interlace 不支持")
 86	  }
 87	  if bit_depth != 8 && bit_depth != 16 {
 88	    raise @types.LoadError::DecodeFailed(
 89	      "PNG 16-bit 解码仅支持 bit depth 8 或 16",
 90	    )
 91	  }
 92	  let ch = match png_channels(color_type) {
 93	    Some(c) => c
 94	    None => raise @types.LoadError::DecodeFailed("PNG 无效 color type")
 95	  }
 96	  let raw = @util.zlib_inflate(Bytes::from_array(idat_data))
 97	  // bytes per pixel for filtering
 98	  let bpp = ch * (bit_depth / 8)
 99	  let stride = width * bpp
100	  let unfiltered : Array[Byte] = Array::make(height * stride, b'\x00')
101	  let mut raw_pos = 0
102	  for y in 0..<height {
103	    if raw_pos >= raw.length() {
104	      raise @types.LoadError::DecodeFailed("PNG 解压数据过短")	<-- UNCOVERED
105	    }
106	    let filter_type = raw[raw_pos].to_int()
107	    raw_pos += 1
108	    for x in 0..<stride {
109	      if raw_pos >= raw.length() {
110	        raise @types.LoadError::DecodeFailed("PNG 解压数据过短")	<-- UNCOVERED
111	      }
112	      let raw_byte = raw[raw_pos].to_int()
113	      raw_pos += 1
114	      let a = if x >= bpp {
115	        unfiltered[y * stride + x - bpp].to_int()
116	      } else {
117	        0
118	      }
119	      let b = if y > 0 { unfiltered[(y - 1) * stride + x].to_int() } else { 0 }
120	      let c = if x >= bpp && y > 0 {
121	        unfiltered[(y - 1) * stride + x - bpp].to_int()
122	      } else {
123	        0
124	      }
125	      let val = match filter_type {
126	        0 => raw_byte
127	        1 => (raw_byte + a) & 0xFF
128	        2 => (raw_byte + b) & 0xFF
129	        3 => (raw_byte + (a + b) / 2) & 0xFF	<-- UNCOVERED
130	        4 => (raw_byte + paeth_predict(a, b, c)) & 0xFF
131	        _ => raise @types.LoadError::DecodeFailed("PNG 无效 filter type")
132	      }
133	      unfiltered[y * stride + x] = val.to_byte()
134	    }
135	  }
136	  // 转换为 16-bit samples (little-endian)
137	  let pixel_count = width * height
138	  let out_ch = match req_channels {
139	    Some(n) => n	<-- UNCOVERED
140	    None => ch
141	  }
142	  // 先提取原生 16-bit samples
143	  let samples16 : Array[Int] = Array::make(pixel_count * ch, 0)
144	  if bit_depth == 16 {
145	    // big-endian → native int
146	    for i in 0..<(pixel_count * ch) {	<-- UNCOVERED
147	      let hi = unfiltered[i * 2].to_int()
148	      let lo = unfiltered[i * 2 + 1].to_int()
149	      samples16[i] = hi * 256 + lo
150	    }
151	  } else {
152	    // 8-bit → upscale: v * 257
153	    for i in 0..<(pixel_count * ch) {
154	      samples16[i] = unfiltered[i].to_int() * 257
155	    }
156	  }
157	  // 通道转换
158	  let out_data : Array[Byte] = Array::make(pixel_count * out_ch * 2, b'\x00')
159	  for i in 0..<pixel_count {
160	    let (r16, g16, b16, a16) = if ch == 1 {
161	      let v = samples16[i]	<-- UNCOVERED
162	      (v, v, v, 65535)
163	    } else if ch == 2 {
164	      let v = samples16[i * 2]	<-- UNCOVERED
165	      let a = samples16[i * 2 + 1]
166	      (v, v, v, a)
167	    } else if ch == 3 {
168	      (samples16[i * 3], samples16[i * 3 + 1], samples16[i * 3 + 2], 65535)
169	    } else {
170	      (	<-- UNCOVERED
171	        samples16[i * 4],
172	        samples16[i * 4 + 1],
173	        samples16[i * 4 + 2],
174	        samples16[i * 4 + 3],
175	      )
176	    }
177	    let (or16, og16, ob16, oa16) = match out_ch {
178	      1 => {	<-- UNCOVERED
179	        let gray = (r16 * 299 + g16 * 587 + b16 * 114) / 1000
180	        (gray, gray, gray, 65535)
181	      }
182	      2 => {	<-- UNCOVERED
183	        let gray = (r16 * 299 + g16 * 587 + b16 * 114) / 1000
184	        (gray, gray, gray, a16)
185	      }
186	      3 => (r16, g16, b16, 65535)
187	      _ => (r16, g16, b16, a16)	<-- UNCOVERED
188	    }
189	    // little-endian uint16
190	    out_data[i * out_ch * 2] = (or16 & 0xFF).to_byte()
191	    out_data[i * out_ch * 2 + 1] = ((or16 >> 8) & 0xFF).to_byte()
192	    if out_ch >= 2 {
193	      out_data[i * out_ch * 2 + 2] = (og16 & 0xFF).to_byte()
194	      out_data[i * out_ch * 2 + 3] = ((og16 >> 8) & 0xFF).to_byte()
195	    }
196	    if out_ch >= 3 {
197	      out_data[i * out_ch * 2 + 4] = (ob16 & 0xFF).to_byte()
198	      out_data[i * out_ch * 2 + 5] = ((ob16 >> 8) & 0xFF).to_byte()
199	    }
200	    if out_ch >= 4 {
201	      out_data[i * out_ch * 2 + 6] = (oa16 & 0xFF).to_byte()	<-- UNCOVERED
202	      out_data[i * out_ch * 2 + 7] = ((oa16 >> 8) & 0xFF).to_byte()
203	    }
204	  }
205	  @types.Image16::{
206	    width,
207	    height,
208	    channels: out_ch,
209	    data: Bytes::from_array(out_data),
210	  }
211	}
   	…

1 uncovered line(s) in src\pure\codec\png_encode.mbt:

 77	pub fn encode_png_pure(img : @types.Image) -> Bytes {
 78	  let width = img.width
 79	  let height = img.height
 80	  let ch = img.channels
 81	  // 确定输出通道数
 82	  let out_ch = if ch == 4 {
 83	    4
 84	  } else if ch == 2 {
 85	    2
 86	  } else if ch == 1 {
 87	    1
 88	  } else {
 89	    3
 90	  }
 91	  let color_type = if out_ch == 1 {
 92	    0
 93	  } else if out_ch == 2 {
 94	    4
 95	  } else if out_ch == 4 {
 96	    6
 97	  } else {
 98	    2
 99	  }
100	  let out : Array[Byte] = []
101	  // PNG signature
102	  out.push(b'\x89')
103	  out.push(b'\x50')
104	  out.push(b'\x4E')
105	  out.push(b'\x47')
106	  out.push(b'\x0D')
107	  out.push(b'\x0A')
108	  out.push(b'\x1A')
109	  out.push(b'\x0A')
110	  // IHDR chunk
111	  let ihdr : Array[Byte] = []
112	  // width (big-endian)
113	  ihdr.push(((width >> 24) & 0xFF).to_byte())
114	  ihdr.push(((width >> 16) & 0xFF).to_byte())
115	  ihdr.push(((width >> 8) & 0xFF).to_byte())
116	  ihdr.push((width & 0xFF).to_byte())
117	  // height (big-endian)
118	  ihdr.push(((height >> 24) & 0xFF).to_byte())
119	  ihdr.push(((height >> 16) & 0xFF).to_byte())
120	  ihdr.push(((height >> 8) & 0xFF).to_byte())
121	  ihdr.push((height & 0xFF).to_byte())
122	  ihdr.push(b'\x08') // bit depth = 8
123	  ihdr.push(color_type.to_byte())
124	  ihdr.push(b'\x00') // compression = 0
125	  ihdr.push(b'\x00') // filter = 0
126	  ihdr.push(b'\x00') // interlace = 0
127	  write_chunk(out, "IHDR", Bytes::from_array(ihdr))
128	  // 构建过滤后的图像数据
129	  let filtered : Array[Byte] = []
130	  for y in 0..<height {
131	    filtered.push(b'\x00') // filter type 0 (None)
132	    for x in 0..<width {
133	      let src_off = (y * width + x) * ch
134	      if ch == out_ch {
135	        // 直接拷贝
136	        for c in 0..<ch {
137	          filtered.push(img.data[src_off + c])
138	        }
139	      } else {
140	        // 通道数不匹配时，取前 out_ch 个通道
141	        for c in 0..<out_ch {
142	          if c < ch {
143	            filtered.push(img.data[src_off + c])
144	          } else {
145	            filtered.push(b'\xFF')	<-- UNCOVERED
146	          }
147	        }
148	      }
149	    }
150	  }
151	  // zlib deflate
152	  let compressed = zlib_deflate_stored(Bytes::from_array(filtered))
153	  // IDAT chunk
154	  write_chunk(out, "IDAT", compressed)
155	  // IEND chunk
156	  write_chunk(out, "IEND", Bytes::from_array([]))
157	  Bytes::from_array(out)
158	}
   	…

3 uncovered line(s) in src\pure\codec\pnm_decode.mbt:

 63	pub fn decode_pnm_pure(data : Bytes) -> @types.Image raise @types.LoadError {
 64	  let len = data.length()
 65	  if len < 2 {
 66	    raise @types.LoadError::DecodeFailed("PNM 数据过短")
 67	  }
 68	  let magic0 = data[0].to_int()
 69	  let magic1 = data[1].to_int()
 70	  // 仅支持 P5 和 P6
 71	  if !(magic0 == 0x50 && (magic1 == 0x35 || magic1 == 0x36)) {
 72	    raise @types.LoadError::DecodeFailed(
 73	      "不支持的 PNM magic，仅支持 P5 和 P6",
 74	    )
 75	  }
 76	  let channels = if magic1 == 0x36 { 3 } else { 1 }
 77	  let pos = Ref(2)
 78	  // 读取 width
 79	  pnm_skip_ws_and_comments(data, pos, len)
 80	  let width = pnm_read_ascii_int(data, pos, len)
 81	  // 读取 height
 82	  pnm_skip_ws_and_comments(data, pos, len)
 83	  let height = pnm_read_ascii_int(data, pos, len)
 84	  // 读取 maxval
 85	  pnm_skip_ws_and_comments(data, pos, len)
 86	  let maxval = pnm_read_ascii_int(data, pos, len)
 87	  // 仅支持 8-bit（maxval < 256）
 88	  if maxval >= 256 {
 89	    raise @types.LoadError::DecodeFailed(
 90	      "不支持的 PNM maxval，仅支持 8-bit（maxval < 256）",
 91	    )
 92	  }
 93	  if maxval <= 0 {
 94	    raise @types.LoadError::DecodeFailed("PNM maxval 无效")	<-- UNCOVERED
 95	  }
 96	  // 尺寸校验
 97	  if width <= 0 || height <= 0 {
 98	    raise @types.LoadError::DecodeFailed("PNM 尺寸无效")
 99	  }
100	  // maxval 后恰好 1 个 whitespace
101	  if pos.val >= len {
102	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
103	      "PNM 像素数据起始 whitespace 缺失",
104	    )
105	  }
106	  let sep = data[pos.val].to_int()
107	  if !pnm_is_whitespace(sep) {
108	    raise @types.LoadError::DecodeFailed("PNM maxval 后分隔符无效")	<-- UNCOVERED
109	  }
110	  pos.val += 1
111	  // 像素数据
112	  @types.check_dims(width, height, channels)
113	  let pixel_count = width * height
114	  let pixel_bytes = pixel_count * channels
115	  if pos.val + pixel_bytes > len {
116	    raise @types.LoadError::DecodeFailed("PNM 像素数据不足")
117	  }
118	  let pixels : Array[Byte] = Array::make(pixel_bytes, b'\x00')
119	  for i in 0..<pixel_bytes {
120	    pixels[i] = data[pos.val + i]
121	  }
122	  @types.Image::{ width, height, channels, data: Bytes::from_array(pixels) }
123	}
   	…

2 uncovered line(s) in src\pure\codec\pnm_decode_16.mbt:

 9	pub fn decode_pnm_16_pure(
10	  data : Bytes,
11	) -> @types.Image16 raise @types.LoadError {
12	  let len = data.length()
13	  if len < 2 {
14	    raise @types.LoadError::DecodeFailed("PNM 数据过短")
15	  }
16	  let magic0 = data[0].to_int()
17	  let magic1 = data[1].to_int()
18	  if !(magic0 == 0x50 && (magic1 == 0x35 || magic1 == 0x36)) {
19	    raise @types.LoadError::DecodeFailed(
20	      "不支持的 PNM magic，仅支持 P5 和 P6",
21	    )
22	  }
23	  let channels = if magic1 == 0x36 { 3 } else { 1 }
24	  let pos = Ref(2)
25	  pnm_skip_ws_and_comments(data, pos, len)
26	  let width = pnm_read_ascii_int(data, pos, len)
27	  pnm_skip_ws_and_comments(data, pos, len)
28	  let height = pnm_read_ascii_int(data, pos, len)
29	  pnm_skip_ws_and_comments(data, pos, len)
30	  let maxval = pnm_read_ascii_int(data, pos, len)
31	  if maxval <= 0 {
32	    raise @types.LoadError::DecodeFailed("PNM maxval 无效")
33	  }
34	  if width <= 0 || height <= 0 {
35	    raise @types.LoadError::DecodeFailed("PNM 尺寸无效")
36	  }
37	  if pos.val >= len {
38	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
39	      "PNM 像素数据起始 whitespace 缺失",
40	    )
41	  }
42	  let sep = data[pos.val].to_int()
43	  if !pnm_is_whitespace(sep) {
44	    raise @types.LoadError::DecodeFailed("PNM maxval 后分隔符无效")	<-- UNCOVERED
45	  }
46	  pos.val += 1
47	  @types.check_dims(width, height, channels)
48	  let pixel_count = width * height
49	  let is_16bit = maxval >= 256
50	  let out_data : Array[Byte] = Array::make(pixel_count * channels * 2, b'\x00')
51	  if is_16bit {
52	    let pixel_bytes = pixel_count * channels * 2
53	    if pos.val + pixel_bytes > len {
54	      raise @types.LoadError::DecodeFailed("PNM 16-bit 像素数据不足")
55	    }
56	    // big-endian → little-endian
57	    for i in 0..<(pixel_count * channels) {
58	      let hi = data[pos.val + i * 2].to_int()
59	      let lo = data[pos.val + i * 2 + 1].to_int()
60	      out_data[i * 2] = lo.to_byte()
61	      out_data[i * 2 + 1] = hi.to_byte()
62	    }
63	  } else {
64	    let pixel_bytes = pixel_count * channels
65	    if pos.val + pixel_bytes > len {
66	      raise @types.LoadError::DecodeFailed("PNM 像素数据不足")
67	    }
68	    // 8-bit → upscale: v * 257
69	    for i in 0..<pixel_bytes {
70	      let v = data[pos.val + i].to_int() * 257
71	      out_data[i * 2] = (v & 0xFF).to_byte()
72	      out_data[i * 2 + 1] = ((v >> 8) & 0xFF).to_byte()
73	    }
74	  }
75	  @types.Image16::{ width, height, channels, data: Bytes::from_array(out_data) }
76	}
  	…

3 uncovered line(s) in src\pure\codec\psd_decode.mbt:

 11	pub fn decode_psd_pure(data : Bytes) -> @types.Image raise @types.LoadError {
 12	  let len = data.length()
 13	  // Header 26 字节
 14	  if len < 26 {
 15	    raise @types.LoadError::DecodeFailed(
 16	      "PSD 数据过短：无法读取完整 header",
 17	    )
 18	  }
 19	  // signature "8BPS" (0x38 0x42 0x50 0x53)
 20	  if data[0].to_int() != 0x38 ||
 21	    data[1].to_int() != 0x42 ||
 22	    data[2].to_int() != 0x50 ||
 23	    data[3].to_int() != 0x53 {
 24	    raise @types.LoadError::DecodeFailed("PSD signature 错误，非 \"8BPS\"")
 25	  }
 26	  // version (2 字节 BE)，必须为 1
 27	  let version = read_u16_be(data, 4)
 28	  if version != 1 {
 29	    raise @types.LoadError::DecodeFailed("PSD version 错误，仅支持 1")
 30	  }
 31	  // reserved(6 字节) 跳过
 32	  // channelCount (2 字节 BE)
 33	  let channel_count = read_u16_be(data, 12)
 34	  if channel_count != 3 && channel_count != 4 {
 35	    raise @types.LoadError::DecodeFailed(
 36	      "不支持的 PSD channelCount，仅支持 3（RGB）和 4（RGBA）",
 37	    )
 38	  }
 39	  // h (4 字节 BE)
 40	  let h = read_u32_be(data, 14)
 41	  // w (4 字节 BE)
 42	  let w = read_u32_be(data, 18)
 43	  // bitdepth (2 字节 BE)，仅支持 8
 44	  let bitdepth = read_u16_be(data, 22)
 45	  if bitdepth != 8 {
 46	    raise @types.LoadError::DecodeFailed(
 47	      "不支持的 PSD bitdepth，仅支持 8",
 48	    )
 49	  }
 50	  // colorMode (2 字节 BE)，仅支持 3（RGB）
 51	  let color_mode = read_u16_be(data, 24)
 52	  if color_mode != 3 {
 53	    raise @types.LoadError::DecodeFailed(
 54	      "不支持的 PSD colorMode，仅支持 3（RGB）",
 55	    )
 56	  }
 57	  // 尺寸校验
 58	  if w == 0 || h == 0 {
 59	    raise @types.LoadError::DecodeFailed("PSD 尺寸无效")
 60	  }
 61	  // 跳过 3 个 length 前缀段（color mode data / image resources / layer and mask data）
 62	  let mut pos = 26
 63	  for _ in 0..<3 {
 64	    if pos + 4 > len {
 65	      raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 66	        "PSD 数据过短：无法读取 4 字节 BE",
 67	      )
 68	    }
 69	    let seg_len = read_u32_be(data, pos)
 70	    pos += 4
 71	    if pos + seg_len > len {
 72	      raise @types.LoadError::DecodeFailed("PSD 段数据越界")	<-- UNCOVERED
 73	    }
 74	    pos += seg_len
 75	  }
 76	  // Image data: compression (2 字节 BE)
 77	  if pos + 2 > len {
 78	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 79	      "PSD 数据过短：无法读取 2 字节 BE",
 80	    )
 81	  }
 82	  let compression = read_u16_be(data, pos)
 83	  pos += 2
 84	  if compression != 0 {
 85	    raise @types.LoadError::DecodeFailed(
 86	      "不支持的 PSD compression，仅支持 0（无压缩）",
 87	    )
 88	  }
 89	  // 像素数据：按通道排列，每通道 w*h 字节
 90	  let pixel_count = w * h
 91	  let pixel_bytes = channel_count * pixel_count
 92	  if pos + pixel_bytes > len {
 93	    raise @types.LoadError::DecodeFailed("PSD 像素数据不足")
 94	  }
 95	  // 交错：data[i * channel_count + c] = raw[c * w * h + i]
 96	  let interleaved : Array[Byte] = Array::make(pixel_bytes, b'\x00')
 97	  for c in 0..<channel_count {
 98	    let channel_base = c * pixel_count
 99	    for i in 0..<pixel_count {
100	      interleaved[i * channel_count + c] = data[pos + channel_base + i]
101	    }
102	  }
103	  @types.Image::{
104	    width: w,
105	    height: h,
106	    channels: channel_count,
107	    data: Bytes::from_array(interleaved),
108	  }
109	}
   	…

1 uncovered line(s) in src\pure\codec\qoi_decode.mbt:

 16	pub fn decode_qoi_pure(data : Bytes) -> @types.Image raise @types.LoadError {
 17	  let len = data.length()
 18	  if len < 14 {
 19	    raise @types.LoadError::DecodeFailed("QOI 数据过短")
 20	  }
 21	  if data[0].to_int() != 0x71 ||
 22	    data[1].to_int() != 0x6F ||
 23	    data[2].to_int() != 0x69 ||
 24	    data[3].to_int() != 0x66 {
 25	    raise @types.LoadError::DecodeFailed("QOI magic 不匹配")
 26	  }
 27	  let width = (data[4].to_int() << 24) |
 28	    (data[5].to_int() << 16) |
 29	    (data[6].to_int() << 8) |
 30	    data[7].to_int()
 31	  let height = (data[8].to_int() << 24) |
 32	    (data[9].to_int() << 16) |
 33	    (data[10].to_int() << 8) |
 34	    data[11].to_int()
 35	  let channels = data[12].to_int()
 36	  if width <= 0 || height <= 0 {
 37	    raise @types.LoadError::DecodeFailed("QOI 尺寸无效")
 38	  }
 39	  let out_channels = if channels == 3 { 3 } else { 4 }
 40	  @types.check_dims(width, height, out_channels)
 41	  let pixel_count = width * height
 42	  let pixels : Array[Byte] = Array::make(pixel_count * out_channels, b'\x00')
 43	  let index_r : Array[Int] = Array::make(64, 0)
 44	  let index_g : Array[Int] = Array::make(64, 0)
 45	  let index_b : Array[Int] = Array::make(64, 0)
 46	  let index_a : Array[Int] = Array::make(64, 0)
 47	  let px_r = Ref(0)
 48	  let px_g = Ref(0)
 49	  let px_b = Ref(0)
 50	  let px_a = Ref(255)
 51	  let pos = Ref(14)
 52	  let pixel_idx = Ref(0)
 53	  while pixel_idx.val < pixel_count && pos.val < len {
 54	    let tag = data[pos.val].to_int()
 55	    pos.val += 1
 56	    if tag == 0xFE {
 57	      px_r.val = data[pos.val].to_int()
 58	      px_g.val = data[pos.val + 1].to_int()
 59	      px_b.val = data[pos.val + 2].to_int()
 60	      pos.val += 3
 61	    } else if tag == 0xFF {
 62	      px_r.val = data[pos.val].to_int()
 63	      px_g.val = data[pos.val + 1].to_int()
 64	      px_b.val = data[pos.val + 2].to_int()
 65	      px_a.val = data[pos.val + 3].to_int()
 66	      pos.val += 4
 67	    } else if (tag & 0xC0) == 0x00 {
 68	      let idx = tag & 0x3F
 69	      px_r.val = index_r[idx]
 70	      px_g.val = index_g[idx]
 71	      px_b.val = index_b[idx]
 72	      px_a.val = index_a[idx]
 73	    } else if (tag & 0xC0) == 0x40 {
 74	      px_r.val = (px_r.val + ((tag >> 4) & 0x03) - 2) & 0xFF
 75	      px_g.val = (px_g.val + ((tag >> 2) & 0x03) - 2) & 0xFF
 76	      px_b.val = (px_b.val + (tag & 0x03) - 2) & 0xFF
 77	    } else if (tag & 0xC0) == 0x80 {
 78	      let dg = (tag & 0x3F) - 32
 79	      px_g.val = (px_g.val + dg) & 0xFF
 80	      let b2 = data[pos.val].to_int()
 81	      pos.val += 1
 82	      let dr_dg = (b2 >> 4) - 8
 83	      let db_dg = (b2 & 0x0F) - 8
 84	      px_r.val = (px_r.val + dr_dg + dg) & 0xFF
 85	      px_b.val = (px_b.val + db_dg + dg) & 0xFF
 86	    } else {
 87	      let run = (tag & 0x3F) + 1
 88	      for _ in 0..<run {
 89	        if pixel_idx.val < pixel_count {
 90	          pixels[pixel_idx.val * out_channels] = px_r.val.to_byte()
 91	          pixels[pixel_idx.val * out_channels + 1] = px_g.val.to_byte()
 92	          pixels[pixel_idx.val * out_channels + 2] = px_b.val.to_byte()
 93	          if out_channels == 4 {
 94	            pixels[pixel_idx.val * out_channels + 3] = px_a.val.to_byte()	<-- UNCOVERED
 95	          }
 96	          pixel_idx.val += 1
 97	        }
 98	      }
 99	      continue
100	    }
101	    pixels[pixel_idx.val * out_channels] = px_r.val.to_byte()
102	    pixels[pixel_idx.val * out_channels + 1] = px_g.val.to_byte()
103	    pixels[pixel_idx.val * out_channels + 2] = px_b.val.to_byte()
104	    if out_channels == 4 {
105	      pixels[pixel_idx.val * out_channels + 3] = px_a.val.to_byte()
106	    }
107	    pixel_idx.val += 1
108	    let h = qoi_hash(px_r.val, px_g.val, px_b.val, px_a.val)
109	    index_r[h] = px_r.val
110	    index_g[h] = px_g.val
111	    index_b[h] = px_b.val
112	    index_a[h] = px_a.val
113	  }
114	  @types.Image::{
115	    width,
116	    height,
117	    channels: out_channels,
118	    data: Bytes::from_array(pixels),
119	  }
120	}
   	…

2 uncovered line(s) in src\pure\codec\qoi_encode.mbt:

 11	pub fn encode_qoi_pure(img : @types.Image) -> Bytes raise @types.LoadError {
 12	  if img.channels != 3 && img.channels != 4 {
 13	    raise @types.LoadError::EncodeFailed(
 14	      "QOI 编码需要 channels 为 3 (RGB) 或 4 (RGBA)",
 15	    )
 16	  }
 17	  let out : Array[Byte] = []
 18	  out.push(b'\x71')
 19	  out.push(b'\x6F')
 20	  out.push(b'\x69')
 21	  out.push(b'\x66')
 22	  out.push(((img.width >> 24) & 0xFF).to_byte())
 23	  out.push(((img.width >> 16) & 0xFF).to_byte())
 24	  out.push(((img.width >> 8) & 0xFF).to_byte())
 25	  out.push((img.width & 0xFF).to_byte())
 26	  out.push(((img.height >> 24) & 0xFF).to_byte())
 27	  out.push(((img.height >> 16) & 0xFF).to_byte())
 28	  out.push(((img.height >> 8) & 0xFF).to_byte())
 29	  out.push((img.height & 0xFF).to_byte())
 30	  out.push(img.channels.to_byte())
 31	  out.push(b'\x00')
 32	  let index_r : Array[Int] = Array::make(64, 0)
 33	  let index_g : Array[Int] = Array::make(64, 0)
 34	  let index_b : Array[Int] = Array::make(64, 0)
 35	  let index_a : Array[Int] = Array::make(64, 0)
 36	  let prev_r = Ref(0)
 37	  let prev_g = Ref(0)
 38	  let prev_b = Ref(0)
 39	  let prev_a = Ref(255)
 40	  let has_alpha = img.channels == 4
 41	  let pixel_count = img.width * img.height
 42	  let run = Ref(0)
 43	  for i in 0..<pixel_count {
 44	    let r = img.data[i * img.channels].to_int()
 45	    let g = img.data[i * img.channels + 1].to_int()
 46	    let b = img.data[i * img.channels + 2].to_int()
 47	    let a = if has_alpha {
 48	      img.data[i * img.channels + 3].to_int()
 49	    } else {
 50	      255
 51	    }
 52	    if r == prev_r.val && g == prev_g.val && b == prev_b.val && a == prev_a.val {
 53	      run.val += 1
 54	      if run.val == 62 {
 55	        out.push((0xC0 | (run.val - 1)).to_byte())	<-- UNCOVERED
 56	        run.val = 0
 57	      }
 58	    } else {
 59	      if run.val > 0 {
 60	        out.push((0xC0 | (run.val - 1)).to_byte())	<-- UNCOVERED
 61	        run.val = 0
 62	      }
 63	      let h = qoi_hash(r, g, b, a)
 64	      if index_r[h] == r &&
 65	        index_g[h] == g &&
 66	        index_b[h] == b &&
 67	        index_a[h] == a {
 68	        out.push((h & 0x3F).to_byte())
 69	      } else if a == prev_a.val {
 70	        let dr = r - prev_r.val
 71	        let dg = g - prev_g.val
 72	        let db = b - prev_b.val
 73	        let dr_dg = dr - dg
 74	        let db_dg = db - dg
 75	        if dr >= -2 && dr <= 1 && dg >= -2 && dg <= 1 && db >= -2 && db <= 1 {
 76	          out.push(
 77	            (0x40 | ((dr + 2) << 4) | ((dg + 2) << 2) | (db + 2)).to_byte(),
 78	          )
 79	        } else if dg >= -32 &&
 80	          dg <= 31 &&
 81	          dr_dg >= -8 &&
 82	          dr_dg <= 7 &&
 83	          db_dg >= -8 &&
 84	          db_dg <= 7 {
 85	          out.push((0x80 | ((dg + 32) & 0x3F)).to_byte())
 86	          out.push((((dr_dg + 8) << 4) | (db_dg + 8)).to_byte())
 87	        } else {
 88	          out.push(b'\xFE')
 89	          out.push(r.to_byte())
 90	          out.push(g.to_byte())
 91	          out.push(b.to_byte())
 92	        }
 93	      } else {
 94	        out.push(b'\xFF')
 95	        out.push(r.to_byte())
 96	        out.push(g.to_byte())
 97	        out.push(b.to_byte())
 98	        out.push(a.to_byte())
 99	      }
100	      index_r[h] = r
101	      index_g[h] = g
102	      index_b[h] = b
103	      index_a[h] = a
104	    }
105	    prev_r.val = r
106	    prev_g.val = g
107	    prev_b.val = b
108	    prev_a.val = a
109	  }
110	  if run.val > 0 {
111	    out.push((0xC0 | (run.val - 1)).to_byte())
112	  }
113	  for i in 0..<8 {
114	    if i == 7 {
115	      out.push(b'\x01')
116	    } else {
117	      out.push(b'\x00')
118	    }
119	  }
120	  Bytes::from_array(out)
121	}
   	…

5 uncovered line(s) in src\pure\codec\tga_decode.mbt:

  9	pub fn decode_tga_pure(data : Bytes) -> @types.Image raise @types.LoadError {
 10	  let len = data.length()
 11	  // TGA header 18 字节
 12	  if len < 18 {
 13	    raise @types.LoadError::DecodeFailed("TGA 数据过短")
 14	  }
 15	  let id_length = data[0].to_int()
 16	  let color_map_type = data[1].to_int()
 17	  let image_type = data[2].to_int()
 18	  let width = read_u16_le(data, 12)
 19	  let height = read_u16_le(data, 14)
 20	  let bpp = data[16].to_int()
 21	  let descriptor = data[17].to_int()
 22	  // 仅支持无颜色映射
 23	  if color_map_type != 0 {
 24	    raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
 25	      "不支持的 TGA 颜色映射类型，仅支持无颜色映射",
 26	    )
 27	  }
 28	  // 仅支持 image type 2（未压缩 RGB）和 type 10（RLE RGB）
 29	  if image_type != 2 && image_type != 10 {
 30	    raise @types.LoadError::DecodeFailed(
 31	      "不支持的 TGA image type，仅支持 type 2 和 type 10",
 32	    )
 33	  }
 34	  // 仅支持 24-bit 和 32-bit
 35	  if bpp != 24 && bpp != 32 {
 36	    raise @types.LoadError::DecodeFailed(
 37	      "不支持的 TGA 位深，仅支持 24-bit 和 32-bit",
 38	    )
 39	  }
 40	  // 尺寸校验
 41	  if width == 0 || height == 0 {
 42	    raise @types.LoadError::DecodeFailed("TGA 尺寸无效")	<-- UNCOVERED
 43	  }
 44	  let bytes_per_pixel = bpp / 8
 45	  let out_channels = bytes_per_pixel
 46	  @types.check_dims(width, height, out_channels)
 47	  // 行序：descriptor bit4=0 bottom-up, bit4=1 top-down
 48	  let bottom_up = (descriptor & 0x10) == 0
 49	  // 像素数据起始位置
 50	  let pixel_data_start = 18 + id_length
 51	  // 解码像素（BGR(A) → RGB(A)）到 raw_pixels（按文件行序）
 52	  let pixel_count = width * height
 53	  let raw_pixels : Array[Byte] = Array::make(
 54	    pixel_count * out_channels,
 55	    b'\x00',
 56	  )
 57	  let pos = Ref(pixel_data_start)
 58	  let pixel_idx = Ref(0)
 59	  if image_type == 2 {
 60	    // 未压缩
 61	    while pixel_idx.val < pixel_count {
 62	      let p = pos.val
 63	      if p + bytes_per_pixel > len {
 64	        raise @types.LoadError::DecodeFailed("TGA 像素数据越界")	<-- UNCOVERED
 65	      }
 66	      // BGR(A) → RGB(A)
 67	      let b = data[p].to_int()
 68	      let g = data[p + 1].to_int()
 69	      let r = data[p + 2].to_int()
 70	      let out_idx = pixel_idx.val * out_channels
 71	      raw_pixels[out_idx] = r.to_byte()
 72	      raw_pixels[out_idx + 1] = g.to_byte()
 73	      raw_pixels[out_idx + 2] = b.to_byte()
 74	      if out_channels == 4 {
 75	        let a = data[p + 3].to_int()
 76	        raw_pixels[out_idx + 3] = a.to_byte()
 77	      }
 78	      pos.val += bytes_per_pixel
 79	      pixel_idx.val += 1
 80	    }
 81	  } else {
 82	    // RLE (image_type == 10)
 83	    while pixel_idx.val < pixel_count && pos.val < len {
 84	      let header = data[pos.val].to_int()
 85	      pos.val += 1
 86	      if (header & 0x80) != 0 {
 87	        // RLE packet: run = (header & 0x7F) + 1, 读 1 像素重复 run 次
 88	        let run = (header & 0x7F) + 1
 89	        let p = pos.val
 90	        if p + bytes_per_pixel > len {
 91	          raise @types.LoadError::DecodeFailed("TGA RLE 像素数据越界")	<-- UNCOVERED
 92	        }
 93	        let b = data[p].to_int()
 94	        let g = data[p + 1].to_int()
 95	        let r = data[p + 2].to_int()
 96	        let a = if out_channels == 4 { data[p + 3].to_int() } else { 0 }
 97	        pos.val += bytes_per_pixel
 98	        for i in 0..<run {
 99	          if pixel_idx.val + i < pixel_count {
100	            let out_idx = (pixel_idx.val + i) * out_channels
101	            raw_pixels[out_idx] = r.to_byte()
102	            raw_pixels[out_idx + 1] = g.to_byte()
103	            raw_pixels[out_idx + 2] = b.to_byte()
104	            if out_channels == 4 {
105	              raw_pixels[out_idx + 3] = a.to_byte()
106	            }
107	          }
108	        }
109	        pixel_idx.val += run
110	      } else {
111	        // raw packet: count = (header & 0x7F) + 1, 读 count 像素
112	        let count = (header & 0x7F) + 1
113	        for i in 0..<count {
114	          let p = pos.val + i * bytes_per_pixel
115	          if p + bytes_per_pixel > len {
116	            raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
117	              "TGA RLE raw 像素数据越界",
118	            )
119	          }
120	          if pixel_idx.val + i < pixel_count {
121	            let b = data[p].to_int()
122	            let g = data[p + 1].to_int()
123	            let r = data[p + 2].to_int()
124	            let out_idx = (pixel_idx.val + i) * out_channels
125	            raw_pixels[out_idx] = r.to_byte()
126	            raw_pixels[out_idx + 1] = g.to_byte()
127	            raw_pixels[out_idx + 2] = b.to_byte()
128	            if out_channels == 4 {
129	              let a = data[p + 3].to_int()
130	              raw_pixels[out_idx + 3] = a.to_byte()
131	            }
132	          }
133	        }
134	        pos.val += count * bytes_per_pixel
135	        pixel_idx.val += count
136	      }
137	    }
138	  }
139	  // 行序处理：按输出行序写入 pixels
140	  let pixels : Array[Byte] = Array::make(pixel_count * out_channels, b'\x00')
141	  for file_row in 0..<height {
142	    let out_row = if bottom_up { height - 1 - file_row } else { file_row }
143	    for x in 0..<width {
144	      let src_idx = (file_row * width + x) * out_channels
145	      let dst_idx = (out_row * width + x) * out_channels
146	      for c in 0..<out_channels {
147	        pixels[dst_idx + c] = raw_pixels[src_idx + c]
148	      }
149	    }
150	  }
151	  @types.Image::{
152	    width,
153	    height,
154	    channels: out_channels,
155	    data: Bytes::from_array(pixels),
156	  }
157	}
   	…

37 uncovered line(s) in src\pure\codec\tiff_codec.mbt:

44	fn tiff_type_size(type_id : Int) -> Int {
45	  match type_id {
46	    1 => 1 // BYTE	<-- UNCOVERED
47	    2 => 1 // ASCII
48	    3 => 2 // SHORT
49	    4 => 4 // LONG
50	    5 => 8 // RATIONAL	<-- UNCOVERED
51	    6 => 1 // SBYTE
52	    7 => 1 // UNDEFINED
53	    8 => 2 // SSHORT
54	    9 => 4 // SLONG
55	    10 => 8 // SRATIONAL
56	    11 => 4 // FLOAT
57	    12 => 8 // DOUBLE
58	    _ => 0
59	  }
60	}
  	…

 64	fn tiff_read_values(ctx : TiffContext, entry : TiffIfdEntry) -> Array[Int] {
 65	  let total_bytes = tiff_type_size(entry.type_id) * entry.count
 66	  let values : Array[Int] = []
 67	  if total_bytes <= 4 {
 68	    // 值内联在 value_or_offset 中
 69	    let v = entry.value_or_offset
 70	    match entry.type_id {
 71	      1 | 7 =>
 72	        for i in 0..<entry.count {	<-- UNCOVERED
 73	          values.push((v >> (i * 8)) & 0xFF)
 74	        }
 75	      3 =>
 76	        for i in 0..<entry.count {
 77	          values.push((v >> (i * 16)) & 0xFFFF)
 78	        }
 79	      4 => values.push(v)
 80	      _ =>
 81	        for _ in 0..<entry.count {	<-- UNCOVERED
 82	          values.push(0)
 83	        }
 84	    }
 85	  } else {
 86	    // 值在偏移处
 87	    let base = entry.value_or_offset
 88	    let len = ctx.data.length()
 89	    match entry.type_id {
 90	      1 | 7 =>
 91	        for i in 0..<entry.count {	<-- UNCOVERED
 92	          if base + i < len {
 93	            values.push(ctx.data[base + i].to_int())
 94	          } else {
 95	            values.push(0)	<-- UNCOVERED
 96	          }
 97	        }
 98	      3 =>
 99	        for i in 0..<entry.count {
100	          values.push(tiff_read_u16(ctx, base + i * 2))
101	        }
102	      4 =>
103	        for i in 0..<entry.count {	<-- UNCOVERED
104	          values.push(tiff_read_u32(ctx, base + i * 4))
105	        }
106	      _ =>
107	        for _ in 0..<entry.count {	<-- UNCOVERED
108	          values.push(0)
109	        }
110	    }
111	  }
112	  values
113	}
   	…

117	fn tiff_parse_ifd(
118	  ctx : TiffContext,
119	  ifd_offset : Int,
120	) -> Array[TiffIfdEntry] raise @types.LoadError {
121	  let len = ctx.data.length()
122	  if ifd_offset + 2 > len {
123	    raise @types.LoadError::DecodeFailed("TIFF IFD 偏移越界")	<-- UNCOVERED
124	  }
125	  let count = tiff_read_u16(ctx, ifd_offset)
126	  if ifd_offset + 2 + count * 12 > len {
127	    raise @types.LoadError::DecodeFailed("TIFF IFD 条目数据不完整")	<-- UNCOVERED
128	  }
129	  let entries : Array[TiffIfdEntry] = []
130	  for i in 0..<count {
131	    let base = ifd_offset + 2 + i * 12
132	    let tag = tiff_read_u16(ctx, base)
133	    let type_id = tiff_read_u16(ctx, base + 2)
134	    let cnt = tiff_read_u32(ctx, base + 4)
135	    let val_or_off = tiff_read_u32(ctx, base + 8)
136	    entries.push(TiffIfdEntry::{
137	      tag,
138	      type_id,
139	      count: cnt,
140	      value_or_offset: val_or_off,
141	    })
142	  }
143	  entries
144	}
   	…

178	pub fn tiff_lzw_decode(data : Bytes) -> Array[Byte] raise @types.LoadError {
179	  let len = data.length()
180	  if len < 2 {
181	    raise @types.LoadError::DecodeFailed("TIFF LZW 数据过短")
182	  }
183	  let init_code_len = data[0].to_int()
184	  if init_code_len < 2 || init_code_len > 11 {
185	    raise @types.LoadError::DecodeFailed("TIFF LZW 初始码长无效")
186	  }
187	  let clear_code = 1 << init_code_len
188	  let eoi_code = clear_code + 1
189	  let dict : Array[Int] = Array::make(512, 0)
190	  for i in 0..<=255 {
191	    dict[i] = i
192	  }
193	  let mut next_code = clear_code + 2
194	  let mut bit_pos = 8
195	  let data_len = data.length()
196	
197	  fn read_bits(data : Bytes, pos : Int, count : Int, len : Int) -> (Int, Int) {
198	    let mut code : Int = 0
199	    let mut p = pos
200	    let mut i : Int = 0
201	    while i < count && p < len * 8 {
202	      let byte_idx = p / 8
203	      let bit_offset = p % 8
204	      if byte_idx < len {
205	        let bit = (data[byte_idx].to_int() >> bit_offset) & 1
206	        code = code | (bit << i)
207	      }
208	      p += 1
209	      i += 1
210	    }
211	    (code, p)
212	  }
213	
214	  // 读取 CLEAR 码并推进 bit_pos
215	  let (code, new_bit_pos) = read_bits(data, 8, init_code_len + 1, data_len)
216	  bit_pos = new_bit_pos
217	  if code != clear_code {
218	    raise @types.LoadError::DecodeFailed("TIFF LZW 首码应为 CLEAR")
219	  }
220	
221	  let output : Array[Byte] = []
222	  let mut prev_code : Int = 0
223	  let mut code_len = init_code_len
224	  let mut done = false
225	
226	  while !done {
227	    let (c, new_pos) = read_bits(data, bit_pos, code_len + 1, data_len)
228	    bit_pos = new_pos
229	
230	    if c == eoi_code {
231	      done = true
232	    } else if c == clear_code {
233	      for j in 0..<=255 {	<-- UNCOVERED
234	        dict[j] = j
235	      }
236	      next_code = clear_code + 2
237	      code_len = init_code_len
238	    } else if c < next_code {
239	      // 输出 c 对应的字符串
240	      if c <= 255 {
241	        output.push(c.to_byte())
242	      } else {
243	        // 回溯字典获取字符串
244	        let k = c	<-- UNCOVERED
245	        let path : Array[Int] = [k]
246	        let mut current = dict[k]
247	        while current >= clear_code &&
248	              current < next_code &&
249	              path.length() < 512 {	<-- UNCOVERED
250	          path.push(current)
251	          current = dict[current]
252	        }
253	        // 手动反转输出
254	        let mut i = path.length()	<-- UNCOVERED
255	        while i > 0 {
256	          i -= 1	<-- UNCOVERED
257	          output.push(path[i].to_byte())
258	        }
259	        if current <= 255 {
260	          output.push(current.to_byte())	<-- UNCOVERED
261	        }
262	      }
263	      // 更新字典
264	      if next_code < 4096 {
265	        dict[next_code] = prev_code
266	        next_code += 1
267	      }
268	      prev_code = c
269	    } else {
270	      raise @types.LoadError::DecodeFailed("TIFF LZW 码越界")	<-- UNCOVERED
271	    }
272	  }
273	  output
274	}
   	…

278	pub fn tiff_packbits_decode(data : Bytes) -> Array[Byte] raise @types.LoadError {
279	  let len = data.length()
280	  let mut pos : Int = 0
281	  let output : Array[Byte] = []
282	
283	  while pos < len {
284	    let count = data[pos].to_int()
285	    pos += 1
286	    let signed_count : Int = if count > 127 { count - 256 } else { count }
287	
288	    if signed_count == -128 {
289	      // Extended PackBits
290	      if pos + 1 >= len {
291	        raise @types.LoadError::DecodeFailed(
292	          "TIFF PackBits 扩展模式数据不足",
293	        )
294	      }
295	      let extended_count = ((data[pos].to_int() << 8) | data[pos + 1].to_int()) -
296	        32768
297	      pos += 2
298	      if extended_count >= 0 {
299	        // 复制 extended_count + 1 个字节
300	        let n = extended_count + 1
301	        let mut i = 0
302	        while i < n && pos < len {
303	          output.push(data[pos])
304	          pos += 1
305	          i += 1
306	        }
307	      } else {
308	        // 重复下一个字节
309	        if pos >= len {
310	          raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
311	            "TIFF PackBits 扩展模式数据不足",
312	          )
313	        }
314	        let repeat_byte = data[pos]
315	        pos += 1
316	        let n = -extended_count + 1
317	        let mut i = 0
318	        while i < n {
319	          output.push(repeat_byte)
320	          i += 1
321	        }
322	      }
323	    } else if signed_count >= 0 {
324	      // 复制 count + 1 个字节
325	      let n = signed_count + 1
326	      let mut i = 0
327	      while i < n && pos < len {
328	        output.push(data[pos])
329	        pos += 1
330	        i += 1
331	      }
332	    } else {
333	      // 重复下一个字节 (-signed_count + 1) 次
334	      let repeat_count = -signed_count + 1
335	      if pos >= len {
336	        raise @types.LoadError::DecodeFailed("TIFF PackBits 数据不足")	<-- UNCOVERED
337	      }
338	      let repeat_byte = data[pos]
339	      pos += 1
340	      let mut i = 0
341	      while i < repeat_count {
342	        output.push(repeat_byte)
343	        i += 1
344	      }
345	    }
346	  }
347	  output
348	}
   	…

353	pub fn decode_tiff_pure(data : Bytes) -> @types.Image raise @types.LoadError {
354	  let len = data.length()
355	  if len < 8 {
356	    raise @types.LoadError::DecodeFailed("TIFF 数据过短")
357	  }
358	  // 检查字节序
359	  let little_endian = match data[0].to_int() {
360	    0x49 => true // 'I' = little-endian
361	    0x4D => false // 'M' = big-endian
362	    _ => raise @types.LoadError::DecodeFailed("TIFF 字节序标记无效")
363	  }
364	  let ctx = TiffContext::{ data, little_endian }
365	  // 检查 magic 42
366	  let magic = tiff_read_u16(ctx, 2)
367	  if magic != 42 {
368	    raise @types.LoadError::DecodeFailed("TIFF magic 无效")
369	  }
370	  let ifd_offset = tiff_read_u32(ctx, 4)
371	  if ifd_offset < 0 || ifd_offset >= len {
372	    raise @types.LoadError::DecodeFailed("TIFF IFD 偏移无效")
373	  }
374	  let entries = tiff_parse_ifd(ctx, ifd_offset)
375	  // 读取必要字段
376	  let width = match tiff_get_value(entries, ctx, 256) {
377	    Some(w) => w
378	    None => raise @types.LoadError::DecodeFailed("TIFF 缺少 ImageWidth")
379	  }
380	  let height = match tiff_get_value(entries, ctx, 257) {
381	    Some(h) => h
382	    None => raise @types.LoadError::DecodeFailed("TIFF 缺少 ImageLength")	<-- UNCOVERED
383	  }
384	  let bits_per_sample = match tiff_get_values(entries, ctx, 258) {
385	    Some(arr) => arr
386	    None => [8]
387	  }
388	  let compression = match tiff_get_value(entries, ctx, 259) {
389	    Some(c) => c
390	    None => 1
391	  }
392	  let photometric = match tiff_get_value(entries, ctx, 262) {
393	    Some(p) => p
394	    None => 1
395	  }
396	  let samples_per_pixel = match tiff_get_value(entries, ctx, 277) {
397	    Some(s) => s
398	    None => 1
399	  }
400	  let strip_offsets = match tiff_get_values(entries, ctx, 273) {
401	    Some(arr) => arr
402	    None => raise @types.LoadError::DecodeFailed("TIFF 缺少 StripOffsets")
403	  }
404	  let rows_per_strip = match tiff_get_value(entries, ctx, 278) {
405	    Some(r) => r
406	    None => height
407	  }
408	  let strip_byte_counts = match tiff_get_values(entries, ctx, 279) {
409	    Some(arr) => arr
410	    None => []
411	  }
412	  // 支持无压缩、LZW、PackBits 和 Deflate 压缩
413	  if compression != 1 &&
414	    compression != 5 &&
415	    compression != 32773 &&
416	    compression != 8 {
417	    raise @types.LoadError::DecodeFailed(
418	      "TIFF 仅支持无压缩 (Compression=1)、LZW (5)、PackBits (32773) 和 Deflate (8)",
419	    )
420	  }
421	  // 仅支持 8-bit
422	  for i in 0..<bits_per_sample.length() {
423	    if bits_per_sample[i] != 8 {
424	      raise @types.LoadError::DecodeFailed("TIFF 仅支持 8-bit 样本")
425	    }
426	  }
427	  if width <= 0 || height <= 0 {
428	    raise @types.LoadError::DecodeFailed("TIFF 尺寸无效")
429	  }
430	  // 确定输出 channels
431	  let out_channels = samples_per_pixel
432	  if out_channels != 1 && out_channels != 3 && out_channels != 4 {
433	    raise @types.LoadError::DecodeFailed(
434	      "TIFF 仅支持 1/3/4 samples per pixel",
435	    )
436	  }
437	  @types.check_dims(width, height, out_channels)
438	  // 读取像素数据（支持 LZW/PackBits 解压）
439	  let pixel_count = width * height
440	  let pixels : Array[Byte] = Array::make(pixel_count * out_channels, b'\x00')
441	  let bytes_per_pixel = out_channels
442	  // 如果使用了压缩，先解压所有 strip 数据
443	  let decompressed : Bytes = if compression == 5 {
444	    // LZW 解压：逐 strip 读取并解码
445	    let all_data : Array[Byte] = []
446	    let mut strip_idx : Int = 0
447	    while strip_idx < strip_offsets.length() {
448	      let strip_offset = strip_offsets[strip_idx]
449	      let byte_count = if strip_idx < strip_byte_counts.length() {
450	        strip_byte_counts[strip_idx]
451	      } else {
452	        width * height * out_channels - all_data.length()	<-- UNCOVERED
453	      }
454	      // 手动提取 strip 数据（Bytes 无 slice 方法，逐字节取）
455	      let strip_data : Bytes = {
456	        let tmp : Array[Byte] = []
457	        let mut i = strip_offset
458	        let end = i + byte_count
459	        while i < end {
460	          if i < len {
461	            tmp.push(ctx.data[i])
462	          }
463	          i += 1
464	        }
465	        Bytes::from_array(tmp)
466	      }
467	      let decoded = tiff_lzw_decode(strip_data)
468	      // 手动拼接（Array 无 concat 方法，逐元素 push）
469	      for d in decoded {
470	        all_data.push(d)
471	      }
472	      strip_idx += 1
473	    }
474	    Bytes::from_array(all_data)
475	  } else if compression == 32773 {
476	    // PackBits 解压：逐 strip 读取并解码
477	    let all_data : Array[Byte] = []
478	    let mut strip_idx : Int = 0
479	    while strip_idx < strip_offsets.length() {
480	      let strip_offset = strip_offsets[strip_idx]
481	      let byte_count = if strip_idx < strip_byte_counts.length() {
482	        strip_byte_counts[strip_idx]
483	      } else {
484	        width * height * out_channels - all_data.length()	<-- UNCOVERED
485	      }
486	      // 手动提取 strip 数据
487	      let strip_data : Bytes = {
488	        let tmp : Array[Byte] = []
489	        let mut i = strip_offset
490	        let end = i + byte_count
491	        while i < end {
492	          if i < len {
493	            tmp.push(ctx.data[i])
494	          }
495	          i += 1
496	        }
497	        Bytes::from_array(tmp)
498	      }
499	      let decoded = tiff_packbits_decode(strip_data)
500	      for d in decoded {
501	        all_data.push(d)
502	      }
503	      strip_idx += 1
504	    }
505	    Bytes::from_array(all_data)
506	  } else if compression == 8 {
507	    // Deflate 解压：逐 strip 读取并解码
508	    let all_data : Array[Byte] = []	<-- UNCOVERED
509	    let mut strip_idx : Int = 0
510	    while strip_idx < strip_offsets.length() {	<-- UNCOVERED
511	      let strip_offset = strip_offsets[strip_idx]
512	      let byte_count = if strip_idx < strip_byte_counts.length() {
513	        strip_byte_counts[strip_idx]
514	      } else {
515	        width * height * out_channels - all_data.length()	<-- UNCOVERED
516	      }
517	      // 手动提取 strip 数据
518	      let strip_data : Bytes = {
519	        let tmp : Array[Byte] = []
520	        let mut i = strip_offset
521	        let end = i + byte_count
522	        while i < end {
523	          if i < len {	<-- UNCOVERED
524	            tmp.push(ctx.data[i])
525	          }
526	          i += 1
527	        }
528	        Bytes::from_array(tmp)	<-- UNCOVERED
529	      }
530	      let decoded = tiff_deflate_decode(strip_data)	<-- UNCOVERED
531	      for d in decoded {
532	        all_data.push(d)	<-- UNCOVERED
533	      }
534	      strip_idx += 1
535	    }
536	    Bytes::from_array(all_data)	<-- UNCOVERED
537	  } else {
538	    // 无压缩：直接从 strip offset 读取像素数据
539	    let all_data : Array[Byte] = []
540	    let mut strip_idx : Int = 0
541	    while strip_idx < strip_offsets.length() {
542	      let strip_offset = strip_offsets[strip_idx]
543	      let byte_count = if strip_idx < strip_byte_counts.length() {
544	        strip_byte_counts[strip_idx]
545	      } else {
546	        width * height * out_channels - all_data.length()
547	      }
548	      let mut i = strip_offset
549	      let end = i + byte_count
550	      while i < end && i < len {
551	        all_data.push(ctx.data[i])
552	        i += 1
553	      }
554	      strip_idx += 1
555	    }
556	    Bytes::from_array(all_data)
557	  }
558	  let len = decompressed.length()
559	  let mut row = 0
560	  for _ in 0..<strip_offsets.length() {
561	    if row >= height {
562	      break	<-- UNCOVERED
563	    }
564	    let rows_in_strip = if row + rows_per_strip > height {
565	      height - row	<-- UNCOVERED
566	    } else {
567	      rows_per_strip
568	    }
569	    for r in 0..<rows_in_strip {
570	      let y = row + r
571	      let row_start = r * width * bytes_per_pixel
572	      for x in 0..<width {
573	        let src = row_start + x * bytes_per_pixel
574	        let dst = (y * width + x) * out_channels
575	        if src + bytes_per_pixel > len {
576	          raise @types.LoadError::DecodeFailed("TIFF 像素数据越界")	<-- UNCOVERED
577	        }
578	        if out_channels == 1 {
579	          // 灰度
580	          let g = decompressed[src].to_int()
581	          let val = if photometric == 0 { 255 - g } else { g }
582	          pixels[dst] = val.to_byte()
583	          // RGB 或 RGBA
584	        } else if photometric == 2 {
585	          // RGB
586	          pixels[dst] = decompressed[src]
587	          pixels[dst + 1] = decompressed[src + 1]
588	          pixels[dst + 2] = decompressed[src + 2]
589	          if out_channels == 4 {
590	            pixels[dst + 3] = decompressed[src + 3]
591	          }
592	        } else if photometric == 0 || photometric == 1 {	<-- UNCOVERED
593	          // 灰度存储为多通道（不常见，但处理之）
594	          let g = decompressed[src].to_int()	<-- UNCOVERED
595	          let val = if photometric == 0 { 255 - g } else { g }
596	          pixels[dst] = val.to_byte()
597	          pixels[dst + 1] = val.to_byte()
598	          pixels[dst + 2] = val.to_byte()
599	          if out_channels == 4 {
600	            pixels[dst + 3] = decompressed[src + 3]	<-- UNCOVERED
601	          }
602	        } else {
603	          raise @types.LoadError::DecodeFailed(	<-- UNCOVERED
604	            "TIFF 不支持的 PhotometricInterpretation",
605	          )
606	        }
607	      }
608	    }
609	    row += rows_in_strip
610	  }
611	  @types.Image::{
612	    width,
613	    height,
614	    channels: out_channels,
615	    data: Bytes::from_array(pixels),
616	  }
617	}
   	…

7 uncovered line(s) in src\pure\codec\webp_decode.mbt:

127	fn read_huffman_tree(br : BitReader) -> HuffmanTree {
128	  let tree = HuffmanTree::new()
129	  let is_simple = br.read_bit()
130	  if is_simple == 1 {
131	    // Simple Huffman: 1 or 2 symbols
132	    let num_symbols = br.read_bit() + 1
133	    if num_symbols == 1 {
134	      // Single symbol
135	      let max_bits = br.read_bits(4) + 1
136	      let symbol = br.read_bits(max_bits)
137	      tree.add_code(0, 1, symbol)
138	    } else {
139	      // Two symbols
140	      let max_bits = br.read_bits(4) + 1
141	      let sym0 = br.read_bits(max_bits)
142	      let sym1 = br.read_bits(max_bits)
143	      tree.add_code(0, 1, sym0)
144	      tree.add_code(1, 1, sym1)
145	    }
146	    return tree
147	  }
148	  // Complex Huffman: read code length code
149	  let num_code_lengths = br.read_bits(4) + 4
150	  // Code length code order
151	  let order = [17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
152	  // Read code length code lengths (3 bits each)
153	  let code_len_code_lens : Array[Int] = Array::make(19, 0)
154	  for i in 0..<num_code_lengths {
155	    code_len_code_lens[order[i]] = br.read_bits(3)
156	  }
157	  // Build code length Huffman tree
158	  let cl_tree = HuffmanTree::new()
159	  for sym in 0..<19 {
160	    if code_len_code_lens[sym] > 0 {
161	      cl_tree.add_code(sym, code_len_code_lens[sym], sym)
162	    }
163	  }
164	  // Decode code lengths using the code length tree
165	  let code_lens : Array[Int] = []
166	  let mut sym = cl_tree.decode(br)
167	  while code_lens.length() < 256 {
168	    if sym < 16 {
169	      code_lens.push(sym)
170	      sym = cl_tree.decode(br)
171	    } else if sym == 16 {
172	      // Repeat previous code length 3+2extra times
173	      let extra = br.read_bits(2)
174	      let repeat = 3 + extra
175	      let prev = if code_lens.length() > 0 {
176	        code_lens[code_lens.length() - 1]
177	      } else {
178	        0
179	      }
180	      for _ in 0..<repeat {
181	        code_lens.push(prev)
182	      }
183	      sym = cl_tree.decode(br)
184	    } else if sym == 17 {
185	      // Repeat zero 3+3extra times
186	      let extra = br.read_bits(3)
187	      let repeat = 3 + extra
188	      for _ in 0..<repeat {
189	        code_lens.push(0)
190	      }
191	      sym = cl_tree.decode(br)
192	    } else if sym == 18 {
193	      // Repeat zero 11+7extra times
194	      let extra = br.read_bits(7)
195	      let repeat = 11 + extra
196	      for _ in 0..<repeat {
197	        code_lens.push(0)
198	      }
199	      sym = cl_tree.decode(br)
200	    } else {
201	      sym = cl_tree.decode(br)	<-- UNCOVERED
202	    }
203	  }
204	  // Build the actual Huffman tree from code lengths
205	  // Assign canonical codes
206	  let max_len = code_lens.fold(init=0, (a, b) => if a > b { a } else { b })
207	  if max_len == 0 {
208	    tree.add_code(0, 1, 0)
209	    return tree
210	  }
211	  // Count codes at each length
212	  let bl_count : Array[Int] = Array::make(max_len + 1, 0)
213	  for cl in code_lens {
214	    if cl > 0 {
215	      bl_count[cl] += 1
216	    }
217	  }
218	  // Compute first code at each length
219	  let next_code : Array[Int] = Array::make(max_len + 1, 0)
220	  let mut code = 0
221	  for bits in 1..<=max_len {
222	    code = (code + bl_count[bits - 1]) << 1
223	    next_code[bits] = code
224	  }
225	  // Assign codes
226	  for n in 0..<code_lens.length() {
227	    let cl = code_lens[n]
228	    if cl > 0 {
229	      tree.add_code(next_code[cl], cl, n)
230	      next_code[cl] += 1
231	    }
232	  }
233	  tree
234	}
   	…

301	pub fn decode_webp_pure(data : Bytes) -> @types.Image raise @types.LoadError {
302	  let len = data.length()
303	  if len < 12 {
304	    raise @types.LoadError::DecodeFailed("WebP 数据过短")
305	  }
306	  // RIFF header
307	  if data[0].to_int() != 0x52 ||
308	    data[1].to_int() != 0x49 ||
309	    data[2].to_int() != 0x46 ||
310	    data[3].to_int() != 0x46 {
311	    raise @types.LoadError::DecodeFailed("RIFF magic 不匹配")
312	  }
313	  // WEBP fourcc
314	  if data[8].to_int() != 0x57 ||
315	    data[9].to_int() != 0x45 ||
316	    data[10].to_int() != 0x42 ||
317	    data[11].to_int() != 0x50 {
318	    raise @types.LoadError::DecodeFailed("WEBP magic 不匹配")
319	  }
320	  // Find VP8L or VP8 chunk
321	  let mut pos = 12
322	  let mut vp8l_data_start = -1
323	  let mut vp8_data_start = -1
324	  while pos + 8 <= len {
325	    let chunk_id = data[pos].to_int() |
326	      (data[pos + 1].to_int() << 8) |
327	      (data[pos + 2].to_int() << 16) |
328	      (data[pos + 3].to_int() << 24)
329	    let chunk_size = data[pos + 4].to_int() |
330	      (data[pos + 5].to_int() << 8) |
331	      (data[pos + 6].to_int() << 16) |
332	      (data[pos + 7].to_int() << 24)
333	    // "VP8L" = 0x4C385056 (little-endian: 56 50 38 4C)
334	    if chunk_id == 0x4C385056 {
335	      vp8l_data_start = pos + 8
336	      let _ = chunk_size
337	      break
338	    }
339	    // "VP8 " = 0x20385056 (lossy)
340	    if chunk_id == 0x20385056 {
341	      vp8_data_start = pos + 8
342	      let _ = chunk_size
343	      break
344	    }
345	    pos += 8 + chunk_size
346	    if chunk_size % 2 == 1 {
347	      pos += 1 // padding
348	    }
349	  }
350	  // VP8 lossy 解码
351	  if vp8_data_start >= 0 {
352	    return decode_vp8_frame(data, vp8_data_start)
353	  }
354	  if vp8l_data_start < 0 {
355	    raise @types.LoadError::DecodeFailed("未找到 VP8L 或 VP8 chunk")
356	  }
357	  // VP8L signature
358	  if data[vp8l_data_start].to_int() != 0x2F {
359	    raise @types.LoadError::DecodeFailed("VP8L 签名不匹配")
360	  }
361	  let br = BitReader::new(data, vp8l_data_start + 1)
362	  // Read image size
363	  let width = br.read_bits(14) + 1
364	  let height = br.read_bits(14) + 1
365	  let alpha_used = br.read_bit()
366	  let _ = br.read_bits(3) // version, reserved
367	  if width <= 0 || height <= 0 || width > 16384 || height > 16384 {
368	    raise @types.LoadError::DecodeFailed("WebP 尺寸无效")	<-- UNCOVERED
369	  }
370	  // Read transforms (up to 4, in reverse order)
371	  let transforms : Array[Int] = [] // transform types in read order
372	  let mut has_transform = true
373	  while has_transform && transforms.length() < 4 {
374	    has_transform = br.read_bit() == 1
375	    if has_transform {
376	      let t_type = br.read_bits(2)
377	      transforms.push(t_type)
378	      // Skip transform-specific data for now
379	      // We'll handle transforms after decoding pixels
380	      match t_type {
381	        0 => {
382	          // Predictor transform
383	          let _ = br.read_bits(3) // bits
384	          let _ = br.read_bits(1) // unused
385	          // Read predictor image size (subset)
386	          let _ = br.read_bits(1) // use_subset
387	        }
388	        1 => {
389	          // Cross-color transform
390	          let _ = br.read_bits(3) // bits
391	          let _ = br.read_bits(1) // unused
392	        }
393	        2 =>
394	          // Subtract-green transform (no data)
395	          ()
396	        3 => {
397	          // Color (palette) transform
398	          let _ = br.read_bits(1) // use_subset
399	        }
400	        _ => ()	<-- UNCOVERED
401	      }
402	    }
403	  }
404	  // Read color cache info
405	  let use_color_cache = br.read_bit()
406	  let cache_bits = if use_color_cache == 1 { br.read_bits(4) } else { 0 }
407	  let cache_size = if cache_bits > 0 { 1 << cache_bits } else { 0 }
408	  let cache_mask = if cache_bits > 0 { cache_size - 1 } else { 0 }
409	  // Read meta Huffman codes
410	  let use_meta = br.read_bit()
411	  let huffman_trees : Array[HuffmanTree] = []
412	  if use_meta == 0 {
413	    // Single Huffman tree for all
414	    huffman_trees.push(read_huffman_tree(br))
415	  } else {
416	    // Meta Huffman: read a Huffman tree for meta codes, then read sub-trees
417	    let meta_tree = read_huffman_tree(br)
418	    let num_meta = br.read_bits(2) + 1
419	    for _ in 0..<num_meta {
420	      huffman_trees.push(read_huffman_tree(br))
421	    }
422	    // Use meta_tree to select which sub-tree to use
423	    // For simplicity, we'll use the first tree
424	    let _ = meta_tree
425	  }
426	  // Decode pixel data using Huffman-coded LZ77
427	  let n = width * height
428	  let pixels : Array[Int] = Array::make(n, 0)
429	  // Color cache
430	  let color_cache : Array[Int] = Array::make(cache_size, 0)
431	  // LZ77 decode
432	  let mut pixel_idx = 0
433	  let tree = if huffman_trees.length() > 0 {
434	    huffman_trees[0]
435	  } else {
436	    HuffmanTree::new()	<-- UNCOVERED
437	  }
438	  while pixel_idx < n {
439	    let sym = tree.decode(br)
440	    if sym < 256 {
441	      // Literal: green is the symbol, read R, B, A from tree
442	      let g = sym
443	      let r = tree.decode(br)
444	      let b = tree.decode(br)
445	      let a = if alpha_used == 1 { tree.decode(br) } else { 255 }
446	      let argb = (a << 24) | (b << 16) | (g << 8) | r
447	      pixels[pixel_idx] = argb
448	      pixel_idx += 1
449	      // Update color cache
450	      if cache_size > 0 {
451	        let hash = (argb * 0x1E35A7BD) & 0xFFFFFFFF
452	        color_cache[(hash >> (32 - cache_bits)) & cache_mask] = argb
453	      }
454	    } else if sym == 256 {
455	      // Color cache index
456	      if cache_size > 0 {
457	        let idx = br.read_bits(cache_bits)
458	        pixels[pixel_idx] = color_cache[idx]
459	        pixel_idx += 1
460	      }
461	    } else if sym >= 257 {
462	      // Copy from back-reference
463	      let copy_len = sym - 256 // 1-based
464	      let copy_code = br.read_bits(4) // distance code
465	      let dist_extra_bits = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7]
466	      let dist_base = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
467	      let extra = if copy_code < 16 {
468	        br.read_bits(dist_extra_bits[copy_code])
469	      } else {
470	        0	<-- UNCOVERED
471	      }
472	      let distance = if copy_code < 16 {
473	        dist_base[copy_code] + extra
474	      } else {
475	        1	<-- UNCOVERED
476	      }
477	      // Copy pixels
478	      for _ in 0..<copy_len {
479	        if pixel_idx < n && pixel_idx - distance >= 0 {
480	          pixels[pixel_idx] = pixels[pixel_idx - distance]	<-- UNCOVERED
481	          pixel_idx += 1
482	        } else {
483	          pixel_idx += 1
484	        }
485	      }
486	    }
487	  }
488	  // Apply inverse transforms in reverse order
489	  // (transforms were read in order, apply in reverse)
490	  for j in 0..<transforms.length() {
491	    let i = transforms.length() - 1 - j
492	    match transforms[i] {
493	      2 => apply_subtract_green_inverse(pixels, n)
494	      0 => apply_predictor_inverse(pixels, width, height, 8)
495	      _ => ()
496	    }
497	  }
498	  // Convert ARGB pixels to Image
499	  let channels = if alpha_used == 1 { 4 } else { 3 }
500	  @types.check_dims(width, height, channels)
501	  let out : Array[Byte] = Array::make(n * channels, b'\x00')
502	  for i in 0..<n {
503	    let argb = pixels[i]
504	    let r = argb & 0xFF
505	    let g = (argb >> 8) & 0xFF
506	    let b = (argb >> 16) & 0xFF
507	    let a = (argb >> 24) & 0xFF
508	    if channels == 4 {
509	      out[i * 4] = r.to_byte()
510	      out[i * 4 + 1] = g.to_byte()
511	      out[i * 4 + 2] = b.to_byte()
512	      out[i * 4 + 3] = a.to_byte()
513	    } else {
514	      out[i * 3] = r.to_byte()
515	      out[i * 3 + 1] = g.to_byte()
516	      out[i * 3 + 2] = b.to_byte()
517	    }
518	  }
519	  @types.Image::{ width, height, channels, data: Bytes::from_array(out) }
520	}
   	…

299 uncovered line(s) in src\pure\codec\webp_vp8_decode.mbt:

45	fn Vp8BitReader::read_bit(self : Vp8BitReader) -> Int {
46	  self.read_bits(1)	<-- UNCOVERED
47	}
  	…

50	fn Vp8BitReader::read_bool(self : Vp8BitReader, prob : Int) -> Int {
51	  // 简化版：用于帧头解析，prob 暂不使用
52	  ignore(prob)	<-- UNCOVERED
53	  self.read_bit()
54	}
  	…

69	fn Vp8BoolDecoder::new(data : Bytes, start : Int) -> Vp8BoolDecoder {
70	  let bd = Vp8BoolDecoder::{	<-- UNCOVERED
71	    data,
72	    pos: start,
73	    bit_count: 0,
74	    range: 255,
75	    value: 0,
76	  }
77	  // 初始化：读取前两个字节
78	  bd.value = if bd.pos < bd.data.length() {	<-- UNCOVERED
79	    bd.data[bd.pos].to_int()
80	  } else {
81	    0	<-- UNCOVERED
82	  }
83	  bd.pos += 1
84	  bd.value = (bd.value << 8) |
85	    (if bd.pos < bd.data.length() { bd.data[bd.pos].to_int() } else { 0 })	<-- UNCOVERED
86	  bd.pos += 1
87	  bd.bit_count = 16
88	  bd
89	}
  	…

 92	fn Vp8BoolDecoder::read_bool(self : Vp8BoolDecoder, prob : Int) -> Int {
 93	  // VP8 布尔解码器核心算法
 94	  let split = 1 + (((self.range - 1) * prob) >> 8)	<-- UNCOVERED
 95	  let bigsplit = split << 8
 96	  let mut bit = 0
 97	  if self.value >= bigsplit {
 98	    self.range -= split	<-- UNCOVERED
 99	    self.value -= bigsplit
100	    bit = 1
101	  } else {
102	    self.range = split	<-- UNCOVERED
103	  }
104	  // 归一化
105	  while self.range < 128 {
106	    self.range = self.range << 1	<-- UNCOVERED
107	    self.value = self.value << 1
108	    self.bit_count -= 1
109	    if self.bit_count == 0 {
110	      self.bit_count = 8	<-- UNCOVERED
111	      if self.pos < self.data.length() {
112	        self.value = self.value | self.data[self.pos].to_int()
113	        self.pos += 1
114	      }
115	    }
116	  }
117	  bit
118	}
   	…

144	fn Vp8AdaptiveProbs::new() -> Vp8AdaptiveProbs {
145	  // VP8 默认概率（基于统计）
146	  let default_zero = [	<-- UNCOVERED
147	    200, 180, 170, 160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40,
148	  ]
149	  let default_one = [
150	    180, 170, 160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30,
151	  ]
152	  let default_eob = [
153	    100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250,
154	  ]
155	  let default_extra = [
156	    140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
157	  ]
158	
159	  Vp8AdaptiveProbs::{
160	    prob_zero: default_zero,
161	    prob_one: default_one,
162	    prob_eob: default_eob,
163	    prob_extra: default_extra,
164	    prob_sign: 128,
165	    count_zero: Array::make(16, 0),	<-- UNCOVERED
166	    count_one: Array::make(16, 0),
167	    count_eob: Array::make(16, 0),
168	    count_extra: Array::make(16, 0),
169	    count_sign_pos: 0,
170	    count_sign_neg: 0,
171	    total_blocks: 0,
172	  }
173	}
   	…

177	fn Vp8AdaptiveProbs::record_zero(self : Vp8AdaptiveProbs, pos : Int) -> Unit {
178	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
179	    self.count_zero[pos] += 1
180	  }
181	}
   	…

185	fn Vp8AdaptiveProbs::record_one(self : Vp8AdaptiveProbs, pos : Int) -> Unit {
186	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
187	    self.count_one[pos] += 1
188	  }
189	}
   	…

193	fn Vp8AdaptiveProbs::record_eob(self : Vp8AdaptiveProbs, pos : Int) -> Unit {
194	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
195	    self.count_eob[pos] += 1
196	  }
197	}
   	…

201	fn Vp8AdaptiveProbs::record_extra(self : Vp8AdaptiveProbs, pos : Int) -> Unit {
202	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
203	    self.count_extra[pos] += 1
204	  }
205	}
   	…

209	fn Vp8AdaptiveProbs::record_sign(
210	  self : Vp8AdaptiveProbs,
211	  is_positive : Bool,
212	) -> Unit {
213	  if is_positive {	<-- UNCOVERED
214	    self.count_sign_pos += 1
215	  } else {
216	    self.count_sign_neg += 1	<-- UNCOVERED
217	  }
218	}
   	…

223	fn Vp8AdaptiveProbs::update(self : Vp8AdaptiveProbs) -> Unit {
224	  self.total_blocks += 1	<-- UNCOVERED
225	  // 每 8 个块更新一次概率（减少计算量）
226	  if self.total_blocks % 8 != 0 {
227	    return	<-- UNCOVERED
228	  }
229	
230	  // EMA 更新：new_prob = old_prob * 7/8 + observed_prob * 1/8
231	  for i in 0..<16 {
232	    let total = self.count_zero[i] + self.count_one[i] + 1	<-- UNCOVERED
233	    // 更新零系数概率
234	    if total > 0 {
235	      let observed_zero = self.count_zero[i] * 256 / total	<-- UNCOVERED
236	      self.prob_zero[i] = (self.prob_zero[i] * 7 + observed_zero) / 8
237	    }
238	    // 更新单系数概率
239	    let non_zero = self.count_one[i] + 1
240	    if non_zero > 0 {
241	      let observed_one = self.count_one[i] * 256 / non_zero	<-- UNCOVERED
242	      self.prob_one[i] = (self.prob_one[i] * 7 + observed_one) / 8
243	    }
244	    // 更新 EOB 概率
245	    let eob_total = self.count_eob[i] + 1
246	    if eob_total > 0 {
247	      let observed_eob = self.count_eob[i] * 256 / eob_total	<-- UNCOVERED
248	      self.prob_eob[i] = (self.prob_eob[i] * 7 + observed_eob) / 8
249	    }
250	    // 更新额外位概率
251	    let extra_total = self.count_extra[i] + 1
252	    if extra_total > 0 {
253	      let observed_extra = self.count_extra[i] * 256 / extra_total	<-- UNCOVERED
254	      self.prob_extra[i] = (self.prob_extra[i] * 7 + observed_extra) / 8
255	    }
256	  }
257	
258	  // 更新符号概率
259	  let sign_total = self.count_sign_pos + self.count_sign_neg + 1
260	  if sign_total > 0 {
261	    let observed_sign = self.count_sign_pos * 256 / sign_total	<-- UNCOVERED
262	    self.prob_sign = (self.prob_sign * 7 + observed_sign) / 8
263	  }
264	
265	  // 重置计数器（保留部分历史）
266	  for i in 0..<16 {
267	    self.count_zero[i] = self.count_zero[i] / 2	<-- UNCOVERED
268	    self.count_one[i] = self.count_one[i] / 2
269	    self.count_eob[i] = self.count_eob[i] / 2
270	    self.count_extra[i] = self.count_extra[i] / 2
271	  }
272	  self.count_sign_pos = self.count_sign_pos / 2
273	  self.count_sign_neg = self.count_sign_neg / 2
274	}
   	…

278	fn Vp8AdaptiveProbs::get_zero_prob(self : Vp8AdaptiveProbs, pos : Int) -> Int {
279	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
280	    self.prob_zero[pos]
281	  } else {
282	    150	<-- UNCOVERED
283	  }
284	}
   	…

288	fn Vp8AdaptiveProbs::get_one_prob(self : Vp8AdaptiveProbs, pos : Int) -> Int {
289	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
290	    self.prob_one[pos]
291	  } else {
292	    170	<-- UNCOVERED
293	  }
294	}
   	…

298	fn Vp8AdaptiveProbs::get_eob_prob(self : Vp8AdaptiveProbs, pos : Int) -> Int {
299	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
300	    self.prob_eob[pos]
301	  } else {
302	    100	<-- UNCOVERED
303	  }
304	}
   	…

308	fn Vp8AdaptiveProbs::get_extra_prob(self : Vp8AdaptiveProbs, pos : Int) -> Int {
309	  if pos >= 0 && pos < 16 {	<-- UNCOVERED
310	    self.prob_extra[pos]
311	  } else {
312	    140	<-- UNCOVERED
313	  }
314	}
   	…

321	fn vp8_zigzag_4x4() -> Array[Int] {
322	  [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15]	<-- UNCOVERED
323	}
   	…

328	fn vp8_parse_residual_4x4(
329	  bd : Vp8BoolDecoder,
330	  probs : Vp8AdaptiveProbs,
331	  is_dc : Bool,
332	) -> Array[Int] {
333	  ignore(is_dc)	<-- UNCOVERED
334	  let coeffs = Array::make(16, 0)
335	  let zigzag = vp8_zigzag_4x4()
336	  let mut eob = false // end of block
337	  for i in 0..<16 {
338	    if eob {	<-- UNCOVERED
339	      break
340	    }
341	    let pos = zigzag[i]
342	    // 使用自适应概率解析 base token
343	    let zero_prob = probs.get_zero_prob(i)	<-- UNCOVERED
344	    let is_zero = bd.read_bool(zero_prob) == 1
345	    if is_zero {
346	      probs.record_zero(i)	<-- UNCOVERED
347	      // 检查是否是 EOB（块结束）
348	      if i > 0 {
349	        let eob_prob = probs.get_eob_prob(i)	<-- UNCOVERED
350	        let is_eob = bd.read_bool(eob_prob) == 1
351	        if is_eob {
352	          probs.record_eob(i)	<-- UNCOVERED
353	          eob = true
354	        }
355	      }
356	      continue
357	    }
358	    // 非零系数：解析符号和值
359	    let one_prob = probs.get_one_prob(i)	<-- UNCOVERED
360	    let is_one = bd.read_bool(one_prob) == 1
361	    let mut value = 0
362	    if is_one {
363	      probs.record_one(i)	<-- UNCOVERED
364	      value = 1
365	    } else {
366	      // 解析额外位
367	      let extra_prob = probs.get_extra_prob(i)	<-- UNCOVERED
368	      probs.record_extra(i)
369	      let mut extra = 1
370	      let mut continue_read = true
371	      while continue_read {
372	        let read_more = bd.read_bool(extra_prob) == 1	<-- UNCOVERED
373	        if read_more {
374	          extra = (extra << 1) | bd.read_bool(128)	<-- UNCOVERED
375	          if extra > 2047 {
376	            continue_read = false	<-- UNCOVERED
377	          }
378	        } else {
379	          continue_read = false	<-- UNCOVERED
380	        }
381	      }
382	      value = extra + 1
383	    }
384	    // 解析符号（使用自适应符号概率）
385	    let sign = bd.read_bool(probs.prob_sign)	<-- UNCOVERED
386	    let is_positive = sign == 0
387	    probs.record_sign(is_positive)	<-- UNCOVERED
388	    if !is_positive {
389	      value = -value	<-- UNCOVERED
390	    }
391	    coeffs[pos] = value
392	  }
393	  // 更新自适应概率
394	  probs.update()	<-- UNCOVERED
395	  coeffs
396	}
   	…

400	fn vp8_parse_dc_coeffs_16x16(
401	  bd : Vp8BoolDecoder,
402	  probs : Vp8AdaptiveProbs,
403	) -> Array[Int] {
404	  // 16x16 块的 DC 系数是一个 4x4 块，使用 WHT 变换
405	  vp8_parse_residual_4x4(bd, probs, true)	<-- UNCOVERED
406	}
   	…

464	fn parse_quantization(br : Vp8BitReader) -> Vp8QuantParams {
465	  let y_ac = br.read_bits(7)	<-- UNCOVERED
466	  let mut y_dc_delta = 0
467	  if br.read_bool(155) == 1 {	<-- UNCOVERED
468	    y_dc_delta = br.read_bits(4)
469	    if y_dc_delta > 7 {
470	      y_dc_delta -= 16	<-- UNCOVERED
471	    }
472	  }
473	  let y_dc = y_ac + y_dc_delta
474	
475	  let mut uv_ac_delta = 0
476	  if br.read_bool(155) == 1 {	<-- UNCOVERED
477	    uv_ac_delta = br.read_bits(4)
478	    if uv_ac_delta > 7 {
479	      uv_ac_delta -= 16	<-- UNCOVERED
480	    }
481	  }
482	  let uv_ac = y_ac + uv_ac_delta
483	
484	  let mut uv_dc_delta = 0
485	  if br.read_bool(155) == 1 {	<-- UNCOVERED
486	    uv_dc_delta = br.read_bits(4)
487	    if uv_dc_delta > 7 {
488	      uv_dc_delta -= 16	<-- UNCOVERED
489	    }
490	  }
491	  let uv_dc = y_dc + uv_dc_delta
492	
493	  Vp8QuantParams::{ y_ac, y_dc, uv_ac, uv_dc }
494	}
   	…

512	fn Vp8SegmentParams::new() -> Vp8SegmentParams {
513	  Vp8SegmentParams::{	<-- UNCOVERED
514	    enabled: false,
515	    update_map: false,
516	    update_features: false,
517	    feature_mode: 0,
518	    quantizer_deltas: [0, 0, 0, 0],
519	    filter_deltas: [0, 0, 0, 0],
520	    mb_segment_tree_probs: [255, 255, 255],
521	  }
522	}
   	…

526	fn parse_segmentation(br : Vp8BitReader) -> Vp8SegmentParams {
527	  let params = Vp8SegmentParams::new()	<-- UNCOVERED
528	
529	  // segmentation_enabled
530	  params.enabled = br.read_bool(255) == 1	<-- UNCOVERED
531	  if !params.enabled {
532	    return params	<-- UNCOVERED
533	  }
534	
535	  // update_mb_segmentation_map
536	  params.update_map = br.read_bool(255) == 1	<-- UNCOVERED
537	
538	  // update_segment_feature_data
539	  params.update_features = br.read_bool(255) == 1	<-- UNCOVERED
540	
541	  if params.update_features {
542	    // segment_feature_mode：0=delta，1=absolute
543	    params.feature_mode = br.read_bool(255)	<-- UNCOVERED
544	
545	    // 解析每个 segment 的量化器增量
546	    for i in 0..<4 {
547	      if br.read_bool(255) == 1 {	<-- UNCOVERED
548	        let mut delta = br.read_bits(7)
549	        if delta > 63 {
550	          delta -= 128	<-- UNCOVERED
551	        }
552	        params.quantizer_deltas[i] = delta
553	      }
554	    }
555	
556	    // 解析每个 segment 的滤波级别增量
557	    for i in 0..<4 {
558	      if br.read_bool(255) == 1 {	<-- UNCOVERED
559	        let mut delta = br.read_bits(6)
560	        if delta > 31 {
561	          delta -= 64	<-- UNCOVERED
562	        }
563	        params.filter_deltas[i] = delta
564	      }
565	    }
566	  }
567	
568	  if params.update_map {
569	    // 解析宏块 segment 树概率（3个）
570	    for i in 0..<3 {	<-- UNCOVERED
571	      if br.read_bool(255) == 1 {
572	        params.mb_segment_tree_probs[i] = br.read_bits(8)
573	      } else {
574	        params.mb_segment_tree_probs[i] = 255	<-- UNCOVERED
575	      }
576	    }
577	  }
578	
579	  params
580	}
   	…

584	fn vp8_parse_mb_segment_id(bd : Vp8BoolDecoder, probs : Array[Int]) -> Int {
585	  // VP8 segment ID 使用二叉树解析
586	  // 树结构：
587	  //       0
588	  //      / \
589	  //     0   1
590	  //    / \ / \
591	  //   0  1 2  3
592	  if bd.read_bool(probs[0]) == 0 {	<-- UNCOVERED
593	    // 左子树：0 或 1
594	    if bd.read_bool(probs[1]) == 0 {	<-- UNCOVERED
595	      0
596	    } else {
597	      1	<-- UNCOVERED
598	    }
599	    // 右子树：2 或 3
600	  } else if bd.read_bool(probs[2]) == 0 {	<-- UNCOVERED
601	    2
602	  } else {
603	    3	<-- UNCOVERED
604	  }
605	}
   	…

609	fn vp8_get_quant_for_segment(
610	  base_quant : Vp8QuantParams,
611	  segment : Vp8SegmentParams,
612	  segment_id : Int,
613	) -> Vp8QuantParams {
614	  if !segment.enabled || segment_id < 0 || segment_id >= 4 {	<-- UNCOVERED
615	    return base_quant
616	  }
617	  let delta = segment.quantizer_deltas[segment_id]
618	  if segment.feature_mode == 0 {
619	    // delta 模式
620	    Vp8QuantParams::{	<-- UNCOVERED
621	      y_ac: base_quant.y_ac + delta,
622	      y_dc: base_quant.y_dc + delta,
623	      uv_ac: base_quant.uv_ac + delta,
624	      uv_dc: base_quant.uv_dc + delta,
625	    }
626	  } else {
627	    // absolute 模式
628	    Vp8QuantParams::{ y_ac: delta, y_dc: delta, uv_ac: delta, uv_dc: delta }	<-- UNCOVERED
629	  }
630	}
   	…

634	fn vp8_get_filter_level_for_segment(
635	  base_level : Int,
636	  segment : Vp8SegmentParams,
637	  segment_id : Int,
638	) -> Int {
639	  if !segment.enabled || segment_id < 0 || segment_id >= 4 {	<-- UNCOVERED
640	    return base_level
641	  }
642	  let delta = segment.filter_deltas[segment_id]
643	  let mut level = if segment.feature_mode == 0 {
644	    base_level + delta	<-- UNCOVERED
645	  } else {
646	    delta	<-- UNCOVERED
647	  }
648	  if level < 0 {
649	    level = 0	<-- UNCOVERED
650	  }
651	  if level > 63 {
652	    level = 63	<-- UNCOVERED
653	  }
654	  level
655	}
   	…

664	fn vp8_idct_4x4(block : Array[Int]) -> Array[Int] {
665	  let result = Array::make(16, 0)	<-- UNCOVERED
666	  // 行变换（一维 IDCT）
667	  for i in 0..<4 {
668	    let a0 = block[i * 4]	<-- UNCOVERED
669	    let a1 = block[i * 4 + 1]
670	    let a2 = block[i * 4 + 2]
671	    let a3 = block[i * 4 + 3]
672	    // VP8 一维 IDCT（使用 c=85627, c*sqrt(2)=121095）
673	    let b0 = a0 * 85627 + a2 * 85627
674	    let b1 = a0 * 85627 - a2 * 85627
675	    let b2 = a1 * 53527 - a3 * 22109
676	    let b3 = a1 * 22109 + a3 * 53527
677	    result[i * 4] = (b0 + b3) >> 17
678	    result[i * 4 + 1] = (b1 + b2) >> 17
679	    result[i * 4 + 2] = (b1 - b2) >> 17
680	    result[i * 4 + 3] = (b0 - b3) >> 17
681	  }
682	  // 列变换（一维 IDCT）
683	  let temp = Array::make(16, 0)	<-- UNCOVERED
684	  for i in 0..<16 {
685	    temp[i] = result[i]	<-- UNCOVERED
686	  }
687	  for j in 0..<4 {
688	    let a0 = temp[j]	<-- UNCOVERED
689	    let a1 = temp[4 + j]
690	    let a2 = temp[8 + j]
691	    let a3 = temp[12 + j]
692	    let b0 = a0 * 85627 + a2 * 85627
693	    let b1 = a0 * 85627 - a2 * 85627
694	    let b2 = a1 * 53527 - a3 * 22109
695	    let b3 = a1 * 22109 + a3 * 53527
696	    result[j] = (b0 + b3) >> 17
697	    result[4 + j] = (b1 + b2) >> 17
698	    result[8 + j] = (b1 - b2) >> 17
699	    result[12 + j] = (b0 - b3) >> 17
700	  }
701	  result
702	}
   	…

708	fn vp8_wht_4x4(block : Array[Int]) -> Array[Int] {
709	  let result = Array::make(16, 0)	<-- UNCOVERED
710	  // 行变换
711	  for i in 0..<4 {
712	    let a0 = block[i * 4]	<-- UNCOVERED
713	    let a1 = block[i * 4 + 1]
714	    let a2 = block[i * 4 + 2]
715	    let a3 = block[i * 4 + 3]
716	    let b0 = a0 + a1
717	    let b1 = a0 - a1
718	    let b2 = a2 + a3
719	    let b3 = a2 - a3
720	    result[i * 4] = b0 + b2
721	    result[i * 4 + 1] = b1 + b3
722	    result[i * 4 + 2] = b1 - b3
723	    result[i * 4 + 3] = b0 - b2
724	  }
725	  // 列变换
726	  let temp = Array::make(16, 0)	<-- UNCOVERED
727	  for i in 0..<16 {
728	    temp[i] = result[i]	<-- UNCOVERED
729	  }
730	  for j in 0..<4 {
731	    let a0 = temp[j]	<-- UNCOVERED
732	    let a1 = temp[4 + j]
733	    let a2 = temp[8 + j]
734	    let a3 = temp[12 + j]
735	    let b0 = a0 + a1
736	    let b1 = a0 - a1
737	    let b2 = a2 + a3
738	    let b3 = a2 - a3
739	    result[j] = (b0 + b2) >> 1
740	    result[4 + j] = (b1 + b3) >> 1
741	    result[8 + j] = (b1 - b3) >> 1
742	    result[12 + j] = (b0 - b2) >> 1
743	  }
744	  result
745	}
   	…

766	fn vp8_intra_predict_16x16(
767	  mode : Vp8I16x16Mode,
768	  above : Array[Int],
769	  left : Array[Int],
770	  above_left : Int,
771	) -> Array[Int] {
772	  let result = Array::make(256, 0)	<-- UNCOVERED
773	  match mode {
774	    I16x16DcPred => {
775	      let mut sum = above_left * 2
776	      for i in 0..<16 {
777	        sum += above[i]	<-- UNCOVERED
778	        sum += left[i]
779	      }
780	      let dc = (sum + 16) >> 5
781	      for i in 0..<256 {
782	        result[i] = dc	<-- UNCOVERED
783	      }
784	    }
785	    I16x16TrueMotionPred =>
786	      for y in 0..<16 {	<-- UNCOVERED
787	        for x in 0..<16 {
788	          let mut sum = 0
789	          for i in 0..<(x + 1) {
790	            sum += above[i]	<-- UNCOVERED
791	          }
792	          for i in 0..<(y + 1) {
793	            sum += left[i]	<-- UNCOVERED
794	          }
795	          sum += above_left
796	          result[y * 16 + x] = (sum + (x + y + 2) / 2) / (x + y + 2)
797	        }
798	      }
799	    I16x16HPred =>
800	      for y in 0..<16 {	<-- UNCOVERED
801	        for x in 0..<16 {
802	          result[y * 16 + x] = left[y]
803	        }
804	      }
805	    I16x16VPred =>
806	      for y in 0..<16 {	<-- UNCOVERED
807	        for x in 0..<16 {
808	          result[y * 16 + x] = above[x]
809	        }
810	      }
811	  }
812	  result
813	}
   	…

837	fn Vp8MbModeProbs::new() -> Vp8MbModeProbs {
838	  // VP8 默认预测模式概率
839	  Vp8MbModeProbs::{	<-- UNCOVERED
840	    i16x16_prob: [180, 120, 100, 140],
841	    i4x4_prob: [160, 100, 120, 140, 80, 90, 70, 60, 50, 40],
842	    uv_prob: [180, 120, 100, 140],
843	    submb_type_prob: 150,
844	    i16x16_count: Array::make(4, 0),	<-- UNCOVERED
845	    i4x4_count: Array::make(10, 0),
846	    uv_count: Array::make(4, 0),
847	    submb_type_count: 0,
848	    total_mbs: 0,
849	  }
850	}
   	…

854	fn vp8_parse_i16x16_mode(
855	  bd : Vp8BoolDecoder,
856	  probs : Vp8MbModeProbs,
857	) -> Vp8I16x16Mode {
858	  // 使用概率模型解析预测模式
859	  // VP8 使用树形概率解析：先判断是否是 DC，然后判断剩余模式
860	  let is_dc = bd.read_bool(probs.i16x16_prob[0]) == 1	<-- UNCOVERED
861	  if is_dc {
862	    probs.i16x16_count[0] += 1	<-- UNCOVERED
863	    return Vp8I16x16Mode::I16x16DcPred
864	  }
865	  let is_tm = bd.read_bool(probs.i16x16_prob[1]) == 1	<-- UNCOVERED
866	  if is_tm {
867	    probs.i16x16_count[1] += 1	<-- UNCOVERED
868	    return Vp8I16x16Mode::I16x16TrueMotionPred
869	  }
870	  let is_h = bd.read_bool(probs.i16x16_prob[2]) == 1	<-- UNCOVERED
871	  if is_h {
872	    probs.i16x16_count[2] += 1	<-- UNCOVERED
873	    return Vp8I16x16Mode::I16x16HPred
874	  }
875	  probs.i16x16_count[3] += 1
876	  Vp8I16x16Mode::I16x16VPred
877	}
   	…

881	fn vp8_parse_uv_mode(
882	  bd : Vp8BoolDecoder,
883	  probs : Vp8MbModeProbs,
884	) -> Vp8I16x16Mode {
885	  let is_dc = bd.read_bool(probs.uv_prob[0]) == 1	<-- UNCOVERED
886	  if is_dc {
887	    probs.uv_count[0] += 1	<-- UNCOVERED
888	    return Vp8I16x16Mode::I16x16DcPred
889	  }
890	  let is_tm = bd.read_bool(probs.uv_prob[1]) == 1	<-- UNCOVERED
891	  if is_tm {
892	    probs.uv_count[1] += 1	<-- UNCOVERED
893	    return Vp8I16x16Mode::I16x16TrueMotionPred
894	  }
895	  let is_h = bd.read_bool(probs.uv_prob[2]) == 1	<-- UNCOVERED
896	  if is_h {
897	    probs.uv_count[2] += 1	<-- UNCOVERED
898	    return Vp8I16x16Mode::I16x16HPred
899	  }
900	  probs.uv_count[3] += 1
901	  Vp8I16x16Mode::I16x16VPred
902	}
   	…

906	fn Vp8MbModeProbs::update(self : Vp8MbModeProbs) -> Unit {
907	  self.total_mbs += 1	<-- UNCOVERED
908	  // 每 16 个宏块更新一次概率
909	  if self.total_mbs % 16 != 0 {
910	    return	<-- UNCOVERED
911	  }
912	
913	  // 更新 I16x16 概率
914	  let i16_total = self.i16x16_count[0] +
915	    self.i16x16_count[1] +
916	    self.i16x16_count[2] +
917	    self.i16x16_count[3] +
918	    1
919	  for i in 0..<4 {
920	    let observed = self.i16x16_count[i] * 256 / i16_total	<-- UNCOVERED
921	    self.i16x16_prob[i] = (self.i16x16_prob[i] * 7 + observed) / 8
922	    self.i16x16_count[i] = self.i16x16_count[i] / 2
923	  }
924	
925	  // 更新 I4x4 概率
926	  let i4_total = self.i4x4_count[0] +
927	    self.i4x4_count[1] +
928	    self.i4x4_count[2] +
929	    self.i4x4_count[3] +
930	    self.i4x4_count[4] +
931	    self.i4x4_count[5] +
932	    self.i4x4_count[6] +
933	    self.i4x4_count[7] +
934	    self.i4x4_count[8] +
935	    self.i4x4_count[9] +
936	    1
937	  for i in 0..<10 {
938	    let observed = self.i4x4_count[i] * 256 / i4_total	<-- UNCOVERED
939	    self.i4x4_prob[i] = (self.i4x4_prob[i] * 7 + observed) / 8
940	    self.i4x4_count[i] = self.i4x4_count[i] / 2
941	  }
942	
943	  // 更新 UV 概率
944	  let uv_total = self.uv_count[0] +
945	    self.uv_count[1] +
946	    self.uv_count[2] +
947	    self.uv_count[3] +
948	    1
949	  for i in 0..<4 {
950	    let observed = self.uv_count[i] * 256 / uv_total	<-- UNCOVERED
951	    self.uv_prob[i] = (self.uv_prob[i] * 7 + observed) / 8
952	    self.uv_count[i] = self.uv_count[i] / 2
953	  }
954	
955	  // 更新子宏块类型概率
956	  let submb_total = self.total_mbs + 1
957	  let observed_submb = self.submb_type_count * 256 / submb_total
958	  self.submb_type_prob = (self.submb_type_prob * 7 + observed_submb) / 8
959	  self.submb_type_count = self.submb_type_count / 2
960	}
   	…

 971	fn vp8_filter_vertical_4(
 972	  plane : Array[Int],
 973	  x : Int,
 974	  y : Int,
 975	  width : Int,
 976	  filter_level : Int,
 977	  inner_level : Int,
 978	) -> Unit {
 979	  if x <= 0 || x >= width {	<-- UNCOVERED
 980	    return
 981	  }
 982	  let idx = y * width + x
 983	  let p1 = plane[idx - 2]
 984	  let p0 = plane[idx - 1]
 985	  let q0 = plane[idx]
 986	  let q1 = plane[idx + 1]
 987	
 988	  // 检查是否需要滤波
 989	  let threshold = filter_level * 2
 990	  if (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 > threshold {	<-- UNCOVERED
 991	    return
 992	  }
 993	
 994	  // 计算滤波值
 995	  let mut p0_new = p0
 996	  let mut q0_new = q0
 997	  let mut p1_new = p1
 998	  let mut q1_new = q1
 999	
1000	  if filter_level > 0 {
1001	    // 简单的 4 抽头滤波
1002	    let filter_val = (p1 + 2 * p0 + 2 * q0 + q1 + 4) / 8	<-- UNCOVERED
1003	    p0_new = filter_val
1004	    q0_new = filter_val
1005	
1006	    // 如果内部滤波级别足够，也滤波 p1 和 q1
1007	    if inner_level > 0 && (p1 - p0).abs() < inner_level {	<-- UNCOVERED
1008	      p1_new = (p1 + p0_new + 1) / 2
1009	    }
1010	    if inner_level > 0 && (q1 - q0).abs() < inner_level {	<-- UNCOVERED
1011	      q1_new = (q1 + q0_new + 1) / 2
1012	    }
1013	  }
1014	
1015	  plane[idx - 2] = p1_new
1016	  plane[idx - 1] = p0_new
1017	  plane[idx] = q0_new
1018	  plane[idx + 1] = q1_new
1019	}
    	…

1023	fn vp8_filter_horizontal_4(
1024	  plane : Array[Int],
1025	  x : Int,
1026	  y : Int,
1027	  width : Int,
1028	  filter_level : Int,
1029	  inner_level : Int,
1030	) -> Unit {
1031	  if y <= 0 {	<-- UNCOVERED
1032	    return
1033	  }
1034	  let idx = y * width + x
1035	  let p1 = plane[idx - 2 * width]
1036	  let p0 = plane[idx - width]
1037	  let q0 = plane[idx]
1038	  let q1 = plane[idx + width]
1039	
1040	  // 检查是否需要滤波
1041	  let threshold = filter_level * 2
1042	  if (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 > threshold {	<-- UNCOVERED
1043	    return
1044	  }
1045	
1046	  // 计算滤波值
1047	  let mut p0_new = p0
1048	  let mut q0_new = q0
1049	  let mut p1_new = p1
1050	  let mut q1_new = q1
1051	
1052	  if filter_level > 0 {
1053	    let filter_val = (p1 + 2 * p0 + 2 * q0 + q1 + 4) / 8	<-- UNCOVERED
1054	    p0_new = filter_val
1055	    q0_new = filter_val
1056	
1057	    if inner_level > 0 && (p1 - p0).abs() < inner_level {	<-- UNCOVERED
1058	      p1_new = (p1 + p0_new + 1) / 2
1059	    }
1060	    if inner_level > 0 && (q1 - q0).abs() < inner_level {	<-- UNCOVERED
1061	      q1_new = (q1 + q0_new + 1) / 2
1062	    }
1063	  }
1064	
1065	  plane[idx - 2 * width] = p1_new
1066	  plane[idx - width] = p0_new
1067	  plane[idx] = q0_new
1068	  plane[idx + width] = q1_new
1069	}
    	…

1074	fn vp8_loop_filter_mb(
1075	  y_plane : Array[Int],
1076	  u_plane : Array[Int],
1077	  v_plane : Array[Int],
1078	  mb_x : Int,
1079	  mb_y : Int,
1080	  width : Int,
1081	  height : Int,
1082	  filter_level : Int,
1083	) -> Unit {
1084	  if filter_level == 0 {	<-- UNCOVERED
1085	    return
1086	  }
1087	
1088	  let inner_level = filter_level / 2
1089	  let mb_px = mb_x * 16
1090	  let mb_py = mb_y * 16
1091	
1092	  // === Y 平面滤波 ===
1093	
1094	  // 宏块左边界垂直滤波（16行）
1095	  if mb_x > 0 {
1096	    for y in 0..<16 {	<-- UNCOVERED
1097	      let py = mb_py + y
1098	      if py < height {
1099	        vp8_filter_vertical_4(	<-- UNCOVERED
1100	          y_plane, mb_px, py, width, filter_level, inner_level,
1101	        )
1102	      }
1103	    }
1104	  }
1105	
1106	  // 宏块上边界水平滤波（16列）
1107	  if mb_y > 0 {
1108	    for x in 0..<16 {	<-- UNCOVERED
1109	      let px = mb_px + x
1110	      if px < width {
1111	        vp8_filter_horizontal_4(	<-- UNCOVERED
1112	          y_plane, px, mb_py, width, filter_level, inner_level,
1113	        )
1114	      }
1115	    }
1116	  }
1117	
1118	  // 子宏块内部垂直边界滤波（x = 4, 8, 12）
1119	  for sub_x in [4, 8, 12] {
1120	    let px = mb_px + sub_x	<-- UNCOVERED
1121	    if px < width {
1122	      for y in 0..<16 {	<-- UNCOVERED
1123	        let py = mb_py + y
1124	        if py < height {
1125	          vp8_filter_vertical_4(	<-- UNCOVERED
1126	            y_plane,
1127	            px,
1128	            py,
1129	            width,
1130	            inner_level,
1131	            inner_level / 2,
1132	          )
1133	        }
1134	      }
1135	    }
1136	  }
1137	
1138	  // 子宏块内部水平边界滤波（y = 4, 8, 12）
1139	  for sub_y in [4, 8, 12] {
1140	    let py = mb_py + sub_y	<-- UNCOVERED
1141	    if py < height {
1142	      for x in 0..<16 {	<-- UNCOVERED
1143	        let px = mb_px + x
1144	        if px < width {
1145	          vp8_filter_horizontal_4(	<-- UNCOVERED
1146	            y_plane,
1147	            px,
1148	            py,
1149	            width,
1150	            inner_level,
1151	            inner_level / 2,
1152	          )
1153	        }
1154	      }
1155	    }
1156	  }
1157	
1158	  // === 色度平面滤波（U 和 V，8x8 块） ===
1159	  let uv_width = width / 2
1160	  let uv_height = height / 2
1161	  let uv_mb_px = mb_x * 8
1162	  let uv_mb_py = mb_y * 8
1163	
1164	  // U 平面：左边界垂直滤波
1165	  if mb_x > 0 {
1166	    for y in 0..<8 {	<-- UNCOVERED
1167	      let py = uv_mb_py + y
1168	      if py < uv_height {
1169	        vp8_filter_vertical_4(	<-- UNCOVERED
1170	          u_plane,
1171	          uv_mb_px,
1172	          py,
1173	          uv_width,
1174	          filter_level / 2,
1175	          inner_level / 2,
1176	        )
1177	      }
1178	    }
1179	  }
1180	
1181	  // U 平面：上边界水平滤波
1182	  if mb_y > 0 {
1183	    for x in 0..<8 {	<-- UNCOVERED
1184	      let px = uv_mb_px + x
1185	      if px < uv_width {
1186	        vp8_filter_horizontal_4(	<-- UNCOVERED
1187	          u_plane,
1188	          px,
1189	          uv_mb_py,
1190	          uv_width,
1191	          filter_level / 2,
1192	          inner_level / 2,
1193	        )
1194	      }
1195	    }
1196	  }
1197	
1198	  // V 平面：左边界垂直滤波
1199	  if mb_x > 0 {
1200	    for y in 0..<8 {	<-- UNCOVERED
1201	      let py = uv_mb_py + y
1202	      if py < uv_height {
1203	        vp8_filter_vertical_4(	<-- UNCOVERED
1204	          v_plane,
1205	          uv_mb_px,
1206	          py,
1207	          uv_width,
1208	          filter_level / 2,
1209	          inner_level / 2,
1210	        )
1211	      }
1212	    }
1213	  }
1214	
1215	  // V 平面：上边界水平滤波
1216	  if mb_y > 0 {
1217	    for x in 0..<8 {	<-- UNCOVERED
1218	      let px = uv_mb_px + x
1219	      if px < uv_width {
1220	        vp8_filter_horizontal_4(	<-- UNCOVERED
1221	          v_plane,
1222	          px,
1223	          uv_mb_py,
1224	          uv_width,
1225	          filter_level / 2,
1226	          inner_level / 2,
1227	        )
1228	      }
1229	    }
1230	  }
1231	}
    	…

1244	fn Vp8MotionVector::new() -> Vp8MotionVector {
1245	  Vp8MotionVector::{ mv_x: 0, mv_y: 0 }	<-- UNCOVERED
1246	}
    	…

1268	fn Vp8ReferenceFrames::new(width : Int, height : Int) -> Vp8ReferenceFrames {
1269	  let y_size = width * height	<-- UNCOVERED
1270	  let uv_size = width / 2 * (height / 2)
1271	  Vp8ReferenceFrames::{
1272	    last_y: Array::make(y_size, 128),	<-- UNCOVERED
1273	    last_u: Array::make(uv_size, 128),
1274	    last_v: Array::make(uv_size, 128),
1275	    golden_y: Array::make(y_size, 128),
1276	    golden_u: Array::make(uv_size, 128),
1277	    golden_v: Array::make(uv_size, 128),
1278	    altref_y: Array::make(y_size, 128),
1279	    altref_u: Array::make(uv_size, 128),
1280	    altref_v: Array::make(uv_size, 128),
1281	    width,
1282	    height,
1283	  }
1284	}
    	…

1288	fn Vp8ReferenceFrames::update(
1289	  self : Vp8ReferenceFrames,
1290	  y_plane : Array[Int],
1291	  u_plane : Array[Int],
1292	  v_plane : Array[Int],
1293	) -> Unit {
1294	  let y_size = self.width * self.height	<-- UNCOVERED
1295	  let uv_size = self.width / 2 * (self.height / 2)
1296	  for i in 0..<y_size {
1297	    self.last_y[i] = y_plane[i]	<-- UNCOVERED
1298	  }
1299	  for i in 0..<uv_size {
1300	    self.last_u[i] = u_plane[i]	<-- UNCOVERED
1301	    self.last_v[i] = v_plane[i]
1302	  }
1303	}
    	…

1308	fn vp8_interpolate_6tap(
1309	  p0 : Int,
1310	  p1 : Int,
1311	  p2 : Int,
1312	  p3 : Int,
1313	  p4 : Int,
1314	  p5 : Int,
1315	) -> Int {
1316	  let sum = p0 - 5 * p1 + 20 * p2 + 20 * p3 - 5 * p4 + p5	<-- UNCOVERED
1317	  let result = (sum + 32) >> 6
1318	  if result < 0 {
1319	    0	<-- UNCOVERED
1320	  } else if result > 255 {
1321	    255
1322	  } else {
1323	    result	<-- UNCOVERED
1324	  }
1325	}
    	…

1329	fn vp8_get_ref_pixel(
1330	  ref_plane : Array[Int],
1331	  x : Int,
1332	  y : Int,
1333	  width : Int,
1334	  height : Int,
1335	) -> Int {
1336	  let cx = if x < 0 { 0 } else if x >= width { width - 1 } else { x }	<-- UNCOVERED
1337	  let cy = if y < 0 { 0 } else if y >= height { height - 1 } else { y }
1338	  ref_plane[cy * width + cx]
1339	}
    	…

1344	fn vp8_interpolate_subpixel(
1345	  ref_plane : Array[Int],
1346	  base_x : Int,
1347	  base_y : Int,
1348	  mv_x_q4 : Int,
1349	  mv_y_q4 : Int,
1350	  width : Int,
1351	  height : Int,
1352	) -> Int {
1353	  // 整数部分和小数部分
1354	  let int_x = mv_x_q4 >> 2	<-- UNCOVERED
1355	  let frac_x = mv_x_q4 & 3
1356	  let int_y = mv_y_q4 >> 2
1357	  let frac_y = mv_y_q4 & 3
1358	
1359	  let px = base_x + int_x
1360	  let py = base_y + int_y
1361	
1362	  // 如果是整数像素，直接返回
1363	  if frac_x == 0 && frac_y == 0 {
1364	    return vp8_get_ref_pixel(ref_plane, px, py, width, height)	<-- UNCOVERED
1365	  }
1366	
1367	  // 半像素插值（使用 6 抽头滤波器）
1368	  // 先进行水平插值得到半像素
1369	  let h_samples : Array[Int] = Array::make(6, 0)	<-- UNCOVERED
1370	  for i in 0..<6 {
1371	    h_samples[i] = vp8_get_ref_pixel(ref_plane, px - 2 + i, py, width, height)	<-- UNCOVERED
1372	  }
1373	  let half_x = if frac_x >= 2 {
1374	    vp8_interpolate_6tap(	<-- UNCOVERED
1375	      h_samples[0],
1376	      h_samples[1],
1377	      h_samples[2],
1378	      h_samples[3],
1379	      h_samples[4],
1380	      h_samples[5],
1381	    )
1382	  } else {
1383	    h_samples[2]	<-- UNCOVERED
1384	  }
1385	
1386	  // 进行垂直插值得到半像素
1387	  let v_samples : Array[Int] = Array::make(6, 0)	<-- UNCOVERED
1388	  for i in 0..<6 {
1389	    v_samples[i] = vp8_get_ref_pixel(ref_plane, px, py - 2 + i, width, height)	<-- UNCOVERED
1390	  }
1391	  let half_y = if frac_y >= 2 {
1392	    vp8_interpolate_6tap(	<-- UNCOVERED
1393	      v_samples[0],
1394	      v_samples[1],
1395	      v_samples[2],
1396	      v_samples[3],
1397	      v_samples[4],
1398	      v_samples[5],
1399	    )
1400	  } else {
1401	    v_samples[2]	<-- UNCOVERED
1402	  }
1403	
1404	  // 双线性插值（四分之一像素）
1405	  if frac_x == 0 {
1406	    // 只有垂直偏移
1407	    let t = frac_y	<-- UNCOVERED
1408	    return (half_x * (4 - t) + half_y * t + 2) >> 2
1409	  }
1410	  if frac_y == 0 {
1411	    // 只有水平偏移
1412	    let t = frac_x	<-- UNCOVERED
1413	    return (half_y * (4 - t) + half_x * t + 2) >> 2
1414	  }
1415	  // 双向偏移：使用四个角的双线性插值
1416	  let h00 = vp8_get_ref_pixel(ref_plane, px, py, width, height)	<-- UNCOVERED
1417	  let h10 = half_x
1418	  let h01 = half_y
1419	  // 计算对角半像素（先水平后垂直，或先垂直后水平，取平均）
1420	  let diag_samples_h : Array[Int] = Array::make(6, 0)	<-- UNCOVERED
1421	  for i in 0..<6 {
1422	    diag_samples_h[i] = vp8_get_ref_pixel(	<-- UNCOVERED
1423	      ref_plane,
1424	      px - 2 + i,
1425	      py + 1,
1426	      width,
1427	      height,
1428	    )
1429	  }
1430	  let h11 = vp8_interpolate_6tap(	<-- UNCOVERED
1431	    diag_samples_h[0],
1432	    diag_samples_h[1],
1433	    diag_samples_h[2],
1434	    diag_samples_h[3],
1435	    diag_samples_h[4],
1436	    diag_samples_h[5],
1437	  )
1438	
1439	  let tx = frac_x
1440	  let ty = frac_y
1441	  let a = h00 * (4 - tx) * (4 - ty)
1442	  let b = h10 * tx * (4 - ty)
1443	  let c = h01 * (4 - tx) * ty
1444	  let d = h11 * tx * ty
1445	  (a + b + c + d + 8) >> 4
1446	}
    	…

1451	fn vp8_motion_compensate_16x4_subpixel(
1452	  ref_y : Array[Int],
1453	  ref_u : Array[Int],
1454	  ref_v : Array[Int],
1455	  mb_x : Int,
1456	  mb_y : Int,
1457	  mv_x_q4 : Int,
1458	  mv_y_q4 : Int,
1459	  width : Int,
1460	  height : Int,
1461	) -> (Array[Int], Array[Int], Array[Int]) {
1462	  let pred_y = Array::make(256, 128)	<-- UNCOVERED
1463	  let pred_u = Array::make(64, 128)
1464	  let pred_v = Array::make(64, 128)
1465	
1466	  // Y 平面：16x16，1/4 像素精度
1467	  for y in 0..<16 {
1468	    for x in 0..<16 {	<-- UNCOVERED
1469	      pred_y[y * 16 + x] = vp8_interpolate_subpixel(
1470	        ref_y,
1471	        mb_x * 16 + x,
1472	        mb_y * 16 + y,
1473	        mv_x_q4,
1474	        mv_y_q4,
1475	        width,
1476	        height,
1477	      )
1478	    }
1479	  }
1480	
1481	  // U/V 平面：8x8，运动向量减半（1/4 像素 → 1/2 像素）
1482	  let uv_width = width / 2
1483	  let uv_height = height / 2
1484	  let uv_mv_x_q4 = mv_x_q4 / 2
1485	  let uv_mv_y_q4 = mv_y_q4 / 2
1486	  for y in 0..<8 {
1487	    for x in 0..<8 {	<-- UNCOVERED
1488	      pred_u[y * 8 + x] = vp8_interpolate_subpixel(
1489	        ref_u,
1490	        mb_x * 8 + x,
1491	        mb_y * 8 + y,
1492	        uv_mv_x_q4,
1493	        uv_mv_y_q4,
1494	        uv_width,
1495	        uv_height,
1496	      )
1497	      pred_v[y * 8 + x] = vp8_interpolate_subpixel(	<-- UNCOVERED
1498	        ref_v,
1499	        mb_x * 8 + x,
1500	        mb_y * 8 + y,
1501	        uv_mv_x_q4,
1502	        uv_mv_y_q4,
1503	        uv_width,
1504	        uv_height,
1505	      )
1506	    }
1507	  }
1508	
1509	  (pred_y, pred_u, pred_v)
1510	}
    	…

1515	fn vp8_read_ue_golomb(bd : Vp8BoolDecoder) -> Int {
1516	  let mut leading_zeros = 0	<-- UNCOVERED
1517	  while bd.read_bool(128) == 0 {
1518	    leading_zeros += 1
1519	    if leading_zeros > 31 {
1520	      return 0	<-- UNCOVERED
1521	    }
1522	  }
1523	  if leading_zeros == 0 {
1524	    return 0	<-- UNCOVERED
1525	  }
1526	  let mut value = 1
1527	  for _ in 0..<leading_zeros {
1528	    value = (value << 1) | bd.read_bool(128)	<-- UNCOVERED
1529	  }
1530	  value - 1
1531	}
    	…

1535	fn vp8_read_se_golomb(bd : Vp8BoolDecoder) -> Int {
1536	  let value = vp8_read_ue_golomb(bd)	<-- UNCOVERED
1537	  if value == 0 {
1538	    return 0	<-- UNCOVERED
1539	  }
1540	  if value % 2 == 1 {
1541	    (value + 1) / 2	<-- UNCOVERED
1542	  } else {
1543	    -(value / 2)	<-- UNCOVERED
1544	  }
1545	}
    	…

1550	fn vp8_parse_mv_residual(bd : Vp8BoolDecoder, prob : Int) -> Int {
1551	  ignore(prob)	<-- UNCOVERED
1552	  // 简化实现：使用 Exp-Golomb 编码
1553	  // 完整 VP8 使用更复杂的运动向量残差编码
1554	  let residual = vp8_read_se_golomb(bd)	<-- UNCOVERED
1555	  residual * 4 // 转换为 1/4 像素单位
1556	}
    	…

1569	fn vp8_parse_ref_frame(bd : Vp8BoolDecoder, ref_prob : Int) -> Vp8RefFrame {
1570	  // 简化实现：使用概率选择参考帧
1571	  let is_last = bd.read_bool(ref_prob) == 1	<-- UNCOVERED
1572	  if is_last {
1573	    return Vp8RefFrame::RefLast	<-- UNCOVERED
1574	  }
1575	  let is_golden = bd.read_bool(128) == 1	<-- UNCOVERED
1576	  if is_golden {
1577	    Vp8RefFrame::RefGolden	<-- UNCOVERED
1578	  } else {
1579	    Vp8RefFrame::RefAltref	<-- UNCOVERED
1580	  }
1581	}
    	…

1585	fn vp8_get_ref_planes(
1586	  refs : Vp8ReferenceFrames,
1587	  ref_frame : Vp8RefFrame,
1588	) -> (Array[Int], Array[Int], Array[Int]) {
1589	  match ref_frame {	<-- UNCOVERED
1590	    Vp8RefFrame::RefLast => (refs.last_y, refs.last_u, refs.last_v)
1591	    Vp8RefFrame::RefGolden => (refs.golden_y, refs.golden_u, refs.golden_v)
1592	    Vp8RefFrame::RefAltref => (refs.altref_y, refs.altref_u, refs.altref_v)
1593	  }
1594	}
    	…

1599	fn vp8_parse_full_motion_vector(
1600	  bd : Vp8BoolDecoder,
1601	  pred_mv : Vp8MotionVector,
1602	  mv_prob : Int,
1603	) -> (Int, Int) {
1604	  // 解析是否使用预测运动向量
1605	  let use_pred = bd.read_bool(mv_prob) == 1	<-- UNCOVERED
1606	  if use_pred {
1607	    return (pred_mv.mv_x * 4, pred_mv.mv_y * 4)	<-- UNCOVERED
1608	  }
1609	  // 解析运动向量残差
1610	  let dx = vp8_parse_mv_residual(bd, 128)	<-- UNCOVERED
1611	  let dy = vp8_parse_mv_residual(bd, 128)
1612	  (pred_mv.mv_x * 4 + dx, pred_mv.mv_y * 4 + dy)
1613	}
    	…

1617	fn vp8_predict_motion_vector(
1618	  left_mv : Vp8MotionVector,
1619	  above_mv : Vp8MotionVector,
1620	  above_left_mv : Vp8MotionVector,
1621	) -> Vp8MotionVector {
1622	  let pred = Vp8MotionVector::new()	<-- UNCOVERED
1623	  // 中值预测
1624	  let xs = [left_mv.mv_x, above_mv.mv_x, above_left_mv.mv_x]
1625	  let ys = [left_mv.mv_y, above_mv.mv_y, above_left_mv.mv_y]
1626	  // 简单排序取中值
1627	  let mut x_median = xs[0]
1628	  if (xs[1] - xs[0]) * (xs[2] - xs[0]) <= 0 {
1629	    x_median = xs[0]	<-- UNCOVERED
1630	  } else if (xs[0] - xs[1]) * (xs[2] - xs[1]) <= 0 {
1631	    x_median = xs[1]
1632	  } else {
1633	    x_median = xs[2]	<-- UNCOVERED
1634	  }
1635	  let mut y_median = ys[0]
1636	  if (ys[1] - ys[0]) * (ys[2] - ys[0]) <= 0 {
1637	    y_median = ys[0]	<-- UNCOVERED
1638	  } else if (ys[0] - ys[1]) * (ys[2] - ys[1]) <= 0 {
1639	    y_median = ys[1]
1640	  } else {
1641	    y_median = ys[2]	<-- UNCOVERED
1642	  }
1643	  pred.mv_x = x_median
1644	  pred.mv_y = y_median
1645	  pred
1646	}
    	…

1652	pub fn decode_vp8_frame(
1653	  data : Bytes,
1654	  offset : Int,
1655	) -> @types.Image raise @types.LoadError {
1656	  let br = Vp8BitReader::new(data, offset)
1657	  let header = parse_vp8_frame_header(br)
1658	
1659	  if !header.key_frame {
1660	    raise @types.LoadError::DecodeFailed("VP8 非关键帧暂不支持")	<-- UNCOVERED
1661	  }
1662	
1663	  if header.width == 0 || header.height == 0 {
1664	    raise @types.LoadError::DecodeFailed("VP8 帧尺寸无效")
1665	  }
1666	
1667	  let w = header.width
1668	  let h = header.height
1669	
1670	  // 解析量化参数
1671	  let quant = parse_quantization(br)	<-- UNCOVERED
1672	
1673	  // 解析 segment 参数
1674	  let segment = parse_segmentation(br)	<-- UNCOVERED
1675	
1676	  // 简化实现：创建一个纯色图像（灰色）
1677	  // 完整的 VP8 解码需要解析残差系数、进行 IDCT、帧内预测等
1678	  // 这里先实现基础框架，后续迭代完善
1679	  let base_y = 128 - quant.y_dc / 4
1680	  let base_u = 128 - quant.uv_ac / 4
1681	  let base_v = 128 - quant.uv_dc / 4
1682	  let y_plane : Array[Int] = Array::make(w * h, base_y)	<-- UNCOVERED
1683	  let u_plane : Array[Int] = Array::make(w / 2 * (h / 2), base_u)
1684	  let v_plane : Array[Int] = Array::make(w / 2 * (h / 2), base_v)
1685	
1686	  // 帧内预测 + 残差系数解析 + IDCT 反变换
1687	  // 创建布尔解码器用于残差系数解析和预测模式解析
1688	  let bd = Vp8BoolDecoder::new(data, offset + 10)	<-- UNCOVERED
1689	  let coeff_probs = Vp8AdaptiveProbs::new()
1690	  let mb_mode_probs = Vp8MbModeProbs::new()
1691	
1692	  // 对每个 16x16 宏块：解析预测模式 + 残差解析
1693	  let mb_w = (w + 15) / 16
1694	  let mb_h = (h + 15) / 16
1695	  // 保存每个宏块的 segment ID（用于环路滤波）
1696	  let segment_id_map : Array[Int] = Array::make(mb_w * mb_h, 0)	<-- UNCOVERED
1697	  for mb_y in 0..<mb_h {
1698	    for mb_x in 0..<mb_w {	<-- UNCOVERED
1699	      // 解析宏块的 segment ID
1700	      let segment_id = if segment.enabled && segment.update_map {	<-- UNCOVERED
1701	        vp8_parse_mb_segment_id(bd, segment.mb_segment_tree_probs)
1702	      } else {
1703	        0	<-- UNCOVERED
1704	      }
1705	      segment_id_map[mb_y * mb_w + mb_x] = segment_id
1706	
1707	      // 根据 segment ID 计算实际量化参数
1708	      let _ = vp8_get_quant_for_segment(quant, segment, segment_id)	<-- UNCOVERED
1709	
1710	      // 生成参考像素
1711	      let above : Array[Int] = Array::make(16, 128)	<-- UNCOVERED
1712	      let left : Array[Int] = Array::make(16, 128)
1713	      let above_left = 128
1714	
1715	      // 从 bitstream 解析 I16x16 预测模式（使用自适应概率模型）
1716	      let mode = vp8_parse_i16x16_mode(bd, mb_mode_probs)	<-- UNCOVERED
1717	
1718	      // 解析色度预测模式（UV 平面）
1719	      let uv_mode = vp8_parse_uv_mode(bd, mb_mode_probs)	<-- UNCOVERED
1720	      ignore(uv_mode) // 色度预测暂未完全实现，UV 平面使用基础值
1721	
1722	      let pred = vp8_intra_predict_16x16(mode, above, left, above_left)	<-- UNCOVERED
1723	
1724	      // 解析 DC 系数（16x16 块的 DC 系数组成 4x4 块）
1725	      let dc_coeffs = vp8_parse_dc_coeffs_16x16(bd, coeff_probs)	<-- UNCOVERED
1726	      let dc_residual = vp8_wht_4x4(dc_coeffs)
1727	
1728	      // 对每个 4x4 子块解析 AC 系数并进行 IDCT
1729	      for sub_y in 0..<4 {
1730	        for sub_x in 0..<4 {	<-- UNCOVERED
1731	          // 解析 AC 系数
1732	          let ac_coeffs = vp8_parse_residual_4x4(bd, coeff_probs, false)	<-- UNCOVERED
1733	          // 将 DC 系数添加到 AC 系数中
1734	          ac_coeffs[0] = ac_coeffs[0] + dc_residual[sub_y * 4 + sub_x]
1735	          // IDCT 反变换
1736	          let residual = vp8_idct_4x4(ac_coeffs)	<-- UNCOVERED
1737	          // 将预测和残差相加
1738	          for y in 0..<4 {
1739	            for x in 0..<4 {	<-- UNCOVERED
1740	              let px = mb_x * 16 + sub_x * 4 + x
1741	              let py = mb_y * 16 + sub_y * 4 + y
1742	              if px < w && py < h {
1743	                let pred_y = sub_y * 4 + y	<-- UNCOVERED
1744	                let pred_x = sub_x * 4 + x
1745	                let mut val = pred[pred_y * 16 + pred_x] + residual[y * 4 + x]
1746	                if val < 0 {
1747	                  val = 0	<-- UNCOVERED
1748	                }
1749	                if val > 255 {
1750	                  val = 255	<-- UNCOVERED
1751	                }
1752	                y_plane[py * w + px] = val
1753	              }
1754	            }
1755	          }
1756	        }
1757	      }
1758	      // 更新宏块预测模式概率（EMA）
1759	      mb_mode_probs.update()	<-- UNCOVERED
1760	    }
1761	  }
1762	
1763	  // 环路滤波（基于 segment 的自适应滤波强度）
1764	  let base_filter_level = quant.y_ac / 4
1765	  for mb_y in 0..<mb_h {
1766	    for mb_x in 0..<mb_w {	<-- UNCOVERED
1767	      let segment_id = segment_id_map[mb_y * mb_w + mb_x]
1768	      let mb_filter_level = vp8_get_filter_level_for_segment(
1769	        base_filter_level, segment, segment_id,
1770	      )
1771	      vp8_loop_filter_mb(	<-- UNCOVERED
1772	        y_plane, u_plane, v_plane, mb_x, mb_y, w, h, mb_filter_level,
1773	      )
1774	    }
1775	  }
1776	
1777	  // YUV420 转 RGB
1778	  let out : Array[Byte] = Array::make(w * h * 3, b'\x00')	<-- UNCOVERED
1779	  for y in 0..<h {
1780	    for x in 0..<w {	<-- UNCOVERED
1781	      let y_val = y_plane[y * w + x]
1782	      let u_val = u_plane[y / 2 * (w / 2) + x / 2]
1783	      let v_val = v_plane[y / 2 * (w / 2) + x / 2]
1784	
1785	      // YUV 转 RGB（简化）
1786	      let c = y_val - 16
1787	      let d = u_val - 128
1788	      let e = v_val - 128
1789	
1790	      let mut r = (298 * c + 409 * e + 128) >> 8
1791	      let mut g = (298 * c - 100 * d - 208 * e + 128) >> 8
1792	      let mut b = (298 * c + 516 * d + 128) >> 8
1793	
1794	      if r < 0 {
1795	        r = 0	<-- UNCOVERED
1796	      }
1797	      if r > 255 {
1798	        r = 255	<-- UNCOVERED
1799	      }
1800	      if g < 0 {
1801	        g = 0	<-- UNCOVERED
1802	      }
1803	      if g > 255 {
1804	        g = 255	<-- UNCOVERED
1805	      }
1806	      if b < 0 {
1807	        b = 0	<-- UNCOVERED
1808	      }
1809	      if b > 255 {
1810	        b = 255	<-- UNCOVERED
1811	      }
1812	
1813	      let idx = (y * w + x) * 3
1814	      out[idx] = r.to_byte()	<-- UNCOVERED
1815	      out[idx + 1] = g.to_byte()
1816	      out[idx + 2] = b.to_byte()
1817	    }
1818	  }
1819	
1820	  @types.Image::{
1821	    width: w,
1822	    height: h,
1823	    channels: 3,
1824	    data: Bytes::from_array(out),	<-- UNCOVERED
1825	  }
1826	}
    	…

1832	pub fn decode_vp8_inter_frame(
1833	  data : Bytes,
1834	  offset : Int,
1835	  refs : Vp8ReferenceFrames,
1836	) -> (@types.Image, Vp8ReferenceFrames) raise @types.LoadError {
1837	  let br = Vp8BitReader::new(data, offset)	<-- UNCOVERED
1838	  let header = parse_vp8_frame_header(br)
1839	
1840	  if header.key_frame {
1841	    // 关键帧：使用常规解码并更新参考帧
1842	    let img = decode_vp8_frame(data, offset)	<-- UNCOVERED
1843	    // 将关键帧复制到参考帧
1844	    let w = img.width
1845	    let h = img.height
1846	    let y_size = w * h
1847	    let uv_size = w / 2 * (h / 2)
1848	    let new_refs = Vp8ReferenceFrames::new(w, h)	<-- UNCOVERED
1849	    // 从 RGB 转换回 YUV（简化：使用亮度作为 Y，UV 固定为 128）
1850	    for i in 0..<y_size {
1851	      let r = img.data[i * 3].to_int()	<-- UNCOVERED
1852	      let g = img.data[i * 3 + 1].to_int()
1853	      let b = img.data[i * 3 + 2].to_int()
1854	      let y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16
1855	      new_refs.last_y[i] = y
1856	      new_refs.golden_y[i] = y
1857	      new_refs.altref_y[i] = y
1858	    }
1859	    for i in 0..<uv_size {
1860	      new_refs.last_u[i] = 128	<-- UNCOVERED
1861	      new_refs.last_v[i] = 128
1862	      new_refs.golden_u[i] = 128
1863	      new_refs.golden_v[i] = 128
1864	      new_refs.altref_u[i] = 128
1865	      new_refs.altref_v[i] = 128
1866	    }
1867	    return (img, new_refs)
1868	  }
1869	
1870	  // 非关键帧：使用帧间预测
1871	  let w = header.width
1872	  let h = header.height
1873	
1874	  if w == 0 || h == 0 {
1875	    raise @types.LoadError::DecodeFailed("VP8 帧尺寸无效")	<-- UNCOVERED
1876	  }
1877	
1878	  // 解析量化参数
1879	  let quant = parse_quantization(br)	<-- UNCOVERED
1880	
1881	  let y_plane : Array[Int] = Array::make(w * h, 128)	<-- UNCOVERED
1882	  let u_plane : Array[Int] = Array::make(w / 2 * (h / 2), 128)
1883	  let v_plane : Array[Int] = Array::make(w / 2 * (h / 2), 128)
1884	
1885	  // 创建布尔解码器
1886	  let bd = Vp8BoolDecoder::new(data, offset + 10)	<-- UNCOVERED
1887	  let coeff_probs = Vp8AdaptiveProbs::new()
1888	  let mb_mode_probs = Vp8MbModeProbs::new()
1889	
1890	  // 运动向量预测缓存（用于相邻宏块的运动向量预测）
1891	  let mb_w = (w + 15) / 16
1892	  let mb_h = (h + 15) / 16
1893	  let mv_cache : Array[Vp8MotionVector] = Array::make(	<-- UNCOVERED
1894	    mb_w * mb_h,
1895	    Vp8MotionVector::new(),	<-- UNCOVERED
1896	  )
1897	
1898	  // 对每个宏块进行解码
1899	  for mb_y in 0..<mb_h {
1900	    for mb_x in 0..<mb_w {	<-- UNCOVERED
1901	      // 解析宏块类型（帧内/帧间/跳过）
1902	      let is_intra = bd.read_bool(180) == 1	<-- UNCOVERED
1903	
1904	      if is_intra {
1905	        // 帧内预测宏块
1906	        let above : Array[Int] = Array::make(16, 128)	<-- UNCOVERED
1907	        let left : Array[Int] = Array::make(16, 128)
1908	        let above_left = 128
1909	        let mode = vp8_parse_i16x16_mode(bd, mb_mode_probs)	<-- UNCOVERED
1910	        let uv_mode = vp8_parse_uv_mode(bd, mb_mode_probs)
1911	        ignore(uv_mode)
1912	        let pred = vp8_intra_predict_16x16(mode, above, left, above_left)
1913	
1914	        // 解析 DC 系数
1915	        let dc_coeffs = vp8_parse_dc_coeffs_16x16(bd, coeff_probs)	<-- UNCOVERED
1916	        let dc_residual = vp8_wht_4x4(dc_coeffs)
1917	
1918	        // 解析 AC 系数并进行 IDCT
1919	        for sub_y in 0..<4 {
1920	          for sub_x in 0..<4 {	<-- UNCOVERED
1921	            let ac_coeffs = vp8_parse_residual_4x4(bd, coeff_probs, false)
1922	            ac_coeffs[0] = ac_coeffs[0] + dc_residual[sub_y * 4 + sub_x]
1923	            let residual = vp8_idct_4x4(ac_coeffs)	<-- UNCOVERED
1924	            for y in 0..<4 {
1925	              for x in 0..<4 {	<-- UNCOVERED
1926	                let px = mb_x * 16 + sub_x * 4 + x
1927	                let py = mb_y * 16 + sub_y * 4 + y
1928	                if px < w && py < h {
1929	                  let pred_y = sub_y * 4 + y	<-- UNCOVERED
1930	                  let pred_x = sub_x * 4 + x
1931	                  let mut val = pred[pred_y * 16 + pred_x] + residual[y * 4 + x]
1932	                  if val < 0 {
1933	                    val = 0	<-- UNCOVERED
1934	                  }
1935	                  if val > 255 {
1936	                    val = 255	<-- UNCOVERED
1937	                  }
1938	                  y_plane[py * w + px] = val
1939	                }
1940	              }
1941	            }
1942	          }
1943	        }
1944	      } else {
1945	        // 帧间预测宏块（运动补偿）
1946	        // 解析参考帧选择
1947	        let ref_frame = vp8_parse_ref_frame(bd, 200)	<-- UNCOVERED
1948	        let (ref_y, ref_u, ref_v) = vp8_get_ref_planes(refs, ref_frame)
1949	
1950	        // 运动向量预测（使用相邻宏块的中值）
1951	        let left_mv = if mb_x > 0 {
1952	          mv_cache[mb_y * mb_w + mb_x - 1]	<-- UNCOVERED
1953	        } else {
1954	          Vp8MotionVector::new()	<-- UNCOVERED
1955	        }
1956	        let above_mv = if mb_y > 0 {
1957	          mv_cache[(mb_y - 1) * mb_w + mb_x]	<-- UNCOVERED
1958	        } else {
1959	          Vp8MotionVector::new()	<-- UNCOVERED
1960	        }
1961	        let above_left_mv = if mb_x > 0 && mb_y > 0 {
1962	          mv_cache[(mb_y - 1) * mb_w + mb_x - 1]	<-- UNCOVERED
1963	        } else {
1964	          Vp8MotionVector::new()	<-- UNCOVERED
1965	        }
1966	        let pred_mv = vp8_predict_motion_vector(	<-- UNCOVERED
1967	          left_mv, above_mv, above_left_mv,
1968	        )
1969	
1970	        // 解析完整运动向量（预测 + 残差），1/4 像素精度
1971	        let (mv_x_q4, mv_y_q4) = vp8_parse_full_motion_vector(bd, pred_mv, 150)	<-- UNCOVERED
1972	
1973	        // 保存运动向量到缓存
1974	        let cur_mv = Vp8MotionVector::new()	<-- UNCOVERED
1975	        cur_mv.mv_x = mv_x_q4 / 4
1976	        cur_mv.mv_y = mv_y_q4 / 4
1977	        mv_cache[mb_y * mb_w + mb_x] = cur_mv
1978	
1979	        // 亚像素运动补偿（1/4 像素精度）
1980	        let (pred_y, pred_u, pred_v) = vp8_motion_compensate_16x4_subpixel(	<-- UNCOVERED
1981	          ref_y, ref_u, ref_v, mb_x, mb_y, mv_x_q4, mv_y_q4, w, h,
1982	        )
1983	
1984	        // 解析残差系数（帧间宏块可能有残差）
1985	        let has_residual = bd.read_bool(128) == 1	<-- UNCOVERED
1986	        if has_residual {
1987	          // 解析 4x4 子块的残差系数
1988	          for sub_y in 0..<4 {	<-- UNCOVERED
1989	            for sub_x in 0..<4 {
1990	              let ac_coeffs = vp8_parse_residual_4x4(bd, coeff_probs, false)
1991	              let residual = vp8_idct_4x4(ac_coeffs)
1992	              for y in 0..<4 {
1993	                for x in 0..<4 {	<-- UNCOVERED
1994	                  let px = mb_x * 16 + sub_x * 4 + x
1995	                  let py = mb_y * 16 + sub_y * 4 + y
1996	                  if px < w && py < h {
1997	                    let pred_idx = (sub_y * 4 + y) * 16 + (sub_x * 4 + x)	<-- UNCOVERED
1998	                    let mut val = pred_y[pred_idx] + residual[y * 4 + x]
1999	                    if val < 0 {
2000	                      val = 0	<-- UNCOVERED
2001	                    }
2002	                    if val > 255 {
2003	                      val = 255	<-- UNCOVERED
2004	                    }
2005	                    y_plane[py * w + px] = val
2006	                  }
2007	                }
2008	              }
2009	            }
2010	          }
2011	        } else {
2012	          // 无残差：直接使用运动补偿预测
2013	          for y in 0..<16 {	<-- UNCOVERED
2014	            for x in 0..<16 {
2015	              let px = mb_x * 16 + x
2016	              let py = mb_y * 16 + y
2017	              if px < w && py < h {
2018	                y_plane[py * w + px] = pred_y[y * 16 + x]	<-- UNCOVERED
2019	              }
2020	            }
2021	          }
2022	        }
2023	
2024	        // 复制 U/V 预测
2025	        for y in 0..<8 {
2026	          for x in 0..<8 {	<-- UNCOVERED
2027	            let px = mb_x * 8 + x
2028	            let py = mb_y * 8 + y
2029	            if px < w / 2 && py < h / 2 {
2030	              u_plane[py * (w / 2) + px] = pred_u[y * 8 + x]	<-- UNCOVERED
2031	              v_plane[py * (w / 2) + px] = pred_v[y * 8 + x]
2032	            }
2033	          }
2034	        }
2035	      }
2036	
2037	      // 更新宏块预测模式概率
2038	      mb_mode_probs.update()	<-- UNCOVERED
2039	    }
2040	  }
2041	
2042	  // 环路滤波
2043	  let filter_level = quant.y_ac / 4
2044	  for mb_y in 0..<mb_h {
2045	    for mb_x in 0..<mb_w {	<-- UNCOVERED
2046	      vp8_loop_filter_mb(
2047	        y_plane, u_plane, v_plane, mb_x, mb_y, w, h, filter_level,
2048	      )
2049	    }
2050	  }
2051	
2052	  // 更新参考帧（将当前帧复制到 last）
2053	  refs.update(y_plane, u_plane, v_plane)	<-- UNCOVERED
2054	
2055	  // YUV420 转 RGB
2056	  let out : Array[Byte] = Array::make(w * h * 3, b'\x00')	<-- UNCOVERED
2057	  for y in 0..<h {
2058	    for x in 0..<w {	<-- UNCOVERED
2059	      let y_val = y_plane[y * w + x]
2060	      let u_val = u_plane[y / 2 * (w / 2) + x / 2]
2061	      let v_val = v_plane[y / 2 * (w / 2) + x / 2]
2062	
2063	      let c = y_val - 16
2064	      let d = u_val - 128
2065	      let e = v_val - 128
2066	
2067	      let mut r = (298 * c + 409 * e + 128) >> 8
2068	      let mut g = (298 * c - 100 * d - 208 * e + 128) >> 8
2069	      let mut b = (298 * c + 516 * d + 128) >> 8
2070	
2071	      if r < 0 {
2072	        r = 0	<-- UNCOVERED
2073	      }
2074	      if r > 255 {
2075	        r = 255	<-- UNCOVERED
2076	      }
2077	      if g < 0 {
2078	        g = 0	<-- UNCOVERED
2079	      }
2080	      if g > 255 {
2081	        g = 255	<-- UNCOVERED
2082	      }
2083	      if b < 0 {
2084	        b = 0	<-- UNCOVERED
2085	      }
2086	      if b > 255 {
2087	        b = 255	<-- UNCOVERED
2088	      }
2089	
2090	      let idx = (y * w + x) * 3
2091	      out[idx] = r.to_byte()	<-- UNCOVERED
2092	      out[idx + 1] = g.to_byte()
2093	      out[idx + 2] = b.to_byte()
2094	    }
2095	  }
2096	
2097	  let img = @types.Image::{
2098	    width: w,
2099	    height: h,
2100	    channels: 3,
2101	    data: Bytes::from_array(out),	<-- UNCOVERED
2102	  }
2103	
2104	  (img, refs)
2105	}
    	…

168 uncovered line(s) in src\pure\codec\webp_vp8_encode.mbt:

15	fn Vp8BoolEncoder::new() -> Vp8BoolEncoder {
16	  Vp8BoolEncoder::{ output: [], low: 0, count: 0, range: 255 }	<-- UNCOVERED
17	}
  	…

21	fn Vp8BoolEncoder::encode_bool(
22	  self : Vp8BoolEncoder,
23	  bit : Int,
24	  probability : Int,
25	) -> Unit {
26	  let split = 1 + (((self.range - 1) * probability) >> 8)	<-- UNCOVERED
27	  if bit != 0 {
28	    self.low += split	<-- UNCOVERED
29	    self.range -= split
30	  } else {
31	    self.range = split	<-- UNCOVERED
32	  }
33	  while self.range < 128 {
34	    self.shift()	<-- UNCOVERED
35	    self.range = self.range << 1
36	  }
37	}
  	…

40	fn Vp8BoolEncoder::shift(self : Vp8BoolEncoder) -> Unit {
41	  let bit = if ((self.low >> 8) & 1) != 0 { 1 } else { 0 }	<-- UNCOVERED
42	  self.low = (self.low << 1) & 0xFFFF
43	  self.count += 1
44	  if self.count == 8 {
45	    self.output.push(bit.to_byte())	<-- UNCOVERED
46	    self.count = 0
47	  }
48	}
  	…

52	fn Vp8BoolEncoder::encode_bit(self : Vp8BoolEncoder, bit : Int) -> Unit {
53	  self.encode_bool(bit, 128)	<-- UNCOVERED
54	}
  	…

58	fn Vp8BoolEncoder::encode_uint(
59	  self : Vp8BoolEncoder,
60	  value : Int,
61	  n : Int,
62	) -> Unit {
63	  for i in 0..<n {	<-- UNCOVERED
64	    let bit = (value >> (n - 1 - i)) & 1
65	    self.encode_bit(bit)
66	  }
67	}
  	…

71	fn Vp8BoolEncoder::finish(self : Vp8BoolEncoder) -> Bytes {
72	  for _ in 0..<self.count {	<-- UNCOVERED
73	    self.output.push(0)
74	  }
75	  Bytes::from_array(self.output)	<-- UNCOVERED
76	}
  	…

 82	fn rgb_to_yuv420(img : @types.Image) -> (Array[Int], Array[Int], Array[Int]) {
 83	  let w = img.width	<-- UNCOVERED
 84	  let h = img.height
 85	  let ch = img.channels
 86	  let y_plane : Array[Int] = Array::make(w * h, 0)	<-- UNCOVERED
 87	  let u_plane : Array[Int] = Array::make(w / 2 * (h / 2), 128)
 88	  let v_plane : Array[Int] = Array::make(w / 2 * (h / 2), 128)
 89	
 90	  for y in 0..<h {
 91	    for x in 0..<w {	<-- UNCOVERED
 92	      let idx = (y * w + x) * ch
 93	      let r = img.data[idx].to_int()
 94	      let g = img.data[idx + 1].to_int()
 95	      let b = img.data[idx + 2].to_int()
 96	      // Y = 0.299R + 0.587G + 0.114B
 97	      let y_val = ((r * 66 + g * 129 + b * 25 + 128) >> 8) + 16
 98	      y_plane[y * w + x] = y_val
 99	      // U = -0.169R - 0.331G + 0.5B + 128
100	      // V = 0.5R - 0.419G - 0.081B + 128
101	      if x % 2 == 0 && y % 2 == 0 {
102	        let u_val = ((-r * 38 - g * 74 + b * 112 + 128) >> 8) + 128	<-- UNCOVERED
103	        let v_val = ((r * 112 - g * 94 - b * 18 + 128) >> 8) + 128
104	        let uv_idx = y / 2 * (w / 2) + x / 2
105	        u_plane[uv_idx] = u_val
106	        v_plane[uv_idx] = v_val
107	      }
108	    }
109	  }
110	
111	  (y_plane, u_plane, v_plane)
112	}
   	…

119	fn vp8_fdct_4x4(block : Array[Int]) -> Array[Int] {
120	  let result = Array::make(16, 0)	<-- UNCOVERED
121	  // 行变换（一维 FDCT）
122	  for i in 0..<4 {
123	    let a0 = block[i * 4]	<-- UNCOVERED
124	    let a1 = block[i * 4 + 1]
125	    let a2 = block[i * 4 + 2]
126	    let a3 = block[i * 4 + 3]
127	    // VP8 一维 FDCT
128	    let b0 = a0 + a3
129	    let b1 = a1 + a2
130	    let b2 = a1 - a2
131	    let b3 = a0 - a3
132	    result[i * 4] = ((b0 + b1) * 85627) >> 16
133	    result[i * 4 + 1] = (b2 * 121095 + b3 * 85627) >> 16
134	    result[i * 4 + 2] = ((b0 - b1) * 85627) >> 16
135	    result[i * 4 + 3] = (b3 * 121095 - b2 * 85627) >> 16
136	  }
137	  // 列变换（一维 FDCT）
138	  let temp = Array::make(16, 0)	<-- UNCOVERED
139	  for i in 0..<16 {
140	    temp[i] = result[i]	<-- UNCOVERED
141	  }
142	  for j in 0..<4 {
143	    let a0 = temp[j]	<-- UNCOVERED
144	    let a1 = temp[4 + j]
145	    let a2 = temp[8 + j]
146	    let a3 = temp[12 + j]
147	    let b0 = a0 + a3
148	    let b1 = a1 + a2
149	    let b2 = a1 - a2
150	    let b3 = a0 - a3
151	    result[j] = ((b0 + b1) * 85627) >> 16
152	    result[4 + j] = (b2 * 121095 + b3 * 85627) >> 16
153	    result[8 + j] = ((b0 - b1) * 85627) >> 16
154	    result[12 + j] = (b3 * 121095 - b2 * 85627) >> 16
155	  }
156	  result
157	}
   	…

161	fn vp8_fwht_4x4(block : Array[Int]) -> Array[Int] {
162	  let result = Array::make(16, 0)	<-- UNCOVERED
163	  // 行变换
164	  for i in 0..<4 {
165	    let a0 = block[i * 4]	<-- UNCOVERED
166	    let a1 = block[i * 4 + 1]
167	    let a2 = block[i * 4 + 2]
168	    let a3 = block[i * 4 + 3]
169	    let b0 = a0 + a1
170	    let b1 = a2 + a3
171	    let b2 = a0 - a1
172	    let b3 = a2 - a3
173	    result[i * 4] = b0 + b1
174	    result[i * 4 + 1] = b2 + b3
175	    result[i * 4 + 2] = b2 - b3
176	    result[i * 4 + 3] = b0 - b1
177	  }
178	  // 列变换
179	  let temp = Array::make(16, 0)	<-- UNCOVERED
180	  for i in 0..<16 {
181	    temp[i] = result[i]	<-- UNCOVERED
182	  }
183	  for j in 0..<4 {
184	    let a0 = temp[j]	<-- UNCOVERED
185	    let a1 = temp[4 + j]
186	    let a2 = temp[8 + j]
187	    let a3 = temp[12 + j]
188	    let b0 = a0 + a1
189	    let b1 = a2 + a3
190	    let b2 = a0 - a1
191	    let b3 = a2 - a3
192	    result[j] = (b0 + b1 + 1) >> 1
193	    result[4 + j] = (b2 + b3 + 1) >> 1
194	    result[8 + j] = (b2 - b3 + 1) >> 1
195	    result[12 + j] = (b0 - b1 + 1) >> 1
196	  }
197	  result
198	}
   	…

210	fn vp8_calc_quant_params(quality : Int) -> Vp8EncQuantParams {
211	  let q = ((100 - quality).clamp(min=1, max=99) * 63 / 100).clamp(min=1, max=63)	<-- UNCOVERED
212	  Vp8EncQuantParams::{ y_ac: q }
213	}
   	…

217	fn vp8_quantize_4x4(coeffs : Array[Int], quant : Int) -> Array[Int] {
218	  let result = Array::make(16, 0)	<-- UNCOVERED
219	  for i in 0..<16 {
220	    let c = coeffs[i]	<-- UNCOVERED
221	    if c.abs() < quant / 2 {
222	      result[i] = 0
223	    } else {
224	      result[i] = c / quant	<-- UNCOVERED
225	    }
226	  }
227	  result
228	}
   	…

234	fn vp8_predict_i16x16_dc(
235	  y_plane : Array[Int],
236	  mb_x : Int,
237	  mb_y : Int,
238	  width : Int,
239	  height : Int,
240	) -> Array[Int] {
241	  let pred = Array::make(256, 128)	<-- UNCOVERED
242	  let mut sum = 0
243	  let mut count = 0
244	  // 上方像素
245	  if mb_y > 0 {
246	    for x in 0..<16 {	<-- UNCOVERED
247	      let px = mb_x * 16 + x
248	      if px < width {
249	        sum += y_plane[(mb_y * 16 - 1) * width + px]	<-- UNCOVERED
250	        count += 1
251	      }
252	    }
253	  }
254	  // 左方像素
255	  if mb_x > 0 {
256	    for y in 0..<16 {	<-- UNCOVERED
257	      let py = mb_y * 16 + y
258	      if py < height {
259	        sum += y_plane[py * width + mb_x * 16 - 1]	<-- UNCOVERED
260	        count += 1
261	      }
262	    }
263	  }
264	  let dc = if count > 0 { (sum + count / 2) / count } else { 128 }	<-- UNCOVERED
265	  for i in 0..<256 {
266	    pred[i] = dc	<-- UNCOVERED
267	  }
268	  pred
269	}
   	…

273	fn vp8_predict_i16x16_tm(
274	  y_plane : Array[Int],
275	  mb_x : Int,
276	  mb_y : Int,
277	  width : Int,
278	  height : Int,
279	) -> Array[Int] {
280	  let pred = Array::make(256, 128)	<-- UNCOVERED
281	  // 获取上方像素、左方像素、左上方像素
282	  let above : Array[Int] = Array::make(16, 128)	<-- UNCOVERED
283	  let left : Array[Int] = Array::make(16, 128)
284	  let mut above_left = 128
285	  if mb_y > 0 {
286	    for x in 0..<16 {	<-- UNCOVERED
287	      let px = mb_x * 16 + x
288	      if px < width {
289	        above[x] = y_plane[(mb_y * 16 - 1) * width + px]	<-- UNCOVERED
290	      }
291	    }
292	    if mb_x > 0 {
293	      above_left = y_plane[(mb_y * 16 - 1) * width + mb_x * 16 - 1]	<-- UNCOVERED
294	    }
295	  }
296	  if mb_x > 0 {
297	    for y in 0..<16 {	<-- UNCOVERED
298	      let py = mb_y * 16 + y
299	      if py < height {
300	        left[y] = y_plane[py * width + mb_x * 16 - 1]	<-- UNCOVERED
301	      }
302	    }
303	  }
304	  // TrueMotion: P[y][x] = above[x] + left[y] - above_left
305	  for y in 0..<16 {
306	    for x in 0..<16 {	<-- UNCOVERED
307	      let mut val = above[x] + left[y] - above_left
308	      if val < 0 {
309	        val = 0	<-- UNCOVERED
310	      }
311	      if val > 255 {
312	        val = 255	<-- UNCOVERED
313	      }
314	      pred[y * 16 + x] = val
315	    }
316	  }
317	  pred
318	}
   	…

322	fn vp8_predict_i16x16_h(
323	  y_plane : Array[Int],
324	  mb_x : Int,
325	  mb_y : Int,
326	  width : Int,
327	  height : Int,
328	) -> Array[Int] {
329	  let pred = Array::make(256, 128)	<-- UNCOVERED
330	  if mb_x > 0 {
331	    for y in 0..<16 {	<-- UNCOVERED
332	      let py = mb_y * 16 + y
333	      if py < height {
334	        let left_val = y_plane[py * width + mb_x * 16 - 1]	<-- UNCOVERED
335	        for x in 0..<16 {
336	          pred[y * 16 + x] = left_val	<-- UNCOVERED
337	        }
338	      }
339	    }
340	  }
341	  pred
342	}
   	…

346	fn vp8_predict_i16x16_v(
347	  y_plane : Array[Int],
348	  mb_x : Int,
349	  mb_y : Int,
350	  width : Int,
351	  _height : Int,
352	) -> Array[Int] {
353	  let pred = Array::make(256, 128)	<-- UNCOVERED
354	  if mb_y > 0 {
355	    for x in 0..<16 {	<-- UNCOVERED
356	      let px = mb_x * 16 + x
357	      if px < width {
358	        let above_val = y_plane[(mb_y * 16 - 1) * width + px]	<-- UNCOVERED
359	        for y in 0..<16 {
360	          pred[y * 16 + x] = above_val	<-- UNCOVERED
361	        }
362	      }
363	    }
364	  }
365	  pred
366	}
   	…

379	fn vp8_select_i16x16_mode(
380	  y_plane : Array[Int],
381	  mb_x : Int,
382	  mb_y : Int,
383	  width : Int,
384	  height : Int,
385	) -> Vp8I16x16PredMode {
386	  // 计算每种预测模式的 SAD（Sum of Absolute Differences）
387	  let modes = [	<-- UNCOVERED
388	    Vp8I16x16PredMode::I16x16DcPred,
389	    Vp8I16x16PredMode::I16x16TrueMotionPred,
390	    Vp8I16x16PredMode::I16x16HPred,
391	    Vp8I16x16PredMode::I16x16VPred,
392	  ]
393	  let mut best_mode = Vp8I16x16PredMode::I16x16DcPred
394	  let mut best_sad = 1000000
395	  for mode in modes {
396	    let pred = match mode {	<-- UNCOVERED
397	      Vp8I16x16PredMode::I16x16DcPred =>
398	        vp8_predict_i16x16_dc(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
399	      Vp8I16x16PredMode::I16x16TrueMotionPred =>
400	        vp8_predict_i16x16_tm(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
401	      Vp8I16x16PredMode::I16x16HPred =>
402	        vp8_predict_i16x16_h(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
403	      Vp8I16x16PredMode::I16x16VPred =>
404	        vp8_predict_i16x16_v(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
405	    }
406	    // 计算 SAD
407	    let mut sad = 0
408	    for y in 0..<16 {
409	      for x in 0..<16 {	<-- UNCOVERED
410	        let px = mb_x * 16 + x
411	        let py = mb_y * 16 + y
412	        if px < width && py < height {
413	          let orig = y_plane[py * width + px]	<-- UNCOVERED
414	          let p = pred[y * 16 + x]
415	          sad += (orig - p).abs()	<-- UNCOVERED
416	        }
417	      }
418	    }
419	    if sad < best_sad {
420	      best_sad = sad	<-- UNCOVERED
421	      best_mode = mode
422	    }
423	  }
424	  best_mode
425	}
   	…

429	fn vp8_get_i16x16_prediction(
430	  mode : Vp8I16x16PredMode,
431	  y_plane : Array[Int],
432	  mb_x : Int,
433	  mb_y : Int,
434	  width : Int,
435	  height : Int,
436	) -> Array[Int] {
437	  match mode {	<-- UNCOVERED
438	    Vp8I16x16PredMode::I16x16DcPred =>
439	      vp8_predict_i16x16_dc(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
440	    Vp8I16x16PredMode::I16x16TrueMotionPred =>
441	      vp8_predict_i16x16_tm(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
442	    Vp8I16x16PredMode::I16x16HPred =>
443	      vp8_predict_i16x16_h(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
444	    Vp8I16x16PredMode::I16x16VPred =>
445	      vp8_predict_i16x16_v(y_plane, mb_x, mb_y, width, height)	<-- UNCOVERED
446	  }
447	}
   	…

453	fn vp8_encode_block_tokens(
454	  enc : Vp8BoolEncoder,
455	  coeffs : Array[Int],
456	  is_dc : Bool,
457	) -> Unit {
458	  ignore(is_dc)	<-- UNCOVERED
459	  // zig-zag 顺序
460	  let zigzag = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15]
461	  let mut eob = false
462	  for i in 0..<16 {
463	    if eob {	<-- UNCOVERED
464	      break
465	    }
466	    let pos = zigzag[i]
467	    let c = coeffs[pos]
468	    if c == 0 {
469	      // 零系数
470	      if i > 0 {	<-- UNCOVERED
471	        // 检查是否是 EOB
472	        let mut has_nonzero = false	<-- UNCOVERED
473	        for j in (i + 1)..<16 {
474	          if coeffs[zigzag[j]] != 0 {	<-- UNCOVERED
475	            has_nonzero = true
476	            break
477	          }
478	        }
479	        if !has_nonzero {
480	          enc.encode_bool(1, 100) // EOB	<-- UNCOVERED
481	          eob = true
482	        } else {
483	          enc.encode_bool(0, 100) // 不是 EOB	<-- UNCOVERED
484	        }
485	      }
486	      enc.encode_bool(1, 200) // 零系数	<-- UNCOVERED
487	    } else {
488	      // 非零系数
489	      if i > 0 {	<-- UNCOVERED
490	        enc.encode_bool(0, 100) // 不是 EOB
491	      }
492	      enc.encode_bool(0, 200) // 非零系数	<-- UNCOVERED
493	      // 编码值（简化：直接编码绝对值和符号）
494	      let abs_val = c.abs()	<-- UNCOVERED
495	      if abs_val == 1 {
496	        enc.encode_bool(1, 170) // 值为 1	<-- UNCOVERED
497	      } else {
498	        enc.encode_bool(0, 170) // 值大于 1	<-- UNCOVERED
499	        // 编码额外位（简化）
500	        let mut extra = abs_val - 1
501	        while extra > 0 {
502	          enc.encode_bit(1) // 继续	<-- UNCOVERED
503	          extra = extra >> 1
504	        }
505	        enc.encode_bit(0) // 结束	<-- UNCOVERED
506	      }
507	      // 符号位
508	      if c < 0 {
509	        enc.encode_bit(1)	<-- UNCOVERED
510	      } else {
511	        enc.encode_bit(0)	<-- UNCOVERED
512	      }
513	    }
514	  }
515	  if !eob {	<-- UNCOVERED
516	    // 没有 EOB，所有系数都非零
517	  }
518	}
   	…

535	fn vp8_enc_segment_params_new(
536	  y_plane : Array[Int],
537	  width : Int,
538	  height : Int,
539	  _base_quant : Int,
540	) -> Vp8EncSegmentParams {
541	  let mb_w = (width + 15) / 16	<-- UNCOVERED
542	  let mb_h = (height + 15) / 16
543	  let total_mbs = mb_w * mb_h
544	  let segment_id_map : Array[Int] = Array::make(total_mbs, 0)	<-- UNCOVERED
545	  // 计算每个宏块的复杂度（方差）
546	  let complexities : Array[Int] = Array::make(total_mbs, 0)	<-- UNCOVERED
547	  for mb_y in 0..<mb_h {
548	    for mb_x in 0..<mb_w {	<-- UNCOVERED
549	      let mut sum = 0
550	      let mut sum_sq = 0
551	      let mut count = 0
552	      for y in 0..<16 {
553	        for x in 0..<16 {	<-- UNCOVERED
554	          let px = mb_x * 16 + x
555	          let py = mb_y * 16 + y
556	          if px < width && py < height {
557	            let val = y_plane[py * width + px]	<-- UNCOVERED
558	            sum += val
559	            sum_sq += val * val
560	            count += 1
561	          }
562	        }
563	      }
564	      if count > 0 {
565	        let mean = sum / count	<-- UNCOVERED
566	        let variance = sum_sq / count - mean * mean
567	        complexities[mb_y * mb_w + mb_x] = variance
568	      }
569	    }
570	  }
571	  // 根据复杂度分配 segment（0=低复杂度, 3=高复杂度）
572	  let mut max_complexity = 1
573	  for c in complexities {
574	    if c > max_complexity {	<-- UNCOVERED
575	      max_complexity = c
576	    }
577	  }
578	  for i in 0..<total_mbs {
579	    let ratio = complexities[i] * 4 / max_complexity	<-- UNCOVERED
580	    segment_id_map[i] = ratio.clamp(min=0, max=3)
581	  }
582	  // 量化器增量（高复杂度 segment 使用更小的量化增量，即更高质量）
583	  let quantizer_delta = [0, -4, -8, -12]
584	  Vp8EncSegmentParams::{
585	    segmentation_enabled: true,
586	    quantizer_delta,
587	    segment_id_map,
588	  }
589	}
   	…

593	fn vp8_enc_get_mb_quant(
594	  segment : Vp8EncSegmentParams,
595	  mb_x : Int,
596	  mb_y : Int,
597	  mb_w : Int,
598	  base_quant : Int,
599	) -> Int {
600	  if !segment.segmentation_enabled {	<-- UNCOVERED
601	    return base_quant
602	  }
603	  let idx = mb_y * mb_w + mb_x
604	  if idx >= 0 && idx < segment.segment_id_map.length() {	<-- UNCOVERED
605	    let seg_id = segment.segment_id_map[idx]
606	    let delta = segment.quantizer_delta[seg_id]
607	    return (base_quant + delta).clamp(min=1, max=127)	<-- UNCOVERED
608	  }
609	  base_quant
610	}
   	…

616	fn vp8_encoder_loop_filter(
617	  y_plane : Array[Int],
618	  width : Int,
619	  height : Int,
620	  filter_level : Int,
621	) -> Unit {
622	  if filter_level == 0 {	<-- UNCOVERED
623	    return
624	  }
625	  let mb_w = (width + 15) / 16
626	  let mb_h = (height + 15) / 16
627	  // 宏块边界垂直滤波
628	  for mb_y in 0..<mb_h {
629	    for mb_x in 1..<mb_w {	<-- UNCOVERED
630	      for y in 0..<16 {
631	        let py = mb_y * 16 + y
632	        if py >= height {
633	          break	<-- UNCOVERED
634	        }
635	        let px = mb_x * 16
636	        if px > 0 && px < width {
637	          let idx = py * width + px	<-- UNCOVERED
638	          let p1 = y_plane[idx - 1]
639	          let p0 = y_plane[idx]
640	          let diff = p0 - p1
641	          if diff.abs() < filter_level {	<-- UNCOVERED
642	            let adj = diff / 2
643	            y_plane[idx - 1] = p1 + adj
644	            y_plane[idx] = p0 - adj
645	          }
646	        }
647	      }
648	    }
649	  }
650	  // 宏块边界水平滤波
651	  for mb_y in 1..<mb_h {
652	    for mb_x in 0..<mb_w {	<-- UNCOVERED
653	      for x in 0..<16 {
654	        let px = mb_x * 16 + x
655	        if px >= width {
656	          break	<-- UNCOVERED
657	        }
658	        let py = mb_y * 16
659	        if py > 0 && py < height {
660	          let idx = py * width + px	<-- UNCOVERED
661	          let p1 = y_plane[idx - width]
662	          let p0 = y_plane[idx]
663	          let diff = p0 - p1
664	          if diff.abs() < filter_level {	<-- UNCOVERED
665	            let adj = diff / 2
666	            y_plane[idx - width] = p1 + adj
667	            y_plane[idx] = p0 - adj
668	          }
669	        }
670	      }
671	    }
672	  }
673	}
   	…

694	fn vp8_enc_adaptive_probs_new() -> Vp8EncAdaptiveProbs {
695	  // 初始概率：均匀分布
696	  let token_probs : Array[Int] = Array::make(64, 128)	<-- UNCOVERED
697	  let pred_mode_probs : Array[Int] = [128, 128, 128, 128]
698	  let token_counts : Array[Int] = Array::make(64, 0)	<-- UNCOVERED
699	  let pred_mode_counts : Array[Int] = [0, 0, 0, 0]
700	  Vp8EncAdaptiveProbs::{
701	    token_probs,
702	    pred_mode_probs,
703	    token_counts,
704	    pred_mode_counts,
705	    update_interval: 8,
706	    blocks_since_update: 0,
707	  }
708	}
   	…

712	fn vp8_enc_adaptive_probs_update(probs : Vp8EncAdaptiveProbs) -> Unit {
713	  // 更新 token 概率
714	  for i in 0..<64 {	<-- UNCOVERED
715	    let count = probs.token_counts[i]
716	    if count > 0 {
717	      // EMA 更新：new_prob = old_prob * 0.875 + observed * 0.125
718	      let observed = (255 * count / (count + 1)).clamp(min=1, max=254)	<-- UNCOVERED
719	      probs.token_probs[i] = (probs.token_probs[i] * 7 + observed) / 8
720	      probs.token_counts[i] = 0
721	    }
722	  }
723	  // 更新预测模式概率
724	  let total = probs.pred_mode_counts[0] +
725	    probs.pred_mode_counts[1] +
726	    probs.pred_mode_counts[2] +
727	    probs.pred_mode_counts[3]
728	  if total > 0 {
729	    for i in 0..<4 {	<-- UNCOVERED
730	      let observed = (255 * probs.pred_mode_counts[i] / total).clamp(
731	        min=1,
732	        max=254,
733	      )
734	      probs.pred_mode_probs[i] = (probs.pred_mode_probs[i] * 7 + observed) / 8
735	      probs.pred_mode_counts[i] = 0
736	    }
737	  }
738	  probs.blocks_since_update = 0
739	}
   	…

743	fn vp8_enc_adaptive_probs_record_mode(
744	  probs : Vp8EncAdaptiveProbs,
745	  mode : Int,
746	) -> Unit {
747	  if mode >= 0 && mode < 4 {	<-- UNCOVERED
748	    probs.pred_mode_counts[mode] += 1
749	  }
750	}
   	…

756	fn vp8_build_frame_header(
757	  width : Int,
758	  height : Int,
759	  first_part_size : Int,
760	) -> Bytes {
761	  let result : Array[Byte] = []	<-- UNCOVERED
762	  // 3 bytes: frame tag
763	  let tag = (0 << 0) | (0 << 1) | (1 << 4) | (first_part_size << 5)
764	  result.push((tag & 0xFF).to_byte())	<-- UNCOVERED
765	  result.push(((tag >> 8) & 0xFF).to_byte())
766	  result.push(((tag >> 16) & 0xFF).to_byte())
767	  // 3 bytes: start code
768	  result.push(b'\x9D')	<-- UNCOVERED
769	  result.push(b'\x01')
770	  result.push(b'\x2A')
771	  // 2 bytes: width + horizontal scale
772	  result.push((width & 0xFF).to_byte())	<-- UNCOVERED
773	  result.push(((width >> 8) & 0x3F).to_byte())
774	  // 2 bytes: height + vertical scale
775	  result.push((height & 0xFF).to_byte())	<-- UNCOVERED
776	  result.push(((height >> 8) & 0x3F).to_byte())
777	  Bytes::from_array(result)
778	}
   	…

782	fn vp8_build_webp_container(
783	  width : Int,
784	  height : Int,
785	  vp8_data : Bytes,
786	) -> Bytes {
787	  let result : Array[Byte] = []	<-- UNCOVERED
788	  // RIFF header
789	  result.push(b'\x52') // R	<-- UNCOVERED
790	  result.push(b'\x49') // I
791	  result.push(b'\x46') // F
792	  result.push(b'\x46') // F
793	  // file size (placeholder)
794	  let file_size_pos = result.length()	<-- UNCOVERED
795	  result.push(b'\x00')
796	  result.push(b'\x00')
797	  result.push(b'\x00')
798	  result.push(b'\x00')
799	  // WEBP
800	  result.push(b'\x57') // W	<-- UNCOVERED
801	  result.push(b'\x45') // E
802	  result.push(b'\x42') // B
803	  result.push(b'\x50') // P
804	  // VP8 chunk
805	  result.push(b'\x56') // V	<-- UNCOVERED
806	  result.push(b'\x50') // P
807	  result.push(b'\x38') // 8
808	  result.push(b'\x20') // space
809	  // chunk size (placeholder)
810	  let chunk_size_pos = result.length()	<-- UNCOVERED
811	  result.push(b'\x00')
812	  result.push(b'\x00')
813	  result.push(b'\x00')
814	  result.push(b'\x00')
815	  // VP8 data
816	  let vp8_len = vp8_data.length()	<-- UNCOVERED
817	  for i in 0..<vp8_len {
818	    result.push(vp8_data[i])	<-- UNCOVERED
819	  }
820	  // 填充到偶数长度
821	  if vp8_len % 2 != 0 {
822	    result.push(b'\x00')	<-- UNCOVERED
823	  }
824	  // 回填 chunk size
825	  let chunk_size = vp8_len
826	  result[chunk_size_pos] = (chunk_size & 0xFF).to_byte()	<-- UNCOVERED
827	  result[chunk_size_pos + 1] = ((chunk_size >> 8) & 0xFF).to_byte()
828	  result[chunk_size_pos + 2] = ((chunk_size >> 16) & 0xFF).to_byte()
829	  result[chunk_size_pos + 3] = ((chunk_size >> 24) & 0xFF).to_byte()
830	  // 回填 file size
831	  let total_size = result.length() - 8	<-- UNCOVERED
832	  result[file_size_pos] = (total_size & 0xFF).to_byte()
833	  result[file_size_pos + 1] = ((total_size >> 8) & 0xFF).to_byte()
834	  result[file_size_pos + 2] = ((total_size >> 16) & 0xFF).to_byte()
835	  result[file_size_pos + 3] = ((total_size >> 24) & 0xFF).to_byte()
836	  let _ = width
837	  let _ = height
838	  Bytes::from_array(result)	<-- UNCOVERED
839	}
   	…

847	pub fn encode_webp_lossy_full(
848	  img : @types.Image,
849	  quality? : Int = 75,
850	) -> Bytes raise @types.LoadError {
851	  let w = img.width	<-- UNCOVERED
852	  let h = img.height
853	  let ch = img.channels
854	  if w <= 0 || h <= 0 {
855	    raise @types.LoadError::EncodeFailed("WebP: 尺寸无效")	<-- UNCOVERED
856	  }
857	  if ch != 3 && ch != 4 {
858	    raise @types.LoadError::EncodeFailed("WebP: 仅支持 RGB/RGBA")	<-- UNCOVERED
859	  }
860	
861	  // 1. RGB → YUV420
862	  let (y_plane, u_plane, v_plane) = rgb_to_yuv420(img)	<-- UNCOVERED
863	
864	  // 2. 计算量化参数
865	  let quant = vp8_calc_quant_params(quality)	<-- UNCOVERED
866	
867	  // 3. 编码宏块
868	  let mb_w = (w + 15) / 16
869	  let mb_h = (h + 15) / 16
870	  let enc = Vp8BoolEncoder::new()	<-- UNCOVERED
871	  // 自适应概率模型
872	  let adaptive_probs = vp8_enc_adaptive_probs_new()	<-- UNCOVERED
873	  // Segment 参数（基于图像复杂度自动分配）
874	  let segment = vp8_enc_segment_params_new(y_plane, w, h, quant.y_ac)	<-- UNCOVERED
875	  // 重建的 Y 平面（用于环路滤波和后续预测）
876	  let recon_y : Array[Int] = Array::make(w * h, 128)	<-- UNCOVERED
877	  for i in 0..<(w * h) {
878	    recon_y[i] = y_plane[i]	<-- UNCOVERED
879	  }
880	
881	  for mb_y in 0..<mb_h {
882	    for mb_x in 0..<mb_w {	<-- UNCOVERED
883	      // 选择最佳 I16x16 预测模式（基于 SAD 代价）
884	      let pred_mode = vp8_select_i16x16_mode(recon_y, mb_x, mb_y, w, h)	<-- UNCOVERED
885	      // 编码预测模式（使用自适应概率）
886	      let mode_idx = match pred_mode {	<-- UNCOVERED
887	        Vp8I16x16PredMode::I16x16DcPred => 0
888	        Vp8I16x16PredMode::I16x16TrueMotionPred => 1
889	        Vp8I16x16PredMode::I16x16HPred => 2
890	        Vp8I16x16PredMode::I16x16VPred => 3
891	      }
892	      // 使用自适应概率编码预测模式（简化：固定概率编码，记录统计）
893	      enc.encode_uint(mode_idx, 2)	<-- UNCOVERED
894	      vp8_enc_adaptive_probs_record_mode(adaptive_probs, mode_idx)
895	
896	      // 获取预测像素
897	      let pred = vp8_get_i16x16_prediction(pred_mode, recon_y, mb_x, mb_y, w, h)	<-- UNCOVERED
898	
899	      // 计算残差
900	      let residual : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
901	      for y in 0..<16 {
902	        for x in 0..<16 {	<-- UNCOVERED
903	          let px = mb_x * 16 + x
904	          let py = mb_y * 16 + y
905	          if px < w && py < h {
906	            residual[y * 16 + x] = y_plane[py * w + px] - pred[y * 16 + x]	<-- UNCOVERED
907	          }
908	        }
909	      }
910	
911	      // 提取 16 个 4x4 子块的 DC 系数
912	      let dc_block : Array[Int] = Array::make(16, 0)	<-- UNCOVERED
913	      for sub_y in 0..<4 {
914	        for sub_x in 0..<4 {	<-- UNCOVERED
915	          let mut sum = 0
916	          for y in 0..<4 {
917	            for x in 0..<4 {	<-- UNCOVERED
918	              sum += residual[(sub_y * 4 + y) * 16 + (sub_x * 4 + x)]
919	            }
920	          }
921	          dc_block[sub_y * 4 + sub_x] = sum / 16
922	        }
923	      }
924	
925	      // 获取当前宏块的量化参数（考虑 segment）
926	      let mb_quant = vp8_enc_get_mb_quant(segment, mb_x, mb_y, mb_w, quant.y_ac)	<-- UNCOVERED
927	
928	      // WHT 变换 DC 系数
929	      let dc_transformed = vp8_fwht_4x4(dc_block)	<-- UNCOVERED
930	      let dc_quantized = vp8_quantize_4x4(dc_transformed, mb_quant * 2)
931	
932	      // 编码 DC 系数
933	      vp8_encode_block_tokens(enc, dc_quantized, true)	<-- UNCOVERED
934	
935	      // 对每个 4x4 子块进行 DCT 和编码 AC 系数
936	      for sub_y in 0..<4 {
937	        for sub_x in 0..<4 {	<-- UNCOVERED
938	          // 提取 4x4 子块
939	          let block : Array[Int] = Array::make(16, 0)	<-- UNCOVERED
940	          for y in 0..<4 {
941	            for x in 0..<4 {	<-- UNCOVERED
942	              block[y * 4 + x] = residual[(sub_y * 4 + y) * 16 + (sub_x * 4 + x)]
943	            }
944	          }
945	          // DCT 变换
946	          let transformed = vp8_fdct_4x4(block)	<-- UNCOVERED
947	          // DC 系数已经在 WHT 中处理，这里置 0
948	          transformed[0] = 0
949	          // 量化（使用 segment 调整后的量化参数）
950	          let quantized = vp8_quantize_4x4(transformed, mb_quant)	<-- UNCOVERED
951	          // 编码
952	          vp8_encode_block_tokens(enc, quantized, false)	<-- UNCOVERED
953	        }
954	      }
955	      // 更新自适应概率模型（每 8 个宏块更新一次）
956	      adaptive_probs.blocks_since_update += 1
957	      if adaptive_probs.blocks_since_update >= adaptive_probs.update_interval {
958	        vp8_enc_adaptive_probs_update(adaptive_probs)	<-- UNCOVERED
959	      }
960	    }
961	  }
962	
963	  // 3.5 环路滤波（对重建的 Y 平面进行宏块边界滤波）
964	  let filter_level = (quant.y_ac / 4).clamp(min=0, max=63)	<-- UNCOVERED
965	  vp8_encoder_loop_filter(recon_y, w, h, filter_level)
966	
967	  // 4. 完成编码
968	  let token_data = enc.finish()	<-- UNCOVERED
969	
970	  // 5. 构建 VP8 frame header
971	  let header = vp8_build_frame_header(w, h, token_data.length())	<-- UNCOVERED
972	
973	  // 6. 合并 VP8 数据
974	  let vp8_data : Array[Byte] = []
975	  for i in 0..<header.length() {	<-- UNCOVERED
976	    vp8_data.push(header[i])
977	  }
978	  for i in 0..<token_data.length() {	<-- UNCOVERED
979	    vp8_data.push(token_data[i])
980	  }
981	
982	  // 7. 构建 WebP 容器
983	  let result = vp8_build_webp_container(w, h, Bytes::from_array(vp8_data))	<-- UNCOVERED
984	
985	  let _ = u_plane
986	  let _ = v_plane
987	  result
988	}
   	…

12 uncovered line(s) in src\pure\util\image_info.mbt:

 93	fn info_png(data : Bytes) -> @types.ImageInfo? {
 94	  // PNG signature: 8 bytes
 95	  // IHDR: length(4) + "IHDR"(4) + width(4) + height(4) + bit_depth(1) + color_type(1) + ...
 96	  // width at offset 16, height at offset 20
 97	  if data.length() < 26 {
 98	    return None
 99	  }
100	  // 验证 PNG signature
101	  if data[0].to_int() != 0x89 ||
102	    data[1].to_int() != 0x50 ||
103	    data[2].to_int() != 0x4E ||
104	    data[3].to_int() != 0x47 {
105	    return None	<-- UNCOVERED
106	  }
107	  let width = info_read_u32_be(data, 16)
108	  let height = info_read_u32_be(data, 20)
109	  let bit_depth = data[24].to_int()
110	  let color_type = data[25].to_int()
111	  let channels = match color_type {
112	    0 => 1 // gray
113	    2 => 3 // rgb
114	    3 => 1 // palette (indexed)
115	    4 => 2 // gray + alpha
116	    6 => 4 // rgba
117	    _ => return None
118	  }
119	  if width <= 0 || height <= 0 || bit_depth <= 0 {
120	    return None	<-- UNCOVERED
121	  }
122	  Some(@types.ImageInfo::{ width, height, channels })
123	}
   	…

127	fn info_jpeg(data : Bytes) -> @types.ImageInfo? {
128	  let len = data.length()
129	  if len < 4 || data[0].to_int() != 0xFF || data[1].to_int() != 0xD8 {
130	    return None	<-- UNCOVERED
131	  }
132	  // 扫描 markers 寻找 SOF (0xFF 0xC0-0xCF, 排除 C4/C8/CC)
133	  let mut pos = 2
134	  while pos + 1 < len {
135	    if data[pos].to_int() != 0xFF {
136	      pos += 1	<-- UNCOVERED
137	      continue
138	    }
139	    let marker = data[pos + 1].to_int()
140	    // SOF markers: C0, C1, C2, C3, C5, C6, C7, C9, CA, CB, CD, CE, CF
141	    // 排除 C4 (DHT), C8 (JPG), CC (DAC)
142	    if marker >= 0xC0 &&
143	      marker <= 0xCF &&
144	      marker != 0xC4 &&
145	      marker != 0xC8 &&
146	      marker != 0xCC {
147	      // SOF: length(2) + precision(1) + height(2) + width(2) + channels(1)
148	      if pos + 9 >= len {
149	        return None
150	      }
151	      let height = info_read_u16_be(data, pos + 5)
152	      let width = info_read_u16_be(data, pos + 7)
153	      let channels = data[pos + 9].to_int()
154	      if width <= 0 || height <= 0 || channels <= 0 {
155	        return None	<-- UNCOVERED
156	      }
157	      return Some(@types.ImageInfo::{ width, height, channels })
158	    }
159	    // 跳过其他 marker
160	    if pos + 3 >= len {
161	      return None	<-- UNCOVERED
162	    }
163	    let seg_len = info_read_u16_be(data, pos + 2)
164	    pos += 2 + seg_len
165	  }
166	  None
167	}
   	…

171	fn info_pnm(data : Bytes) -> @types.ImageInfo? {
172	  let len = data.length()
173	  if len < 4 {
174	    return None	<-- UNCOVERED
175	  }
176	  // P5: gray (1ch), P6: rgb (3ch)
177	  let channels = match data[1].to_int() {
178	    0x35 => 1 // P5
179	    0x36 => 3 // P6
180	    _ => return None	<-- UNCOVERED
181	  }
182	  // 解析 ASCII header: P5/P6 + whitespace + width + whitespace + height + whitespace + maxval
183	  let mut pos = 2
184	  // 跳过空白和注释
185	  fn skip_ws_comments(data : Bytes, pos : Int, len : Int) -> Int {
186	    let mut p = pos
187	    while p < len {
188	      let b = data[p].to_int()
189	      if b == 0x23 {
190	        // # 注释行，跳过到行尾
191	        while p < len && data[p].to_int() != 0x0A {
192	          p += 1
193	        }
194	        p += 1
195	      } else if b == 0x20 || b == 0x0A || b == 0x0D || b == 0x09 {
196	        p += 1
197	      } else {
198	        break
199	      }
200	    }
201	    p
202	  }
203	  fn read_ascii_int(data : Bytes, pos : Int, len : Int) -> (Int, Int)? {
204	    let mut p = pos
205	    let mut val = 0
206	    let mut has_digit = false
207	    while p < len {
208	      let b = data[p].to_int()
209	      if b >= 0x30 && b <= 0x39 {
210	        val = val * 10 + (b - 0x30)
211	        has_digit = true
212	        p += 1
213	      } else {
214	        break
215	      }
216	    }
217	    if has_digit {
218	      Some((val, p))
219	    } else {
220	      None	<-- UNCOVERED
221	    }
222	  }
223	  pos = skip_ws_comments(data, pos, len)
224	  let (width, pos2) = match read_ascii_int(data, pos, len) {
225	    Some(v) => v
226	    None => return None	<-- UNCOVERED
227	  }
228	  pos = skip_ws_comments(data, pos2, len)
229	  let (height, _) = match read_ascii_int(data, pos, len) {
230	    Some(v) => v
231	    None => return None	<-- UNCOVERED
232	  }
233	  if width <= 0 || height <= 0 {
234	    return None	<-- UNCOVERED
235	  }
236	  Some(@types.ImageInfo::{ width, height, channels })
237	}
   	…

23 uncovered line(s) in src\pure\util\zlib.mbt:

132	pub fn zlib_inflate(data : Bytes) -> Bytes raise @types.LoadError {
133	  // 跳过 zlib 2 字节头 (CMF, FLG)
134	  if data.length() < 2 {
135	    raise @types.LoadError::DecodeFailed("zlib 数据过短")
136	  }
137	  let reader = bit_reader_new(data)
138	  reader.pos = 2
139	  let output : Array[Byte] = []
140	  let mut done = false
141	  while !done {
142	    let bfinal = bit_reader_read_bits(reader, 1)
143	    let btype = bit_reader_read_bits(reader, 2)
144	    match btype {
145	      0 => {
146	        // Stored block: align to byte boundary
147	        reader.bit_buf = 0
148	        reader.bit_count = 0
149	        if reader.pos + 4 > reader.data.length() {
150	          raise @types.LoadError::DecodeFailed(
151	            "DEFLATE stored block 数据过短",
152	          )
153	        }
154	        let block_len = reader.data[reader.pos].to_int() |
155	          (reader.data[reader.pos + 1].to_int() << 8)
156	        reader.pos += 4
157	        if reader.pos + block_len > reader.data.length() {
158	          raise @types.LoadError::DecodeFailed(
159	            "DEFLATE stored block 数据越界",
160	          )
161	        }
162	        for i in 0..<block_len {
163	          output.push(reader.data[reader.pos + i])
164	        }
165	        reader.pos += block_len
166	      }
167	      1 => {
168	        // Fixed Huffman codes
169	        let (lit_tree, dist_tree) = build_fixed_trees()
170	        inflate_block_data(reader, output, lit_tree, dist_tree)
171	      }
172	      2 => {	<-- UNCOVERED
173	        // Dynamic Huffman codes
174	        let (lit_tree, dist_tree) = read_dynamic_trees(reader)	<-- UNCOVERED
175	        inflate_block_data(reader, output, lit_tree, dist_tree)
176	      }
177	      _ => raise @types.LoadError::DecodeFailed("DEFLATE 无效 block type")
178	    }
179	    if bfinal != 0 {
180	      done = true
181	    }
182	  }
183	  Bytes::from_array(output)
184	}
   	…

219	fn length_base_extra(code : Int) -> (Int, Int) {
220	  let base = [
221	    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67,
222	    83, 99, 115, 131, 163, 195, 227, 258,
223	  ]
224	  let extra = [
225	    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
226	    5, 5, 0,
227	  ]
228	  if code < 0 || code >= base.length() {
229	    (0, 0)	<-- UNCOVERED
230	  } else {
231	    (base[code], extra[code])
232	  }
233	}
   	…

236	fn distance_base_extra(code : Int) -> (Int, Int) {
237	  let base = [
238	    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
239	    1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
240	  ]
241	  let extra = [
242	    0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11,
243	    11, 12, 12, 13, 13,
244	  ]
245	  if code < 0 || code >= base.length() {
246	    (0, 0)	<-- UNCOVERED
247	  } else {
248	    (base[code], extra[code])
249	  }
250	}
   	…

272	fn read_dynamic_trees(
273	  reader : BitReader,
274	) -> (HuffmanTree, HuffmanTree) raise @types.LoadError {
275	  let hlit = bit_reader_read_bits(reader, 5)	<-- UNCOVERED
276	  let hdist = bit_reader_read_bits(reader, 5)
277	  let hclen = bit_reader_read_bits(reader, 4)
278	  let num_lit = hlit + 257
279	  let num_dist = hdist + 1
280	  let num_clen = hclen + 4
281	  // 读取 code length code lengths
282	  let cl_order = [
283	    16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
284	  ]
285	  let cl_lengths : Array[Int] = Array::make(19, 0)	<-- UNCOVERED
286	  for i in 0..<num_clen {
287	    let cl = bit_reader_read_bits(reader, 3)	<-- UNCOVERED
288	    cl_lengths[cl_order[i]] = cl
289	  }
290	  let cl_tree = build_huffman_tree(cl_lengths, 7)	<-- UNCOVERED
291	  // 读取 literal/length 和 distance code lengths
292	  let all_lengths : Array[Int] = Array::make(num_lit + num_dist, 0)	<-- UNCOVERED
293	  let mut idx = 0
294	  while idx < num_lit + num_dist {
295	    let sym = bit_reader_read_huffman(reader, cl_tree)	<-- UNCOVERED
296	    if sym < 16 {
297	      all_lengths[idx] = sym	<-- UNCOVERED
298	      idx += 1
299	    } else if sym == 16 {	<-- UNCOVERED
300	      let repeat = bit_reader_read_bits(reader, 2)
301	      let count = repeat + 3
302	      let prev = if idx > 0 { all_lengths[idx - 1] } else { 0 }	<-- UNCOVERED
303	      for _ in 0..<count {
304	        if idx < all_lengths.length() {	<-- UNCOVERED
305	          all_lengths[idx] = prev
306	        }
307	        idx += 1
308	      }
309	    } else if sym == 17 {	<-- UNCOVERED
310	      let repeat = bit_reader_read_bits(reader, 3)
311	      let count = repeat + 3
312	      for _ in 0..<count {
313	        if idx < all_lengths.length() {	<-- UNCOVERED
314	          all_lengths[idx] = 0
315	        }
316	        idx += 1
317	      }
318	    } else {
319	      // sym == 18
320	      let repeat = bit_reader_read_bits(reader, 7)	<-- UNCOVERED
321	      let count = repeat + 11
322	      for _ in 0..<count {
323	        if idx < all_lengths.length() {	<-- UNCOVERED
324	          all_lengths[idx] = 0
325	        }
326	        idx += 1
327	      }
328	    }
329	  }
330	  // 拆分 lit 和 dist
331	  let lit_lengths : Array[Int] = Array::make(num_lit, 0)	<-- UNCOVERED
332	  for i in 0..<num_lit {
333	    lit_lengths[i] = all_lengths[i]	<-- UNCOVERED
334	  }
335	  let dist_lengths : Array[Int] = Array::make(num_dist, 0)	<-- UNCOVERED
336	  for i in 0..<num_dist {
337	    dist_lengths[i] = all_lengths[num_lit + i]	<-- UNCOVERED
338	  }
339	  (build_huffman_tree(lit_lengths, 15), build_huffman_tree(dist_lengths, 15))	<-- UNCOVERED
340	}
   	…

556 uncovered line(s) in src\reexport.mbt:

2989	pub fn segment_chars_projection(
2990	  img : @types.Image,
2991	  line : @ocr.TextLineInfo,
2992	  min_width? : Int = 2,
2993	  max_gap? : Int = 2,
2994	) -> Array[@ocr.CharSegment] raise @types.LoadError {
2995	  @ocr.segment_chars_projection(img, line, min_width~, max_gap~)	<-- UNCOVERED
2996	}
    	…

2999	pub fn extract_text_line_image(
3000	  img : @types.Image,
3001	  line : @ocr.TextLineInfo,
3002	  padding? : Int = 2,
3003	) -> @types.Image raise @types.LoadError {
3004	  @ocr.extract_text_line_image(img, line, padding~)	<-- UNCOVERED
3005	}
    	…

3008	pub fn extract_char_image(
3009	  img : @types.Image,
3010	  char : @ocr.CharSegment,
3011	  target_size? : Int = 32,
3012	  padding? : Int = 2,
3013	) -> @types.Image raise @types.LoadError {
3014	  @ocr.extract_char_image(img, char, target_size~, padding~)	<-- UNCOVERED
3015	}
    	…

3303	pub fn recognize_digits(
3304	  img : @types.Image,
3305	  config? : @ocr.DigitOcrConfig,
3306	) -> @ocr.OcrResult raise @types.LoadError {
3307	  match config {
3308	    Some(c) => @ocr.recognize_digits(img, config=c)	<-- UNCOVERED
3309	    None => @ocr.recognize_digits(img)
3310	  }
3311	}
    	…

3331	pub fn recognize_english(
3332	  img : @types.Image,
3333	  config? : @ocr.EnglishOcrConfig,
3334	) -> @ocr.OcrResult raise @types.LoadError {
3335	  match config {
3336	    Some(c) => @ocr.recognize_english(img, config=c)	<-- UNCOVERED
3337	    None => @ocr.recognize_english(img)
3338	  }
3339	}
    	…

3400	pub fn ocr_postprocess(
3401	  result : @ocr.OcrResult,
3402	  config? : @ocr.PostProcessConfig,
3403	  dictionary? : @ocr.Dictionary,
3404	  ngram_model? : @ocr.NgramModel,
3405	) -> @ocr.OcrResult raise @types.LoadError {
3406	  match config {
3407	    Some(c) =>
3408	      match dictionary {	<-- UNCOVERED
3409	        Some(d) =>
3410	          match ngram_model {	<-- UNCOVERED
3411	            Some(n) =>
3412	              @ocr.ocr_postprocess(	<-- UNCOVERED
3413	                result,
3414	                config=c,
3415	                dictionary=d,
3416	                ngram_model=n,
3417	              )
3418	            None => @ocr.ocr_postprocess(result, config=c, dictionary=d)	<-- UNCOVERED
3419	          }
3420	        None => @ocr.ocr_postprocess(result, config=c)	<-- UNCOVERED
3421	      }
3422	    None => @ocr.ocr_postprocess(result)
3423	  }
3424	}
    	…

3452	pub fn recognize_chinese(
3453	  img : @types.Image,
3454	  config? : @ocr.ChineseOcrConfig,
3455	) -> @ocr.OcrResult raise @types.LoadError {
3456	  match config {
3457	    Some(c) => @ocr.recognize_chinese(img, config=c)	<-- UNCOVERED
3458	    None => @ocr.recognize_chinese(img)
3459	  }
3460	}
    	…

3511	pub fn extract_cell_image(
3512	  img : @types.Image,
3513	  cell : @ocr.TableCell,
3514	  padding? : Int = 1,
3515	) -> @types.Image raise @types.LoadError {
3516	  @ocr.extract_cell_image(img, cell, padding~)	<-- UNCOVERED
3517	}
    	…

3520	pub fn extract_all_cells(
3521	  img : @types.Image,
3522	  structure : @ocr.TableStructure,
3523	  padding? : Int = 1,
3524	) -> Array[@types.Image] raise @types.LoadError {
3525	  @ocr.extract_all_cells(img, structure, padding~)	<-- UNCOVERED
3526	}
    	…

3559	pub fn recognize_chinese_advanced(
3560	  img : @types.Image,
3561	  config? : @ocr.AdvancedChineseOcrConfig,
3562	) -> @ocr.OcrResult raise @types.LoadError {
3563	  match config {
3564	    Some(c) => @ocr.recognize_chinese_advanced(img, config=c)	<-- UNCOVERED
3565	    None => @ocr.recognize_chinese_advanced(img)
3566	  }
3567	}
    	…

3619	pub fn ensemble_predict(
3620	  features : Array[Double],
3621	  knn_classifier : @ocr.KnnClassifier,
3622	  templates : Array[(String, Array[Double])],
3623	  config? : @ocr.EnsembleConfig,
3624	) -> @ocr.ClassificationResult raise @types.LoadError {
3625	  match config {
3626	    Some(c) =>
3627	      @ocr.ensemble_predict(features, knn_classifier, templates, config=c)	<-- UNCOVERED
3628	    None => @ocr.ensemble_predict(features, knn_classifier, templates)
3629	  }
3630	}
    	…

3737	pub fn recognize_handwriting(
3738	  img : @types.Image,
3739	  config? : @ocr.HandwritingOcrConfig,
3740	) -> @ocr.OcrResult raise @types.LoadError {
3741	  match config {
3742	    Some(c) => @ocr.recognize_handwriting(img, config=c)	<-- UNCOVERED
3743	    None => @ocr.recognize_handwriting(img)
3744	  }
3745	}
    	…

3783	pub fn recognize_multilingual(
3784	  img : @types.Image,
3785	  config? : @ocr.MultilingualOcrConfig,
3786	) -> @ocr.OcrResult raise @types.LoadError {
3787	  match config {
3788	    Some(c) => @ocr.recognize_multilingual(img, config=c)	<-- UNCOVERED
3789	    None => @ocr.recognize_multilingual(img)
3790	  }
3791	}
    	…

3819	pub fn structure_based_filter(
3820	  char_img : @types.Image,
3821	  char_db : Array[@ocr.ChineseCharInfo],
3822	  max_candidates? : Int = 50,
3823	) -> Array[@ocr.ChineseCharInfo] {
3824	  @ocr.structure_based_filter(char_img, char_db, max_candidates~)	<-- UNCOVERED
3825	}
    	…

3828	pub fn extract_stroke_features(
3829	  char_img : @types.Image,
3830	) -> Array[Double] raise @types.LoadError {
3831	  @ocr.extract_stroke_features(char_img)	<-- UNCOVERED
3832	}
    	…

3835	pub fn recognize_enhanced_chinese(
3836	  img : @types.Image,
3837	  config? : @ocr.EnhancedChineseOcrConfig,
3838	) -> @ocr.OcrResult raise @types.LoadError {
3839	  match config {
3840	    Some(c) => @ocr.recognize_enhanced_chinese(img, config=c)	<-- UNCOVERED
3841	    None => @ocr.recognize_enhanced_chinese(img)
3842	  }
3843	}
    	…

3914	pub fn recognize_ocr_enhanced(
3915	  img : @types.Image,
3916	  templates : Array[(String, Array[Double])],
3917	  config? : @ocr.OcrAccuracyEnhancedConfig,
3918	) -> @ocr.OcrResult raise @types.LoadError {
3919	  match config {
3920	    Some(c) => @ocr.recognize_ocr_enhanced(img, templates, config=c)	<-- UNCOVERED
3921	    None => @ocr.recognize_ocr_enhanced(img, templates)
3922	  }
3923	}
    	…

3945	pub fn analyze_layout_enhanced(
3946	  img : @types.Image,
3947	  config? : @ocr.DocumentUnderstandingEnhancedConfig,
3948	) -> Array[@ocr.DocumentBlock] raise @types.LoadError {
3949	  match config {
3950	    Some(c) => @ocr.analyze_layout_enhanced(img, config=c)	<-- UNCOVERED
3951	    None => @ocr.analyze_layout_enhanced(img)
3952	  }
3953	}
    	…

4003	pub fn understand_document_enhanced(
4004	  img : @types.Image,
4005	  config? : @ocr.DocumentUnderstandingEnhancedConfig,
4006	) -> (Array[@ocr.DocumentBlock], Array[@ocr.DocumentTreeNode], String) raise @types.LoadError {
4007	  match config {
4008	    Some(c) => @ocr.understand_document_enhanced(img, config=c)	<-- UNCOVERED
4009	    None => @ocr.understand_document_enhanced(img)
4010	  }
4011	}
    	…

4069	pub fn recognize_handwriting_enhanced(
4070	  img : @types.Image,
4071	  config? : @ocr.HandwritingOcrEnhancedConfig,
4072	) -> @ocr.OcrResult raise @types.LoadError {
4073	  match config {
4074	    Some(c) => @ocr.recognize_handwriting_enhanced(img, config=c)	<-- UNCOVERED
4075	    None => @ocr.recognize_handwriting_enhanced(img)
4076	  }
4077	}
    	…

4133	pub fn recognize_multilingual_enhanced(
4134	  img : @types.Image,
4135	  config? : @ocr.MultilingualOcrEnhancedConfig,
4136	) -> @ocr.OcrResult raise @types.LoadError {
4137	  match config {
4138	    Some(c) => @ocr.recognize_multilingual_enhanced(img, config=c)	<-- UNCOVERED
4139	    None => @ocr.recognize_multilingual_enhanced(img)
4140	  }
4141	}
    	…

4163	pub fn decode_ean13(
4164	  img : @types.Image,
4165	  config? : @barcode.BarcodeConfig,
4166	) -> @barcode.BarcodeResult? {
4167	  match config {
4168	    Some(c) => @barcode.decode_ean13(img, config=c)	<-- UNCOVERED
4169	    None => @barcode.decode_ean13(img)
4170	  }
4171	}
    	…

4181	pub fn decode_qrcode(
4182	  img : @types.Image,
4183	  config? : @barcode.BarcodeConfig,
4184	) -> @barcode.BarcodeResult? {
4185	  match config {
4186	    Some(c) => @barcode.decode_qrcode(img, config=c)	<-- UNCOVERED
4187	    None => @barcode.decode_qrcode(img)
4188	  }
4189	}
    	…

4192	pub fn decode_barcode(
4193	  img : @types.Image,
4194	  config? : @barcode.BarcodeConfig,
4195	) -> Array[@barcode.BarcodeResult] {
4196	  match config {
4197	    Some(c) => @barcode.decode_barcode(img, config=c)	<-- UNCOVERED
4198	    None => @barcode.decode_barcode(img)
4199	  }
4200	}
    	…

4275	pub fn stitch_images(
4276	  img1 : @types.Image,
4277	  img2 : @types.Image,
4278	  config? : @stitch.StitchConfig,
4279	) -> @types.Image? {
4280	  match config {
4281	    Some(c) => @stitch.stitch_images(img1, img2, config=c)	<-- UNCOVERED
4282	    None => @stitch.stitch_images(img1, img2)
4283	  }
4284	}
    	…

4314	pub fn super_resolution_edge_preserving(
4315	  img : @types.Image,
4316	  scale : Double,
4317	  edge_threshold? : Double = 30.0,
4318	  sharpen_amount? : Double = 0.5,
4319	) -> @types.Image {
4320	  @superres.super_resolution_edge_preserving(	<-- UNCOVERED
4321	    img,
4322	    scale,
4323	    edge_threshold~,
4324	    sharpen_amount~,
4325	  )
4326	}
    	…

4329	pub fn super_resolution(
4330	  img : @types.Image,
4331	  config? : @superres.SuperResolutionConfig,
4332	) -> @types.Image {
4333	  match config {	<-- UNCOVERED
4334	    Some(c) => @superres.super_resolution(img, config=c)
4335	    None => @superres.super_resolution(img)
4336	  }
4337	}
    	…

4355	pub fn decode_code128(img : @types.Image) -> @barcode.BarcodeResult? {
4356	  @barcode.decode_code128(img)	<-- UNCOVERED
4357	}
    	…

4360	pub fn decode_upca(img : @types.Image) -> @barcode.BarcodeResult? {
4361	  @barcode.decode_upca(img)	<-- UNCOVERED
4362	}
    	…

4365	pub fn decode_code39(img : @types.Image) -> @barcode.BarcodeResult? {
4366	  @barcode.decode_code39(img)	<-- UNCOVERED
4367	}
    	…

4370	pub fn decode_linear_barcode(
4371	  img : @types.Image,
4372	) -> Array[@barcode.BarcodeResult] {
4373	  @barcode.decode_linear_barcode(img)	<-- UNCOVERED
4374	}
    	…

4382	pub fn decode_qrcode_full(img : @types.Image) -> @barcode.QrDecodeResult? {
4383	  @barcode.decode_qrcode_full(img)	<-- UNCOVERED
4384	}
    	…

4387	pub fn reed_solomon_decode(
4388	  data : Array[Int],
4389	  ec_bytes : Int,
4390	) -> (Array[Int], Int) {
4391	  @barcode.reed_solomon_decode(data, ec_bytes)	<-- UNCOVERED
4392	}
    	…

4433	pub fn stylize(
4434	  img : @types.Image,
4435	  style : String,
4436	  config? : @stylize.StylizeConfig,
4437	) -> @types.Image {
4438	  match config {
4439	    Some(c) => @stylize.stylize(img, style, config=c)	<-- UNCOVERED
4440	    None => @stylize.stylize(img, style)
4441	  }
4442	}
    	…

4463	pub fn estimate_atmospheric_light(
4464	  img : @types.Image,
4465	  dark : @types.Image,
4466	  top_percent? : Int = 1000,
4467	) -> Array[Int] raise @types.LoadError {
4468	  @enhance.estimate_atmospheric_light(img, dark, top_percent~)	<-- UNCOVERED
4469	}
    	…

4472	pub fn estimate_transmission(
4473	  img : @types.Image,
4474	  atmospheric : Array[Int],
4475	  omega? : Double = 0.95,
4476	  patch_size? : Int = 15,
4477	) -> @types.Image raise @types.LoadError {
4478	  @enhance.estimate_transmission(img, atmospheric, omega~, patch_size~)	<-- UNCOVERED
4479	}
    	…

4482	pub fn guided_filter_image(
4483	  input : @types.Image,
4484	  guide : @types.Image,
4485	  radius? : Int = 20,
4486	  eps? : Double = 0.0001,
4487	) -> @types.Image raise @types.LoadError {
4488	  @enhance.guided_filter_image(input, guide, radius~, eps~)	<-- UNCOVERED
4489	}
    	…

4492	pub fn dehaze_recover(
4493	  img : @types.Image,
4494	  transmission : @types.Image,
4495	  atmospheric : Array[Int],
4496	  t_min? : Double = 0.1,
4497	) -> @types.Image raise @types.LoadError {
4498	  @enhance.dehaze_recover(img, transmission, atmospheric, t_min~)	<-- UNCOVERED
4499	}
    	…

4502	pub fn dehaze_advanced(
4503	  img : @types.Image,
4504	  config? : @enhance.DehazeConfig,
4505	) -> @types.Image raise @types.LoadError {
4506	  match config {
4507	    Some(c) => @enhance.dehaze_advanced(img, config=c)	<-- UNCOVERED
4508	    None => @enhance.dehaze_advanced(img)
4509	  }
4510	}
    	…

4530	pub fn hdr_fusion_weighted(
4531	  images : Array[@types.Image],
4532	) -> @types.Image raise @types.LoadError {
4533	  @enhance.hdr_fusion_weighted(images)	<-- UNCOVERED
4534	}
    	…

4561	pub fn hdr_process(
4562	  images : Array[@types.Image],
4563	  config? : @enhance.HdrConfig,
4564	) -> @types.Image raise @types.LoadError {
4565	  match config {
4566	    Some(c) => @enhance.hdr_process(images, config=c)	<-- UNCOVERED
4567	    None => @enhance.hdr_process(images)
4568	  }
4569	}
    	…

4603	pub fn remove_small_objects(
4604	  img : @types.Image,
4605	  max_size? : Int = 100,
4606	) -> @types.Image raise @types.LoadError {
4607	  @enhance.remove_small_objects(img, max_size~)	<-- UNCOVERED
4608	}
    	…

4611	pub fn remove_watermark(
4612	  img : @types.Image,
4613	  x : Int,
4614	  y : Int,
4615	  width : Int,
4616	  height : Int,
4617	) -> @types.Image raise @types.LoadError {
4618	  @enhance.remove_watermark(img, x, y, width, height)	<-- UNCOVERED
4619	}
    	…

4632	pub fn denoise_nlm(
4633	  img : @types.Image,
4634	  config? : @enhance.NlmConfig,
4635	) -> @types.Image raise @types.LoadError {
4636	  match config {
4637	    Some(c) => @enhance.denoise_nlm(img, config=c)	<-- UNCOVERED
4638	    None => @enhance.denoise_nlm(img)
4639	  }
4640	}
    	…

4682	pub fn watershed_segment(
4683	  img : @types.Image,
4684	  config? : @segment.WatershedConfig,
4685	) -> @segment.SegmentationResult {
4686	  match config {
4687	    Some(c) => @segment.watershed_segment(img, config=c)	<-- UNCOVERED
4688	    None => @segment.watershed_segment(img)
4689	  }
4690	}
    	…

4702	pub fn segmentation_to_color(
4703	  result : @segment.SegmentationResult,
4704	  width : Int,
4705	  height : Int,
4706	) -> @types.Image {
4707	  @segment.segmentation_to_color(result, width, height)	<-- UNCOVERED
4708	}
    	…

4716	pub fn bbox_new(
4717	  x : Int,
4718	  y : Int,
4719	  width : Int,
4720	  height : Int,
4721	  score? : Double = 0.0,
4722	  class_id? : Int = 0,
4723	) -> @detection.BoundingBox raise @types.LoadError {
4724	  @detection.bbox_new(x, y, width, height, score~, class_id~)	<-- UNCOVERED
4725	}
    	…

4728	pub fn bbox_iou(
4729	  a : @detection.BoundingBox,
4730	  b : @detection.BoundingBox,
4731	) -> Double raise @types.LoadError {
4732	  @detection.bbox_iou(a, b)	<-- UNCOVERED
4733	}
    	…

4736	pub fn nms(
4737	  boxes : Array[@detection.BoundingBox],
4738	  iou_threshold? : Double = 0.5,
4739	) -> Array[@detection.BoundingBox] raise @types.LoadError {
4740	  @detection.nms(boxes, iou_threshold~)	<-- UNCOVERED
4741	}
    	…

4744	pub fn draw_detections(
4745	  img : @types.Image,
4746	  boxes : Array[@detection.BoundingBox],
4747	) -> @types.Image raise @types.LoadError {
4748	  @detection.draw_detections(img, boxes)	<-- UNCOVERED
4749	}
    	…

4762	pub fn compute_mae(img1 : @types.Image, img2 : @types.Image) -> Double {
4763	  @quality.compute_mae(img1, img2)	<-- UNCOVERED
4764	}
    	…

4772	pub fn compute_ssim(img1 : @types.Image, img2 : @types.Image) -> Double {
4773	  @quality.compute_ssim(img1, img2)	<-- UNCOVERED
4774	}
    	…

4777	pub fn compute_quality_metrics(
4778	  img1 : @types.Image,
4779	  img2 : @types.Image,
4780	) -> @quality.QualityMetrics {
4781	  @quality.compute_quality_metrics(img1, img2)	<-- UNCOVERED
4782	}
    	…

4790	pub fn compute_diff_image(
4791	  img1 : @types.Image,
4792	  img2 : @types.Image,
4793	) -> @types.Image {
4794	  @quality.compute_diff_image(img1, img2)	<-- UNCOVERED
4795	}
    	…

4817	pub fn fuse_multifocus(
4818	  img1 : @types.Image,
4819	  img2 : @types.Image,
4820	) -> @types.Image {
4821	  @fusion.fuse_multifocus(img1, img2)	<-- UNCOVERED
4822	}
    	…

4825	pub fn fuse_exposure(images : Array[@types.Image]) -> @types.Image {
4826	  @fusion.fuse_exposure(images)	<-- UNCOVERED
4827	}
    	…

4830	pub fn fuse_images(
4831	  img1 : @types.Image,
4832	  img2 : @types.Image,
4833	  config? : @fusion.FusionConfig,
4834	) -> @types.Image {
4835	  match config {
4836	    Some(c) => @fusion.fuse_images(img1, img2, config=c)	<-- UNCOVERED
4837	    None => @fusion.fuse_images(img1, img2)
4838	  }
4839	}
    	…

4855	pub fn affine_translation(
4856	  dx : Double,
4857	  dy : Double,
4858	) -> @registration.AffineTransform {
4859	  @registration.affine_translation(dx, dy)	<-- UNCOVERED
4860	}
    	…

4863	pub fn affine_rotation(angle : Double) -> @registration.AffineTransform {
4864	  @registration.affine_rotation(angle)	<-- UNCOVERED
4865	}
    	…

4868	pub fn affine_scale(sx : Double, sy : Double) -> @registration.AffineTransform {
4869	  @registration.affine_scale(sx, sy)	<-- UNCOVERED
4870	}
    	…

4873	pub fn affine_compose(
4874	  t1 : @registration.AffineTransform,
4875	  t2 : @registration.AffineTransform,
4876	) -> @registration.AffineTransform {
4877	  @registration.affine_compose(t1, t2)	<-- UNCOVERED
4878	}
    	…

4881	pub fn affine_apply(
4882	  t : @registration.AffineTransform,
4883	  x : Double,
4884	  y : Double,
4885	) -> (Double, Double) {
4886	  @registration.affine_apply(t, x, y)	<-- UNCOVERED
4887	}
    	…

4890	pub fn estimate_translation(
4891	  img1 : @types.Image,
4892	  img2 : @types.Image,
4893	  max_offset? : Int = 20,
4894	) -> @registration.RegistrationResult {
4895	  @registration.estimate_translation(img1, img2, max_offset~)	<-- UNCOVERED
4896	}
    	…

4899	pub fn warp_affine_transform(
4900	  img : @types.Image,
4901	  transform : @registration.AffineTransform,
4902	  out_width? : Int = 0,
4903	  out_height? : Int = 0,
4904	) -> @types.Image {
4905	  @registration.warp_affine_transform(img, transform, out_width~, out_height~)	<-- UNCOVERED
4906	}
    	…

4909	pub fn align_images(
4910	  img1 : @types.Image,
4911	  img2 : @types.Image,
4912	) -> (@types.Image, @registration.RegistrationResult) {
4913	  @registration.align_images(img1, img2)	<-- UNCOVERED
4914	}
    	…

4922	pub fn video_frames_new(
4923	  frames : Array[@types.Image],
4924	  fps? : Double = 30.0,
4925	) -> @video.VideoFrames {
4926	  @video.video_frames_new(frames, fps~)	<-- UNCOVERED
4927	}
    	…

4930	pub fn video_frame_count(video : @video.VideoFrames) -> Int {
4931	  @video.video_frame_count(video)	<-- UNCOVERED
4932	}
    	…

4935	pub fn video_get_frame(
4936	  video : @video.VideoFrames,
4937	  index : Int,
4938	) -> @types.Image? {
4939	  @video.video_get_frame(video, index)	<-- UNCOVERED
4940	}
    	…

4943	pub fn interpolate_frames(
4944	  frame1 : @types.Image,
4945	  frame2 : @types.Image,
4946	  num_frames? : Int = 1,
4947	) -> Array[@types.Image] {
4948	  @video.interpolate_frames(frame1, frame2, num_frames~)	<-- UNCOVERED
4949	}
    	…

4952	pub fn video_fade_in(
4953	  video : @video.VideoFrames,
4954	  duration_frames? : Int = 30,
4955	) -> @video.VideoFrames {
4956	  @video.video_fade_in(video, duration_frames~)	<-- UNCOVERED
4957	}
    	…

4960	pub fn video_fade_out(
4961	  video : @video.VideoFrames,
4962	  duration_frames? : Int = 30,
4963	) -> @video.VideoFrames {
4964	  @video.video_fade_out(video, duration_frames~)	<-- UNCOVERED
4965	}
    	…

4968	pub fn video_sample(
4969	  video : @video.VideoFrames,
4970	  interval? : Int = 2,
4971	) -> @video.VideoFrames {
4972	  @video.video_sample(video, interval~)	<-- UNCOVERED
4973	}
    	…

4976	pub fn video_reverse(video : @video.VideoFrames) -> @video.VideoFrames {
4977	  @video.video_reverse(video)	<-- UNCOVERED
4978	}
    	…

4981	pub fn video_concat(
4982	  video1 : @video.VideoFrames,
4983	  video2 : @video.VideoFrames,
4984	) -> @video.VideoFrames {
4985	  @video.video_concat(video1, video2)	<-- UNCOVERED
4986	}
    	…

4989	pub fn create_animation(
4990	  frames : Array[@types.Image],
4991	  fps? : Double = 10.0,
4992	) -> @video.VideoFrames {
4993	  @video.create_animation(frames, fps~)	<-- UNCOVERED
4994	}
    	…

4997	pub fn video_frame_average(video : @video.VideoFrames) -> @types.Image {
4998	  @video.video_frame_average(video)	<-- UNCOVERED
4999	}
    	…

5012	pub fn depth_to_colormap(
5013	  depth : @types.Image,
5014	  colormap? : String = "jet",
5015	) -> @types.Image {
5016	  @stereo3d.depth_to_colormap(depth, colormap~)	<-- UNCOVERED
5017	}
    	…

5020	pub fn depth_to_pointcloud(
5021	  depth : @types.Image,
5022	  fx? : Double = 500.0,
5023	  fy? : Double = 500.0,
5024	  cx? : Double = 0.0,
5025	  cy? : Double = 0.0,
5026	  scale? : Double = 1.0,
5027	) -> @stereo3d.PointCloud {
5028	  @stereo3d.depth_to_pointcloud(depth, fx~, fy~, cx~, cy~, scale~)	<-- UNCOVERED
5029	}
    	…

5032	pub fn pointcloud_voxel_downsample(
5033	  cloud : @stereo3d.PointCloud,
5034	  voxel_size? : Double = 1.0,
5035	) -> @stereo3d.PointCloud {
5036	  @stereo3d.pointcloud_voxel_downsample(cloud, voxel_size~)	<-- UNCOVERED
5037	}
    	…

5040	pub fn compute_disparity(
5041	  left : @types.Image,
5042	  right : @types.Image,
5043	  max_disparity? : Int = 64,
5044	  block_size? : Int = 7,
5045	) -> @types.Image {
5046	  @stereo3d.compute_disparity(left, right, max_disparity~, block_size~)	<-- UNCOVERED
5047	}
    	…

5050	pub fn disparity_to_depth(
5051	  disparity : @types.Image,
5052	  baseline? : Double = 1.0,
5053	  focal_length? : Double = 500.0,
5054	) -> @types.Image {
5055	  @stereo3d.disparity_to_depth(disparity, baseline~, focal_length~)	<-- UNCOVERED
5056	}
    	…

5084	pub fn apply_window_level(
5085	  img : @types.Image,
5086	  config : @medical.WindowLevel,
5087	) -> @types.Image {
5088	  @medical.apply_window_level(img, config)	<-- UNCOVERED
5089	}
    	…

5092	pub fn medical_colormap(
5093	  img : @types.Image,
5094	  colormap? : String = "hot",
5095	) -> @types.Image {
5096	  @medical.medical_colormap(img, colormap~)	<-- UNCOVERED
5097	}
    	…

5100	pub fn medical_contrast_enhance(
5101	  img : @types.Image,
5102	  clip_limit? : Double = 2.0,
5103	  tile_size? : Int = 8,
5104	) -> @types.Image {
5105	  @medical.medical_contrast_enhance(img, clip_limit~, tile_size~)	<-- UNCOVERED
5106	}
    	…

5109	pub fn medical_denoise(
5110	  img : @types.Image,
5111	  iterations? : Int = 5,
5112	  kappa? : Double = 30.0,
5113	) -> @types.Image {
5114	  @medical.medical_denoise(img, iterations~, kappa~)	<-- UNCOVERED
5115	}
    	…

5118	pub fn measure_distance(
5119	  x1 : Int,
5120	  y1 : Int,
5121	  x2 : Int,
5122	  y2 : Int,
5123	  pixel_spacing? : Double = 1.0,
5124	) -> Double {
5125	  @medical.measure_distance(x1, y1, x2, y2, pixel_spacing~)	<-- UNCOVERED
5126	}
    	…

5129	pub fn measure_area(mask : @types.Image, pixel_area? : Double = 1.0) -> Double {
5130	  @medical.measure_area(mask, pixel_area~)	<-- UNCOVERED
5131	}
    	…

5139	pub fn multispectral_new(
5140	  width : Int,
5141	  height : Int,
5142	  bands : Array[@types.Image],
5143	  band_names? : Array[String] = [],
5144	) -> @remote_sensing.MultispectralImage {
5145	  @remote_sensing.multispectral_new(width, height, bands, band_names~)	<-- UNCOVERED
5146	}
    	…

5149	pub fn compute_ndvi(nir : @types.Image, red : @types.Image) -> @types.Image {
5150	  @remote_sensing.compute_ndvi(nir, red)	<-- UNCOVERED
5151	}
    	…

5154	pub fn compute_ndwi(green : @types.Image, nir : @types.Image) -> @types.Image {
5155	  @remote_sensing.compute_ndwi(green, nir)	<-- UNCOVERED
5156	}
    	…

5159	pub fn compute_ndbi(swir : @types.Image, nir : @types.Image) -> @types.Image {
5160	  @remote_sensing.compute_ndbi(swir, nir)	<-- UNCOVERED
5161	}
    	…

5164	pub fn compute_evi(
5165	  nir : @types.Image,
5166	  red : @types.Image,
5167	  blue : @types.Image,
5168	) -> @types.Image {
5169	  @remote_sensing.compute_evi(nir, red, blue)	<-- UNCOVERED
5170	}
    	…

5173	pub fn false_color_composite(
5174	  nir : @types.Image,
5175	  red : @types.Image,
5176	  green : @types.Image,
5177	) -> @types.Image {
5178	  @remote_sensing.false_color_composite(nir, red, green)	<-- UNCOVERED
5179	}
    	…

5182	pub fn true_color_composite(
5183	  red : @types.Image,
5184	  green : @types.Image,
5185	  blue : @types.Image,
5186	) -> @types.Image {
5187	  @remote_sensing.true_color_composite(red, green, blue)	<-- UNCOVERED
5188	}
    	…

5191	pub fn vegetation_coverage(
5192	  ndvi : @types.Image,
5193	  ndvi_soil? : Double = 0.2,
5194	  ndvi_veg? : Double = 0.8,
5195	) -> @types.Image {
5196	  @remote_sensing.vegetation_coverage(ndvi, ndvi_soil~, ndvi_veg~)	<-- UNCOVERED
5197	}
    	…

5200	pub fn land_cover_classification(
5201	  ndvi : @types.Image,
5202	  ndwi : @types.Image,
5203	  ndbi : @types.Image,
5204	) -> @types.Image {
5205	  @remote_sensing.land_cover_classification(ndvi, ndwi, ndbi)	<-- UNCOVERED
5206	}
    	…

5209	pub fn classification_colormap(classification : @types.Image) -> @types.Image {
5210	  @remote_sensing.classification_colormap(classification)	<-- UNCOVERED
5211	}
    	…

5224	pub fn embed_watermark_lsb(
5225	  cover : @types.Image,
5226	  watermark : @types.Image,
5227	  key? : Int = 42,
5228	) -> @types.Image {
5229	  @watermark.embed_watermark_lsb(cover, watermark, key~)	<-- UNCOVERED
5230	}
    	…

5233	pub fn extract_watermark_lsb(stego : @types.Image) -> @types.Image {
5234	  @watermark.extract_watermark_lsb(stego)	<-- UNCOVERED
5235	}
    	…

5238	pub fn embed_text_watermark(
5239	  cover : @types.Image,
5240	  text : String,
5241	  key? : Int = 42,
5242	) -> @types.Image {
5243	  @watermark.embed_text_watermark(cover, text, key~)	<-- UNCOVERED
5244	}
    	…

5247	pub fn extract_text_watermark(
5248	  stego : @types.Image,
5249	  length : Int,
5250	  key? : Int = 42,
5251	) -> String {
5252	  @watermark.extract_text_watermark(stego, length, key~)	<-- UNCOVERED
5253	}
    	…

5256	pub fn watermark_psnr(
5257	  original : @types.Image,
5258	  watermarked : @types.Image,
5259	) -> Double {
5260	  @watermark.watermark_psnr(original, watermarked)	<-- UNCOVERED
5261	}
    	…

5264	pub fn watermark_robustness_test(
5265	  watermarked : @types.Image,
5266	  original_watermark : @types.Image,
5267	  noise_level? : Double = 10.0,
5268	) -> Double {
5269	  @watermark.watermark_robustness_test(	<-- UNCOVERED
5270	    watermarked,
5271	    original_watermark,
5272	    noise_level~,
5273	  )
5274	}
    	…

5277	pub fn batch_embed_watermark(
5278	  images : Array[@types.Image],
5279	  watermark : @types.Image,
5280	  key? : Int = 42,
5281	) -> Array[@types.Image] {
5282	  @watermark.batch_embed_watermark(images, watermark, key~)	<-- UNCOVERED
5283	}
    	…

5302	pub fn extract_image_features(
5303	  img : @types.Image,
5304	  image_id : String,
5305	) -> @retrieval.ImageFeature {
5306	  @retrieval.extract_image_features(img, image_id)	<-- UNCOVERED
5307	}
    	…

5310	pub fn database_add_image(
5311	  db : @retrieval.ImageDatabase,
5312	  feature : @retrieval.ImageFeature,
5313	) -> @retrieval.ImageDatabase {
5314	  @retrieval.database_add_image(db, feature)	<-- UNCOVERED
5315	}
    	…

5318	pub fn database_remove_image(
5319	  db : @retrieval.ImageDatabase,
5320	  image_id : String,
5321	) -> @retrieval.ImageDatabase {
5322	  @retrieval.database_remove_image(db, image_id)	<-- UNCOVERED
5323	}
    	…

5326	pub fn search_similar_images(
5327	  db : @retrieval.ImageDatabase,
5328	  query : @retrieval.ImageFeature,
5329	  top_k? : Int = 5,
5330	  method_type? : String = "cosine",
5331	) -> Array[@retrieval.SearchResult] {
5332	  @retrieval.search_similar_images(db, query, top_k~, method_type~)	<-- UNCOVERED
5333	}
    	…

5336	pub fn image_similarity(
5337	  img1 : @types.Image,
5338	  img2 : @types.Image,
5339	  method_type? : String = "cosine",
5340	) -> Double {
5341	  @retrieval.image_similarity(img1, img2, method_type~)	<-- UNCOVERED
5342	}
    	…

5345	pub fn batch_extract_features(
5346	  images : Array[(@types.Image, String)],
5347	) -> Array[@retrieval.ImageFeature] {
5348	  @retrieval.batch_extract_features(images)	<-- UNCOVERED
5349	}
    	…

5352	pub fn database_size(db : @retrieval.ImageDatabase) -> Int {
5353	  @retrieval.database_size(db)	<-- UNCOVERED
5354	}
    	…

5370	pub fn encrypt_image_logistic(
5371	  img : @types.Image,
5372	  key? : Double = 0.123456789,
5373	  mu? : Double = 3.99,
5374	) -> @types.Image {
5375	  @encryption.encrypt_image_logistic(img, key~, mu~)	<-- UNCOVERED
5376	}
    	…

5379	pub fn decrypt_image_logistic(
5380	  img : @types.Image,
5381	  key? : Double = 0.123456789,
5382	  mu? : Double = 3.99,
5383	) -> @types.Image {
5384	  @encryption.decrypt_image_logistic(img, key~, mu~)	<-- UNCOVERED
5385	}
    	…

5388	pub fn encrypt_image_xor(img : @types.Image, key : String) -> @types.Image {
5389	  @encryption.encrypt_image_xor(img, key)	<-- UNCOVERED
5390	}
    	…

5393	pub fn decrypt_image_xor(img : @types.Image, key : String) -> @types.Image {
5394	  @encryption.decrypt_image_xor(img, key)	<-- UNCOVERED
5395	}
    	…

5398	pub fn histogram_entropy(img : @types.Image) -> Double {
5399	  @encryption.histogram_entropy(img)	<-- UNCOVERED
5400	}
    	…

5403	pub fn adjacent_correlation(
5404	  img : @types.Image,
5405	  direction? : String = "horizontal",
5406	) -> Double {
5407	  @encryption.adjacent_correlation(img, direction~)	<-- UNCOVERED
5408	}
    	…

5411	pub fn evaluate_encryption_quality(
5412	  original : @types.Image,
5413	  encrypted : @types.Image,
5414	) -> @encryption.EncryptionQuality {
5415	  @encryption.evaluate_encryption_quality(original, encrypted)	<-- UNCOVERED
5416	}
    	…

5419	pub fn key_sensitivity_test(
5420	  img : @types.Image,
5421	  key : Double,
5422	  delta? : Double = 0.0000001,
5423	) -> Double {
5424	  @encryption.key_sensitivity_test(img, key, delta~)	<-- UNCOVERED
5425	}
    	…

5428	pub fn batch_encrypt(
5429	  images : Array[@types.Image],
5430	  key : Double,
5431	) -> Array[@types.Image] {
5432	  @encryption.batch_encrypt(images, key)	<-- UNCOVERED
5433	}
    	…

5446	pub fn low_light_enhance(
5447	  img : @types.Image,
5448	  config? : @photo_enhance.LowLightConfig,
5449	) -> @types.Image {
5450	  match config {	<-- UNCOVERED
5451	    Some(cfg) => @photo_enhance.low_light_enhance(img, config=cfg)
5452	    None => @photo_enhance.low_light_enhance(img)
5453	  }
5454	}
    	…

5457	pub fn deblur_motion(
5458	  img : @types.Image,
5459	  kernel_size? : Int = 15,
5460	  angle? : Double = 0.0,
5461	) -> @types.Image {
5462	  @photo_enhance.deblur_motion(img, kernel_size~, angle~)	<-- UNCOVERED
5463	}
    	…

5466	pub fn color_enhance(
5467	  img : @types.Image,
5468	  saturation? : Double = 1.3,
5469	  contrast? : Double = 1.1,
5470	) -> @types.Image {
5471	  @photo_enhance.color_enhance(img, saturation~, contrast~)	<-- UNCOVERED
5472	}
    	…

5475	pub fn clahe_enhance(
5476	  img : @types.Image,
5477	  clip_limit? : Double = 2.0,
5478	  tile_size? : Int = 8,
5479	) -> @types.Image {
5480	  @photo_enhance.clahe_enhance(img, clip_limit~, tile_size~)	<-- UNCOVERED
5481	}
    	…

5484	pub fn white_balance(img : @types.Image) -> @types.Image {
5485	  @photo_enhance.white_balance(img)	<-- UNCOVERED
5486	}
    	…

5489	pub fn batch_photo_enhance(
5490	  images : Array[@types.Image],
5491	  method_type : String,
5492	) -> Array[@types.Image] {
5493	  @photo_enhance.batch_photo_enhance(images, method_type)	<-- UNCOVERED
5494	}
    	…

5589	pub fn recognize_symbols_markdown(text : String) -> Array[@ocr.MarkdownSymbol] {
5590	  @ocr.recognize_symbols_markdown(text)	<-- UNCOVERED
5591	}
    	…

5594	pub fn detect_image_regions_markdown(
5595	  binary_img : @types.Image,
5596	  min_area? : Int = 500,
5597	) -> Array[@ocr.MarkdownImage] raise @types.LoadError {
5598	  @ocr.detect_image_regions_markdown(binary_img, min_area~)	<-- UNCOVERED
5599	}
    	…

5602	pub fn classify_markdown_element(
5603	  block : @ocr.TextBlock,
5604	  avg_font_size? : Double = 16.0,
5605	) -> @ocr.MarkdownElementType {
5606	  @ocr.classify_markdown_element(block, avg_font_size~)	<-- UNCOVERED
5607	}
    	…

5610	pub fn markdown_table_to_string(table : @ocr.MarkdownTable) -> String {
5611	  @ocr.markdown_table_to_string(table)	<-- UNCOVERED
5612	}
    	…

5615	pub fn determine_reading_order_markdown(
5616	  elements : Array[@ocr.MarkdownElement],
5617	) -> Array[Int] {
5618	  @ocr.determine_reading_order_markdown(elements)	<-- UNCOVERED
5619	}
    	…

5622	pub fn markdown_document_to_string(doc : @ocr.MarkdownDocument) -> String {
5623	  @ocr.markdown_document_to_string(doc)	<-- UNCOVERED
5624	}
    	…

5627	pub fn build_markdown_document(
5628	  ocr_result : @ocr.OCRPageResult,
5629	  title? : String,
5630	) -> @ocr.MarkdownDocument raise @types.LoadError {
5631	  match title {	<-- UNCOVERED
5632	    Some(t) => @ocr.build_markdown_document(ocr_result, title=t)
5633	    None => @ocr.build_markdown_document(ocr_result)
5634	  }
5635	}
    	…

5638	pub fn generate_markdown_image_placeholder(
5639	  image : @ocr.MarkdownImage,
5640	  index : Int,
5641	) -> String {
5642	  @ocr.generate_markdown_image_placeholder(image, index)	<-- UNCOVERED
5643	}
    	…

5646	pub fn analyze_markdown_element_distribution(
5647	  doc : @ocr.MarkdownDocument,
5648	) -> Array[(String, Int)] {
5649	  @ocr.analyze_markdown_element_distribution(doc)	<-- UNCOVERED
5650	}
    	…

5679	pub fn detect_layout_type(
5680	  text_lines : Array[@ocr.TextLineAttributes],
5681	  page_width : Int,
5682	  page_height : Int,
5683	) -> @ocr.LayoutType raise @types.LoadError {
5684	  @ocr.detect_layout_type(text_lines, page_width, page_height)	<-- UNCOVERED
5685	}
    	…

5688	pub fn detect_header_footer(
5689	  text_lines : Array[@ocr.TextLineAttributes],
5690	  page_height : Int,
5691	) -> (Int, Int) raise @types.LoadError {
5692	  @ocr.detect_header_footer(text_lines, page_height)	<-- UNCOVERED
5693	}
    	…

5696	pub fn detect_column_boundaries(
5697	  text_lines : Array[@ocr.TextLineAttributes],
5698	  page_width : Int,
5699	  layout_type : @ocr.LayoutType,
5700	) -> Array[Int] {
5701	  @ocr.detect_column_boundaries(text_lines, page_width, layout_type)	<-- UNCOVERED
5702	}
    	…

5705	pub fn is_heading_line(
5706	  line : @ocr.TextLineAttributes,
5707	  avg_font_size : Double,
5708	) -> (Bool, Int) {
5709	  @ocr.is_heading_line(line, avg_font_size)	<-- UNCOVERED
5710	}
    	…

5713	pub fn detect_list_item(line : @ocr.TextLineAttributes) -> @ocr.ListItemInfo {
5714	  @ocr.detect_list_item(line)	<-- UNCOVERED
5715	}
    	…

5718	pub fn is_code_block_line(
5719	  line : @ocr.TextLineAttributes,
5720	  avg_indent : Int,
5721	) -> Bool {
5722	  @ocr.is_code_block_line(line, avg_indent)	<-- UNCOVERED
5723	}
    	…

5726	pub fn is_quote_line(line : @ocr.TextLineAttributes, avg_x : Int) -> Bool {
5727	  @ocr.is_quote_line(line, avg_x)	<-- UNCOVERED
5728	}
    	…

5731	pub fn segment_paragraphs(
5732	  lines : Array[@ocr.TextLineAttributes],
5733	) -> Array[Array[@ocr.TextLineAttributes]] {
5734	  @ocr.segment_paragraphs(lines)	<-- UNCOVERED
5735	}
    	…

5738	pub fn smart_reading_order(
5739	  elements : Array[@ocr.EnhancedDocumentElement],
5740	  layout_type : @ocr.LayoutType,
5741	  column_boundaries : Array[Int],
5742	) -> Array[Int] raise @types.LoadError {
5743	  @ocr.smart_reading_order(elements, layout_type, column_boundaries)	<-- UNCOVERED
5744	}
    	…

5747	pub fn build_enhanced_elements(
5748	  lines : Array[@ocr.TextLineAttributes],
5749	  page_width : Int,
5750	  page_height : Int,
5751	) -> @ocr.EnhancedDocument raise @types.LoadError {
5752	  @ocr.build_enhanced_elements(lines, page_width, page_height)	<-- UNCOVERED
5753	}
    	…

5756	pub fn enhanced_document_to_markdown(
5757	  doc : @ocr.EnhancedDocument,
5758	  include_header_footer? : Bool = false,
5759	) -> String {
5760	  @ocr.enhanced_document_to_markdown(doc, include_header_footer~)	<-- UNCOVERED
5761	}
    	…

5764	pub fn enhanced_element_distribution(
5765	  doc : @ocr.EnhancedDocument,
5766	) -> Array[(String, Int)] {
5767	  @ocr.enhanced_element_distribution(doc)	<-- UNCOVERED
5768	}
    	…

5785	pub fn detect_horizontal_table_lines(
5786	  binary_img : @types.Image,
5787	  min_length? : Int = 50,
5788	  max_gap? : Int = 3,
5789	) -> Array[(Int, Int, Int)] raise @types.LoadError {
5790	  @ocr.detect_horizontal_table_lines(binary_img, min_length~, max_gap~)	<-- UNCOVERED
5791	}
    	…

5794	pub fn detect_vertical_table_lines(
5795	  binary_img : @types.Image,
5796	  min_length? : Int = 50,
5797	  max_gap? : Int = 3,
5798	) -> Array[(Int, Int, Int)] raise @types.LoadError {
5799	  @ocr.detect_vertical_table_lines(binary_img, min_length~, max_gap~)	<-- UNCOVERED
5800	}
    	…

5803	pub fn find_line_intersections(
5804	  h_lines : Array[(Int, Int, Int)],
5805	  v_lines : Array[(Int, Int, Int)],
5806	) -> Array[(Int, Int)] {
5807	  @ocr.find_line_intersections(h_lines, v_lines)	<-- UNCOVERED
5808	}
    	…

5811	pub fn detect_table_enhanced(
5812	  binary_img : @types.Image,
5813	  min_table_size? : Int = 100,
5814	) -> Array[@ocr.EnhancedTableResult] raise @types.LoadError {
5815	  @ocr.detect_table_enhanced(binary_img, min_table_size~)	<-- UNCOVERED
5816	}
    	…

5819	pub fn enhanced_table_to_markdown(table : @ocr.EnhancedTableResult) -> String {
5820	  @ocr.enhanced_table_to_markdown(table)	<-- UNCOVERED
5821	}
    	…

5824	pub fn extract_images_enhanced(
5825	  binary_img : @types.Image,
5826	  min_area? : Int = 500,
5827	) -> Array[@ocr.ExtractedImage] {
5828	  @ocr.extract_images_enhanced(binary_img, min_area~)	<-- UNCOVERED
5829	}
    	…

5832	pub fn generate_image_extraction_report(
5833	  images : Array[@ocr.ExtractedImage],
5834	) -> String {
5835	  @ocr.generate_image_extraction_report(images)	<-- UNCOVERED
5836	}
    	…

5839	pub fn analyze_table_results(
5840	  tables : Array[@ocr.EnhancedTableResult],
5841	) -> Array[(String, Int)] {
5842	  @ocr.analyze_table_results(tables)	<-- UNCOVERED
5843	}
    	…

5951	pub fn correct_similar_chars(
5952	  text : String,
5953	  context_type : String,
5954	) -> @ocr.CorrectionResult raise @types.LoadError {
5955	  @ocr.correct_similar_chars(text, context_type)	<-- UNCOVERED
5956	}
    	…

5959	pub fn correct_common_patterns(text : String) -> @ocr.CorrectionResult {
5960	  @ocr.correct_common_patterns(text)	<-- UNCOVERED
5961	}
    	…

5964	pub fn full_postprocess_correction(
5965	  text : String,
5966	  context_type? : String = "mixed",
5967	) -> @ocr.CorrectionResult raise @types.LoadError {
5968	  @ocr.full_postprocess_correction(text, context_type~)	<-- UNCOVERED
5969	}
    	…

5972	pub fn batch_correct_texts(
5973	  texts : Array[String],
5974	  context_type? : String = "mixed",
5975	) -> Array[@ocr.CorrectionResult] raise @types.LoadError {
5976	  @ocr.batch_correct_texts(texts, context_type~)	<-- UNCOVERED
5977	}
    	…

5980	pub fn generate_correction_report(
5981	  results : Array[@ocr.CorrectionResult],
5982	) -> String {
5983	  @ocr.generate_correction_report(results)	<-- UNCOVERED
5984	}
    	…

5987	pub fn calculate_accuracy_improvement(
5988	  before_results : Array[@ocr.CorrectionResult],
5989	  expected_texts : Array[String],
5990	) -> (Double, Double, Double) {
5991	  @ocr.calculate_accuracy_improvement(before_results, expected_texts)	<-- UNCOVERED
5992	}
    	…

6020	pub fn lookup_by_radical(
6021	  dict : @ocr.DictChineseDict,
6022	  radical : String,
6023	) -> Array[@ocr.DictCharEntry] raise @types.LoadError {
6024	  @ocr.lookup_by_radical(dict, radical)	<-- UNCOVERED
6025	}
    	…

6028	pub fn lookup_by_pinyin(
6029	  dict : @ocr.DictChineseDict,
6030	  pinyin : String,
6031	) -> Array[@ocr.DictCharEntry] raise @types.LoadError {
6032	  @ocr.lookup_by_pinyin(dict, pinyin)	<-- UNCOVERED
6033	}
    	…

6036	pub fn lookup_by_structure(
6037	  dict : @ocr.DictChineseDict,
6038	  structure : @ocr.DictCharStructure,
6039	) -> Array[@ocr.DictCharEntry] {
6040	  @ocr.lookup_by_structure(dict, structure)	<-- UNCOVERED
6041	}
    	…

6044	pub fn calculate_chinese_similarity(
6045	  char1 : @ocr.DictCharEntry,
6046	  char2 : @ocr.DictCharEntry,
6047	) -> Double {
6048	  @ocr.calculate_chinese_similarity(char1, char2)	<-- UNCOVERED
6049	}
    	…

6052	pub fn find_similar_chinese_chars(
6053	  dict : @ocr.DictChineseDict,
6054	  target_char : String,
6055	  top_n? : Int = 5,
6056	) -> Array[@ocr.DictCharMatch] raise @types.LoadError {
6057	  @ocr.find_similar_chinese_chars(dict, target_char, top_n~)	<-- UNCOVERED
6058	}
    	…

6061	pub fn lookup_char(
6062	  dict : @ocr.DictChineseDict,
6063	  char : String,
6064	) -> @ocr.DictCharEntry? raise @types.LoadError {
6065	  @ocr.lookup_char(dict, char)	<-- UNCOVERED
6066	}
    	…

6069	pub fn recognize_by_components(
6070	  dict : @ocr.DictChineseDict,
6071	  detected_structure : @ocr.DictCharStructure,
6072	  detected_components : Array[String],
6073	  stroke_count_hint? : Int = 0,
6074	) -> Array[(String, Double)] {
6075	  @ocr.recognize_by_components(	<-- UNCOVERED
6076	    dict,
6077	    detected_structure,
6078	    detected_components,
6079	    stroke_count_hint~,
6080	  )
6081	}
    	…

6084	pub fn generate_chinese_dictionary_report(
6085	  dict : @ocr.DictChineseDict,
6086	) -> String {
6087	  @ocr.generate_chinese_dictionary_report(dict)	<-- UNCOVERED
6088	}
    	…

6114	pub fn select_adaptive_config(
6115	  quality : @ocr.ScanQuality,
6116	) -> @ocr.AdaptivePreprocessConfig raise @types.LoadError {
6117	  @ocr.select_adaptive_config(quality)	<-- UNCOVERED
6118	}
    	…

6121	pub fn detect_skew_angle_robust(
6122	  binary_img : @types.Image,
6123	) -> Double raise @types.LoadError {
6124	  @ocr.detect_skew_angle_robust(binary_img)	<-- UNCOVERED
6125	}
    	…

6128	pub fn detect_distortions(
6129	  img : @types.Image,
6130	) -> Array[@ocr.DistortionDetectionResult] raise @types.LoadError {
6131	  @ocr.detect_distortions(img)	<-- UNCOVERED
6132	}
    	…

6135	pub fn analyze_complex_layout(
6136	  text_blocks : Array[@ocr.TextBlock],
6137	  page_width : Int,
6138	  page_height : Int,
6139	) -> @ocr.ComplexLayoutAnalysis {
6140	  @ocr.analyze_complex_layout(text_blocks, page_width, page_height)	<-- UNCOVERED
6141	}
    	…

6144	pub fn generate_robustness_report(
6145	  quality : @ocr.ScanQuality,
6146	  distortions : Array[@ocr.DistortionDetectionResult],
6147	  layout : @ocr.ComplexLayoutAnalysis,
6148	) -> @ocr.RobustnessReport {
6149	  @ocr.generate_robustness_report(quality, distortions, layout)	<-- UNCOVERED
6150	}
    	…

6153	pub fn robustness_report_to_string(report : @ocr.RobustnessReport) -> String {
6154	  @ocr.robustness_report_to_string(report)	<-- UNCOVERED
6155	}
    	…

6183	pub fn create_batch_tasks(
6184	  input_files : Array[String],
6185	  output_directory : String,
6186	  output_format : String,
6187	) -> Array[@ocr.BatchTask] raise @types.LoadError {
6188	  @ocr.create_batch_tasks(input_files, output_directory, output_format)	<-- UNCOVERED
6189	}
    	…

6192	pub fn update_task_progress(
6193	  task : @ocr.BatchTask,
6194	  progress : Double,
6195	  current_page : Int,
6196	  total_pages : Int,
6197	) -> @ocr.BatchTask {
6198	  @ocr.update_task_progress(task, progress, current_page, total_pages)	<-- UNCOVERED
6199	}
    	…

6202	pub fn mark_task_completed(
6203	  task : @ocr.BatchTask,
6204	  char_count : Int,
6205	  accuracy : Double,
6206	  end_time : Double,
6207	) -> @ocr.BatchTask {
6208	  @ocr.mark_task_completed(task, char_count, accuracy, end_time)	<-- UNCOVERED
6209	}
    	…

6212	pub fn mark_task_failed(
6213	  task : @ocr.BatchTask,
6214	  error_message : String,
6215	  end_time : Double,
6216	) -> @ocr.BatchTask {
6217	  @ocr.mark_task_failed(task, error_message, end_time)	<-- UNCOVERED
6218	}
    	…

6221	pub fn calculate_batch_progress(
6222	  tasks : Array[@ocr.BatchTask],
6223	  elapsed_time_ms : Double,
6224	) -> @ocr.BatchProgress raise @types.LoadError {
6225	  @ocr.calculate_batch_progress(tasks, elapsed_time_ms)	<-- UNCOVERED
6226	}
    	…

6229	pub fn generate_batch_summary(
6230	  tasks : Array[@ocr.BatchTask],
6231	  total_time_ms : Double,
6232	) -> @ocr.BatchResultSummary raise @types.LoadError {
6233	  @ocr.generate_batch_summary(tasks, total_time_ms)	<-- UNCOVERED
6234	}
    	…

6237	pub fn batch_summary_to_string(summary : @ocr.BatchResultSummary) -> String {
6238	  @ocr.batch_summary_to_string(summary)	<-- UNCOVERED
6239	}
    	…

6242	pub fn progress_bar_to_string(progress : Double, width? : Int = 30) -> String {
6243	  @ocr.progress_bar_to_string(progress, width~)	<-- UNCOVERED
6244	}
    	…

6247	pub fn estimate_memory_usage(
6248	  image_count : Int,
6249	  image_width : Int,
6250	  image_height : Int,
6251	  channels : Int,
6252	) -> @ocr.MemoryUsageStats {
6253	  @ocr.estimate_memory_usage(image_count, image_width, image_height, channels)	<-- UNCOVERED
6254	}
    	…

6286	pub fn create_default_char_cnn_config(
6287	  num_classes : Int,
6288	) -> @ocr.DLCnnModelConfig {
6289	  @ocr.create_default_char_cnn_config(num_classes)	<-- UNCOVERED
6290	}
    	…

6293	pub fn create_neural_layer(
6294	  layer_type : @ocr.DLLayerType,
6295	  name : String,
6296	  input_shape : Array[Int],
6297	) -> @ocr.DLNeuralLayer {
6298	  @ocr.create_neural_layer(layer_type, name, input_shape)	<-- UNCOVERED
6299	}
    	…

6302	pub fn normalize_image(
6303	  image_data : Array[Double],
6304	  mean : Double,
6305	  std : Double,
6306	) -> Array[Double] raise @types.LoadError {
6307	  @ocr.normalize_image(image_data, mean, std)	<-- UNCOVERED
6308	}
    	…

6311	pub fn resize_image_for_cnn(
6312	  img : @types.Image,
6313	  target_width : Int,
6314	  target_height : Int,
6315	) -> Array[Double] raise @types.LoadError {
6316	  @ocr.resize_image_for_cnn(img, target_width, target_height)	<-- UNCOVERED
6317	}
    	…

6320	pub fn relu(x : Double) -> Double {
6321	  @ocr.relu(x)	<-- UNCOVERED
6322	}
    	…

6325	pub fn sigmoid(x : Double) -> Double {
6326	  @ocr.sigmoid(x)	<-- UNCOVERED
6327	}
    	…

6330	pub fn softmax(logits : Array[Double]) -> Array[Double] {
6331	  @ocr.softmax(logits)	<-- UNCOVERED
6332	}
    	…

6335	pub fn conv2d(
6336	  input : Array[Double],
6337	  in_h : Int,
6338	  in_w : Int,
6339	  in_c : Int,
6340	  kernel : Array[Double],
6341	  out_c : Int,
6342	  k_size : Int,
6343	  stride : Int,
6344	  padding : Int,
6345	) -> Array[Double] raise @types.LoadError {
6346	  @ocr.conv2d(input, in_h, in_w, in_c, kernel, out_c, k_size, stride, padding)	<-- UNCOVERED
6347	}
    	…

6350	pub fn max_pool2d(
6351	  input : Array[Double],
6352	  in_h : Int,
6353	  in_w : Int,
6354	  channels : Int,
6355	  pool_size : Int,
6356	) -> Array[Double] raise @types.LoadError {
6357	  @ocr.max_pool2d(input, in_h, in_w, channels, pool_size)	<-- UNCOVERED
6358	}
    	…

6361	pub fn dense_forward(
6362	  input : Array[Double],
6363	  weights : Array[Double],
6364	  biases : Array[Double],
6365	  in_features : Int,
6366	  out_features : Int,
6367	  activation : String,
6368	) -> Array[Double] {
6369	  @ocr.dense_forward(	<-- UNCOVERED
6370	    input, weights, biases, in_features, out_features, activation,
6371	  )
6372	}
    	…

6375	pub fn cnn_infer(
6376	  image_data : Array[Double],
6377	  config : @ocr.DLCnnModelConfig,
6378	  labels : Array[String],
6379	) -> @ocr.DLInferenceResult {
6380	  @ocr.cnn_infer(image_data, config, labels)	<-- UNCOVERED
6381	}
    	…

6384	pub fn create_training_sample(
6385	  img : @types.Image,
6386	  label : Int,
6387	  label_text : String,
6388	) -> @ocr.DLTrainingSample raise @types.LoadError {
6389	  @ocr.create_training_sample(img, label, label_text)	<-- UNCOVERED
6390	}
    	…

6393	pub fn cross_entropy_loss(predicted : Array[Double], target : Int) -> Double {
6394	  @ocr.cross_entropy_loss(predicted, target)	<-- UNCOVERED
6395	}
    	…

6398	pub fn create_training_history(
6399	  epoch : Int,
6400	  train_loss : Double,
6401	  train_accuracy : Double,
6402	  val_loss : Double,
6403	  val_accuracy : Double,
6404	  learning_rate : Double,
6405	  time_ms : Double,
6406	) -> @ocr.DLTrainingHistory {
6407	  @ocr.create_training_history(	<-- UNCOVERED
6408	    epoch, train_loss, train_accuracy, val_loss, val_accuracy, learning_rate, time_ms,
6409	  )
6410	}
    	…

6413	pub fn generate_training_report(
6414	  history : Array[@ocr.DLTrainingHistory],
6415	  config : @ocr.DLCnnModelConfig,
6416	) -> String {
6417	  @ocr.generate_training_report(history, config)	<-- UNCOVERED
6418	}
    	…

6421	pub fn save_model(
6422	  config : @ocr.DLCnnModelConfig,
6423	  weights : Array[Double],
6424	  path : String,
6425	) -> Bool {
6426	  @ocr.save_model(config, weights, path)	<-- UNCOVERED
6427	}
    	…

6430	pub fn load_model(path : String) -> @ocr.DLCnnModelConfig? {
6431	  @ocr.load_model(path)	<-- UNCOVERED
6432	}
    	…

6435	pub fn create_feature_extractor(
6436	  model_name : String,
6437	  feature_dim : Int,
6438	) -> @ocr.DLFeatureExtractor {
6439	  @ocr.create_feature_extractor(model_name, feature_dim)	<-- UNCOVERED
6440	}
    	…

6443	pub fn extract_features(
6444	  extractor : @ocr.DLFeatureExtractor,
6445	  img : @types.Image,
6446	) -> Array[Double] {
6447	  @ocr.extract_features(extractor, img)	<-- UNCOVERED
6448	}
    	…

6451	pub fn cosine_similarity(
6452	  feat1 : Array[Double],
6453	  feat2 : Array[Double],
6454	) -> Double {
6455	  @ocr.cosine_similarity(feat1, feat2)	<-- UNCOVERED
6456	}
    	…

6461	pub fn relu_backward(
6462	  grad : Array[Double],
6463	  fwd : Array[Double],
6464	) -> Array[Double] {
6465	  @ocr.relu_backward(grad, fwd)	<-- UNCOVERED
6466	}
    	…

6469	pub fn sigmoid_backward(
6470	  grad : Array[Double],
6471	  fwd : Array[Double],
6472	) -> Array[Double] {
6473	  @ocr.sigmoid_backward(grad, fwd)	<-- UNCOVERED
6474	}
    	…

6477	pub fn softmax_cross_entropy_backward(
6478	  predicted : Array[Double],
6479	  target : Int,
6480	) -> Array[Double] raise @types.LoadError {
6481	  @ocr.softmax_cross_entropy_backward(predicted, target)	<-- UNCOVERED
6482	}
    	…

6485	pub fn dense_backward(
6486	  grad_output : Array[Double],
6487	  weights : Array[Double],
6488	  in_features : Int,
6489	  out_features : Int,
6490	) -> (Array[Double], Array[Double], Array[Double]) {
6491	  @ocr.dense_backward(grad_output, weights, in_features, out_features)	<-- UNCOVERED
6492	}
    	…

6495	pub fn max_pool2d_backward(
6496	  grad_output : Array[Double],
6497	  input : Array[Double],
6498	  in_h : Int,
6499	  in_w : Int,
6500	  in_c : Int,
6501	  pool_size : Int,
6502	) -> Array[Double] {
6503	  @ocr.max_pool2d_backward(grad_output, input, in_h, in_w, in_c, pool_size)	<-- UNCOVERED
6504	}
    	…

6507	pub fn update_weights_sgd(
6508	  weights : Array[Double],
6509	  gradients : Array[Double],
6510	  learning_rate : Double,
6511	) -> Array[Double] raise @types.LoadError {
6512	  @ocr.update_weights_sgd(weights, gradients, learning_rate)	<-- UNCOVERED
6513	}
    	…

6516	pub fn cnn_train_step(
6517	  input : Array[Double],
6518	  target : Int,
6519	  conv_weights : Array[Double],
6520	  conv_bias : Array[Double],
6521	  dense_weights : Array[Double],
6522	  dense_bias : Array[Double],
6523	  in_h : Int,
6524	  in_w : Int,
6525	  in_c : Int,
6526	  conv_out_c : Int,
6527	  k_size : Int,
6528	  num_classes : Int,
6529	  learning_rate : Double,
6530	) -> (Array[Double], Array[Double], Array[Double], Array[Double], Double) raise @types.LoadError {
6531	  @ocr.cnn_train_step(	<-- UNCOVERED
6532	    input, target, conv_weights, conv_bias, dense_weights, dense_bias, in_h, in_w,
6533	    in_c, conv_out_c, k_size, num_classes, learning_rate,
6534	  )
6535	}
    	…

6538	pub fn cnn_train(
6539	  samples : Array[@ocr.DLTrainingSample],
6540	  conv_weights : Array[Double],
6541	  conv_bias : Array[Double],
6542	  dense_weights : Array[Double],
6543	  dense_bias : Array[Double],
6544	  in_h : Int,
6545	  in_w : Int,
6546	  in_c : Int,
6547	  conv_out_c : Int,
6548	  k_size : Int,
6549	  num_classes : Int,
6550	  learning_rate : Double,
6551	  epochs : Int,
6552	) -> (
6553	  Array[Double],
6554	  Array[Double],
6555	  Array[Double],
6556	  Array[Double],
6557	  Array[@ocr.DLTrainingHistory],
6558	) raise @types.LoadError {
6559	  @ocr.cnn_train(	<-- UNCOVERED
6560	    samples, conv_weights, conv_bias, dense_weights, dense_bias, in_h, in_w, in_c,
6561	    conv_out_c, k_size, num_classes, learning_rate, epochs,
6562	  )
6563	}
    	…

6566	pub fn cnn_recognize_char(
6567	  img : @types.Image,
6568	  config : @ocr.DLCnnModelConfig,
6569	  labels : Array[String],
6570	) -> @ocr.DLInferenceResult raise @types.LoadError {
6571	  @ocr.cnn_recognize_char(img, config, labels)	<-- UNCOVERED
6572	}
    	…

6575	pub fn cnn_recognize_digits(
6576	  img : @types.Image,
6577	  config : @ocr.DLCnnModelConfig,
6578	) -> @ocr.DLInferenceResult raise @types.LoadError {
6579	  @ocr.cnn_recognize_digits(img, config)	<-- UNCOVERED
6580	}
    	…

6583	pub fn cnn_recognize_letters(
6584	  img : @types.Image,
6585	  config : @ocr.DLCnnModelConfig,
6586	) -> @ocr.DLInferenceResult raise @types.LoadError {
6587	  @ocr.cnn_recognize_letters(img, config)	<-- UNCOVERED
6588	}
    	…

6591	pub fn evaluate_cnn_model(
6592	  test_samples : Array[@ocr.DLTrainingSample],
6593	  config : @ocr.DLCnnModelConfig,
6594	  labels : Array[String],
6595	) -> (Double, Double) raise @types.LoadError {
6596	  @ocr.evaluate_cnn_model(test_samples, config, labels)	<-- UNCOVERED
6597	}
    	…

6600	pub fn cnn_confusion_matrix(
6601	  test_samples : Array[@ocr.DLTrainingSample],
6602	  config : @ocr.DLCnnModelConfig,
6603	  labels : Array[String],
6604	) -> Array[Array[Int]] raise @types.LoadError {
6605	  @ocr.cnn_confusion_matrix(test_samples, config, labels)	<-- UNCOVERED
6606	}
    	…

6646	pub fn create_data_loader(
6647	  samples : Array[@ocr.DLTrainingSample],
6648	  batch_size : Int,
6649	  shuffle : Bool,
6650	) -> @ocr.DataLoader raise @types.LoadError {
6651	  @ocr.create_data_loader(samples, batch_size, shuffle)	<-- UNCOVERED
6652	}
    	…

6655	pub fn next_batch(
6656	  loader : @ocr.DataLoader,
6657	) -> (Array[@ocr.DLTrainingSample], @ocr.DataLoader) raise @types.LoadError {
6658	  @ocr.next_batch(loader)	<-- UNCOVERED
6659	}
    	…

6662	pub fn calculate_learning_rate(
6663	  config : @ocr.LRSchedulerConfig,
6664	  epoch : Int,
6665	  current_lr : Double,
6666	  val_loss : Double,
6667	) -> Double raise @types.LoadError {
6668	  @ocr.calculate_learning_rate(config, epoch, current_lr, val_loss)	<-- UNCOVERED
6669	}
    	…

6672	pub fn augment_random_rotation(
6673	  sample : @ocr.DLTrainingSample,
6674	  angle_range : Double,
6675	) -> @ocr.DLTrainingSample {
6676	  @ocr.augment_random_rotation(sample, angle_range)	<-- UNCOVERED
6677	}
    	…

6680	pub fn augment_random_shift(
6681	  sample : @ocr.DLTrainingSample,
6682	  shift_range : Double,
6683	) -> @ocr.DLTrainingSample {
6684	  @ocr.augment_random_shift(sample, shift_range)	<-- UNCOVERED
6685	}
    	…

6688	pub fn augment_random_scale(
6689	  sample : @ocr.DLTrainingSample,
6690	  scale_range : Double,
6691	) -> @ocr.DLTrainingSample {
6692	  @ocr.augment_random_scale(sample, scale_range)	<-- UNCOVERED
6693	}
    	…

6696	pub fn augment_random_noise(
6697	  sample : @ocr.DLTrainingSample,
6698	  noise_level : Double,
6699	) -> @ocr.DLTrainingSample {
6700	  @ocr.augment_random_noise(sample, noise_level)	<-- UNCOVERED
6701	}
    	…

6704	pub fn apply_augmentation(
6705	  sample : @ocr.DLTrainingSample,
6706	  config : @ocr.AugmentationConfig,
6707	) -> @ocr.DLTrainingSample {
6708	  @ocr.apply_augmentation(sample, config)	<-- UNCOVERED
6709	}
    	…

6712	pub fn calculate_accuracy(
6713	  predictions : Array[Int],
6714	  labels : Array[Int],
6715	) -> Double {
6716	  @ocr.calculate_accuracy(predictions, labels)	<-- UNCOVERED
6717	}
    	…

6720	pub fn check_early_stopping(
6721	  state : @ocr.TrainingState,
6722	  config : @ocr.TrainingConfig,
6723	  current_val_accuracy : Double,
6724	) -> (Bool, @ocr.TrainingState) {
6725	  @ocr.check_early_stopping(state, config, current_val_accuracy)	<-- UNCOVERED
6726	}
    	…

6734	pub fn record_training_metrics(
6735	  state : @ocr.TrainingState,
6736	  metrics : @ocr.TrainingMetrics,
6737	) -> @ocr.TrainingState {
6738	  @ocr.record_training_metrics(state, metrics)	<-- UNCOVERED
6739	}
    	…

6742	pub fn generate_training_progress_report(
6743	  state : @ocr.TrainingState,
6744	  config : @ocr.TrainingConfig,
6745	) -> String {
6746	  @ocr.generate_training_progress_report(state, config)	<-- UNCOVERED
6747	}
    	…

6750	pub fn split_train_validation(
6751	  samples : Array[@ocr.DLTrainingSample],
6752	  validation_split : Double,
6753	  shuffle : Bool,
6754	) -> (Array[@ocr.DLTrainingSample], Array[@ocr.DLTrainingSample]) {
6755	  @ocr.split_train_validation(samples, validation_split, shuffle)	<-- UNCOVERED
6756	}
    	…

6759	pub fn compute_confusion_matrix(
6760	  predictions : Array[Int],
6761	  labels : Array[Int],
6762	  num_classes : Int,
6763	) -> Array[Array[Int]] {
6764	  @ocr.compute_confusion_matrix(predictions, labels, num_classes)	<-- UNCOVERED
6765	}
    	…

6768	pub fn compute_per_class_accuracy(
6769	  confusion_matrix : Array[Array[Int]],
6770	) -> Array[Double] {
6771	  @ocr.compute_per_class_accuracy(confusion_matrix)	<-- UNCOVERED
6772	}
    	…

6782	pub fn build_threshold_lut(threshold : Int) -> Array[Int] {
6783	  @ocr.build_threshold_lut(threshold)	<-- UNCOVERED
6784	}
    	…

6787	pub fn fast_threshold_lut(
6788	  img : @types.Image,
6789	  lut : Array[Int],
6790	) -> @types.Image raise @types.LoadError {
6791	  @ocr.fast_threshold_lut(img, lut)	<-- UNCOVERED
6792	}
    	…

6795	pub fn cache_friendly_horizontal_projection(
6796	  img : @types.Image,
6797	) -> Array[Int] raise @types.LoadError {
6798	  @ocr.cache_friendly_horizontal_projection(img)	<-- UNCOVERED
6799	}
    	…

6802	pub fn cache_friendly_vertical_projection(
6803	  img : @types.Image,
6804	) -> Array[Int] raise @types.LoadError {
6805	  @ocr.cache_friendly_vertical_projection(img)	<-- UNCOVERED
6806	}
    	…

6809	pub fn unrolled_pixel_sum(img : @types.Image) -> Int raise @types.LoadError {
6810	  @ocr.unrolled_pixel_sum(img)	<-- UNCOVERED
6811	}
    	…

6814	pub fn fast_connected_components(
6815	  binary_img : @types.Image,
6816	) -> Array[Int] raise @types.LoadError {
6817	  @ocr.fast_connected_components(binary_img)	<-- UNCOVERED
6818	}
    	…

6821	pub fn build_gaussian_kernel_lut(
6822	  sigma : Double,
6823	  radius : Int,
6824	) -> Array[Double] raise @types.LoadError {
6825	  @ocr.build_gaussian_kernel_lut(sigma, radius)	<-- UNCOVERED
6826	}
    	…

6829	pub fn fast_gaussian_blur_1d(
6830	  img : @types.Image,
6831	  kernel : Array[Double],
6832	) -> @types.Image raise @types.LoadError {
6833	  @ocr.fast_gaussian_blur_1d(img, kernel)	<-- UNCOVERED
6834	}
    	…

6837	pub fn batch_recognize_chars(
6838	  images : Array[@types.Image],
6839	  config : @ocr.DLCnnModelConfig,
6840	  labels : Array[String],
6841	) -> Array[@ocr.DLInferenceResult] raise @types.LoadError {
6842	  @ocr.batch_recognize_chars(images, config, labels)	<-- UNCOVERED
6843	}
    	…

6846	pub fn ocr_pipeline_stats(
6847	  img : @types.Image,
6848	) -> (Int, Int, Double) raise @types.LoadError {
6849	  @ocr.ocr_pipeline_stats(img)	<-- UNCOVERED
6850	}
    	…

6853	pub fn fast_downsample_2x(
6854	  img : @types.Image,
6855	) -> @types.Image raise @types.LoadError {
6856	  @ocr.fast_downsample_2x(img)	<-- UNCOVERED
6857	}
    	…

6885	pub fn generate_dockerfile(config : @ocr.DockerConfig) -> String {
6886	  @ocr.generate_dockerfile(config)	<-- UNCOVERED
6887	}
    	…

6890	pub fn generate_docker_compose(config : @ocr.DockerConfig) -> String {
6891	  @ocr.generate_docker_compose(config)	<-- UNCOVERED
6892	}
    	…

6895	pub fn generate_api_config_json(config : @ocr.ApiServerConfig) -> String {
6896	  @ocr.generate_api_config_json(config)	<-- UNCOVERED
6897	}
    	…

6905	pub fn parse_cli_args(
6906	  args : Array[String],
6907	) -> @ocr.CliConfig raise @types.LoadError {
6908	  @ocr.parse_cli_args(args)	<-- UNCOVERED
6909	}
    	…

6912	pub fn generate_model_manifest(config : @ocr.ModelPackageConfig) -> String {
6913	  @ocr.generate_model_manifest(config)	<-- UNCOVERED
6914	}
    	…

6917	pub fn generate_env_config(env : @ocr.DeploymentEnvironment) -> String {
6918	  @ocr.generate_env_config(env)	<-- UNCOVERED
6919	}
    	…

6922	pub fn generate_health_check_config(config : @ocr.ApiServerConfig) -> String {
6923	  @ocr.generate_health_check_config(config)	<-- UNCOVERED
6924	}
    	…

6927	pub fn generate_deployment_package(
6928	  docker_config : @ocr.DockerConfig,
6929	  api_config : @ocr.ApiServerConfig,
6930	  model_config : @ocr.ModelPackageConfig,
6931	) -> String {
6932	  @ocr.generate_deployment_package(docker_config, api_config, model_config)	<-- UNCOVERED
6933	}
    	…

6967	pub fn detect_handwriting_slant(
6968	  img : @types.Image,
6969	) -> Double raise @types.LoadError {
6970	  @ocr.detect_handwriting_slant(img)	<-- UNCOVERED
6971	}
    	…

6974	pub fn normalize_handwriting_size(
6975	  img : @types.Image,
6976	  target_size : Int,
6977	) -> @types.Image raise @types.LoadError {
6978	  @ocr.normalize_handwriting_size(img, target_size)	<-- UNCOVERED
6979	}
    	…

6982	pub fn extract_hw_stroke_features(
6983	  img : @types.Image,
6984	) -> Array[@ocr.StrokeFeature] {
6985	  @ocr.extract_hw_stroke_features(img)	<-- UNCOVERED
6986	}
    	…

6989	pub fn segment_connected_strokes(
6990	  img : @types.Image,
6991	  config : @ocr.HandwritingRecognitionConfig,
6992	) -> @ocr.StrokeSegmentationResult {
6993	  @ocr.segment_connected_strokes(img, config)	<-- UNCOVERED
6994	}
    	…

6997	pub fn extract_handwriting_features(
6998	  img : @types.Image,
6999	  char : String,
7000	) -> @ocr.HandwritingCharFeature raise @types.LoadError {
7001	  @ocr.extract_handwriting_features(img, char)	<-- UNCOVERED
7002	}
    	…

7005	pub fn handwriting_feature_similarity(
7006	  feat1 : @ocr.HandwritingCharFeature,
7007	  feat2 : @ocr.HandwritingCharFeature,
7008	) -> Double {
7009	  @ocr.handwriting_feature_similarity(feat1, feat2)	<-- UNCOVERED
7010	}
    	…

7013	pub fn create_handwriting_template(
7014	  char : String,
7015	  style : @ocr.HandwritingStyle,
7016	  samples : Array[@types.Image],
7017	) -> @ocr.HandwritingTemplate raise @types.LoadError {
7018	  @ocr.create_handwriting_template(char, style, samples)	<-- UNCOVERED
7019	}
    	…

7022	pub fn recognize_handwriting_char(
7023	  img : @types.Image,
7024	  templates : Array[@ocr.HandwritingTemplate],
7025	  config : @ocr.HandwritingRecognitionConfig,
7026	) -> @ocr.HandwritingRecognitionResult raise @types.LoadError {
7027	  @ocr.recognize_handwriting_char(img, templates, config)	<-- UNCOVERED
7028	}
    	…

7031	pub fn handwriting_report_to_string(
7032	  result : @ocr.HandwritingRecognitionResult,
7033	) -> String {
7034	  @ocr.handwriting_report_to_string(result)	<-- UNCOVERED
7035	}
    	…

7075	pub fn compute_frame_difference(
7076	  frame1 : @types.Image,
7077	  frame2 : @types.Image,
7078	  threshold? : Double = 30.0,
7079	) -> Double {
7080	  @video.compute_frame_difference(frame1, frame2, threshold~)	<-- UNCOVERED
7081	}
    	…

7084	pub fn compute_optical_flow(
7085	  frame1 : @types.Image,
7086	  frame2 : @types.Image,
7087	  block_size? : Int = 8,
7088	  search_range? : Int = 4,
7089	) -> @video.OpticalFlowField {
7090	  @video.compute_optical_flow(frame1, frame2, block_size~, search_range~)	<-- UNCOVERED
7091	}
    	…

7094	pub fn initialize_tracking(
7095	  first_frame : @types.Image,
7096	  roi_x : Int,
7097	  roi_y : Int,
7098	  roi_width : Int,
7099	  roi_height : Int,
7100	  label : String,
7101	) -> @video.TrackedObject {
7102	  @video.initialize_tracking(	<-- UNCOVERED
7103	    first_frame, roi_x, roi_y, roi_width, roi_height, label,
7104	  )
7105	}
    	…

7108	pub fn update_tracking(
7109	  obj : @video.TrackedObject,
7110	  frame : @types.Image,
7111	) -> @video.TrackedObject {
7112	  @video.update_tracking(obj, frame)	<-- UNCOVERED
7113	}
    	…

7116	pub fn recognize_action(
7117	  frames : Array[@types.Image],
7118	  start_frame : Int,
7119	  end_frame : Int,
7120	) -> @video.ActionRecognitionResult {
7121	  @video.recognize_action(frames, start_frame, end_frame)	<-- UNCOVERED
7122	}
    	…

7125	pub fn detect_scene_change(
7126	  frame1 : @types.Image,
7127	  frame2 : @types.Image,
7128	  threshold? : Double = 0.3,
7129	) -> (Bool, Double) {
7130	  @video.detect_scene_change(frame1, frame2, threshold~)	<-- UNCOVERED
7131	}
    	…

7134	pub fn extract_keyframes(
7135	  frames : Array[@types.Image],
7136	  fps : Double,
7137	  method_type? : String = "auto",
7138	) -> Array[@video.Keyframe] {
7139	  @video.extract_keyframes(frames, fps, method_type~)	<-- UNCOVERED
7140	}
    	…

7143	pub fn generate_video_summary(
7144	  frames : Array[@types.Image],
7145	  fps : Double,
7146	) -> @video.VideoSummary {
7147	  @video.generate_video_summary(frames, fps)	<-- UNCOVERED
7148	}
    	…

7151	pub fn stabilize_video(
7152	  frames : Array[@types.Image],
7153	  config : @video.VideoStabilizationConfig,
7154	) -> Array[@types.Image] {
7155	  @video.stabilize_video(frames, config)	<-- UNCOVERED
7156	}
    	…

7195	pub fn create_default_dof_config(
7196	  width : Int,
7197	  height : Int,
7198	) -> @photo_enhance.DepthOfFieldConfig {
7199	  @photo_enhance.create_default_dof_config(width, height)	<-- UNCOVERED
7200	}
    	…

7208	pub fn detect_exposure_type(
7209	  img : @types.Image,
7210	) -> (@photo_enhance.ExposureType, Double) {
7211	  @photo_enhance.detect_exposure_type(img)	<-- UNCOVERED
7212	}
    	…

7215	pub fn hdr_merge(
7216	  images : Array[@types.Image],
7217	  exposures : Array[Double],
7218	) -> @photo_enhance.HdrImage {
7219	  @photo_enhance.hdr_merge(images, exposures)	<-- UNCOVERED
7220	}
    	…

7223	pub fn tone_map(
7224	  hdr : @photo_enhance.HdrImage,
7225	  config : @photo_enhance.ToneMappingConfig,
7226	) -> @types.Image {
7227	  @photo_enhance.tone_map(hdr, config)	<-- UNCOVERED
7228	}
    	…

7231	pub fn stitch_panorama(
7232	  images : Array[@types.Image],
7233	  config : @photo_enhance.PanoramaConfig,
7234	) -> @photo_enhance.PanoramaResult {
7235	  @photo_enhance.stitch_panorama(images, config)	<-- UNCOVERED
7236	}
    	…

7239	pub fn apply_depth_of_field(
7240	  img : @types.Image,
7241	  config : @photo_enhance.DepthOfFieldConfig,
7242	) -> @types.Image {
7243	  @photo_enhance.apply_depth_of_field(img, config)	<-- UNCOVERED
7244	}
    	…

7247	pub fn enhance_night(
7248	  img : @types.Image,
7249	  config : @photo_enhance.NightEnhanceConfig,
7250	) -> @types.Image {
7251	  @photo_enhance.enhance_night(img, config)	<-- UNCOVERED
7252	}
    	…

7255	pub fn computational_photography_report(
7256	  hdr : @photo_enhance.HdrImage,
7257	  panorama : @photo_enhance.PanoramaResult,
7258	) -> String {
7259	  @photo_enhance.computational_photography_report(hdr, panorama)	<-- UNCOVERED
7260	}
    	…

7291	pub fn calculate_iou(
7292	  box1 : @detection.YoloBoundingBox,
7293	  box2 : @detection.YoloBoundingBox,
7294	) -> Double raise @types.LoadError {
7295	  @detection.calculate_iou(box1, box2)	<-- UNCOVERED
7296	}
    	…

7299	pub fn non_max_suppression(
7300	  boxes : Array[@detection.YoloBoundingBox],
7301	  iou_threshold : Double,
7302	  max_detections : Int,
7303	) -> Array[@detection.YoloBoundingBox] raise @types.LoadError {
7304	  @detection.non_max_suppression(boxes, iou_threshold, max_detections)	<-- UNCOVERED
7305	}
    	…

7308	pub fn detect_objects_simple(
7309	  img : @types.Image,
7310	  config : @detection.YoloConfig,
7311	) -> @detection.YoloDetectionResult raise @types.LoadError {
7312	  @detection.detect_objects_simple(img, config)	<-- UNCOVERED
7313	}
    	…

7316	pub fn initialize_object_tracking(
7317	  detection : @detection.YoloDetectionResult,
7318	) -> Array[@detection.YoloTrackedObject] raise @types.LoadError {
7319	  @detection.initialize_object_tracking(detection)	<-- UNCOVERED
7320	}
    	…

7323	pub fn update_object_tracking(
7324	  tracked_objects : Array[@detection.YoloTrackedObject],
7325	  detection : @detection.YoloDetectionResult,
7326	  max_age : Int,
7327	) -> Array[@detection.YoloTrackedObject] raise @types.LoadError {
7328	  @detection.update_object_tracking(tracked_objects, detection, max_age)	<-- UNCOVERED
7329	}
    	…

7332	pub fn calculate_detection_metrics(
7333	  predictions : Array[@detection.YoloBoundingBox],
7334	  ground_truth : Array[@detection.YoloBoundingBox],
7335	  iou_threshold : Double,
7336	) -> @detection.YoloDetectionMetrics raise @types.LoadError {
7337	  @detection.calculate_detection_metrics(	<-- UNCOVERED
7338	    predictions, ground_truth, iou_threshold,
7339	  )
7340	}
    	…

7343	pub fn detection_report_to_string(
7344	  result : @detection.YoloDetectionResult,
7345	  metrics : @detection.YoloDetectionMetrics,
7346	) -> String {
7347	  @detection.detection_report_to_string(result, metrics)	<-- UNCOVERED
7348	}
    	…

7389	pub fn threshold_segmentation(
7390	  img : @types.Image,
7391	  threshold : Double,
7392	) -> @segmentation.SemanticSegmentationResult {
7393	  @segmentation.threshold_segmentation(img, threshold)	<-- UNCOVERED
7394	}
    	…

7397	pub fn otsu_threshold_value(img : @types.Image) -> Double {
7398	  @segmentation.otsu_threshold_value(img)	<-- UNCOVERED
7399	}
    	…

7402	pub fn kmeans_segmentation(
7403	  img : @types.Image,
7404	  config : @segmentation.KMeansConfig,
7405	) -> @segmentation.SemanticSegmentationResult {
7406	  @segmentation.kmeans_segmentation(img, config)	<-- UNCOVERED
7407	}
    	…

7410	pub fn superpixel_segmentation(
7411	  img : @types.Image,
7412	  num_superpixels : Int,
7413	) -> @segmentation.SuperpixelResult {
7414	  @segmentation.superpixel_segmentation(img, num_superpixels)	<-- UNCOVERED
7415	}
    	…

7418	pub fn watershed_segmentation(
7419	  img : @types.Image,
7420	  config : @segmentation.WatershedConfig,
7421	) -> @segmentation.SemanticSegmentationResult {
7422	  @segmentation.watershed_segmentation(img, config)	<-- UNCOVERED
7423	}
    	…

7426	pub fn grabcut_segmentation(
7427	  img : @types.Image,
7428	  rect_x : Int,
7429	  rect_y : Int,
7430	  rect_w : Int,
7431	  rect_h : Int,
7432	  config : @segmentation.GrabCutConfig,
7433	) -> @segmentation.SemanticSegmentationResult {
7434	  @segmentation.grabcut_segmentation(	<-- UNCOVERED
7435	    img, rect_x, rect_y, rect_w, rect_h, config,
7436	  )
7437	}
    	…

7440	pub fn calculate_segmentation_metrics(
7441	  prediction : Array[Int],
7442	  ground_truth : Array[Int],
7443	  num_classes : Int,
7444	) -> @segmentation.SegmentationMetrics {
7445	  @segmentation.calculate_segmentation_metrics(	<-- UNCOVERED
7446	    prediction, ground_truth, num_classes,
7447	  )
7448	}
    	…

7451	pub fn segmentation_to_image(
7452	  result : @segmentation.SemanticSegmentationResult,
7453	) -> @types.Image {
7454	  @segmentation.segmentation_to_image(result)	<-- UNCOVERED
7455	}
    	…

7458	pub fn segmentation_report_to_string(
7459	  result : @segmentation.SemanticSegmentationResult,
7460	  metrics : @segmentation.SegmentationMetrics,
7461	) -> String {
7462	  @segmentation.segmentation_report_to_string(result, metrics)	<-- UNCOVERED
7463	}
    	…

7500	pub fn create_default_camera_intrinsics_3d(
7501	  width : Int,
7502	  height : Int,
7503	) -> @stereo3d.CameraIntrinsics3d {
7504	  @stereo3d.create_default_camera_intrinsics(width, height)	<-- UNCOVERED
7505	}
    	…

7508	pub fn depth_to_point_cloud_3d(
7509	  depth : @stereo3d.DepthMap3d,
7510	  intrinsics : @stereo3d.CameraIntrinsics3d,
7511	) -> @stereo3d.PointCloud3D {
7512	  @stereo3d.depth_to_point_cloud(depth, intrinsics)	<-- UNCOVERED
7513	}
    	…

7516	pub fn stereo_match_3d(
7517	  left : @types.Image,
7518	  right : @types.Image,
7519	  config : @stereo3d.StereoConfig3d,
7520	) -> @stereo3d.DepthMap3d {
7521	  @stereo3d.stereo_match(left, right, config)	<-- UNCOVERED
7522	}
    	…

7525	pub fn voxel_downsample_3d(
7526	  cloud : @stereo3d.PointCloud3D,
7527	  voxel_size : Double,
7528	) -> @stereo3d.PointCloud3D {
7529	  @stereo3d.voxel_downsample(cloud, voxel_size)	<-- UNCOVERED
7530	}
    	…

7533	pub fn statistical_outlier_removal_3d(
7534	  cloud : @stereo3d.PointCloud3D,
7535	  mean_k : Int,
7536	  std_ratio : Double,
7537	) -> @stereo3d.PointCloud3D {
7538	  @stereo3d.statistical_outlier_removal(cloud, mean_k, std_ratio)	<-- UNCOVERED
7539	}
    	…

7542	pub fn icp_registration_3d(
7543	  source : @stereo3d.PointCloud3D,
7544	  target : @stereo3d.PointCloud3D,
7545	  max_iterations : Int,
7546	  tolerance : Double,
7547	) -> @stereo3d.RegistrationResult3d {
7548	  @stereo3d.icp_registration(source, target, max_iterations, tolerance)	<-- UNCOVERED
7549	}
    	…

7552	pub fn estimate_normals_3d(
7553	  cloud : @stereo3d.PointCloud3D,
7554	  search_radius : Double,
7555	) -> Array[(Double, Double, Double)] {
7556	  @stereo3d.estimate_normals(cloud, search_radius)	<-- UNCOVERED
7557	}
    	…

7560	pub fn depth_map_to_image_3d(depth : @stereo3d.DepthMap3d) -> @types.Image {
7561	  @stereo3d.depth_map_to_image(depth)	<-- UNCOVERED
7562	}
    	…

7565	pub fn vision_3d_report_to_string(
7566	  cloud : @stereo3d.PointCloud3D,
7567	  depth : @stereo3d.DepthMap3d,
7568	  registration : @stereo3d.RegistrationResult3d,
7569	) -> String {
7570	  @stereo3d.vision_3d_report_to_string(cloud, depth, registration)	<-- UNCOVERED
7571	}
    	…

7605	pub fn create_default_window_level_medical(
7606	  preset : String,
7607	) -> @medical.WindowLevelConfig {
7608	  @medical.create_default_window_level(preset)	<-- UNCOVERED
7609	}
    	…

7612	pub fn apply_window_level_medical(
7613	  image : @medical.MedicalImage,
7614	  config : @medical.WindowLevelConfig,
7615	) -> @types.Image {
7616	  @medical.apply_window_level_medical(image, config)	<-- UNCOVERED
7617	}
    	…

7620	pub fn medical_threshold_segmentation_medical(
7621	  image : @medical.MedicalImage,
7622	  lower : Double,
7623	  upper : Double,
7624	) -> Array[Bool] {
7625	  @medical.medical_threshold_segmentation(image, lower, upper)	<-- UNCOVERED
7626	}
    	…

7629	pub fn region_growing_segmentation_medical(
7630	  image : @medical.MedicalImage,
7631	  seed_x : Int,
7632	  seed_y : Int,
7633	  seed_z : Int,
7634	  threshold : Double,
7635	) -> Array[Bool] {
7636	  @medical.region_growing_segmentation(image, seed_x, seed_y, seed_z, threshold)	<-- UNCOVERED
7637	}
    	…

7640	pub fn calculate_roi_stats_medical(
7641	  image : @medical.MedicalImage,
7642	  mask : Array[Bool],
7643	) -> (Double, Double, Int) {
7644	  @medical.calculate_roi_stats(image, mask)	<-- UNCOVERED
7645	}
    	…

7648	pub fn detect_lesions_simple_medical(
7649	  image : @medical.MedicalImage,
7650	  min_diameter : Double,
7651	  max_diameter : Double,
7652	) -> Array[@medical.Lesion] {
7653	  @medical.detect_lesions_simple(image, min_diameter, max_diameter)	<-- UNCOVERED
7654	}
    	…

7657	pub fn medical_image_registration_medical(
7658	  fixed : @medical.MedicalImage,
7659	  moving : @medical.MedicalImage,
7660	  max_iterations : Int,
7661	) -> @medical.MedicalRegistrationResult {
7662	  @medical.medical_image_registration(fixed, moving, max_iterations)	<-- UNCOVERED
7663	}
    	…

7666	pub fn calculate_medical_image_quality_medical(
7667	  image : @medical.MedicalImage,
7668	) -> @medical.MedicalImageQuality {
7669	  @medical.calculate_medical_image_quality(image)	<-- UNCOVERED
7670	}
    	…

7673	pub fn medical_image_report_to_string_medical(
7674	  image : @medical.MedicalImage,
7675	  lesions : Array[@medical.Lesion],
7676	  quality : @medical.MedicalImageQuality,
7677	) -> String {
7678	  @medical.medical_image_report_to_string(image, lesions, quality)	<-- UNCOVERED
7679	}
    	…

7708	pub fn create_default_remote_sensing_image(
7709	  width : Int,
7710	  height : Int,
7711	  bands : Int,
7712	) -> @remote_sensing.RemoteSensingImage {
7713	  @remote_sensing.create_default_remote_sensing_image(width, height, bands)	<-- UNCOVERED
7714	}
    	…

7717	pub fn calculate_ndvi_rs(
7718	  image : @remote_sensing.RemoteSensingImage,
7719	  red_band : Int,
7720	  nir_band : Int,
7721	) -> Array[Double] {
7722	  @remote_sensing.calculate_ndvi(image, red_band, nir_band)	<-- UNCOVERED
7723	}
    	…

7726	pub fn calculate_ndwi_rs(
7727	  image : @remote_sensing.RemoteSensingImage,
7728	  green_band : Int,
7729	  nir_band : Int,
7730	) -> Array[Double] {
7731	  @remote_sensing.calculate_ndwi(image, green_band, nir_band)	<-- UNCOVERED
7732	}
    	…

7735	pub fn calculate_ndbi_rs(
7736	  image : @remote_sensing.RemoteSensingImage,
7737	  swir_band : Int,
7738	  nir_band : Int,
7739	) -> Array[Double] {
7740	  @remote_sensing.calculate_ndbi(image, swir_band, nir_band)	<-- UNCOVERED
7741	}
    	…

7744	pub fn calculate_all_vegetation_indices_rs(
7745	  image : @remote_sensing.RemoteSensingImage,
7746	) -> @remote_sensing.VegetationIndices {
7747	  @remote_sensing.calculate_all_vegetation_indices(image)	<-- UNCOVERED
7748	}
    	…

7751	pub fn classify_land_cover_simple_rs(
7752	  image : @remote_sensing.RemoteSensingImage,
7753	) -> @remote_sensing.ClassificationResult {
7754	  @remote_sensing.classify_land_cover_simple(image)	<-- UNCOVERED
7755	}
    	…

7758	pub fn detect_changes_simple_rs(
7759	  image1 : @remote_sensing.RemoteSensingImage,
7760	  image2 : @remote_sensing.RemoteSensingImage,
7761	  threshold : Double,
7762	) -> @remote_sensing.ChangeDetectionResult {
7763	  @remote_sensing.detect_changes_simple(image1, image2, threshold)	<-- UNCOVERED
7764	}
    	…

7767	pub fn calculate_texture_features_rs(
7768	  image : @types.Image,
7769	  x : Int,
7770	  y : Int,
7771	  window_size : Int,
7772	) -> @remote_sensing.TextureFeatures {
7773	  @remote_sensing.calculate_texture_features(image, x, y, window_size)	<-- UNCOVERED
7774	}
    	…

7777	pub fn calculate_spectral_signature_rs(
7778	  image : @remote_sensing.RemoteSensingImage,
7779	  x : Int,
7780	  y : Int,
7781	) -> @remote_sensing.SpectralSignature {
7782	  @remote_sensing.calculate_spectral_signature(image, x, y)	<-- UNCOVERED
7783	}
    	…

7786	pub fn calculate_remote_sensing_quality_rs(
7787	  image : @remote_sensing.RemoteSensingImage,
7788	) -> @remote_sensing.RemoteSensingQuality {
7789	  @remote_sensing.calculate_remote_sensing_quality(image)	<-- UNCOVERED
7790	}
    	…

7793	pub fn remote_sensing_report_to_string_rs(
7794	  image : @remote_sensing.RemoteSensingImage,
7795	  classification : @remote_sensing.ClassificationResult,
7796	  changes : @remote_sensing.ChangeDetectionResult,
7797	  quality : @remote_sensing.RemoteSensingQuality,
7798	) -> String {
7799	  @remote_sensing.remote_sensing_report_to_string(	<-- UNCOVERED
7800	    image, classification, changes, quality,
7801	  )
7802	}
    	…

7841	pub fn xor_encrypt_enc(
7842	  img : @types.Image,
7843	  key : Array[Byte],
7844	) -> @encryption.EncryptionResult {
7845	  @encryption.xor_encrypt(img, key)	<-- UNCOVERED
7846	}
    	…

7849	pub fn xor_decrypt_enc(
7850	  encrypted : @encryption.EncryptionResult,
7851	  key : Array[Byte],
7852	) -> @types.Image {
7853	  @encryption.xor_decrypt(encrypted, key)	<-- UNCOVERED
7854	}
    	…

7857	pub fn chaotic_encrypt_enc(
7858	  img : @types.Image,
7859	  params : @encryption.ChaoticParams,
7860	) -> @encryption.EncryptionResult {
7861	  @encryption.chaotic_encrypt(img, params)	<-- UNCOVERED
7862	}
    	…

7865	pub fn arnold_scramble_enc(
7866	  img : @types.Image,
7867	  params : @encryption.ArnoldParams,
7868	) -> @encryption.EncryptionResult {
7869	  @encryption.arnold_scramble(img, params)	<-- UNCOVERED
7870	}
    	…

7873	pub fn embed_watermark_lsb_enc(
7874	  img : @types.Image,
7875	  watermark : Array[Byte],
7876	  strength : Int,
7877	) -> (@types.Image, @encryption.WatermarkInfo) {
7878	  @encryption.embed_watermark_lsb(img, watermark, strength)	<-- UNCOVERED
7879	}
    	…

7882	pub fn extract_watermark_lsb_enc(
7883	  img : @types.Image,
7884	  watermark_length : Int,
7885	  strength : Int,
7886	) -> @encryption.WatermarkExtractionResult {
7887	  @encryption.extract_watermark_lsb(img, watermark_length, strength)	<-- UNCOVERED
7888	}
    	…

7891	pub fn embed_visible_watermark_enc(
7892	  img : @types.Image,
7893	  watermark_text : String,
7894	  x : Int,
7895	  y : Int,
7896	  opacity : Double,
7897	) -> @types.Image {
7898	  @encryption.embed_visible_watermark(img, watermark_text, x, y, opacity)	<-- UNCOVERED
7899	}
    	…

7902	pub fn calculate_image_entropy_enc(img : @types.Image) -> Double {
7903	  @encryption.calculate_image_entropy(img)	<-- UNCOVERED
7904	}
    	…

7907	pub fn calculate_correlation_enc(
7908	  img1 : @types.Image,
7909	  img2 : @types.Image,
7910	) -> Double {
7911	  @encryption.calculate_correlation(img1, img2)	<-- UNCOVERED
7912	}
    	…

7915	pub fn calculate_psnr_enc(
7916	  original : @types.Image,
7917	  encrypted : @types.Image,
7918	) -> Double {
7919	  @encryption.calculate_psnr(original, encrypted)	<-- UNCOVERED
7920	}
    	…

7923	pub fn evaluate_image_encryption_quality_enc(
7924	  original : @types.Image,
7925	  encrypted : @types.Image,
7926	) -> @encryption.ImageEncryptionQuality {
7927	  @encryption.evaluate_image_encryption_quality(original, encrypted)	<-- UNCOVERED
7928	}
    	…

7931	pub fn encryption_report_to_string_enc(
7932	  encryption : @encryption.EncryptionResult,
7933	  watermark : @encryption.WatermarkInfo,
7934	  quality : @encryption.ImageEncryptionQuality,
7935	) -> String {
7936	  @encryption.encryption_report_to_string(encryption, watermark, quality)	<-- UNCOVERED
7937	}
    	…

7987	pub fn extract_color_histogram_cbir(
7988	  img : @types.Image,
7989	  config : @retrieval.ColorHistogramConfig,
7990	) -> @retrieval.CBIRImageFeature {
7991	  @retrieval.extract_color_histogram(img, config)	<-- UNCOVERED
7992	}
    	…

7995	pub fn extract_lbp_feature_cbir(
7996	  img : @types.Image,
7997	  config : @retrieval.LBPConfig,
7998	) -> @retrieval.CBIRImageFeature {
7999	  @retrieval.extract_lbp_feature(img, config)	<-- UNCOVERED
8000	}
    	…

8003	pub fn extract_hog_feature_cbir(
8004	  img : @types.Image,
8005	  config : @retrieval.HOGConfig,
8006	) -> @retrieval.CBIRImageFeature {
8007	  @retrieval.extract_hog_feature(img, config)	<-- UNCOVERED
8008	}
    	…

8011	pub fn cosine_similarity_cbir_api(
8012	  feat1 : Array[Double],
8013	  feat2 : Array[Double],
8014	) -> Double {
8015	  @retrieval.cosine_similarity_cbir(feat1, feat2)	<-- UNCOVERED
8016	}
    	…

8019	pub fn euclidean_distance_cbir_api(
8020	  feat1 : Array[Double],
8021	  feat2 : Array[Double],
8022	) -> Double {
8023	  @retrieval.euclidean_distance_cbir(feat1, feat2)	<-- UNCOVERED
8024	}
    	…

8027	pub fn manhattan_distance_cbir_api(
8028	  feat1 : Array[Double],
8029	  feat2 : Array[Double],
8030	) -> Double {
8031	  @retrieval.manhattan_distance_cbir(feat1, feat2)	<-- UNCOVERED
8032	}
    	…

8035	pub fn histogram_intersection_cbir_api(
8036	  feat1 : Array[Double],
8037	  feat2 : Array[Double],
8038	) -> Double {
8039	  @retrieval.histogram_intersection_cbir(feat1, feat2)	<-- UNCOVERED
8040	}
    	…

8043	pub fn calculate_similarity_cbir(
8044	  feat1 : Array[Double],
8045	  feat2 : Array[Double],
8046	  metric : @retrieval.SimilarityMetric,
8047	) -> Double {
8048	  @retrieval.calculate_similarity(feat1, feat2, metric)	<-- UNCOVERED
8049	}
    	…

8057	pub fn add_to_index_cbir(
8058	  index : @retrieval.CBIRImageIndex,
8059	  feature : @retrieval.CBIRImageFeature,
8060	) -> @retrieval.CBIRImageIndex {
8061	  @retrieval.add_to_index(index, feature)	<-- UNCOVERED
8062	}
    	…

8065	pub fn retrieve_images_cbir(
8066	  query_feature : @retrieval.CBIRImageFeature,
8067	  index : @retrieval.CBIRImageIndex,
8068	  num_results : Int,
8069	  metric : @retrieval.SimilarityMetric,
8070	) -> @retrieval.CBIRRetrievalResultList {
8071	  @retrieval.retrieve_images(query_feature, index, num_results, metric)	<-- UNCOVERED
8072	}
    	…

8075	pub fn extract_combined_features_cbir(
8076	  img : @types.Image,
8077	) -> @retrieval.CBIRImageFeature {
8078	  @retrieval.extract_combined_features(img)	<-- UNCOVERED
8079	}
    	…

8082	pub fn retrieval_report_to_string_cbir(
8083	  results : @retrieval.CBIRRetrievalResultList,
8084	  query_feature : @retrieval.CBIRImageFeature,
8085	) -> String {
8086	  @retrieval.retrieval_report_to_string(results, query_feature)	<-- UNCOVERED
8087	}
    	…

8130	pub fn extract_features_advanced(
8131	  image : Array[Double],
8132	  width : Int,
8133	  height : Int,
8134	  config : @image_retrieval_advanced.ImageRetrievalConfig,
8135	) -> @image_retrieval_advanced.FeatureExtractionResult {
8136	  @image_retrieval_advanced.extract_features(image, width, height, config)	<-- UNCOVERED
8137	}
    	…

8140	pub fn calculate_similarity_advanced(
8141	  features1 : Array[Double],
8142	  features2 : Array[Double],
8143	  metric : @image_retrieval_advanced.SimilarityMetricType,
8144	) -> Double {
8145	  @image_retrieval_advanced.calculate_similarity(features1, features2, metric)	<-- UNCOVERED
8146	}
    	…

8149	pub fn retrieve_images_advanced(
8150	  query_image : Array[Double],
8151	  query_width : Int,
8152	  query_height : Int,
8153	  database_images : Array[Array[Double]],
8154	  config : @image_retrieval_advanced.ImageRetrievalConfig,
8155	) -> @image_retrieval_advanced.RetrievalResult {
8156	  @image_retrieval_advanced.retrieve_images(	<-- UNCOVERED
8157	    query_image, query_width, query_height, database_images, config,
8158	  )
8159	}
    	…

8162	pub fn build_index_advanced(
8163	  features : Array[Array[Double]],
8164	  index_type : @image_retrieval_advanced.IndexStructureType,
8165	) -> @image_retrieval_advanced.IndexConstructionResult {
8166	  @image_retrieval_advanced.build_index(features, index_type)	<-- UNCOVERED
8167	}
    	…

8170	pub fn expand_query_advanced(
8171	  original_query : Array[Double],
8172	  relevant_features : Array[Array[Double]],
8173	  config : @image_retrieval_advanced.ImageRetrievalConfig,
8174	) -> @image_retrieval_advanced.QueryExpansionResult {
8175	  @image_retrieval_advanced.expand_query(	<-- UNCOVERED
8176	    original_query, relevant_features, config,
8177	  )
8178	}
    	…

8181	pub fn relevance_feedback_advanced(
8182	  original_query : Array[Double],
8183	  relevant_indices : Array[Int],
8184	  irrelevant_indices : Array[Int],
8185	  database_features : Array[Array[Double]],
8186	  config : @image_retrieval_advanced.ImageRetrievalConfig,
8187	) -> @image_retrieval_advanced.RelevanceFeedbackResult {
8188	  @image_retrieval_advanced.relevance_feedback(	<-- UNCOVERED
8189	    original_query, relevant_indices, irrelevant_indices, database_features, config,
8190	  )
8191	}
    	…

8194	pub fn image_retrieval_report_to_string_advanced(
8195	  task_type : @image_retrieval_advanced.ImageRetrievalTaskType,
8196	  config : @image_retrieval_advanced.ImageRetrievalConfig,
8197	  result_description : String,
8198	) -> String {
8199	  @image_retrieval_advanced.image_retrieval_report_to_string(	<-- UNCOVERED
8200	    task_type, config, result_description,
8201	  )
8202	}
    	…

8232	pub fn nearest_neighbor_interpolate_sr(
8233	  img : @types.Image,
8234	  scale_factor : Double,
8235	) -> @types.Image {
8236	  @superres.nearest_neighbor_interpolate(img, scale_factor)	<-- UNCOVERED
8237	}
    	…

8240	pub fn bilinear_interpolate_sr(
8241	  img : @types.Image,
8242	  scale_factor : Double,
8243	) -> @types.Image {
8244	  @superres.bilinear_interpolate(img, scale_factor)	<-- UNCOVERED
8245	}
    	…

8248	pub fn bicubic_interpolate_sr(
8249	  img : @types.Image,
8250	  scale_factor : Double,
8251	) -> @types.Image {
8252	  @superres.bicubic_interpolate(img, scale_factor)	<-- UNCOVERED
8253	}
    	…

8256	pub fn sharpen_image_sr(img : @types.Image, strength : Double) -> @types.Image {
8257	  @superres.sharpen_image(img, strength)	<-- UNCOVERED
8258	}
    	…

8261	pub fn denoise_image_sr(img : @types.Image, strength : Double) -> @types.Image {
8262	  @superres.denoise_image(img, strength)	<-- UNCOVERED
8263	}
    	…

8266	pub fn super_resolve_sr(
8267	  img : @types.Image,
8268	  config : @superres.InterpolationConfig,
8269	) -> @superres.SuperResolutionResult {
8270	  @superres.super_resolve(img, config)	<-- UNCOVERED
8271	}
    	…

8274	pub fn calculate_psnr_sr_api(
8275	  original : @types.Image,
8276	  processed : @types.Image,
8277	) -> Double {
8278	  @superres.calculate_psnr_sr(original, processed)	<-- UNCOVERED
8279	}
    	…

8282	pub fn calculate_ssim_sr_api(
8283	  original : @types.Image,
8284	  processed : @types.Image,
8285	) -> Double {
8286	  @superres.calculate_ssim_sr(original, processed)	<-- UNCOVERED
8287	}
    	…

8290	pub fn calculate_image_quality_metrics_sr(
8291	  original : @types.Image,
8292	  processed : @types.Image,
8293	) -> @superres.ImageQualityMetrics {
8294	  @superres.calculate_image_quality_metrics(original, processed)	<-- UNCOVERED
8295	}
    	…

8298	pub fn enhance_image_sr(
8299	  img : @types.Image,
8300	  config : @superres.EnhancementConfig,
8301	) -> @types.Image {
8302	  @superres.enhance_image(img, config)	<-- UNCOVERED
8303	}
    	…

8306	pub fn super_resolution_report_to_string_sr(
8307	  result : @superres.SuperResolutionResult,
8308	  metrics : @superres.ImageQualityMetrics,
8309	) -> String {
8310	  @superres.super_resolution_report_to_string(result, metrics)	<-- UNCOVERED
8311	}
    	…

8339	pub fn calculate_color_statistics_st(
8340	  img : @types.Image,
8341	) -> @style_transfer.ColorStatistics {
8342	  @style_transfer.calculate_color_statistics(img)	<-- UNCOVERED
8343	}
    	…

8346	pub fn color_transfer_st(
8347	  content : @types.Image,
8348	  style : @types.Image,
8349	  strength : Double,
8350	) -> @types.Image {
8351	  @style_transfer.color_transfer(content, style, strength)	<-- UNCOVERED
8352	}
    	…

8355	pub fn calculate_texture_features_st(
8356	  img : @types.Image,
8357	) -> @style_transfer.StyleTransferTextureFeatures {
8358	  @style_transfer.calculate_texture_features(img)	<-- UNCOVERED
8359	}
    	…

8362	pub fn cartoonize_st(img : @types.Image, strength : Double) -> @types.Image {
8363	  @style_transfer.cartoonize(img, strength)	<-- UNCOVERED
8364	}
    	…

8367	pub fn oil_painting_st(
8368	  img : @types.Image,
8369	  radius : Int,
8370	  intensity_levels : Int,
8371	) -> @types.Image {
8372	  @style_transfer.oil_painting(img, radius, intensity_levels)	<-- UNCOVERED
8373	}
    	…

8376	pub fn sketch_effect_st(img : @types.Image, strength : Double) -> @types.Image {
8377	  @style_transfer.sketch_effect(img, strength)	<-- UNCOVERED
8378	}
    	…

8381	pub fn style_transfer_st(
8382	  content : @types.Image,
8383	  style : @types.Image,
8384	  config : @style_transfer.StyleTransferConfig,
8385	) -> @style_transfer.StyleTransferResult {
8386	  @style_transfer.style_transfer(content, style, config)	<-- UNCOVERED
8387	}
    	…

8390	pub fn style_transfer_report_to_string_st(
8391	  result : @style_transfer.StyleTransferResult,
8392	  content_stats : @style_transfer.ColorStatistics,
8393	  style_stats : @style_transfer.ColorStatistics,
8394	  texture : @style_transfer.StyleTransferTextureFeatures,
8395	) -> String {
8396	  @style_transfer.style_transfer_report_to_string(	<-- UNCOVERED
8397	    result, content_stats, style_stats, texture,
8398	  )
8399	}
    	…

8430	pub fn create_rect_mask_ip(
8431	  width : Int,
8432	  height : Int,
8433	  x : Int,
8434	  y : Int,
8435	  rect_width : Int,
8436	  rect_height : Int,
8437	) -> @types.Image {
8438	  @inpainting.create_rect_mask(width, height, x, y, rect_width, rect_height)	<-- UNCOVERED
8439	}
    	…

8442	pub fn inpaint_simple_ip(
8443	  img : @types.Image,
8444	  mask : @types.Image,
8445	  radius : Int,
8446	) -> @types.Image {
8447	  @inpainting.inpaint_simple(img, mask, radius)	<-- UNCOVERED
8448	}
    	…

8451	pub fn inpaint_diffusion_ip(
8452	  img : @types.Image,
8453	  mask : @types.Image,
8454	  radius : Int,
8455	) -> @types.Image {
8456	  @inpainting.inpaint_diffusion(img, mask, radius)	<-- UNCOVERED
8457	}
    	…

8460	pub fn inpaint_ip(
8461	  img : @types.Image,
8462	  mask : @types.Image,
8463	  config : @inpainting.InpaintingConfig,
8464	) -> @inpainting.InpaintingResult {
8465	  @inpainting.inpaint(img, mask, config)	<-- UNCOVERED
8466	}
    	…

8469	pub fn dehaze_dark_channel_ip(
8470	  img : @types.Image,
8471	  omega : Double,
8472	  t0 : Double,
8473	) -> @inpainting.DehazeResult {
8474	  @inpainting.dehaze_dark_channel(img, omega, t0)	<-- UNCOVERED
8475	}
    	…

8478	pub fn dehaze_clahe_ip(
8479	  img : @types.Image,
8480	  clip_limit : Double,
8481	  tile_size : Int,
8482	) -> @types.Image {
8483	  @inpainting.dehaze_clahe(img, clip_limit, tile_size)	<-- UNCOVERED
8484	}
    	…

8487	pub fn dehaze_ip(
8488	  img : @types.Image,
8489	  algorithm : @inpainting.DehazeAlgorithm,
8490	) -> @inpainting.DehazeResult {
8491	  @inpainting.dehaze(img, algorithm)	<-- UNCOVERED
8492	}
    	…

8495	pub fn derain_morphological_ip(img : @types.Image) -> @inpainting.DerainResult {
8496	  @inpainting.derain_morphological(img)	<-- UNCOVERED
8497	}
    	…

8500	pub fn derain_ip(
8501	  img : @types.Image,
8502	  algorithm : @inpainting.DerainAlgorithm,
8503	) -> @inpainting.DerainResult {
8504	  @inpainting.derain(img, algorithm)	<-- UNCOVERED
8505	}
    	…

8508	pub fn inpainting_report_to_string_ip(
8509	  result : @inpainting.InpaintingResult,
8510	) -> String {
8511	  @inpainting.inpainting_report_to_string(result)	<-- UNCOVERED
8512	}
    	…

8548	pub fn fuse_weighted_average_fe(
8549	  images : Array[@types.Image],
8550	  weights : Array[Double],
8551	) -> @types.Image {
8552	  @fusion_enhanced.fuse_weighted_average(images, weights)	<-- UNCOVERED
8553	}
    	…

8556	pub fn fuse_maximum_fe(images : Array[@types.Image]) -> @types.Image {
8557	  @fusion_enhanced.fuse_maximum(images)	<-- UNCOVERED
8558	}
    	…

8561	pub fn fuse_minimum_fe(images : Array[@types.Image]) -> @types.Image {
8562	  @fusion_enhanced.fuse_minimum(images)	<-- UNCOVERED
8563	}
    	…

8566	pub fn fuse_simple_mean_fe(images : Array[@types.Image]) -> @types.Image {
8567	  @fusion_enhanced.fuse_simple_mean(images)	<-- UNCOVERED
8568	}
    	…

8571	pub fn fuse_laplacian_pyramid_fe(
8572	  images : Array[@types.Image],
8573	  levels : Int,
8574	) -> @types.Image {
8575	  @fusion_enhanced.fuse_laplacian_pyramid(images, levels)	<-- UNCOVERED
8576	}
    	…

8579	pub fn fuse_images_fe(
8580	  images : Array[@types.Image],
8581	  config : @fusion_enhanced.FusionConfig,
8582	) -> @fusion_enhanced.FusionResult {
8583	  @fusion_enhanced.fuse_images(images, config)	<-- UNCOVERED
8584	}
    	…

8587	pub fn register_phase_correlation_fe(
8588	  reference : @types.Image,
8589	  target : @types.Image,
8590	) -> @fusion_enhanced.RegistrationResult {
8591	  @fusion_enhanced.register_phase_correlation(reference, target)	<-- UNCOVERED
8592	}
    	…

8595	pub fn register_images_fe(
8596	  reference : @types.Image,
8597	  target : @types.Image,
8598	  config : @fusion_enhanced.RegistrationConfig,
8599	) -> @fusion_enhanced.RegistrationResult {
8600	  @fusion_enhanced.register_images(reference, target, config)	<-- UNCOVERED
8601	}
    	…

8604	pub fn apply_translation_fe(
8605	  img : @types.Image,
8606	  dx : Int,
8607	  dy : Int,
8608	) -> @types.Image {
8609	  @fusion_enhanced.apply_translation(img, dx, dy)	<-- UNCOVERED
8610	}
    	…

8613	pub fn calculate_fusion_quality_fe(fused : @types.Image) -> Double {
8614	  @fusion_enhanced.calculate_fusion_quality(fused)	<-- UNCOVERED
8615	}
    	…

8618	pub fn fusion_report_to_string_fe(
8619	  result : @fusion_enhanced.FusionResult,
8620	  quality : Double,
8621	) -> String {
8622	  @fusion_enhanced.fusion_report_to_string(result, quality)	<-- UNCOVERED
8623	}
    	…

8666	pub fn analyze_noise_pattern_fs(
8667	  img : @types.Image,
8668	) -> @forensics.NoiseAnalysisResult {
8669	  @forensics.analyze_noise_pattern(img)	<-- UNCOVERED
8670	}
    	…

8673	pub fn analyze_frequency_domain_fs(
8674	  img : @types.Image,
8675	) -> @forensics.FrequencyAnalysisResult {
8676	  @forensics.analyze_frequency_domain(img)	<-- UNCOVERED
8677	}
    	…

8680	pub fn analyze_color_distribution_fs(
8681	  img : @types.Image,
8682	) -> @forensics.ColorAnalysisResult {
8683	  @forensics.analyze_color_distribution(img)	<-- UNCOVERED
8684	}
    	…

8687	pub fn analyze_texture_fs(
8688	  img : @types.Image,
8689	) -> @forensics.TextureAnalysisResult {
8690	  @forensics.analyze_texture(img)	<-- UNCOVERED
8691	}
    	…

8694	pub fn analyze_edges_fs(img : @types.Image) -> @forensics.EdgeAnalysisResult {
8695	  @forensics.analyze_edges(img)	<-- UNCOVERED
8696	}
    	…

8699	pub fn analyze_compression_artifacts_fs(
8700	  img : @types.Image,
8701	) -> @forensics.CompressionAnalysisResult {
8702	  @forensics.analyze_compression_artifacts(img)	<-- UNCOVERED
8703	}
    	…

8706	pub fn analyze_details_fs(
8707	  img : @types.Image,
8708	) -> @forensics.DetailAnalysisResult {
8709	  @forensics.analyze_details(img)	<-- UNCOVERED
8710	}
    	…

8713	pub fn verify_image_source_fs(
8714	  img : @types.Image,
8715	  config : @forensics.SourceVerificationConfig,
8716	) -> @forensics.SourceVerificationResult {
8717	  @forensics.verify_image_source(img, config)	<-- UNCOVERED
8718	}
    	…

8721	pub fn source_verification_report_to_string_fs(
8722	  result : @forensics.SourceVerificationResult,
8723	) -> String {
8724	  @forensics.source_verification_report_to_string(result)	<-- UNCOVERED
8725	}
    	…

8756	pub fn detect_ui_elements_fs(
8757	  img : @types.Image,
8758	) -> @forensics.UIDetectionResult {
8759	  @forensics.detect_ui_elements(img)	<-- UNCOVERED
8760	}
    	…

8763	pub fn detect_text_regions_fs(
8764	  img : @types.Image,
8765	) -> @forensics.TextDetectionResult {
8766	  @forensics.detect_text_regions(img)	<-- UNCOVERED
8767	}
    	…

8770	pub fn analyze_geometry_fs(
8771	  img : @types.Image,
8772	) -> @forensics.GeometryAnalysisResult {
8773	  @forensics.analyze_geometry(img)	<-- UNCOVERED
8774	}
    	…

8777	pub fn analyze_color_quantization_fs(
8778	  img : @types.Image,
8779	) -> @forensics.ColorQuantizationResult {
8780	  @forensics.analyze_color_quantization(img)	<-- UNCOVERED
8781	}
    	…

8784	pub fn verify_image_source_enhanced_fs(
8785	  img : @types.Image,
8786	  config : @forensics.EnhancedVerificationConfig,
8787	) -> @forensics.EnhancedVerificationResult {
8788	  @forensics.verify_image_source_enhanced(img, config)	<-- UNCOVERED
8789	}
    	…

8792	pub fn enhanced_verification_report_to_string_fs(
8793	  result : @forensics.EnhancedVerificationResult,
8794	) -> String {
8795	  @forensics.enhanced_verification_report_to_string(result)	<-- UNCOVERED
8796	}
    	…

8828	pub fn analyze_ela_fe(
8829	  img : @types.Image,
8830	  quality_level : Int,
8831	) -> @forensics_enhanced.ElaAnalysisResult raise @types.LoadError {
8832	  @forensics_enhanced.analyze_ela(img, quality_level)	<-- UNCOVERED
8833	}
    	…

8836	pub fn analyze_noise_residual_fe(
8837	  img : @types.Image,
8838	) -> @forensics_enhanced.NoiseResidualResult {
8839	  @forensics_enhanced.analyze_noise_residual(img)	<-- UNCOVERED
8840	}
    	…

8843	pub fn analyze_dct_coefficients_fe(
8844	  img : @types.Image,
8845	) -> @forensics_enhanced.DctCoefficientResult {
8846	  @forensics_enhanced.analyze_dct_coefficients(img)	<-- UNCOVERED
8847	}
    	…

8850	pub fn detect_clone_regions_fe(
8851	  img : @types.Image,
8852	  block_size : Int,
8853	  threshold : Double,
8854	) -> @forensics_enhanced.CloneDetectionResult raise @types.LoadError {
8855	  @forensics_enhanced.detect_clone_regions(img, block_size, threshold)	<-- UNCOVERED
8856	}
    	…

8859	pub fn analyze_illumination_consistency_fe(
8860	  img : @types.Image,
8861	) -> @forensics_enhanced.IlluminationConsistencyResult {
8862	  @forensics_enhanced.analyze_illumination_consistency(img)	<-- UNCOVERED
8863	}
    	…

8866	pub fn analyze_metadata_forensics_fe(
8867	  img : @types.Image,
8868	) -> @forensics_enhanced.MetadataForensicsResult {
8869	  @forensics_enhanced.analyze_metadata_forensics(img)	<-- UNCOVERED
8870	}
    	…

8873	pub fn analyze_jpeg_ghost_fe(
8874	  img : @types.Image,
8875	) -> @forensics_enhanced.JpegGhostResult {
8876	  @forensics_enhanced.analyze_jpeg_ghost(img)	<-- UNCOVERED
8877	}
    	…

8880	pub fn analyze_cfa_pattern_fe(
8881	  img : @types.Image,
8882	) -> @forensics_enhanced.CfaAnalysisResult {
8883	  @forensics_enhanced.analyze_cfa_pattern(img)	<-- UNCOVERED
8884	}
    	…

8887	pub fn detect_tamper_comprehensive_fe(
8888	  img : @types.Image,
8889	  ela_quality : Int,
8890	) -> @forensics_enhanced.TamperDetectionResult raise @types.LoadError {
8891	  @forensics_enhanced.detect_tamper_comprehensive(img, ela_quality)	<-- UNCOVERED
8892	}
    	…

8923	pub fn run_length_encode_cp(data : Bytes) -> Bytes {
8924	  @compression.run_length_encode(data)	<-- UNCOVERED
8925	}
    	…

8928	pub fn run_length_decode_cp(data : Bytes) -> Bytes {
8929	  @compression.run_length_decode(data)	<-- UNCOVERED
8930	}
    	…

8933	pub fn calculate_image_entropy_cp(img : @types.Image) -> Double {
8934	  @compression.calculate_image_entropy(img)	<-- UNCOVERED
8935	}
    	…

8938	pub fn quantize_image_cp(img : @types.Image, levels : Int) -> @types.Image {
8939	  @compression.quantize_image(img, levels)	<-- UNCOVERED
8940	}
    	…

8943	pub fn predictive_encode_cp(img : @types.Image) -> Bytes {
8944	  @compression.predictive_encode(img)	<-- UNCOVERED
8945	}
    	…

8948	pub fn calculate_compression_ratio_cp(
8949	  original_size : Int,
8950	  compressed_size : Int,
8951	) -> Double {
8952	  @compression.calculate_compression_ratio(original_size, compressed_size)	<-- UNCOVERED
8953	}
    	…

8956	pub fn calculate_bits_per_pixel_cp(
8957	  compressed_size : Int,
8958	  width : Int,
8959	  height : Int,
8960	) -> Double {
8961	  @compression.calculate_bits_per_pixel(compressed_size, width, height)	<-- UNCOVERED
8962	}
    	…

8965	pub fn calculate_compression_quality_cp(
8966	  original : @types.Image,
8967	  compressed : @types.Image,
8968	  original_size : Int,
8969	  compressed_size : Int,
8970	) -> @compression.CompressionQualityMetrics {
8971	  @compression.calculate_compression_quality(	<-- UNCOVERED
8972	    original, compressed, original_size, compressed_size,
8973	  )
8974	}
    	…

8977	pub fn compress_jpeg_simple_cp(
8978	  img : @types.Image,
8979	  quality : Double,
8980	) -> @compression.CompressionResult {
8981	  @compression.compress_jpeg_simple(img, quality)	<-- UNCOVERED
8982	}
    	…

8985	pub fn compress_png_simple_cp(
8986	  img : @types.Image,
8987	) -> @compression.CompressionResult {
8988	  @compression.compress_png_simple(img)	<-- UNCOVERED
8989	}
    	…

8992	pub fn compress_image_cp(
8993	  img : @types.Image,
8994	  config : @compression.CompressionConfig,
8995	) -> @compression.CompressionResult {
8996	  @compression.compress_image(img, config)	<-- UNCOVERED
8997	}
    	…

9000	pub fn compression_report_to_string_cp(
9001	  result : @compression.CompressionResult,
9002	  metrics : @compression.CompressionQualityMetrics,
9003	) -> String {
9004	  @compression.compression_report_to_string(result, metrics)	<-- UNCOVERED
9005	}
    	…

9033	pub fn mean_filter_denoise_dn(
9034	  img : @types.Image,
9035	  kernel_size : Int,
9036	) -> @types.Image {
9037	  @denoise.mean_filter_denoise(img, kernel_size)	<-- UNCOVERED
9038	}
    	…

9041	pub fn median_filter_denoise_dn(
9042	  img : @types.Image,
9043	  kernel_size : Int,
9044	) -> @types.Image {
9045	  @denoise.median_filter_denoise(img, kernel_size)	<-- UNCOVERED
9046	}
    	…

9049	pub fn gaussian_filter_denoise_dn(
9050	  img : @types.Image,
9051	  kernel_size : Int,
9052	  sigma : Double,
9053	) -> @types.Image {
9054	  @denoise.gaussian_filter_denoise(img, kernel_size, sigma)	<-- UNCOVERED
9055	}
    	…

9058	pub fn bilateral_filter_denoise_dn(
9059	  img : @types.Image,
9060	  kernel_size : Int,
9061	  sigma_color : Double,
9062	  sigma_space : Double,
9063	) -> @types.Image {
9064	  @denoise.bilateral_filter_denoise(img, kernel_size, sigma_color, sigma_space)	<-- UNCOVERED
9065	}
    	…

9068	pub fn calculate_noise_level_dn(img : @types.Image) -> Double {
9069	  @denoise.calculate_noise_level(img)	<-- UNCOVERED
9070	}
    	…

9073	pub fn histogram_equalization_enhance_dn(img : @types.Image) -> @types.Image {
9074	  @denoise.histogram_equalization_enhance(img)	<-- UNCOVERED
9075	}
    	…

9078	pub fn gamma_correction_enhance_dn(
9079	  img : @types.Image,
9080	  gamma : Double,
9081	) -> @types.Image {
9082	  @denoise.gamma_correction_enhance(img, gamma)	<-- UNCOVERED
9083	}
    	…

9086	pub fn unsharp_mask_enhance_dn(
9087	  img : @types.Image,
9088	  amount : Double,
9089	  radius : Int,
9090	) -> @types.Image {
9091	  @denoise.unsharp_mask_enhance(img, amount, radius)	<-- UNCOVERED
9092	}
    	…

9095	pub fn calculate_brightness_dn(img : @types.Image) -> Double {
9096	  @denoise.calculate_brightness(img)	<-- UNCOVERED
9097	}
    	…

9100	pub fn calculate_contrast_dn(img : @types.Image) -> Double {
9101	  @denoise.calculate_contrast(img)	<-- UNCOVERED
9102	}
    	…

9105	pub fn calculate_sharpness_dn(img : @types.Image) -> Double {
9106	  @denoise.calculate_sharpness(img)	<-- UNCOVERED
9107	}
    	…

9110	pub fn denoise_image_dn(
9111	  img : @types.Image,
9112	  config : @denoise.DenoiseConfig,
9113	) -> @denoise.DenoiseResult {
9114	  @denoise.denoise_image(img, config)	<-- UNCOVERED
9115	}
    	…

9118	pub fn enhance_image_advanced_dn(
9119	  img : @types.Image,
9120	  algorithm : @denoise.EnhancementAlgorithm,
9121	) -> @denoise.EnhancementResult {
9122	  @denoise.enhance_image_advanced(img, algorithm)	<-- UNCOVERED
9123	}
    	…

9126	pub fn denoise_report_to_string_dn(result : @denoise.DenoiseResult) -> String {
9127	  @denoise.denoise_report_to_string(result)	<-- UNCOVERED
9128	}
    	…

9159	pub fn phase_correlation_registration_ra(
9160	  img1 : @types.Image,
9161	  img2 : @types.Image,
9162	) -> @registration_advanced.RegistrationResultAdvanced {
9163	  @registration_advanced.phase_correlation_registration(img1, img2)	<-- UNCOVERED
9164	}
    	…

9167	pub fn apply_translation_ra(
9168	  img : @types.Image,
9169	  dx : Int,
9170	  dy : Int,
9171	) -> @types.Image {
9172	  @registration_advanced.apply_translation(img, dx, dy)	<-- UNCOVERED
9173	}
    	…

9176	pub fn normalized_cross_correlation_ra(
9177	  img1 : @types.Image,
9178	  img2 : @types.Image,
9179	) -> Double {
9180	  @registration_advanced.normalized_cross_correlation(img1, img2)	<-- UNCOVERED
9181	}
    	…

9184	pub fn calculate_mutual_information_ra(
9185	  img1 : @types.Image,
9186	  img2 : @types.Image,
9187	) -> Double {
9188	  @registration_advanced.calculate_mutual_information(img1, img2)	<-- UNCOVERED
9189	}
    	…

9192	pub fn detect_harris_corners_ra(
9193	  img : @types.Image,
9194	  threshold : Double,
9195	) -> Array[@registration_advanced.FeaturePoint] {
9196	  @registration_advanced.detect_harris_corners(img, threshold)	<-- UNCOVERED
9197	}
    	…

9200	pub fn match_feature_points_ra(
9201	  points1 : Array[@registration_advanced.FeaturePoint],
9202	  points2 : Array[@registration_advanced.FeaturePoint],
9203	  max_distance : Double,
9204	) -> Array[@registration_advanced.FeatureMatch] {
9205	  @registration_advanced.match_feature_points(points1, points2, max_distance)	<-- UNCOVERED
9206	}
    	…

9209	pub fn estimate_affine_transform_ra(
9210	  matches : Array[@registration_advanced.FeatureMatch],
9211	) -> @registration_advanced.TransformMatrix {
9212	  @registration_advanced.estimate_affine_transform(matches)	<-- UNCOVERED
9213	}
    	…

9216	pub fn apply_affine_transform_ra(
9217	  img : @types.Image,
9218	  matrix : @registration_advanced.TransformMatrix,
9219	) -> @types.Image {
9220	  @registration_advanced.apply_affine_transform(img, matrix)	<-- UNCOVERED
9221	}
    	…

9224	pub fn feature_based_registration_ra(
9225	  img1 : @types.Image,
9226	  img2 : @types.Image,
9227	  config : @registration_advanced.RegistrationConfigAdvanced,
9228	) -> @registration_advanced.RegistrationResultAdvanced {
9229	  @registration_advanced.feature_based_registration(img1, img2, config)	<-- UNCOVERED
9230	}
    	…

9233	pub fn register_images_advanced_ra(
9234	  img1 : @types.Image,
9235	  img2 : @types.Image,
9236	  config : @registration_advanced.RegistrationConfigAdvanced,
9237	) -> @registration_advanced.RegistrationResultAdvanced {
9238	  @registration_advanced.register_images_advanced(img1, img2, config)	<-- UNCOVERED
9239	}
    	…

9242	pub fn registration_report_to_string_advanced_ra(
9243	  result : @registration_advanced.RegistrationResultAdvanced,
9244	) -> String {
9245	  @registration_advanced.registration_report_to_string_advanced(result)	<-- UNCOVERED
9246	}
    	…

9280	pub fn detect_features_slam_sl(
9281	  img : @types.Image,
9282	  max_features : Int,
9283	  threshold : Double,
9284	) -> Array[Array[Double]] {
9285	  @slam.detect_features_slam(img, max_features, threshold)	<-- UNCOVERED
9286	}
    	…

9289	pub fn track_features_slam_sl(
9290	  prev_img : @types.Image,
9291	  curr_img : @types.Image,
9292	  prev_features : Array[Array[Double]],
9293	  search_radius : Int,
9294	) -> @slam.FeatureTrackingResult {
9295	  @slam.track_features_slam(prev_img, curr_img, prev_features, search_radius)	<-- UNCOVERED
9296	}
    	…

9299	pub fn estimate_camera_pose_sl(
9300	  tracking_result : @slam.FeatureTrackingResult,
9301	  config : @slam.SLAMConfig,
9302	  prev_pose : @slam.CameraPose,
9303	) -> @slam.CameraPose {
9304	  @slam.estimate_camera_pose(tracking_result, config, prev_pose)	<-- UNCOVERED
9305	}
    	…

9308	pub fn triangulate_points_sl(
9309	  features : Array[Array[Double]],
9310	  pose1 : @slam.CameraPose,
9311	  pose2 : @slam.CameraPose,
9312	  config : @slam.SLAMConfig,
9313	) -> Array[@slam.MapPoint] {
9314	  @slam.triangulate_points(features, pose1, pose2, config)	<-- UNCOVERED
9315	}
    	…

9318	pub fn detect_loop_closure_sl(
9319	  current_features : Array[Array[Double]],
9320	  keyframes : Array[@slam.KeyFrame],
9321	  threshold : Double,
9322	) -> @slam.LoopClosureResult {
9323	  @slam.detect_loop_closure(current_features, keyframes, threshold)	<-- UNCOVERED
9324	}
    	…

9327	pub fn initialize_slam_sl(config : @slam.SLAMConfig) -> @slam.SLAMState {
9328	  @slam.initialize_slam(config)	<-- UNCOVERED
9329	}
    	…

9332	pub fn process_frame_slam_sl(
9333	  state : @slam.SLAMState,
9334	  img : @types.Image,
9335	  config : @slam.SLAMConfig,
9336	) -> @slam.SLAMState {
9337	  @slam.process_frame_slam(state, img, config)	<-- UNCOVERED
9338	}
    	…

9341	pub fn slam_report_to_string_sl(state : @slam.SLAMState) -> String {
9342	  @slam.slam_report_to_string(state)	<-- UNCOVERED
9343	}
    	…

9398	pub fn detect_keypoints_pe(
9399	  img : @types.Image,
9400	  config : @pose_estimation.PoseEstimationConfig,
9401	) -> Array[@pose_estimation.HumanPose] {
9402	  @pose_estimation.detect_keypoints(img, config)	<-- UNCOVERED
9403	}
    	…

9406	pub fn extract_skeleton_pe(
9407	  pose : @pose_estimation.HumanPose,
9408	) -> Array[@pose_estimation.SkeletonConnection] {
9409	  @pose_estimation.extract_skeleton(pose)	<-- UNCOVERED
9410	}
    	…

9413	pub fn calculate_joint_angle_pe(
9414	  pose : @pose_estimation.HumanPose,
9415	  joint1 : Int,
9416	  joint_center : Int,
9417	  joint2 : Int,
9418	) -> Double {
9419	  @pose_estimation.calculate_joint_angle(pose, joint1, joint_center, joint2)	<-- UNCOVERED
9420	}
    	…

9423	pub fn recognize_action_pe(
9424	  poses : Array[@pose_estimation.HumanPose],
9425	  config : @pose_estimation.ActionRecognitionConfig,
9426	) -> @pose_estimation.ActionRecognitionResult {
9427	  @pose_estimation.recognize_action(poses, config)	<-- UNCOVERED
9428	}
    	…

9431	pub fn recognize_gesture_pe(
9432	  pose : @pose_estimation.HumanPose,
9433	) -> @pose_estimation.GestureRecognitionResult {
9434	  @pose_estimation.recognize_gesture(pose)	<-- UNCOVERED
9435	}
    	…

9438	pub fn visualize_pose_pe(
9439	  img : @types.Image,
9440	  pose : @pose_estimation.HumanPose,
9441	) -> @types.Image {
9442	  @pose_estimation.visualize_pose(img, pose)	<-- UNCOVERED
9443	}
    	…

9446	pub fn pose_estimation_report_to_string_pe(
9447	  poses : Array[@pose_estimation.HumanPose],
9448	  action_result : @pose_estimation.ActionRecognitionResult,
9449	) -> String {
9450	  @pose_estimation.pose_estimation_report_to_string(poses, action_result)	<-- UNCOVERED
9451	}
    	…

9494	pub fn create_face_database_fr(
9495	  algorithm : @face_recognition.FaceRecognitionAlgorithm,
9496	  threshold : Double,
9497	) -> @face_recognition.FaceDatabase {
9498	  @face_recognition.create_face_database(algorithm, threshold)	<-- UNCOVERED
9499	}
    	…

9502	pub fn detect_faces_fr(
9503	  img : @types.Image,
9504	  config : @face_recognition.FaceRecognitionConfig,
9505	) -> Array[@face_recognition.FaceDetectionResult] {
9506	  @face_recognition.detect_faces(img, config)	<-- UNCOVERED
9507	}
    	…

9510	pub fn extract_face_feature_fr(
9511	  img : @types.Image,
9512	  face : @face_recognition.FaceDetectionResult,
9513	  config : @face_recognition.FaceRecognitionConfig,
9514	) -> @face_recognition.FaceFeature {
9515	  @face_recognition.extract_face_feature(img, face, config)	<-- UNCOVERED
9516	}
    	…

9519	pub fn calculate_cosine_similarity_fr(
9520	  feat1 : @face_recognition.FaceFeature,
9521	  feat2 : @face_recognition.FaceFeature,
9522	) -> Double {
9523	  @face_recognition.calculate_cosine_similarity(feat1, feat2)	<-- UNCOVERED
9524	}
    	…

9527	pub fn add_face_to_database_fr(
9528	  database : @face_recognition.FaceDatabase,
9529	  feature : @face_recognition.FaceFeature,
9530	  identity : String,
9531	) -> @face_recognition.FaceDatabase {
9532	  @face_recognition.add_face_to_database(database, feature, identity)	<-- UNCOVERED
9533	}
    	…

9536	pub fn recognize_face_in_database_fr(
9537	  database : @face_recognition.FaceDatabase,
9538	  feature : @face_recognition.FaceFeature,
9539	  bounding_box : Array[Double],
9540	) -> @face_recognition.FaceRecognitionResult {
9541	  @face_recognition.recognize_face_in_database(database, feature, bounding_box)	<-- UNCOVERED
9542	}
    	…

9545	pub fn recognize_expression_fr(
9546	  img : @types.Image,
9547	  face : @face_recognition.FaceDetectionResult,
9548	) -> @face_recognition.ExpressionRecognitionResult {
9549	  @face_recognition.recognize_expression(img, face)	<-- UNCOVERED
9550	}
    	…

9553	pub fn estimate_age_gender_fr(
9554	  img : @types.Image,
9555	  face : @face_recognition.FaceDetectionResult,
9556	) -> @face_recognition.AgeGenderEstimationResult {
9557	  @face_recognition.estimate_age_gender(img, face)	<-- UNCOVERED
9558	}
    	…

9561	pub fn evaluate_face_quality_fr(
9562	  img : @types.Image,
9563	  face : @face_recognition.FaceDetectionResult,
9564	) -> Double {
9565	  @face_recognition.evaluate_face_quality(img, face)	<-- UNCOVERED
9566	}
    	…

9569	pub fn analyze_face_attributes_fr(
9570	  img : @types.Image,
9571	  face : @face_recognition.FaceDetectionResult,
9572	  database : @face_recognition.FaceDatabase,
9573	  config : @face_recognition.FaceRecognitionConfig,
9574	) -> @face_recognition.FaceAttributeAnalysis {
9575	  @face_recognition.analyze_face_attributes(img, face, database, config)	<-- UNCOVERED
9576	}
    	…

9579	pub fn face_recognition_report_to_string_fr(
9580	  analysis : @face_recognition.FaceAttributeAnalysis,
9581	) -> String {
9582	  @face_recognition.face_recognition_report_to_string(analysis)	<-- UNCOVERED
9583	}
    	…

9622	pub fn generate_random_latent_ig(
9623	  dimension : Int,
9624	  seed : Int,
9625	) -> @image_generation.LatentVector {
9626	  @image_generation.generate_random_latent(dimension, seed)	<-- UNCOVERED
9627	}
    	…

9630	pub fn generate_from_latent_ig(
9631	  latent : @image_generation.LatentVector,
9632	  config : @image_generation.GenerationConfig,
9633	) -> @image_generation.GenerationResult {
9634	  @image_generation.generate_from_latent(latent, config)	<-- UNCOVERED
9635	}
    	…

9638	pub fn generate_random_image_ig(
9639	  config : @image_generation.GenerationConfig,
9640	) -> @image_generation.GenerationResult {
9641	  @image_generation.generate_random_image(config)	<-- UNCOVERED
9642	}
    	…

9645	pub fn inpaint_image_ig(
9646	  img : @types.Image,
9647	  mask : @types.Image,
9648	  config : @image_generation.EditingConfig,
9649	) -> @image_generation.EditingResult {
9650	  @image_generation.inpaint_image(img, mask, config)	<-- UNCOVERED
9651	}
    	…

9654	pub fn outpaint_image_ig(
9655	  img : @types.Image,
9656	  new_width : Int,
9657	  new_height : Int,
9658	  config : @image_generation.EditingConfig,
9659	) -> @image_generation.EditingResult {
9660	  @image_generation.outpaint_image(img, new_width, new_height, config)	<-- UNCOVERED
9661	}
    	…

9664	pub fn colorize_image_ig(
9665	  img : @types.Image,
9666	  config : @image_generation.EditingConfig,
9667	) -> @image_generation.EditingResult {
9668	  @image_generation.colorize_image(img, config)	<-- UNCOVERED
9669	}
    	…

9672	pub fn compose_images_ig(
9673	  background : @types.Image,
9674	  foreground : @types.Image,
9675	  mask : @types.Image,
9676	  config : @image_generation.CompositionConfig,
9677	) -> @types.Image {
9678	  @image_generation.compose_images(background, foreground, mask, config)	<-- UNCOVERED
9679	}
    	…

9682	pub fn remove_background_ig(
9683	  img : @types.Image,
9684	  config : @image_generation.EditingConfig,
9685	) -> @image_generation.EditingResult {
9686	  @image_generation.remove_background(img, config)	<-- UNCOVERED
9687	}
    	…

9690	pub fn edit_image_ig(
9691	  img : @types.Image,
9692	  mask : @types.Image,
9693	  config : @image_generation.EditingConfig,
9694	) -> @image_generation.EditingResult {
9695	  @image_generation.edit_image(img, mask, config)	<-- UNCOVERED
9696	}
    	…

9699	pub fn generation_report_to_string_ig(
9700	  result : @image_generation.GenerationResult,
9701	) -> String {
9702	  @image_generation.generation_report_to_string(result)	<-- UNCOVERED
9703	}
    	…

9706	pub fn editing_report_to_string_ig(
9707	  result : @image_generation.EditingResult,
9708	) -> String {
9709	  @image_generation.editing_report_to_string(result)	<-- UNCOVERED
9710	}
    	…

9755	pub fn classify_scene_su(
9756	  img : @types.Image,
9757	  config : @scene_understanding.SceneUnderstandingConfig,
9758	) -> @scene_understanding.SceneClassificationResult {
9759	  @scene_understanding.classify_scene(img, config)	<-- UNCOVERED
9760	}
    	…

9763	pub fn generate_caption_su(
9764	  img : @types.Image,
9765	  config : @scene_understanding.SceneUnderstandingConfig,
9766	) -> @scene_understanding.ImageCaptioningResult {
9767	  @scene_understanding.generate_caption(img, config)	<-- UNCOVERED
9768	}
    	…

9771	pub fn answer_visual_question_su(
9772	  img : @types.Image,
9773	  question : String,
9774	  config : @scene_understanding.SceneUnderstandingConfig,
9775	) -> @scene_understanding.VisualQuestionAnsweringResult {
9776	  @scene_understanding.answer_visual_question(img, question, config)	<-- UNCOVERED
9777	}
    	…

9780	pub fn generate_scene_graph_su(
9781	  img : @types.Image,
9782	  config : @scene_understanding.SceneUnderstandingConfig,
9783	) -> @scene_understanding.SceneGraph {
9784	  @scene_understanding.generate_scene_graph(img, config)	<-- UNCOVERED
9785	}
    	…

9788	pub fn understand_scene_su(
9789	  img : @types.Image,
9790	  config : @scene_understanding.SceneUnderstandingConfig,
9791	) -> (
9792	  @scene_understanding.SceneClassificationResult,
9793	  @scene_understanding.ImageCaptioningResult,
9794	  @scene_understanding.SceneGraph,
9795	) {
9796	  @scene_understanding.understand_scene(img, config)	<-- UNCOVERED
9797	}
    	…

9800	pub fn scene_understanding_report_to_string_su(
9801	  classification : @scene_understanding.SceneClassificationResult,
9802	  caption : @scene_understanding.ImageCaptioningResult,
9803	  vqa : @scene_understanding.VisualQuestionAnsweringResult,
9804	  scene_graph : @scene_understanding.SceneGraph,
9805	) -> String {
9806	  @scene_understanding.scene_understanding_report_to_string(	<-- UNCOVERED
9807	    classification, caption, vqa, scene_graph,
9808	  )
9809	}
    	…

9862	pub fn generate_rays_nr(
9863	  camera : @neural_rendering.CameraParams,
9864	) -> Array[@neural_rendering.Ray] {
9865	  @neural_rendering.generate_rays(camera)	<-- UNCOVERED
9866	}
    	…

9869	pub fn render_nerf_nr(
9870	  camera : @neural_rendering.CameraParams,
9871	  config : @neural_rendering.NeRFConfig,
9872	) -> @neural_rendering.RenderingResult {
9873	  @neural_rendering.render_nerf(camera, config)	<-- UNCOVERED
9874	}
    	…

9877	pub fn render_gaussian_splatting_nr(
9878	  camera : @neural_rendering.CameraParams,
9879	  gaussians : Array[@neural_rendering.Gaussian3D],
9880	  config : @neural_rendering.GaussianSplattingConfig,
9881	) -> @neural_rendering.RenderingResult {
9882	  @neural_rendering.render_gaussian_splatting(camera, gaussians, config)	<-- UNCOVERED
9883	}
    	…

9886	pub fn generate_sample_gaussians_nr(
9887	  num_gaussians : Int,
9888	) -> Array[@neural_rendering.Gaussian3D] {
9889	  @neural_rendering.generate_sample_gaussians(num_gaussians)	<-- UNCOVERED
9890	}
    	…

9893	pub fn multiview_stereo_reconstruction_nr(
9894	  images : Array[@types.Image],
9895	  cameras : Array[@neural_rendering.CameraParams],
9896	) -> @neural_rendering.ReconstructionResult {
9897	  @neural_rendering.multiview_stereo_reconstruction(images, cameras)	<-- UNCOVERED
9898	}
    	…

9901	pub fn visualize_point_cloud_nr(
9902	  point_cloud : Array[@neural_rendering.Point3D],
9903	  width : Int,
9904	  height : Int,
9905	) -> @types.Image {
9906	  @neural_rendering.visualize_point_cloud(point_cloud, width, height)	<-- UNCOVERED
9907	}
    	…

9910	pub fn neural_rendering_report_to_string_nr(
9911	  result : @neural_rendering.RenderingResult,
9912	  algorithm : @neural_rendering.NeuralRenderingAlgorithm,
9913	) -> String {
9914	  @neural_rendering.neural_rendering_report_to_string(result, algorithm)	<-- UNCOVERED
9915	}
    	…

9918	pub fn reconstruction_report_to_string_nr(
9919	  result : @neural_rendering.ReconstructionResult,
9920	  algorithm : @neural_rendering.ReconstructionAlgorithm,
9921	) -> String {
9922	  @neural_rendering.reconstruction_report_to_string(result, algorithm)	<-- UNCOVERED
9923	}
    	…

9974	pub fn create_default_coded_aperture_ci(
9975	  size : Int,
9976	) -> @computational_imaging.CodedAperturePattern {
9977	  @computational_imaging.create_default_coded_aperture(size)	<-- UNCOVERED
9978	}
    	…

9981	pub fn generate_sample_lightfield_ci(
9982	  base_image : @types.Image,
9983	  params : @computational_imaging.LightFieldCameraParams,
9984	) -> @computational_imaging.LightFieldData {
9985	  @computational_imaging.generate_sample_lightfield(base_image, params)	<-- UNCOVERED
9986	}
    	…

9989	pub fn lightfield_refocus_ci(
9990	  lightfield : @computational_imaging.LightFieldData,
9991	  focus_depth : Double,
9992	  aperture_size : Double,
9993	) -> @computational_imaging.RefocusResult {
9994	  @computational_imaging.lightfield_refocus(	<-- UNCOVERED
9995	    lightfield, focus_depth, aperture_size,
9996	  )
9997	}
    	…

10000	pub fn synthesize_view_ci(
10001	  lightfield : @computational_imaging.LightFieldData,
10002	  target_x : Double,
10003	  target_y : Double,
10004	) -> @computational_imaging.ViewSynthesisResult {
10005	  @computational_imaging.synthesize_view(lightfield, target_x, target_y)	<-- UNCOVERED
10006	}
     	…

10009	pub fn generate_hologram_ci(
10010	  image : @types.Image,
10011	  wavelength : Double,
10012	  distance : Double,
10013	  pixel_size : Double,
10014	) -> @computational_imaging.Hologram {
10015	  @computational_imaging.generate_hologram(	<-- UNCOVERED
10016	    image, wavelength, distance, pixel_size,
10017	  )
10018	}
     	…

10021	pub fn reconstruct_hologram_ci(
10022	  hologram : @computational_imaging.Hologram,
10023	) -> @computational_imaging.ImagingReconstructionResult {
10024	  @computational_imaging.reconstruct_hologram(hologram)	<-- UNCOVERED
10025	}
     	…

10028	pub fn ct_reconstruct_fbp_ci(
10029	  projections : @computational_imaging.CTProjectionData,
10030	  output_size : Int,
10031	) -> @computational_imaging.ImagingReconstructionResult {
10032	  @computational_imaging.ct_reconstruct_fbp(projections, output_size)	<-- UNCOVERED
10033	}
     	…

10036	pub fn phase_retrieval_ci(
10037	  magnitude : @types.Image,
10038	  config : @computational_imaging.PhaseRetrievalConfig,
10039	) -> @computational_imaging.ImagingReconstructionResult {
10040	  @computational_imaging.phase_retrieval(magnitude, config)	<-- UNCOVERED
10041	}
     	…

10044	pub fn coded_aperture_reconstruct_ci(
10045	  measurement : @types.Image,
10046	  pattern : @computational_imaging.CodedAperturePattern,
10047	) -> @computational_imaging.ImagingReconstructionResult {
10048	  @computational_imaging.coded_aperture_reconstruct(measurement, pattern)	<-- UNCOVERED
10049	}
     	…

10052	pub fn computational_imaging_report_to_string_ci(
10053	  result : @computational_imaging.ImagingReconstructionResult,
10054	  algorithm_name : String,
10055	) -> String {
10056	  @computational_imaging.computational_imaging_report_to_string(	<-- UNCOVERED
10057	    result, algorithm_name,
10058	  )
10059	}
     	…

10120	pub fn compute_image_text_similarity_vlm(
10121	  image : @types.Image,
10122	  text : String,
10123	  config : @vlm_multimodal.VLMConfig,
10124	) -> @vlm_multimodal.VisionTextEmbedding {
10125	  @vlm_multimodal.compute_image_text_similarity(image, text, config)	<-- UNCOVERED
10126	}
     	…

10129	pub fn fuse_modalities_vlm(
10130	  image_features : Array[Double],
10131	  text_features : Array[Double],
10132	  config : @vlm_multimodal.FusionConfig,
10133	) -> @vlm_multimodal.FusionResult {
10134	  @vlm_multimodal.fuse_modalities(image_features, text_features, config)	<-- UNCOVERED
10135	}
     	…

10138	pub fn diffusion_generate_vlm(
10139	  prompt : String,
10140	  negative_prompt : String,
10141	  config : @vlm_multimodal.DiffusionConfig,
10142	  seed : Int,
10143	) -> @vlm_multimodal.DiffusionResult {
10144	  @vlm_multimodal.diffusion_generate(prompt, negative_prompt, config, seed)	<-- UNCOVERED
10145	}
     	…

10148	pub fn text_to_image_vlm(
10149	  prompt : String,
10150	  config : @vlm_multimodal.GenerativeAIConfig,
10151	  seed : Int,
10152	) -> @vlm_multimodal.GenerativeAIResult {
10153	  @vlm_multimodal.text_to_image(prompt, config, seed)	<-- UNCOVERED
10154	}
     	…

10157	pub fn image_to_text_vlm(
10158	  image : @types.Image,
10159	  config : @vlm_multimodal.GenerativeAIConfig,
10160	) -> @vlm_multimodal.GenerativeAIResult {
10161	  @vlm_multimodal.image_to_text(image, config)	<-- UNCOVERED
10162	}
     	…

10165	pub fn vlm_report_to_string_vlm(
10166	  embedding : @vlm_multimodal.VisionTextEmbedding,
10167	  model_type : @vlm_multimodal.VisionLanguageModelType,
10168	) -> String {
10169	  @vlm_multimodal.vlm_report_to_string(embedding, model_type)	<-- UNCOVERED
10170	}
     	…

10173	pub fn fusion_report_to_string_vlm(
10174	  result : @vlm_multimodal.FusionResult,
10175	) -> String {
10176	  @vlm_multimodal.fusion_report_to_string(result)	<-- UNCOVERED
10177	}
     	…

10229	pub fn detect_objects_iv(
10230	  image : @types.Image,
10231	  config : @intelligent_vision.IntelligentVisionConfig,
10232	) -> Array[@intelligent_vision.DetectionResultIV] {
10233	  @intelligent_vision.detect_objects_iv(image, config)	<-- UNCOVERED
10234	}
     	…

10237	pub fn detect_lanes_iv(
10238	  image : @types.Image,
10239	  config : @intelligent_vision.IntelligentVisionConfig,
10240	) -> @intelligent_vision.LaneDetectionResult {
10241	  @intelligent_vision.detect_lanes(image, config)	<-- UNCOVERED
10242	}
     	…

10245	pub fn recognize_traffic_signs_iv(
10246	  image : @types.Image,
10247	  config : @intelligent_vision.IntelligentVisionConfig,
10248	) -> Array[@intelligent_vision.TrafficSignResult] {
10249	  @intelligent_vision.recognize_traffic_signs(image, config)	<-- UNCOVERED
10250	}
     	…

10253	pub fn detect_defects_iv(
10254	  image : @types.Image,
10255	  config : @intelligent_vision.IntelligentVisionConfig,
10256	) -> Array[@intelligent_vision.DefectDetectionResult] {
10257	  @intelligent_vision.detect_defects(image, config)	<-- UNCOVERED
10258	}
     	…

10261	pub fn detect_intrusion_iv(
10262	  image : @types.Image,
10263	  roi : Array[Double],
10264	  config : @intelligent_vision.IntelligentVisionConfig,
10265	) -> @intelligent_vision.IntrusionDetectionResult {
10266	  @intelligent_vision.detect_intrusion(image, roi, config)	<-- UNCOVERED
10267	}
     	…

10270	pub fn detect_crops_iv(
10271	  image : @types.Image,
10272	  config : @intelligent_vision.IntelligentVisionConfig,
10273	) -> Array[@intelligent_vision.CropDetectionResult] {
10274	  @intelligent_vision.detect_crops(image, config)	<-- UNCOVERED
10275	}
     	…

10278	pub fn detect_stars_iv(
10279	  image : @types.Image,
10280	  config : @intelligent_vision.IntelligentVisionConfig,
10281	) -> @intelligent_vision.StarDetectionResult {
10282	  @intelligent_vision.detect_stars(image, config)	<-- UNCOVERED
10283	}
     	…

10286	pub fn intelligent_vision_report_to_string_iv(
10287	  task_name : String,
10288	  num_results : Int,
10289	  avg_confidence : Double,
10290	  config : @intelligent_vision.IntelligentVisionConfig,
10291	) -> String {
10292	  @intelligent_vision.intelligent_vision_report_to_string(	<-- UNCOVERED
10293	    task_name, num_results, avg_confidence, config,
10294	  )
10295	}
     	…

10333	pub fn create_default_advanced_imaging_config_ai(
10334	  modality : @advanced_imaging.ImagingModality,
10335	) -> @advanced_imaging.AdvancedImagingConfig {
10336	  @advanced_imaging.create_default_advanced_imaging_config(modality)	<-- UNCOVERED
10337	}
     	…

10340	pub fn quantum_ghost_imaging_ai(
10341	  num_patterns : Int,
10342	  width : Int,
10343	  height : Int,
10344	  algorithm : @advanced_imaging.QuantumImagingAlgorithm,
10345	) -> @types.Image {
10346	  @advanced_imaging.quantum_ghost_imaging(	<-- UNCOVERED
10347	    num_patterns, width, height, algorithm,
10348	  )
10349	}
     	…

10352	pub fn infrared_pseudocolor_ai(
10353	  infrared_image : @types.Image,
10354	  ir_type : @advanced_imaging.InfraredImagingType,
10355	) -> @types.Image {
10356	  @advanced_imaging.infrared_pseudocolor(infrared_image, ir_type)	<-- UNCOVERED
10357	}
     	…

10360	pub fn generate_hyperspectral_cube_ai(
10361	  base_image : @types.Image,
10362	  num_bands : Int,
10363	  start_wavelength : Double,
10364	  end_wavelength : Double,
10365	) -> @advanced_imaging.HyperspectralData {
10366	  @advanced_imaging.generate_hyperspectral_cube(	<-- UNCOVERED
10367	    base_image, num_bands, start_wavelength, end_wavelength,
10368	  )
10369	}
     	…

10372	pub fn analyze_polarization_ai(
10373	  images_0deg : @types.Image,
10374	  images_45deg : @types.Image,
10375	  images_90deg : @types.Image,
10376	  images_135deg : @types.Image,
10377	) -> @advanced_imaging.PolarizationData {
10378	  @advanced_imaging.analyze_polarization(	<-- UNCOVERED
10379	    images_0deg, images_45deg, images_90deg, images_135deg,
10380	  )
10381	}
     	…

10384	pub fn thermal_to_temperature_ai(
10385	  thermal_image : @types.Image,
10386	  emissivity : Double,
10387	  min_temp : Double,
10388	  max_temp : Double,
10389	) -> @advanced_imaging.ThermalData {
10390	  @advanced_imaging.thermal_to_temperature(	<-- UNCOVERED
10391	    thermal_image, emissivity, min_temp, max_temp,
10392	  )
10393	}
     	…

10396	pub fn xray_enhance_ai(
10397	  xray_image : @types.Image,
10398	  energy_kev : Double,
10399	) -> @advanced_imaging.XRayData {
10400	  @advanced_imaging.xray_enhance(xray_image, energy_kev)	<-- UNCOVERED
10401	}
     	…

10404	pub fn ultrasound_synthesize_ai(
10405	  width : Int,
10406	  height : Int,
10407	  frequency_mhz : Double,
10408	  depth_cm : Double,
10409	) -> @advanced_imaging.UltrasoundData {
10410	  @advanced_imaging.ultrasound_synthesize(	<-- UNCOVERED
10411	    width, height, frequency_mhz, depth_cm,
10412	  )
10413	}
     	…

10416	pub fn mri_synthesize_ai(
10417	  width : Int,
10418	  height : Int,
10419	  field_strength_t : Double,
10420	) -> @advanced_imaging.MRIData {
10421	  @advanced_imaging.mri_synthesize(width, height, field_strength_t)	<-- UNCOVERED
10422	}
     	…

10425	pub fn pet_synthesize_ai(
10426	  width : Int,
10427	  height : Int,
10428	  radiotracer : String,
10429	) -> @advanced_imaging.PETData {
10430	  @advanced_imaging.pet_synthesize(width, height, radiotracer)	<-- UNCOVERED
10431	}
     	…

10434	pub fn advanced_imaging_report_to_string_ai(
10435	  modality : @advanced_imaging.ImagingModality,
10436	  image : @types.Image,
10437	  config : @advanced_imaging.AdvancedImagingConfig,
10438	) -> String {
10439	  @advanced_imaging.advanced_imaging_report_to_string(modality, image, config)	<-- UNCOVERED
10440	}
     	…

10450	pub fn num_tiles(
10451	  width : Int,
10452	  height : Int,
10453	  config : @performance.TileConfig,
10454	) -> (Int, Int) {
10455	  @performance.num_tiles(width, height, config)	<-- UNCOVERED
10456	}
     	…

10459	pub fn tile_region(
10460	  tile_col : Int,
10461	  tile_row : Int,
10462	  config : @performance.TileConfig,
10463	) -> (Int, Int, Int, Int) {
10464	  @performance.tile_region(tile_col, tile_row, config)	<-- UNCOVERED
10465	}
     	…

10468	pub fn extract_tile(
10469	  img : @types.Image,
10470	  x0 : Int,
10471	  y0 : Int,
10472	  tile_w : Int,
10473	  tile_h : Int,
10474	) -> @types.Image raise @types.LoadError {
10475	  @performance.extract_tile(img, x0, y0, tile_w, tile_h)	<-- UNCOVERED
10476	}
     	…

10479	pub fn paste_tile(
10480	  img : @types.Image,
10481	  tile : @types.Image,
10482	  x0 : Int,
10483	  y0 : Int,
10484	) -> @types.Image raise @types.LoadError {
10485	  @performance.paste_tile(img, tile, x0, y0)	<-- UNCOVERED
10486	}
     	…

10489	pub fn bulk_copy_4x(
10490	  src : Bytes,
10491	  src_offset : Int,
10492	  dst : Array[Byte],
10493	  dst_offset : Int,
10494	  len : Int,
10495	) -> Unit {
10496	  @performance.bulk_copy_4x(src, src_offset, dst, dst_offset, len)	<-- UNCOVERED
10497	}
     	…

10500	pub fn bulk_fill_4x(
10501	  dst : Array[Byte],
10502	  offset : Int,
10503	  len : Int,
10504	  value : Byte,
10505	) -> Unit {
10506	  @performance.bulk_fill_4x(dst, offset, len, value)	<-- UNCOVERED
10507	}
     	…

10532	pub fn pipeline_describe(p : @pipeline.Pipeline) -> String {
10533	  @pipeline.pipeline_describe(p)	<-- UNCOVERED
10534	}
     	…

10537	pub fn pipeline_len(p : @pipeline.Pipeline) -> Int {
10538	  @pipeline.pipeline_len(p)	<-- UNCOVERED
10539	}
     	…

10545	pub fn batch_execute(
10546	  tasks : Array[@pipeline.BatchTask],
10547	) -> @pipeline.BatchResult {
10548	  @pipeline.batch_execute(tasks)	<-- UNCOVERED
10549	}
     	…

10552	pub fn batch_summary(result : @pipeline.BatchResult) -> String {
10553	  @pipeline.batch_summary(result)	<-- UNCOVERED
10554	}
     	…

10557	pub fn cli_parse(args : Array[String]) -> @pipeline.CliCommand {
10558	  @pipeline.cli_parse(args)	<-- UNCOVERED
10559	}
     	…

1 uncovered line(s) in src\util\color_map.mbt:

 83	fn interp_gradient(
 84	  v : Int,
 85	  stops : Array[(Int, Byte, Byte, Byte)],
 86	) -> (Byte, Byte, Byte) {
 87	  let len = stops.length()
 88	  if v <= stops[0].0 {
 89	    return (stops[0].1, stops[0].2, stops[0].3)
 90	  }
 91	  if v >= stops[len - 1].0 {
 92	    return (stops[len - 1].1, stops[len - 1].2, stops[len - 1].3)
 93	  }
 94	  for i in 0..<(len - 1) {
 95	    let (p0, r0, g0, b0) = stops[i]
 96	    let (p1, r1, g1, b1) = stops[i + 1]
 97	    if v >= p0 && v <= p1 {
 98	      let range = p1 - p0
 99	      if range == 0 {
100	        return (r0, g0, b0)	<-- UNCOVERED
101	      }
102	      let t = v - p0
103	      let r = r0.to_int() + (r1.to_int() - r0.to_int()) * t / range
104	      let g = g0.to_int() + (g1.to_int() - g0.to_int()) * t / range
105	      let b = b0.to_int() + (b1.to_int() - b0.to_int()) * t / range
106	      return (clamp_b(r), clamp_b(g), clamp_b(b))
107	    }
108	  }
109	  (b'\x00', b'\x00', b'\x00')
110	}
   	…

20 uncovered line(s) in src\util\image_combine.mbt:

 7	pub fn alpha_blend(
 8	  img1 : @types.Image,
 9	  img2 : @types.Image,
10	  alpha : Float,
11	) -> @types.Image raise @types.LoadError {
12	  if img1.width != img2.width || img1.height != img2.height {	<-- UNCOVERED
13	    raise @types.LoadError::DecodeFailed("alpha_blend: 图像尺寸不同")
14	  }
15	  if img1.channels != img2.channels {
16	    raise @types.LoadError::DecodeFailed("alpha_blend: 图像通道数不同")	<-- UNCOVERED
17	  }
18	  let n = img1.width * img1.height * img1.channels
19	  let result : Array[Byte] = []
20	  let a = alpha.clamp(min=0.0F, max=1.0F)	<-- UNCOVERED
21	  for i in 0..<n {
22	    let v1 = img1.data[i].to_int()	<-- UNCOVERED
23	    let v2 = img2.data[i].to_int()
24	    let blended = (Float::from_int(v1) * (1.0F - a) + Float::from_int(v2) * a).to_int()
25	    result.push(blended.clamp(min=0, max=255).to_byte())
26	  }
27	  @types.Image::{
28	    width: img1.width,
29	    height: img1.height,
30	    channels: img1.channels,
31	    data: Bytes::from_array(result),	<-- UNCOVERED
32	  }
33	}
  	…

39	pub fn mask_blend(
40	  img1 : @types.Image,
41	  img2 : @types.Image,
42	  mask : @types.Image,
43	) -> @types.Image raise @types.LoadError {
44	  if img1.width != img2.width || img1.height != img2.height {	<-- UNCOVERED
45	    raise @types.LoadError::DecodeFailed("mask_blend: 图像尺寸不同")
46	  }
47	  if img1.channels != img2.channels {
48	    raise @types.LoadError::DecodeFailed("mask_blend: 图像通道数不同")	<-- UNCOVERED
49	  }
50	  if mask.channels != 1 {
51	    raise @types.LoadError::DecodeFailed("mask_blend: 掩码必须为单通道")	<-- UNCOVERED
52	  }
53	  if mask.width != img1.width || mask.height != img1.height {
54	    raise @types.LoadError::DecodeFailed("mask_blend: 掩码尺寸不同")	<-- UNCOVERED
55	  }
56	  let w = img1.width
57	  let h = img1.height
58	  let ch = img1.channels
59	  let result : Array[Byte] = []
60	  for y in 0..<h {
61	    for x in 0..<w {	<-- UNCOVERED
62	      let m = Float::from_int(mask.data[y * w + x].to_int()) / 255.0F
63	      for c in 0..<ch {
64	        let idx = (y * w + x) * ch + c	<-- UNCOVERED
65	        let v1 = img1.data[idx].to_int()
66	        let v2 = img2.data[idx].to_int()
67	        let blended = (Float::from_int(v1) * (1.0F - m) +
68	        Float::from_int(v2) * m).to_int()
69	        result.push(blended.clamp(min=0, max=255).to_byte())
70	      }
71	    }
72	  }
73	  @types.Image::{
74	    width: w,
75	    height: h,
76	    channels: ch,
77	    data: Bytes::from_array(result),	<-- UNCOVERED
78	  }
79	}
  	…

 83	pub fn flip_horizontal(img : @types.Image) -> @types.Image {
 84	  let w = img.width	<-- UNCOVERED
 85	  let h = img.height
 86	  let ch = img.channels
 87	  let result : Array[Byte] = []
 88	  for y in 0..<h {
 89	    for x in 0..<w {	<-- UNCOVERED
 90	      let src_x = w - 1 - x
 91	      for c in 0..<ch {
 92	        result.push(img.data[(y * w + src_x) * ch + c])	<-- UNCOVERED
 93	      }
 94	    }
 95	  }
 96	  @types.Image::{
 97	    width: w,
 98	    height: h,
 99	    channels: ch,
100	    data: Bytes::from_array(result),	<-- UNCOVERED
101	  }
102	}
   	…

106	pub fn flip_vertical(img : @types.Image) -> @types.Image {
107	  let w = img.width	<-- UNCOVERED
108	  let h = img.height
109	  let ch = img.channels
110	  let result : Array[Byte] = []
111	  for y in 0..<h {
112	    let src_y = h - 1 - y	<-- UNCOVERED
113	    for x in 0..<w {
114	      for c in 0..<ch {	<-- UNCOVERED
115	        result.push(img.data[(src_y * w + x) * ch + c])
116	      }
117	    }
118	  }
119	  @types.Image::{
120	    width: w,
121	    height: h,
122	    channels: ch,
123	    data: Bytes::from_array(result),	<-- UNCOVERED
124	  }
125	}
   	…

15 uncovered line(s) in src\util\image_stats.mbt:

51	pub fn variance_value(img : @types.Image) -> Float {
52	  let stats = compute_stats(img)	<-- UNCOVERED
53	  stats.stddev * stats.stddev
54	}
  	…

58	pub fn entropy_value(img : @types.Image) -> Float {
59	  let n = img.width * img.height * img.channels	<-- UNCOVERED
60	  if n == 0 {
61	    return 0.0F	<-- UNCOVERED
62	  }
63	  // 计算直方图
64	  let histogram : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
65	  for i in 0..<n {
66	    let v = img.data[i].to_int()	<-- UNCOVERED
67	    histogram[v] += 1
68	  }
69	  // 计算熵
70	  let mut entropy = 0.0F
71	  let n_f = Float::from_int(n)	<-- UNCOVERED
72	  for v in 0..<256 {
73	    if histogram[v] > 0 {	<-- UNCOVERED
74	      let p = Float::from_int(histogram[v]) / n_f
75	      entropy = entropy - p * Float::from_double(@math.log2(p.to_double()))
76	    }
77	  }
78	  entropy
79	}
  	…

 83	pub fn median_value(img : @types.Image) -> Int {
 84	  let n = img.width * img.height * img.channels	<-- UNCOVERED
 85	  if n == 0 {
 86	    return 0	<-- UNCOVERED
 87	  }
 88	  // 计算直方图
 89	  let histogram : Array[Int] = Array::make(256, 0)	<-- UNCOVERED
 90	  for i in 0..<n {
 91	    let v = img.data[i].to_int()	<-- UNCOVERED
 92	    histogram[v] += 1
 93	  }
 94	  // 计算中位数
 95	  let mut cumulative = 0
 96	  let half = n / 2
 97	  for v in 0..<256 {
 98	    cumulative += histogram[v]	<-- UNCOVERED
 99	    if cumulative >= half {
100	      return v	<-- UNCOVERED
101	    }
102	  }
103	  255
104	}
   	…

108	pub fn contrast_value(img : @types.Image) -> Float {
109	  let stats = compute_stats(img)	<-- UNCOVERED
110	  stats.stddev
111	}
   	…

115	pub fn brightness_value(img : @types.Image) -> Float {
116	  let stats = compute_stats(img)	<-- UNCOVERED
117	  stats.mean
118	}
   	…

1 uncovered line(s) in src\util\image_stats_16f.mbt:

58	pub fn compute_stats_f(img : @types.ImageF) -> ImageStatsF {
59	  let n = img.width * img.height * img.channels
60	  let bpp = 4
61	  let mut sum = 0.0F
62	  let mut sum_sq = 0.0F
63	  let mut min_v = read_f32_le_s(img.data, 0)
64	  let mut max_v = min_v
65	  for i in 0..<n {
66	    let v = read_f32_le_s(img.data, i * bpp)
67	    sum += v
68	    sum_sq += v * v
69	    if v < min_v {
70	      min_v = v	<-- UNCOVERED
71	    }
72	    if v > max_v {
73	      max_v = v
74	    }
75	  }
76	  let n_f = Float::from_int(n)
77	  let mean = sum / n_f
78	  let variance = sum_sq / n_f - mean * mean
79	  let stddev = (if variance < 0.0F { 0.0F } else { variance }).sqrt()
80	  ImageStatsF::{ mean, stddev, min_val: min_v, max_val: max_v }
81	}
  	…

1 uncovered line(s) in src\util\image_util.mbt:

51	pub fn add_border(
52	  img : @types.Image,
53	  top : Int,
54	  right : Int,
55	  bottom : Int,
56	  left : Int,
57	  border_color : Array[Byte],
58	) -> @types.Image raise @types.LoadError {
59	  if top < 0 || right < 0 || bottom < 0 || left < 0 {
60	    raise @types.LoadError::DecodeFailed("border 宽度必须非负")	<-- UNCOVERED
61	  }
62	  let ch = img.channels
63	  let new_w = img.width + left + right
64	  let new_h = img.height + top + bottom
65	  let out : Array[Byte] = Array::make(new_w * new_h * ch, b'\x00')
66	  let color_len = border_color.length()
67	  if color_len == 0 {
68	    raise @types.LoadError::DecodeFailed("border_color 不能为空")
69	  }
70	  for y in 0..<new_h {
71	    for x in 0..<new_w {
72	      let dst_off = (y * new_w + x) * ch
73	      if x >= left && x < left + img.width && y >= top && y < top + img.height {
74	        let src_off = ((y - top) * img.width + (x - left)) * ch
75	        for c in 0..<ch {
76	          out[dst_off + c] = img.data[src_off + c]
77	        }
78	      } else {
79	        for c in 0..<ch {
80	          out[dst_off + c] = border_color[c % color_len]
81	        }
82	      }
83	    }
84	  }
85	  @types.Image::{
86	    width: new_w,
87	    height: new_h,
88	    channels: ch,
89	    data: Bytes::from_array(out),
90	  }
91	}
  	…

2 uncovered line(s) in src\util\pixel_advanced.mbt:

 4	fn clamp_b(v : Int) -> Byte {
 5	  if v < 0 {
 6	    b'\x00'	<-- UNCOVERED
 7	  } else if v > 255 {
 8	    b'\xFF'	<-- UNCOVERED
 9	  } else {
10	    v.to_byte()
11	  }
12	}
  	…

Total: 6611 uncovered line(s) in 261 file(s)
