scirs2_vision/prompt_segmentation/
image_encoder.rs1use crate::error::{Result, VisionError};
10use scirs2_core::ndarray::{Array1, Array2};
11
12use super::types::SAMConfig;
13
14#[derive(Debug, Clone)]
23pub struct PatchEmbedding {
24 weights: Array2<f64>,
26 bias: Array1<f64>,
28 in_channels: usize,
30 out_channels: usize,
32 kernel_size: usize,
34 stride: usize,
36}
37
38impl PatchEmbedding {
39 pub fn new(in_channels: usize, out_channels: usize, kernel_size: usize, stride: usize) -> Self {
41 let fan_in = in_channels * kernel_size * kernel_size;
42 let std_dev = (2.0 / fan_in as f64).sqrt();
43
44 let total = out_channels * fan_in;
47 let mut weights_vec = Vec::with_capacity(total);
48 let mut seed: u64 = 42;
49 for _ in 0..total {
50 seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
51 let u = (seed >> 33) as f64 / (1u64 << 31) as f64 - 1.0;
52 weights_vec.push(u * std_dev);
53 }
54
55 let weights = Array2::from_shape_vec((out_channels, fan_in), weights_vec)
56 .unwrap_or_else(|_| Array2::zeros((out_channels, fan_in)));
57 let bias = Array1::zeros(out_channels);
58
59 Self {
60 weights,
61 bias,
62 in_channels,
63 out_channels,
64 kernel_size,
65 stride,
66 }
67 }
68
69 pub fn forward(
74 &self,
75 input: &Array2<f64>,
76 height: usize,
77 width: usize,
78 ) -> Result<(usize, usize, Array2<f64>)> {
79 let (n_pixels, in_ch) = input.dim();
80 if n_pixels != height * width {
81 return Err(VisionError::InvalidParameter(format!(
82 "PatchEmbedding: pixel count {n_pixels} != H*W = {}",
83 height * width,
84 )));
85 }
86 if in_ch != self.in_channels {
87 return Err(VisionError::InvalidParameter(format!(
88 "PatchEmbedding: expected {}, got {in_ch} input channels",
89 self.in_channels,
90 )));
91 }
92
93 let pad = self.kernel_size / 2;
94 let out_h = (height + 2 * pad - self.kernel_size) / self.stride + 1;
95 let out_w = (width + 2 * pad - self.kernel_size) / self.stride + 1;
96
97 let mut output = Array2::zeros((out_h * out_w, self.out_channels));
98
99 for oy in 0..out_h {
100 for ox in 0..out_w {
101 let iy_start = oy * self.stride;
102 let ix_start = ox * self.stride;
103
104 let patch_len = self.in_channels * self.kernel_size * self.kernel_size;
106 let mut patch = vec![0.0f64; patch_len];
107 let mut idx = 0;
108 for c in 0..self.in_channels {
109 for ky in 0..self.kernel_size {
110 for kx in 0..self.kernel_size {
111 let iy = iy_start + ky;
112 let ix = ix_start + kx;
113 let sy = iy as isize - pad as isize;
115 let sx = ix as isize - pad as isize;
116 if sy >= 0 && (sy as usize) < height && sx >= 0 && (sx as usize) < width
117 {
118 let pixel_idx = sy as usize * width + sx as usize;
119 patch[idx] = input[[pixel_idx, c]];
120 }
121 idx += 1;
123 }
124 }
125 }
126
127 let out_idx = oy * out_w + ox;
129 for oc in 0..self.out_channels {
130 let mut val = self.bias[oc];
131 for (pi, &patch_val) in patch.iter().enumerate().take(patch_len) {
132 val += self.weights[[oc, pi]] * patch_val;
133 }
134 output[[out_idx, oc]] = val;
135 }
136 }
137 }
138
139 Ok((out_h, out_w, output))
140 }
141}
142
143fn layer_norm(input: &mut Array2<f64>, eps: f64) {
149 let (rows, cols) = input.dim();
150 if cols == 0 {
151 return;
152 }
153 for r in 0..rows {
154 let mut mean = 0.0f64;
155 for c in 0..cols {
156 mean += input[[r, c]];
157 }
158 mean /= cols as f64;
159
160 let mut var = 0.0f64;
161 for c in 0..cols {
162 let diff = input[[r, c]] - mean;
163 var += diff * diff;
164 }
165 var /= cols as f64;
166
167 let inv_std = 1.0 / (var + eps).sqrt();
168 for c in 0..cols {
169 input[[r, c]] = (input[[r, c]] - mean) * inv_std;
170 }
171 }
172}
173
174fn relu_inplace(arr: &mut Array2<f64>) {
176 arr.mapv_inplace(|v| v.max(0.0));
177}
178
179#[derive(Debug, Clone)]
185struct EncoderStage {
186 down_conv: PatchEmbedding,
188 proj_conv: PatchEmbedding,
190 refine_conv: PatchEmbedding,
192}
193
194impl EncoderStage {
195 fn new(in_channels: usize, out_channels: usize) -> Self {
196 Self {
197 down_conv: PatchEmbedding::new(in_channels, out_channels, 3, 2),
198 proj_conv: PatchEmbedding::new(in_channels, out_channels, 1, 2),
199 refine_conv: PatchEmbedding::new(out_channels, out_channels, 3, 1),
200 }
201 }
202
203 fn forward(
205 &self,
206 input: &Array2<f64>,
207 h: usize,
208 w: usize,
209 ) -> Result<(usize, usize, Array2<f64>)> {
210 let (h1, w1, mut main) = self.down_conv.forward(input, h, w)?;
212 layer_norm(&mut main, 1e-5);
213 relu_inplace(&mut main);
214
215 let (_h2, _w2, refined) = self.refine_conv.forward(&main, h1, w1)?;
216
217 let (_rh, _rw, residual) = self.proj_conv.forward(input, h, w)?;
219
220 let (rows, cols) = refined.dim();
222 let (rrows, rcols) = residual.dim();
223 let min_rows = rows.min(rrows);
224 let min_cols = cols.min(rcols);
225 let mut out = refined;
226 for r in 0..min_rows {
227 for c in 0..min_cols {
228 out[[r, c]] += residual[[r, c]];
229 }
230 }
231
232 Ok((h1, w1, out))
233 }
234}
235
236#[derive(Debug, Clone)]
246pub struct SimpleImageEncoder {
247 initial_embed: PatchEmbedding,
249 stages: Vec<EncoderStage>,
251 config: SAMConfig,
253}
254
255impl SimpleImageEncoder {
256 pub fn new(config: &SAMConfig) -> Self {
258 let base_ch = config.embed_dim / 4; let initial_embed = PatchEmbedding::new(1, base_ch, 3, 2);
262
263 let mut stages = Vec::with_capacity(config.encoder_stages);
265 let mut ch = base_ch;
266 for _ in 0..config.encoder_stages {
267 let next_ch = (ch * 2).min(config.embed_dim);
268 stages.push(EncoderStage::new(ch, next_ch));
269 ch = next_ch;
270 }
271
272 Self {
273 initial_embed,
274 stages,
275 config: config.clone(),
276 }
277 }
278
279 pub fn encode(&self, image: &Array2<f64>) -> Result<Vec<Array2<f64>>> {
291 let (img_h, img_w) = image.dim();
292 if img_h == 0 || img_w == 0 {
293 return Err(VisionError::InvalidParameter(
294 "image_encoder: image must have non-zero dimensions".into(),
295 ));
296 }
297
298 let flat_len = img_h * img_w;
300 let mut flat = Array2::zeros((flat_len, 1));
301 for r in 0..img_h {
302 for c in 0..img_w {
303 flat[[r * img_w + c, 0]] = image[[r, c]];
304 }
305 }
306
307 let (mut h, mut w, mut features) = self.initial_embed.forward(&flat, img_h, img_w)?;
309 layer_norm(&mut features, 1e-5);
310 relu_inplace(&mut features);
311
312 let mut multi_scale: Vec<Array2<f64>> = Vec::with_capacity(self.config.encoder_stages);
314 for stage in &self.stages {
315 let (nh, nw, new_feat) = stage.forward(&features, h, w)?;
316 multi_scale.push(new_feat.clone());
317 h = nh;
318 w = nw;
319 features = new_feat;
320 }
321
322 Ok(multi_scale)
323 }
324
325 pub fn stage_channels(&self) -> Vec<usize> {
327 let base_ch = self.config.embed_dim / 4;
328 let mut channels = Vec::with_capacity(self.config.encoder_stages);
329 let mut ch = base_ch;
330 for _ in 0..self.config.encoder_stages {
331 ch = (ch * 2).min(self.config.embed_dim);
332 channels.push(ch);
333 }
334 channels
335 }
336}
337
338#[cfg(test)]
343mod tests {
344 use super::*;
345 use scirs2_core::ndarray::Array2;
346
347 #[test]
348 fn test_patch_embedding_forward() {
349 let pe = PatchEmbedding::new(1, 4, 3, 2);
350 let input = Array2::ones((16, 1)); let (oh, ow, out) = pe.forward(&input, 4, 4).expect("forward failed");
352 assert_eq!(oh, 2);
353 assert_eq!(ow, 2);
354 assert_eq!(out.dim(), (4, 4));
355 }
356
357 #[test]
358 fn test_layer_norm() {
359 let mut arr = Array2::from_shape_vec((2, 4), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
360 .expect("shape");
361 layer_norm(&mut arr, 1e-5);
362 let row_mean: f64 = (0..4).map(|c| arr[[0, c]]).sum::<f64>() / 4.0;
364 assert!(row_mean.abs() < 1e-6);
365 }
366
367 #[test]
368 fn test_simple_image_encoder_smoke() {
369 let cfg = SAMConfig {
370 image_size: 32,
371 embed_dim: 16,
372 num_mask_outputs: 3,
373 iou_head_hidden: 16,
374 encoder_stages: 2,
375 };
376 let encoder = SimpleImageEncoder::new(&cfg);
377 let image = Array2::from_elem((8, 8), 0.5);
378 let features = encoder.encode(&image).expect("encode failed");
379 assert_eq!(features.len(), 2);
380 for f in &features {
382 assert!(f.dim().0 > 0);
383 assert!(f.dim().1 > 0);
384 }
385 }
386
387 #[test]
388 fn test_encoder_stage_channels() {
389 let cfg = SAMConfig {
390 embed_dim: 64,
391 encoder_stages: 3,
392 ..SAMConfig::default()
393 };
394 let enc = SimpleImageEncoder::new(&cfg);
395 let chs = enc.stage_channels();
396 assert_eq!(chs, vec![32, 64, 64]);
397 }
398
399 #[test]
400 fn test_patch_embedding_channel_mismatch() {
401 let pe = PatchEmbedding::new(3, 8, 3, 2);
402 let input = Array2::ones((16, 1)); let err = pe.forward(&input, 4, 4);
404 assert!(err.is_err());
405 }
406
407 #[test]
408 fn test_encoder_empty_image() {
409 let cfg = SAMConfig::default();
410 let enc = SimpleImageEncoder::new(&cfg);
411 let img = Array2::<f64>::zeros((0, 0));
412 assert!(enc.encode(&img).is_err());
413 }
414}