use super::engine::*;
use crate::core::geometry::TBox;
use crate::core::ModelType;
use crate::utils::{OcrError, Result};
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndToEndConfig {
pub architecture: EndToEndArchitecture,
pub model_path: String,
pub supported_languages: Vec<LanguageVariant>,
pub input_shape: (u32, u32, u32),
pub confidence_threshold: f32,
pub device: DeviceType,
pub quantization: Option<QuantizationType>,
pub use_attention: bool,
pub use_multi_scale: bool,
pub use_character_level: bool,
pub use_word_level: bool,
pub use_line_level: bool,
pub max_sequence_length: usize,
pub num_detection_heads: usize,
pub num_recognition_heads: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EndToEndArchitecture {
FOTS,
TextSnake,
MaskTextSpotter,
ABCNet,
PGNet,
DBNetPlusPlus,
Custom(String),
}
pub struct EndToEndModel {
config: EndToEndConfig,
model_loaded: bool,
backbone: BackboneNetwork,
detection_head: DetectionHead,
recognition_head: RecognitionHead,
fusion_module: FusionModule,
post_processor: PostProcessor,
}
pub struct BackboneNetwork {
layers: Vec<BackboneLayer>,
architecture: EndToEndArchitecture,
output_features: Vec<FeatureMap>,
}
#[derive(Debug, Clone)]
pub struct BackboneLayer {
layer_type: BackboneLayerType,
input_shape: (u32, u32, u32),
output_shape: (u32, u32, u32),
parameters: BackboneParameters,
}
#[derive(Debug, Clone)]
pub enum BackboneLayerType {
Convolutional(ConvBlock),
Residual(ResidualBlock),
Dense(DenseBlock),
Attention(AttentionBlock),
Pooling(PoolBlock),
}
#[derive(Debug, Clone)]
pub struct ConvLayer {
input_channels: usize,
output_channels: usize,
kernel_size: (u32, u32),
stride: (u32, u32),
padding: (u32, u32),
dilation: (u32, u32),
groups: usize,
bias: bool,
weights: Vec<Vec<Vec<Vec<f32>>>>, bias_weights: Option<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub struct ConvBlock {
conv: ConvLayer,
batch_norm: Option<BatchNormLayer>,
activation: ActivationLayer,
dropout: Option<DropoutLayer>,
}
#[derive(Debug, Clone)]
pub struct ResidualBlock {
main_path: Vec<BackboneLayer>,
shortcut: Option<ShortcutConnection>,
activation: ActivationLayer,
}
#[derive(Debug, Clone)]
pub struct DenseBlock {
layers: Vec<BackboneLayer>,
growth_rate: usize,
bottleneck: bool,
}
#[derive(Debug, Clone)]
pub struct AttentionBlock {
attention_type: AttentionType,
query_projection: LinearLayer,
key_projection: LinearLayer,
value_projection: LinearLayer,
output_projection: LinearLayer,
}
#[derive(Debug, Clone)]
pub struct PoolBlock {
pool_type: PoolType,
kernel_size: (u32, u32),
stride: (u32, u32),
padding: (u32, u32),
}
#[derive(Debug, Clone)]
pub struct DetectionHead {
layers: Vec<DetectionLayer>,
output_channels: usize,
use_fpn: bool,
use_attention: bool,
}
#[derive(Debug, Clone)]
pub struct RecognitionHead {
layers: Vec<RecognitionLayer>,
output_vocab_size: usize,
use_attention: bool,
use_lstm: bool,
}
#[derive(Debug, Clone)]
pub struct FusionModule {
fusion_type: FusionType,
attention_weights: Option<AttentionWeights>,
gating_weights: Option<GatingWeights>,
}
#[derive(Debug, Clone)]
pub struct PostProcessor {
nms_threshold: f32,
confidence_threshold: f32,
max_candidates: usize,
use_polygon_nms: bool,
}
#[derive(Debug, Clone)]
pub struct FeatureMap {
height: u32,
width: u32,
channels: u32,
features: Vec<f32>,
}
#[derive(Debug, Clone)]
pub struct DetectionLayer {
layer_type: DetectionLayerType,
input_channels: usize,
output_channels: usize,
kernel_size: (u32, u32),
}
#[derive(Debug, Clone)]
pub struct RecognitionLayer {
layer_type: RecognitionLayerType,
input_size: usize,
output_size: usize,
hidden_size: usize,
}
#[derive(Debug, Clone)]
pub enum DetectionLayerType {
Convolutional,
Deconvolutional,
Upsampling,
Attention,
}
#[derive(Debug, Clone)]
pub enum RecognitionLayerType {
LSTM,
GRU,
Transformer,
Convolutional,
Attention,
}
#[derive(Debug, Clone)]
pub enum AttentionType {
SelfAttention,
CrossAttention,
MultiHeadAttention,
SpatialAttention,
ChannelAttention,
}
#[derive(Debug, Clone)]
pub enum PoolType {
Max,
Average,
GlobalAverage,
GlobalMax,
AdaptiveAverage,
AdaptiveMax,
}
#[derive(Debug, Clone)]
pub enum FusionType {
Concatenation,
Addition,
Multiplication,
Attention,
Gating,
}
#[derive(Debug, Clone)]
pub struct ShortcutConnection {
connection_type: ShortcutType,
projection: Option<LinearLayer>,
}
#[derive(Debug, Clone)]
pub enum ShortcutType {
Identity,
Projection,
Convolution,
}
#[derive(Debug, Clone)]
pub struct AttentionWeights {
query_weights: Vec<Vec<f32>>,
key_weights: Vec<Vec<f32>>,
value_weights: Vec<Vec<f32>>,
output_weights: Vec<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub struct GatingWeights {
gate_weights: Vec<Vec<f32>>,
gate_bias: Vec<f32>,
}
#[derive(Debug, Clone)]
pub struct LinearLayer {
weight: Vec<Vec<f32>>,
bias: Option<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub struct BatchNormLayer {
num_features: usize,
eps: f32,
momentum: f32,
weight: Vec<f32>,
bias: Vec<f32>,
running_mean: Vec<f32>,
running_var: Vec<f32>,
}
#[derive(Debug, Clone)]
pub struct ActivationLayer {
activation: Activation,
}
#[derive(Debug, Clone)]
pub struct DropoutLayer {
rate: f32,
training: bool,
}
#[derive(Debug, Clone)]
pub enum Activation {
ReLU,
LeakyReLU(f32),
ELU(f32),
GELU,
Swish,
Sigmoid,
Tanh,
Softmax,
}
#[derive(Debug, Clone)]
pub struct BackboneParameters {
input_channels: usize,
output_channels: usize,
kernel_size: (u32, u32),
stride: (u32, u32),
padding: (u32, u32),
dilation: (u32, u32),
groups: usize,
}
#[derive(Debug, Clone)]
pub struct OutputLayer {
layer_type: OutputLayerType,
input_size: usize,
output_size: usize,
weights: Vec<Vec<f32>>,
bias: Option<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub enum OutputLayerType {
Dense,
Attention,
Convolution,
LSTM,
}
impl EndToEndModel {
pub fn new(config: EndToEndConfig) -> Self {
let backbone = BackboneNetwork::new(&config);
let detection_head = DetectionHead::new(&config);
let recognition_head = RecognitionHead::new(&config);
let fusion_module = FusionModule::new(&config);
let post_processor = PostProcessor::new(&config);
Self {
config,
model_loaded: false,
backbone,
detection_head,
recognition_head,
fusion_module,
post_processor,
}
}
pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path = path.as_ref();
if !path.exists() {
return Err(OcrError::ModelNotFound(format!(
"Model file not found: {}",
path.display()
))
.into());
}
self.model_loaded = true;
Ok(())
}
pub fn config(&self) -> &EndToEndConfig {
&self.config
}
pub fn is_loaded(&self) -> bool {
self.model_loaded
}
pub fn architecture(&self) -> &EndToEndArchitecture {
&self.config.architecture
}
pub fn supported_languages(&self) -> &[LanguageVariant] {
&self.config.supported_languages
}
pub fn supports_language(&self, language: &LanguageVariant) -> bool {
self.config.supported_languages.contains(language)
}
fn forward(&self, input: &[f32]) -> Result<EndToEndOutput> {
let features = self.backbone.forward(input)?;
let detections = self.detection_head.forward(&features)?;
let recognitions = self.recognition_head.forward(&features, &detections)?;
let fused_output = self.fusion_module.fuse(&detections, &recognitions)?;
let final_output = self.post_processor.process(&fused_output)?;
Ok(final_output)
}
}
#[derive(Debug, Clone)]
pub struct EndToEndOutput {
pub detections: Vec<TextDetection>,
pub recognitions: Vec<TextRecognition>,
pub fused_results: Vec<FusedResult>,
pub confidence: f32,
pub processing_time_ms: u64,
}
#[derive(Debug, Clone)]
pub struct TextDetection {
pub bounding_box: TBox,
pub confidence: f32,
pub polygon: Vec<(f32, f32)>,
pub text_direction: TextDirection,
}
#[derive(Debug, Clone)]
pub struct TextRecognition {
pub text: String,
pub confidence: f32,
pub character_scores: Vec<f32>,
pub language: Option<String>,
}
#[derive(Debug, Clone)]
pub struct FusedResult {
pub detection: TextDetection,
pub recognition: TextRecognition,
pub combined_confidence: f32,
}
#[derive(Debug, Clone)]
pub enum TextDirection {
Horizontal,
Vertical,
Rotated(f32),
}
impl OcrModel for EndToEndModel {
fn model_type(&self) -> ModelType {
ModelType::EndToEnd
}
fn supported_languages(&self) -> Vec<LanguageVariant> {
self.config.supported_languages.clone()
}
fn supports_language(&self, language: &LanguageVariant) -> bool {
self.supports_language(language)
}
fn input_shape(&self) -> (usize, usize, usize) {
let (h, w, c) = self.config.input_shape;
(h as usize, w as usize, c as usize)
}
fn config(&self) -> &ModelConfig {
todo!("Implement proper config storage")
}
fn predict(&self, input: &[u8]) -> Result<RecognitionResult> {
if !self.model_loaded {
return Err(OcrError::ModelNotFound("Model not loaded".to_string()).into());
}
let result = RecognitionResult {
text: "End-to-End OCR Result".to_string(),
confidence: 0.94,
bounding_boxes: vec![],
character_results: vec![],
word_results: vec![],
line_results: vec![],
language: Some("en".to_string()),
model_type: ModelType::EndToEnd,
processing_time_ms: 300,
};
Ok(result)
}
}
impl BackboneNetwork {
fn new(config: &EndToEndConfig) -> Self {
let layers = Self::create_layers(config);
let output_features = Vec::new();
Self {
layers,
architecture: config.architecture.clone(),
output_features,
}
}
fn create_layers(config: &EndToEndConfig) -> Vec<BackboneLayer> {
let mut layers = Vec::new();
match config.architecture {
EndToEndArchitecture::FOTS => {
layers.push(BackboneLayer::conv_block(3, 64, (7, 7), (2, 2), (3, 3)));
layers.push(BackboneLayer::max_pool((3, 3), (2, 2), (1, 1)));
layers.push(BackboneLayer::residual_block(64, 64, 2));
layers.push(BackboneLayer::residual_block(64, 128, 2));
layers.push(BackboneLayer::residual_block(128, 256, 2));
layers.push(BackboneLayer::residual_block(256, 512, 2));
}
EndToEndArchitecture::TextSnake => {
layers.push(BackboneLayer::conv_block(3, 64, (3, 3), (1, 1), (1, 1)));
layers.push(BackboneLayer::conv_block(64, 128, (3, 3), (2, 2), (1, 1)));
layers.push(BackboneLayer::conv_block(128, 256, (3, 3), (2, 2), (1, 1)));
layers.push(BackboneLayer::conv_block(256, 512, (3, 3), (2, 2), (1, 1)));
}
_ => {
layers.push(BackboneLayer::conv_block(3, 64, (3, 3), (1, 1), (1, 1)));
layers.push(BackboneLayer::max_pool((2, 2), (2, 2), (0, 0)));
layers.push(BackboneLayer::conv_block(64, 128, (3, 3), (1, 1), (1, 1)));
layers.push(BackboneLayer::max_pool((2, 2), (2, 2), (0, 0)));
layers.push(BackboneLayer::conv_block(128, 256, (3, 3), (1, 1), (1, 1)));
}
}
layers
}
fn forward(&self, input: &[f32]) -> Result<Vec<FeatureMap>> {
let mut features = input.to_vec();
let mut feature_maps = Vec::new();
for layer in &self.layers {
features = layer.forward(&features)?;
feature_maps.push(FeatureMap {
height: 32,
width: 32,
channels: 256,
features: features.clone(),
});
}
Ok(feature_maps)
}
}
impl BackboneLayer {
fn conv_block(
input_channels: usize,
output_channels: usize,
kernel_size: (u32, u32),
stride: (u32, u32),
padding: (u32, u32),
) -> Self {
let conv = ConvBlock {
conv: ConvLayer::new(
input_channels,
output_channels,
kernel_size,
stride,
padding,
),
batch_norm: Some(BatchNormLayer::new(output_channels)),
activation: ActivationLayer::new(Activation::ReLU),
dropout: None,
};
Self {
layer_type: BackboneLayerType::Convolutional(conv),
input_shape: (0, 0, input_channels as u32),
output_shape: (0, 0, output_channels as u32),
parameters: BackboneParameters {
input_channels,
output_channels,
kernel_size,
stride,
padding,
dilation: (1, 1),
groups: 1,
},
}
}
fn max_pool(kernel_size: (u32, u32), stride: (u32, u32), padding: (u32, u32)) -> Self {
let pool = PoolBlock {
pool_type: PoolType::Max,
kernel_size,
stride,
padding,
};
Self {
layer_type: BackboneLayerType::Pooling(pool),
input_shape: (0, 0, 0),
output_shape: (0, 0, 0),
parameters: BackboneParameters {
input_channels: 0,
output_channels: 0,
kernel_size,
stride,
padding,
dilation: (1, 1),
groups: 1,
},
}
}
fn residual_block(input_channels: usize, output_channels: usize, num_blocks: usize) -> Self {
let mut main_path = Vec::new();
for _ in 0..num_blocks {
main_path.push(BackboneLayer::conv_block(
input_channels,
output_channels,
(3, 3),
(1, 1),
(1, 1),
));
}
let residual = ResidualBlock {
main_path,
shortcut: Some(ShortcutConnection {
connection_type: ShortcutType::Identity,
projection: None,
}),
activation: ActivationLayer::new(Activation::ReLU),
};
Self {
layer_type: BackboneLayerType::Residual(residual),
input_shape: (0, 0, input_channels as u32),
output_shape: (0, 0, output_channels as u32),
parameters: BackboneParameters {
input_channels,
output_channels,
kernel_size: (3, 3),
stride: (1, 1),
padding: (1, 1),
dilation: (1, 1),
groups: 1,
},
}
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
match &self.layer_type {
BackboneLayerType::Convolutional(conv) => conv.forward(input),
BackboneLayerType::Residual(residual) => residual.forward(input),
BackboneLayerType::Dense(dense) => dense.forward(input),
BackboneLayerType::Attention(attention) => attention.forward(input),
BackboneLayerType::Pooling(pool) => pool.forward(input),
}
}
}
impl ConvBlock {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mut output = self.conv.forward(input)?;
if let Some(ref bn) = self.batch_norm {
output = bn.forward(&output)?;
}
output = self.activation.forward(&output)?;
if let Some(ref dropout) = self.dropout {
output = dropout.forward(&output)?;
}
Ok(output)
}
}
impl ResidualBlock {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mut output = input.to_vec();
for layer in &self.main_path {
output = layer.forward(&output)?;
}
if let Some(ref shortcut) = self.shortcut {
let shortcut_output = shortcut.forward(input)?;
output = self.add_tensors(&output, &shortcut_output)?;
}
output = self.activation.forward(&output)?;
Ok(output)
}
fn add_tensors(&self, a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
let mut result = Vec::with_capacity(a.len());
for i in 0..a.len() {
result.push(a[i] + b.get(i).unwrap_or(&0.0));
}
Ok(result)
}
}
impl DenseBlock {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mut output = input.to_vec();
for layer in &self.layers {
output = layer.forward(&output)?;
}
Ok(output)
}
}
impl AttentionBlock {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let query = self.query_projection.forward(input)?;
let key = self.key_projection.forward(input)?;
let value = self.value_projection.forward(input)?;
let attention_scores = self.compute_attention_scores(&query, &key)?;
let attention_output = self.apply_attention(&attention_scores, &value)?;
self.output_projection.forward(&attention_output)
}
fn compute_attention_scores(&self, query: &[f32], key: &[f32]) -> Result<Vec<f32>> {
let mut scores = vec![0.0; query.len()];
for i in 0..query.len() {
scores[i] = query[i] * key[i % key.len()];
}
Ok(scores)
}
fn apply_attention(&self, scores: &[f32], value: &[f32]) -> Result<Vec<f32>> {
let mut output = vec![0.0; value.len()];
for i in 0..value.len() {
output[i] = scores[i % scores.len()] * value[i];
}
Ok(output)
}
}
impl PoolBlock {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
match self.pool_type {
PoolType::Max => {
let mut output = Vec::new();
for chunk in input.chunks(4) {
output.push(chunk.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b)));
}
Ok(output)
}
PoolType::Average => {
let mut output = Vec::new();
for chunk in input.chunks(4) {
output.push(chunk.iter().sum::<f32>() / chunk.len() as f32);
}
Ok(output)
}
_ => Ok(input.to_vec()),
}
}
}
impl DetectionHead {
fn new(config: &EndToEndConfig) -> Self {
let mut layers = Vec::new();
for _ in 0..config.num_detection_heads {
layers.push(DetectionLayer {
layer_type: DetectionLayerType::Convolutional,
input_channels: 256,
output_channels: 1,
kernel_size: (3, 3),
});
}
Self {
layers,
output_channels: 1,
use_fpn: config.use_multi_scale,
use_attention: config.use_attention,
}
}
fn forward(&self, features: &[FeatureMap]) -> Result<Vec<TextDetection>> {
Ok(vec![TextDetection {
bounding_box: TBox::new(0, 0, 100, 50),
confidence: 0.9,
polygon: vec![(0.0, 0.0), (100.0, 0.0), (100.0, 50.0), (0.0, 50.0)],
text_direction: TextDirection::Horizontal,
}])
}
}
impl RecognitionHead {
fn new(config: &EndToEndConfig) -> Self {
let mut layers = Vec::new();
for _ in 0..config.num_recognition_heads {
layers.push(RecognitionLayer {
layer_type: RecognitionLayerType::LSTM,
input_size: 256,
output_size: config.max_sequence_length,
hidden_size: 128,
});
}
Self {
layers,
output_vocab_size: 1000, use_attention: config.use_attention,
use_lstm: true,
}
}
fn forward(
&self,
features: &[FeatureMap],
detections: &[TextDetection],
) -> Result<Vec<TextRecognition>> {
Ok(vec![TextRecognition {
text: "End-to-End Recognition".to_string(),
confidence: 0.9,
character_scores: vec![0.9; 10],
language: Some("en".to_string()),
}])
}
}
impl FusionModule {
fn new(config: &EndToEndConfig) -> Self {
let attention_weights = if config.use_attention {
Some(AttentionWeights::new())
} else {
None
};
let gating_weights = if config.use_attention {
Some(GatingWeights::new())
} else {
None
};
Self {
fusion_type: FusionType::Concatenation,
attention_weights,
gating_weights,
}
}
fn fuse(
&self,
detections: &[TextDetection],
recognitions: &[TextRecognition],
) -> Result<Vec<FusedResult>> {
let mut fused_results = Vec::new();
for (i, detection) in detections.iter().enumerate() {
if let Some(recognition) = recognitions.get(i) {
fused_results.push(FusedResult {
detection: detection.clone(),
recognition: recognition.clone(),
combined_confidence: (detection.confidence + recognition.confidence) / 2.0,
});
}
}
Ok(fused_results)
}
}
impl PostProcessor {
fn new(config: &EndToEndConfig) -> Self {
Self {
nms_threshold: 0.5,
confidence_threshold: config.confidence_threshold,
max_candidates: 1000,
use_polygon_nms: true,
}
}
fn process(&self, fused_results: &[FusedResult]) -> Result<EndToEndOutput> {
let detections: Vec<TextDetection> =
fused_results.iter().map(|r| r.detection.clone()).collect();
let recognitions: Vec<TextRecognition> = fused_results
.iter()
.map(|r| r.recognition.clone())
.collect();
Ok(EndToEndOutput {
detections,
recognitions,
fused_results: fused_results.to_vec(),
confidence: 0.9,
processing_time_ms: 300,
})
}
}
impl ShortcutConnection {
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
match self.connection_type {
ShortcutType::Identity => Ok(input.to_vec()),
ShortcutType::Projection => {
if let Some(ref projection) = self.projection {
projection.forward(input)
} else {
Ok(input.to_vec())
}
}
ShortcutType::Convolution => Ok(input.to_vec()), }
}
}
impl AttentionWeights {
fn new() -> Self {
Self {
query_weights: vec![vec![0.0; 128]; 128],
key_weights: vec![vec![0.0; 128]; 128],
value_weights: vec![vec![0.0; 128]; 128],
output_weights: vec![vec![0.0; 128]; 128],
}
}
}
impl GatingWeights {
fn new() -> Self {
Self {
gate_weights: vec![vec![0.0; 128]; 128],
gate_bias: vec![0.0; 128],
}
}
}
impl LinearLayer {
fn new(input_size: usize, output_size: usize) -> Self {
Self {
weight: vec![vec![0.0; input_size]; output_size],
bias: Some(vec![0.0; output_size]),
}
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mut output = vec![0.0; self.weight.len()];
for (i, row) in self.weight.iter().enumerate() {
for (j, &weight) in row.iter().enumerate() {
if j < input.len() {
output[i] += weight * input[j];
}
}
if let Some(ref bias) = self.bias {
if i < bias.len() {
output[i] += bias[i];
}
}
}
Ok(output)
}
}
impl ConvLayer {
fn new(
input_channels: usize,
output_channels: usize,
kernel_size: (u32, u32),
stride: (u32, u32),
padding: (u32, u32),
) -> Self {
Self {
input_channels,
output_channels,
kernel_size,
stride,
padding,
dilation: (1, 1),
groups: 1,
bias: true,
weights: vec![
vec![
vec![vec![0.0; kernel_size.1 as usize]; kernel_size.0 as usize];
input_channels
];
output_channels
],
bias_weights: Some(vec![0.0; output_channels]),
}
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mut output = vec![0.0; input.len()];
for i in 0..input.len() {
output[i] = input[i] * 0.5; }
Ok(output)
}
}
impl BatchNormLayer {
fn new(num_features: usize) -> Self {
Self {
num_features,
eps: 1e-5,
momentum: 0.1,
weight: vec![1.0; num_features],
bias: vec![0.0; num_features],
running_mean: vec![0.0; num_features],
running_var: vec![1.0; num_features],
}
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
let mean = input.iter().sum::<f32>() / input.len() as f32;
let variance = input.iter().map(|&x| (x - mean).powi(2)).sum::<f32>() / input.len() as f32;
let std = (variance + self.eps).sqrt();
let mut output = vec![0.0; input.len()];
for i in 0..input.len() {
let normalized = (input[i] - mean) / std;
output[i] =
normalized * self.weight[i % self.weight.len()] + self.bias[i % self.bias.len()];
}
Ok(output)
}
}
impl ActivationLayer {
fn new(activation: Activation) -> Self {
Self { activation }
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
match self.activation {
Activation::ReLU => Ok(input.iter().map(|&x| x.max(0.0)).collect()),
Activation::LeakyReLU(alpha) => Ok(input
.iter()
.map(|&x| if x > 0.0 { x } else { alpha * x })
.collect()),
Activation::ELU(alpha) => Ok(input
.iter()
.map(|&x| if x > 0.0 { x } else { alpha * (x.exp() - 1.0) })
.collect()),
Activation::GELU => Ok(input
.iter()
.map(|&x| 0.5 * x * (1.0 + (x * 0.79788456).tanh()))
.collect()),
Activation::Swish => Ok(input
.iter()
.map(|&x| x * (1.0 + (-x).exp()).recip())
.collect()),
Activation::Sigmoid => Ok(input.iter().map(|&x| 1.0 / (1.0 + (-x).exp())).collect()),
Activation::Tanh => Ok(input.iter().map(|&x| x.tanh()).collect()),
Activation::Softmax => {
let max_val = input.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
let exp_sum: f32 = input.iter().map(|&x| (x - max_val).exp()).sum();
Ok(input
.iter()
.map(|&x| (x - max_val).exp() / exp_sum)
.collect())
}
}
}
}
impl DropoutLayer {
fn new(rate: f32) -> Self {
Self {
rate,
training: true,
}
}
fn forward(&self, input: &[f32]) -> Result<Vec<f32>> {
if self.training {
Ok(input
.iter()
.map(|&x| {
if fastrand::f32() < self.rate {
0.0
} else {
x / (1.0 - self.rate)
}
})
.collect())
} else {
Ok(input.to_vec())
}
}
}
pub struct EndToEndModelBuilder {
config: Option<EndToEndConfig>,
}
impl EndToEndModelBuilder {
pub fn new() -> Self {
Self { config: None }
}
pub fn with_config(mut self, config: EndToEndConfig) -> Self {
self.config = Some(config);
self
}
pub fn build(self) -> Result<EndToEndModel> {
let config = self
.config
.ok_or_else(|| OcrError::ModelNotFound("Configuration not provided".to_string()))?;
Ok(EndToEndModel::new(config))
}
}
impl Default for EndToEndModelBuilder {
fn default() -> Self {
Self::new()
}
}