1use core::marker::PhantomData;
4
5use burn_core::tensor::{backend::Backend, DType, Tensor, TensorData};
6use j2k::{
7 BatchDecodeOptions, CpuBatchDecodeResult, CpuBatchSamples, EncodedImage, NativeSampleType,
8 PreparedBatch, PreparedImage,
9};
10
11use crate::batch_contract::{dtype, tensor_shape};
12use crate::{BurnBatchDecode, BurnBatchGroup, BurnBatchTensor, BurnDecodeError};
13
14#[derive(Debug)]
16pub struct CpuBurnDecoder<B: Backend> {
17 codec: j2k::CpuBatchDecoder,
18 device: B::Device,
19 backend: PhantomData<B>,
20}
21
22impl<B: Backend> CpuBurnDecoder<B> {
23 #[must_use]
25 pub fn new(device: B::Device, options: BatchDecodeOptions) -> Self {
26 Self {
27 codec: j2k::CpuBatchDecoder::new(options),
28 device,
29 backend: PhantomData,
30 }
31 }
32
33 #[must_use]
35 pub const fn codec(&self) -> &j2k::CpuBatchDecoder {
36 &self.codec
37 }
38
39 #[must_use]
41 pub const fn device(&self) -> &B::Device {
42 &self.device
43 }
44
45 pub fn prepare(&self, inputs: Vec<EncodedImage>) -> Result<PreparedBatch, BurnDecodeError> {
47 Ok(self.codec.prepare(inputs)?)
48 }
49
50 pub fn prepare_prepared_images(
55 &self,
56 images: Vec<PreparedImage>,
57 ) -> Result<PreparedBatch, BurnDecodeError> {
58 Ok(self.codec.prepare_prepared_images(images)?)
59 }
60
61 pub fn decode(
63 &mut self,
64 inputs: Vec<EncodedImage>,
65 ) -> Result<BurnBatchDecode<B>, BurnDecodeError> {
66 let prepared = self.prepare(inputs)?;
67 self.decode_prepared(&prepared)
68 }
69
70 pub fn decode_prepared_images(
72 &mut self,
73 images: Vec<PreparedImage>,
74 ) -> Result<BurnBatchDecode<B>, BurnDecodeError> {
75 let prepared = self.prepare_prepared_images(images)?;
76 self.decode_prepared(&prepared)
77 }
78
79 pub fn decode_prepared(
81 &mut self,
82 prepared: &PreparedBatch,
83 ) -> Result<BurnBatchDecode<B>, BurnDecodeError> {
84 self.ensure_prepared_dtypes(prepared)?;
85 let decoded = self.codec.decode_prepared(prepared)?;
86 materialize::<B>(decoded, &self.device)
87 }
88
89 fn ensure_prepared_dtypes(&self, prepared: &PreparedBatch) -> Result<(), BurnDecodeError> {
90 for group in prepared.groups() {
91 let dtype = dtype(group.info().sample_type)?;
92 if !B::supports_dtype(&self.device, dtype) {
93 return Err(BurnDecodeError::UnsupportedDType { dtype });
94 }
95 }
96 Ok(())
97 }
98}
99
100fn materialize<B: Backend>(
101 decoded: CpuBatchDecodeResult,
102 device: &B::Device,
103) -> Result<BurnBatchDecode<B>, BurnDecodeError> {
104 let (codec_groups, errors) = decoded.into_parts();
105 let mut groups = Vec::new();
106 groups
107 .try_reserve_exact(codec_groups.len())
108 .map_err(|_| BurnDecodeError::SizeOverflow)?;
109
110 for group in codec_groups {
111 let (info, source_indices, decoded_rects, warnings, samples) = group.into_parts();
112 let shape = tensor_shape(source_indices.len(), &info)?;
113 let tensor = match (info.sample_type, samples) {
114 (NativeSampleType::U8, CpuBatchSamples::U8(samples)) => BurnBatchTensor::U8(
115 Tensor::from_data(TensorData::new(samples, shape), (device, DType::U8)),
116 ),
117 (NativeSampleType::U16, CpuBatchSamples::U16(samples)) => BurnBatchTensor::U16(
118 Tensor::from_data(TensorData::new(samples, shape), (device, DType::U16)),
119 ),
120 (NativeSampleType::I16, CpuBatchSamples::I16(samples)) => BurnBatchTensor::I16(
121 Tensor::from_data(TensorData::new(samples, shape), (device, DType::I16)),
122 ),
123 _ => return Err(BurnDecodeError::SampleTypeMismatch),
124 };
125 groups.push(BurnBatchGroup {
126 tensor,
127 info,
128 source_indices,
129 decoded_rects,
130 warnings,
131 });
132 }
133
134 Ok(BurnBatchDecode {
135 groups,
136 errors,
137 group_errors: Vec::new(),
138 })
139}