1use scirs2_core::ndarray::{s, Array2, ArrayView2};
8use scirs2_core::numeric::{Float, FromPrimitive};
9use std::collections::{HashMap, HashSet};
10
11use crate::error::{NdimageError, NdimageResult};
12use crate::hyperdimensional_computing::memory::HDCMemory;
13use crate::hyperdimensional_computing::types::{
14 CompositionResult, FeatureDetection, HDCConfig, Hypervector, PatternMatch, SequenceEncoding,
15};
16use crate::hyperdimensional_computing::utils::{
17 analyze_patch_for_feature, non_maximum_suppression,
18};
19use crate::hyperdimensional_computing::vector_ops::vector_utils;
20
21#[derive(Debug, Clone)]
23pub struct ImageHDCEncoder {
24 pub height: usize,
26 pub width: usize,
27 pub position_encoders: HashMap<(usize, usize), Hypervector>,
29 pub value_encoders: HashMap<u8, Hypervector>,
31 pub feature_encoders: HashMap<String, Hypervector>,
33 pub config: HDCConfig,
35}
36
37impl ImageHDCEncoder {
38 pub fn new(height: usize, width: usize, config: HDCConfig) -> Self {
50 let mut encoder = Self {
51 height,
52 width,
53 position_encoders: HashMap::new(),
54 value_encoders: HashMap::new(),
55 feature_encoders: HashMap::new(),
56 config,
57 };
58
59 encoder.initialize_encoders();
60 encoder
61 }
62
63 fn initialize_encoders(&mut self) {
65 for y in 0..self.height {
67 for x in 0..self.width {
68 let position_hv =
69 Hypervector::random(self.config.hypervector_dim, self.config.sparsity);
70 self.position_encoders.insert((y, x), position_hv);
71 }
72 }
73
74 for value in 0..=255 {
76 let value_hv = Hypervector::random(self.config.hypervector_dim, self.config.sparsity);
77 self.value_encoders.insert(value, value_hv);
78 }
79 }
80
81 pub fn encode_image<T>(&self, image: ArrayView2<T>) -> NdimageResult<Hypervector>
91 where
92 T: Float + FromPrimitive + Copy,
93 {
94 let (img_height, img_width) = image.dim();
95
96 if img_height > self.height || img_width > self.width {
97 return Err(NdimageError::InvalidInput(format!(
98 "Image size ({}×{}) exceeds encoder capacity ({}×{})",
99 img_height, img_width, self.height, self.width
100 )));
101 }
102
103 let mut encodings = Vec::new();
104
105 for y in 0..img_height {
106 for x in 0..img_width {
107 let pixel_value = image[[y, x]];
108
109 let intensity = (pixel_value.to_f64().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
111
112 if let (Some(pos_hv), Some(val_hv)) = (
114 self.position_encoders.get(&(y, x)),
115 self.value_encoders.get(&intensity),
116 ) {
117 let pixel_encoding = pos_hv.bind(val_hv)?;
119 encodings.push(pixel_encoding);
120 }
121 }
122 }
123
124 if encodings.is_empty() {
126 Ok(Hypervector::zeros(self.config.hypervector_dim))
127 } else {
128 vector_utils::bundle_multiple(&encodings)
129 }
130 }
131
132 pub fn encode_patch<T>(
143 &self,
144 patch: ArrayView2<T>,
145 feature_type: &str,
146 ) -> NdimageResult<Hypervector>
147 where
148 T: Float + FromPrimitive + Copy,
149 {
150 let base_encoding = self.encode_image(patch)?;
151
152 if let Some(feature_hv) = self.feature_encoders.get(feature_type) {
153 base_encoding.bind(feature_hv)
154 } else {
155 Ok(base_encoding)
156 }
157 }
158
159 pub fn add_feature_encoder(&mut self, feature_type: String, feature_hv: Hypervector) {
166 self.feature_encoders.insert(feature_type, feature_hv);
167 }
168
169 pub fn get_position_encoding(&self, y: usize, x: usize) -> Option<&Hypervector> {
171 self.position_encoders.get(&(y, x))
172 }
173
174 pub fn get_value_encoding(&self, intensity: u8) -> Option<&Hypervector> {
176 self.value_encoders.get(&intensity)
177 }
178}
179
180#[allow(dead_code)]
194pub fn hdc_pattern_recognition<T>(
195 image: ArrayView2<T>,
196 patterns: &[(&ArrayView2<T>, &str)],
197 config: &HDCConfig,
198) -> NdimageResult<Vec<PatternMatch>>
199where
200 T: Float + FromPrimitive + Copy + Send + Sync,
201{
202 let (height, width) = image.dim();
203 let encoder = ImageHDCEncoder::new(height, width, config.clone());
204 let mut memory = HDCMemory::new(config.clone());
205
206 for (pattern, label) in patterns {
208 let encoded_pattern = encoder.encode_image(**pattern)?;
209 memory.store(label.to_string(), encoded_pattern);
210 }
211
212 let mut matches = Vec::new();
213 let patch_size = 32; for y in 0..height.saturating_sub(patch_size) {
217 for x in 0..width.saturating_sub(patch_size) {
218 let patch = image.slice(s![y..y + patch_size, x..x + patch_size]);
219 let encoded_patch = encoder.encode_image(patch)?;
220
221 if let Some((matched_label, confidence)) = memory.retrieve(&encoded_patch) {
222 matches.push(PatternMatch {
223 label: matched_label,
224 confidence,
225 position: (y, x),
226 size: (patch_size, patch_size),
227 });
228 }
229 }
230 }
231
232 let filtered_matches = non_maximum_suppression(matches, 0.5)?;
234
235 Ok(filtered_matches)
236}
237
238#[allow(dead_code)]
243pub fn hdc_feature_detection<T>(
244 image: ArrayView2<T>,
245 feature_types: &[String],
246 config: &HDCConfig,
247) -> NdimageResult<HashMap<String, Vec<FeatureDetection>>>
248where
249 T: Float + FromPrimitive + Copy + Send + Sync,
250{
251 let (height, width) = image.dim();
252 let mut encoder = ImageHDCEncoder::new(height, width, config.clone());
253
254 for feature_type in feature_types {
256 let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
257 encoder.add_feature_encoder(feature_type.clone(), feature_hv);
258 }
259
260 let mut feature_detections = HashMap::new();
261
262 for feature_type in feature_types {
263 let mut detections = Vec::new();
264 let window_size = 16; for y in 0..height.saturating_sub(window_size) {
267 for x in 0..width.saturating_sub(window_size) {
268 let patch = image.slice(s![y..y + window_size, x..x + window_size]);
269
270 let feature_strength = analyze_patch_for_feature(&patch, feature_type)?;
272
273 if feature_strength > config.similarity_threshold {
274 let encoded_feature = encoder.encode_patch(patch, feature_type)?;
275
276 detections.push(FeatureDetection {
277 feature_type: feature_type.clone(),
278 position: (y, x),
279 strength: feature_strength,
280 hypervector: encoded_feature,
281 patch_size: (window_size, window_size),
282 });
283 }
284 }
285 }
286
287 feature_detections.insert(feature_type.clone(), detections);
288 }
289
290 Ok(feature_detections)
291}
292
293#[allow(dead_code)]
298pub fn hdc_sequence_processing<T>(
299 image_sequence: &[ArrayView2<T>],
300 sequence_length: usize,
301 config: &HDCConfig,
302) -> NdimageResult<SequenceEncoding>
303where
304 T: Float + FromPrimitive + Copy + Send + Sync,
305{
306 if image_sequence.is_empty() {
307 return Err(NdimageError::InvalidInput("Empty image sequence".into()));
308 }
309
310 let (height, width) = image_sequence[0].dim();
311 let encoder = ImageHDCEncoder::new(height, width, config.clone());
312
313 let mut temporal_encoders = Vec::new();
315 for t in 0..sequence_length {
316 temporal_encoders.push(Hypervector::random(config.hypervector_dim, config.sparsity));
317 }
318
319 let mut sequence_encodings = Vec::new();
320 let mut temporal_positions = Vec::new();
321
322 for (t, image) in image_sequence.iter().enumerate().take(sequence_length) {
324 let frame_encoding = encoder.encode_image(*image)?;
325
326 if t < temporal_encoders.len() {
327 let temporal_binding = frame_encoding.bind(&temporal_encoders[t])?;
328 sequence_encodings.push(temporal_binding);
329 temporal_positions.push(t);
330 }
331 }
332
333 let final_encoding = if sequence_encodings.is_empty() {
335 Hypervector::zeros(config.hypervector_dim)
336 } else {
337 vector_utils::bundle_multiple(&sequence_encodings)?
338 };
339
340 let mut similarities = Vec::new();
342 for i in 0..sequence_encodings.len().saturating_sub(1) {
343 let sim = sequence_encodings[i].similarity(&sequence_encodings[i + 1]);
344 similarities.push(sim);
345 }
346
347 let confidence = if similarities.is_empty() {
348 1.0
349 } else {
350 similarities.iter().sum::<f64>() / similarities.len() as f64
351 };
352
353 Ok(SequenceEncoding {
354 encoding: final_encoding,
355 temporal_positions,
356 confidence,
357 })
358}
359
360#[allow(dead_code)]
364pub fn multiscale_encoding<T>(
365 image: ArrayView2<T>,
366 scales: &[f64],
367 config: &HDCConfig,
368) -> NdimageResult<Hypervector>
369where
370 T: Float + FromPrimitive + Copy,
371{
372 let (height, width) = image.dim();
373 let mut scale_encodings = Vec::new();
374
375 for &scale in scales {
376 let scaled_height = (height as f64 * scale) as usize;
377 let scaled_width = (width as f64 * scale) as usize;
378
379 if scaled_height == 0 || scaled_width == 0 {
380 continue;
381 }
382
383 let mut scaled_image = Array2::zeros((scaled_height, scaled_width));
385 for y in 0..scaled_height {
386 for x in 0..scaled_width {
387 let orig_y = (y * height) / scaled_height;
388 let orig_x = (x * width) / scaled_width;
389 scaled_image[[y, x]] = image[[orig_y, orig_x]];
390 }
391 }
392
393 let encoder = ImageHDCEncoder::new(scaled_height, scaled_width, config.clone());
394 let scale_encoding = encoder.encode_image(scaled_image.view())?;
395 scale_encodings.push(scale_encoding);
396 }
397
398 if scale_encodings.is_empty() {
399 Ok(Hypervector::zeros(config.hypervector_dim))
400 } else {
401 vector_utils::bundle_multiple(&scale_encodings)
402 }
403}
404
405pub fn create_patches<T>(
407 image: ArrayView2<T>,
408 patch_size: (usize, usize),
409 stride: (usize, usize),
410) -> Vec<Array2<T>>
411where
412 T: Clone,
413{
414 let (height, width) = image.dim();
415 let (patch_h, patch_w) = patch_size;
416 let (stride_y, stride_x) = stride;
417
418 let mut patches = Vec::new();
419
420 let mut y = 0;
421 while y + patch_h <= height {
422 let mut x = 0;
423 while x + patch_w <= width {
424 let patch_view = image.slice(s![y..y + patch_h, x..x + patch_w]);
425 let patch =
426 Array2::from_shape_fn((patch_h, patch_w), |(i, j)| patch_view[[i, j]].clone());
427 patches.push(patch);
428 x += stride_x;
429 }
430 y += stride_y;
431 }
432
433 patches
434}
435
436pub fn encode_semantic_concepts(
438 concepts: &[String],
439 config: &HDCConfig,
440) -> NdimageResult<Hypervector> {
441 if concepts.is_empty() {
442 return Ok(Hypervector::zeros(config.hypervector_dim));
443 }
444
445 let mut concept_encodings = Vec::new();
446
447 for concept in concepts {
448 let mut concept_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
450
451 use std::collections::hash_map::DefaultHasher;
453 use std::hash::{Hash, Hasher};
454
455 let mut hasher = DefaultHasher::new();
456 concept.hash(&mut hasher);
457 let hash_value = hasher.finish();
458
459 let permutation_seed = hash_value as usize;
461 if permutation_seed % 2 == 0 {
462 concept_hv = concept_hv.scale(-1.0); }
464
465 concept_encodings.push(concept_hv);
466 }
467
468 vector_utils::bundle_multiple(&concept_encodings)
469}
470
471#[allow(dead_code)]
476pub fn hdc_image_classification<T>(
477 images: &[ArrayView2<T>],
478 labels: &[String],
479 test_images: &[ArrayView2<T>],
480 config: &HDCConfig,
481) -> NdimageResult<Vec<(String, f64)>>
482where
483 T: Float + FromPrimitive + Copy + Send + Sync,
484{
485 if images.is_empty() || images.len() != labels.len() {
486 return Err(NdimageError::InvalidInput(
487 "Invalid training data".to_string(),
488 ));
489 }
490
491 let (height, width) = images[0].dim();
492
493 let encoder = ImageHDCEncoder::new(height, width, config.clone());
495 let mut memory = HDCMemory::new(config.clone());
496
497 for (image, label) in images.iter().zip(labels.iter()) {
499 let encoded_image = encoder.encode_image(*image)?;
500
501 if let Some(existing) = memory.patterns.get(label) {
503 let bundled = existing.bundle(&encoded_image)?;
504 memory.store(label.clone(), bundled);
505 } else {
506 memory.store(label.clone(), encoded_image);
507 }
508 }
509
510 let mut results = Vec::new();
512
513 for test_image in test_images {
514 let encoded_test = encoder.encode_image(*test_image)?;
515
516 if let Some((predicted_label, confidence)) = memory.retrieve(&encoded_test) {
517 results.push((predicted_label, confidence));
518 } else {
519 results.push(("unknown".to_string(), 0.0));
520 }
521 }
522
523 Ok(results)
524}
525
526#[allow(dead_code)]
531pub fn hdc_pattern_matching<T>(
532 image: ArrayView2<T>,
533 patterns: &[(ArrayView2<T>, String)],
534 config: &HDCConfig,
535) -> NdimageResult<Vec<PatternMatch>>
536where
537 T: Float + FromPrimitive + Copy + Send + Sync,
538{
539 let (height, width) = image.dim();
540 let encoder = ImageHDCEncoder::new(height, width, config.clone());
541 let mut memory = HDCMemory::new(config.clone());
542
543 for (pattern, label) in patterns {
545 let encoded_pattern = encoder.encode_image(*pattern)?;
546 memory.store(label.clone(), encoded_pattern);
547 }
548
549 let mut matches = Vec::new();
550 let patch_size = 32; for y in 0..height.saturating_sub(patch_size) {
554 for x in 0..width.saturating_sub(patch_size) {
555 let patch = image.slice(s![y..y + patch_size, x..x + patch_size]);
556 let encoded_patch = encoder.encode_image(patch)?;
557
558 if let Some((matched_label, confidence)) = memory.retrieve(&encoded_patch) {
559 matches.push(PatternMatch {
560 label: matched_label,
561 confidence,
562 position: (y, x),
563 size: (patch_size, patch_size),
564 });
565 }
566 }
567 }
568
569 let filtered_matches = non_maximum_suppression(matches, 0.5)?;
571
572 Ok(filtered_matches)
573}
574
575#[allow(dead_code)]
580pub fn hdc_compositional_reasoning<T>(
581 image: ArrayView2<T>,
582 concept_memory: &HDCMemory,
583 query_concepts: &[String],
584 config: &HDCConfig,
585) -> NdimageResult<CompositionResult>
586where
587 T: Float + FromPrimitive + Copy + Send + Sync,
588{
589 let (height, width) = image.dim();
590 let encoder = ImageHDCEncoder::new(height, width, config.clone());
591
592 let encoded_image = encoder.encode_image(image)?;
594
595 let mut composed_query: Option<Hypervector> = None;
597
598 for concept in query_concepts {
599 if let Some(concept_hv) = concept_memory.get_item(concept) {
600 if let Some(ref current_query) = composed_query {
601 composed_query = Some(current_query.bind(concept_hv)?);
602 } else {
603 composed_query = Some(concept_hv.clone());
604 }
605 }
606 }
607
608 if let Some(query_hv) = composed_query {
609 let similarity = encoded_image.similarity(&query_hv);
611
612 let mut concept_presence = HashMap::new();
614
615 for (concept_name, concept_hv) in &concept_memory.item_memory {
616 let presence_strength = encoded_image.similarity(concept_hv);
617 if presence_strength > config.cleanup_threshold {
618 concept_presence.insert(concept_name.clone(), presence_strength);
619 }
620 }
621
622 Ok(CompositionResult {
623 query_similarity: similarity,
624 concept_presence,
625 composed_representation: query_hv,
626 image_representation: encoded_image,
627 })
628 } else {
629 Err(NdimageError::InvalidInput(
630 "No valid concepts found".to_string(),
631 ))
632 }
633}
634
635#[cfg(test)]
636mod tests {
637 use super::*;
638 use crate::hyperdimensional_computing::calculate_overlap;
639 use scirs2_core::ndarray::Array2;
640
641 #[test]
642 fn test_image_hdc_encoder_creation() {
643 let config = HDCConfig::default();
644 let encoder = ImageHDCEncoder::new(10, 10, config);
645
646 assert_eq!(encoder.height, 10);
647 assert_eq!(encoder.width, 10);
648 assert_eq!(encoder.position_encoders.len(), 100); assert_eq!(encoder.value_encoders.len(), 256); }
651
652 #[test]
653 fn test_image_encoding() {
654 let config = HDCConfig::default();
655 let encoder = ImageHDCEncoder::new(5, 5, config);
656
657 let image = Array2::from_shape_fn((5, 5), |(i, j)| (i + j) as f64 / 10.0);
658 let result = encoder.encode_image(image.view());
659
660 assert!(result.is_ok());
661 let encoded = result.expect("Operation failed");
662 assert_eq!(encoded.dimension, encoder.config.hypervector_dim);
663 assert!(encoded.norm > 0.0);
664 }
665
666 #[test]
667 fn test_feature_encoder() {
668 let config = HDCConfig::default();
669 let mut encoder = ImageHDCEncoder::new(5, 5, config.clone());
670
671 let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
672 encoder.add_feature_encoder("test_feature".to_string(), feature_hv);
673
674 assert!(encoder.feature_encoders.contains_key("test_feature"));
675 }
676
677 #[test]
678 fn test_patch_encoding() {
679 let config = HDCConfig::default();
680 let mut encoder = ImageHDCEncoder::new(10, 10, config.clone());
681
682 let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
683 encoder.add_feature_encoder("edge".to_string(), feature_hv);
684
685 let patch = Array2::<f64>::ones((3, 3));
686 let result = encoder.encode_patch(patch.view(), "edge");
687
688 assert!(result.is_ok());
689 let encoded = result.expect("Operation failed");
690 assert_eq!(encoded.dimension, encoder.config.hypervector_dim);
691 }
692
693 #[test]
694 fn test_multiscale_encoding() {
695 let config = HDCConfig {
697 hypervector_dim: 500, ..Default::default()
699 };
700 let image = Array2::from_shape_fn((10, 10), |(i, j)| (i + j) as f64 / 20.0);
701 let scales = vec![1.0, 0.5];
702
703 let result = multiscale_encoding(image.view(), &scales, &config);
704 assert!(result.is_ok());
705
706 let encoded = result.expect("Operation failed");
707 assert_eq!(encoded.dimension, config.hypervector_dim);
708 assert!(encoded.norm > 0.0);
709 }
710
711 #[test]
712 fn test_create_patches() {
713 let image = Array2::from_shape_fn((10, 10), |(i, j)| i + j);
714 let patches = create_patches(image.view(), (3, 3), (2, 2));
715
716 assert!(!patches.is_empty());
717
718 assert!(!patches.is_empty());
721
722 for patch in &patches {
723 assert_eq!(patch.dim(), (3, 3));
724 }
725 }
726
727 #[test]
728 fn test_encode_semantic_concepts() {
729 let config = HDCConfig::default();
730 let concepts = vec!["cat".to_string(), "dog".to_string(), "bird".to_string()];
731
732 let result = encode_semantic_concepts(&concepts, &config);
733 assert!(result.is_ok());
734
735 let encoded = result.expect("Operation failed");
736 assert_eq!(encoded.dimension, config.hypervector_dim);
737 assert!(encoded.norm > 0.0);
738
739 let result2 = encode_semantic_concepts(&concepts, &config);
741 assert!(result2.is_ok());
742 let encoded2 = result2.expect("Operation failed");
743 assert_eq!(encoded2.dimension, config.hypervector_dim);
744 assert!(encoded2.norm > 0.0);
745 }
746
747 #[test]
748 fn test_analyze_patch_for_feature() {
749 let patch = Array2::<f64>::ones((5, 5));
750
751 let edge_strength =
752 analyze_patch_for_feature(&patch.view(), "edge").expect("Operation failed");
753 assert_eq!(edge_strength, 0.8);
754
755 let corner_strength =
756 analyze_patch_for_feature(&patch.view(), "corner").expect("Operation failed");
757 assert_eq!(corner_strength, 0.6);
758
759 let texture_strength =
760 analyze_patch_for_feature(&patch.view(), "texture").expect("Operation failed");
761 assert_eq!(texture_strength, 0.7);
762
763 let unknown_strength =
764 analyze_patch_for_feature(&patch.view(), "unknown").expect("Operation failed");
765 assert_eq!(unknown_strength, 0.5);
766 }
767
768 #[test]
769 fn test_calculate_overlap() {
770 let match1 = PatternMatch {
771 label: "test1".to_string(),
772 confidence: 0.9,
773 position: (0, 0),
774 size: (10, 10),
775 };
776
777 let match2 = PatternMatch {
778 label: "test2".to_string(),
779 confidence: 0.8,
780 position: (5, 5),
781 size: (10, 10),
782 };
783
784 let overlap = calculate_overlap(&match1, &match2);
785 assert!(overlap > 0.0);
786 assert!(overlap < 1.0);
787
788 let match3 = PatternMatch {
790 label: "test3".to_string(),
791 confidence: 0.7,
792 position: (15, 15),
793 size: (5, 5),
794 };
795
796 let no_overlap = calculate_overlap(&match1, &match3);
797 assert_eq!(no_overlap, 0.0);
798 }
799
800 #[test]
801 fn test_sequence_encoding() {
802 let config = HDCConfig::default();
803
804 let img1 = Array2::zeros((5, 5));
805 let img2 = Array2::ones((5, 5));
806 let img3 = Array2::from_elem((5, 5), 0.5);
807
808 let sequence = vec![img1.view(), img2.view(), img3.view()];
809 let result = hdc_sequence_processing(&sequence, 3, &config);
810
811 assert!(result.is_ok());
812 let seq_encoding = result.expect("Operation failed");
813
814 assert_eq!(seq_encoding.encoding.dimension, config.hypervector_dim);
815 assert_eq!(seq_encoding.temporal_positions.len(), 3);
816 assert!(seq_encoding.confidence >= -1.0); assert!(seq_encoding.confidence <= 1.0);
818 }
819}