diff --git a/src/models/cua_s1/native/THIRD_PARTY_NOTICES.md b/src/models/cua_s1/native/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..54b788e --- /dev/null +++ b/src/models/cua_s1/native/THIRD_PARTY_NOTICES.md @@ -0,0 +1,24 @@ +# Image preprocessing attributions + +`src/image_preprocess.rs` is a Rust adaptation of the following algorithms. +It is modified for decoded interleaved RGB8 input, fixed Cua-S1 4B settings, +bounded dimensions, standard-library buffers, and a standalone CPU API. + +- PyTorch 2.14.0, `aten/src/ATen/native/cpu/UpSampleKernel.cpp` + (`_compute_indices_min_size_weights_aa`, `_compute_index_ranges_int16_weights`, + and the separable uint8 horizontal/vertical loops), plus the cubic polynomial + helpers in `aten/src/ATen/native/UpSample.h`. See [PyTorch license](licenses/PYTORCH-LICENSE) + for the retained copyright notices, redistribution conditions, and disclaimer. +- PyTorch's bicubic filter credits Pillow's `src/libImaging/Resample.c`. + The retained PIL/Pillow notice is in [Pillow license](licenses/PILLOW-LICENSE). +- Transformers 5.17.0, + `src/transformers/models/qwen2_vl/image_processing_qwen2_vl.py` (smart resize + and patch ordering) and `src/transformers/image_processing_backends.py` + (fused normalization). Copyright 2024 The Qwen team, Alibaba Group and the + HuggingFace Inc. team. All rights reserved. The backend file is + Copyright 2025 The HuggingFace Inc. team. Licensed under the + [Apache License, Version 2.0](licenses/APACHE-2.0). + +No upstream runtime or image decoder is linked by this module. The above +notices and license texts must accompany redistributed adaptations as required +by their respective licenses. diff --git a/src/models/cua_s1/native/licenses/APACHE-2.0 b/src/models/cua_s1/native/licenses/APACHE-2.0 new file mode 100644 index 0000000..68b7d66 --- /dev/null +++ b/src/models/cua_s1/native/licenses/APACHE-2.0 @@ -0,0 +1,203 @@ +Copyright 2018- The Hugging Face team. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/models/cua_s1/native/licenses/PILLOW-LICENSE b/src/models/cua_s1/native/licenses/PILLOW-LICENSE new file mode 100644 index 0000000..10dd42d --- /dev/null +++ b/src/models/cua_s1/native/licenses/PILLOW-LICENSE @@ -0,0 +1,30 @@ +The Python Imaging Library (PIL) is + + Copyright © 1997-2011 by Secret Labs AB + Copyright © 1995-2011 by Fredrik Lundh and contributors + +Pillow is the friendly PIL fork. It is + + Copyright © 2010 by Jeffrey A. Clark and contributors + +Like PIL, Pillow is licensed under the open source MIT-CMU License: + +By obtaining, using, and/or copying this software and/or its associated +documentation, you agree that you have read, understood, and will comply +with the following terms and conditions: + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies, and that +both that copyright notice and this permission notice appear in supporting +documentation, and that the name of Secret Labs AB or the author not be +used in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/src/models/cua_s1/native/licenses/PYTORCH-LICENSE b/src/models/cua_s1/native/licenses/PYTORCH-LICENSE new file mode 100644 index 0000000..c23172f --- /dev/null +++ b/src/models/cua_s1/native/licenses/PYTORCH-LICENSE @@ -0,0 +1,84 @@ +From PyTorch: + +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston) +Copyright (c) 2006 Idiap Research Institute (Samy Bengio) +Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz) + +From Caffe2: + +Copyright (c) 2016-present, Facebook Inc. All rights reserved. + +All contributions by Facebook: +Copyright (c) 2016 Facebook Inc. + +All contributions by Google: +Copyright (c) 2015 Google Inc. +All rights reserved. + +All contributions by Yangqing Jia: +Copyright (c) 2015 Yangqing Jia +All rights reserved. + +All contributions by Kakao Brain: +Copyright 2019-2020 Kakao Brain + +All contributions by Cruise LLC: +Copyright (c) 2022 Cruise LLC. +All rights reserved. + +All contributions by Tri Dao: +Copyright (c) 2024 Tri Dao. +All rights reserved. + +All contributions by Arm: +Copyright (c) 2021, 2023-2025 Arm Limited and/or its affiliates + +All contributions from Caffe: +Copyright(c) 2013, 2014, 2015, the respective contributors +All rights reserved. + +All other contributions: +Copyright(c) 2015, 2016 the respective contributors +All rights reserved. + +Caffe2 uses a copyright model similar to Caffe: each contributor holds +copyright over their contributions to Caffe2. The project versioning records +all such contribution and copyright details. If a contributor wants to further +mark their specific copyright on a particular contribution, they should +indicate their copyright solely in the commit message of the change when it is +committed. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America + and IDIAP Research Institute nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/src/models/cua_s1/native/src/image_preprocess.rs b/src/models/cua_s1/native/src/image_preprocess.rs new file mode 100644 index 0000000..9009fb2 --- /dev/null +++ b/src/models/cua_s1/native/src/image_preprocess.rs @@ -0,0 +1,246 @@ +//! CPU preprocessing for the fixed Qwen3.5-4B / Cua-S1 image processor. +//! +//! Input is already decoded, interleaved RGB8. No image codecs, GPU, model +//! weights, or HTTP handling are involved. The output is row-major +//! `[patches, 1536]`, ready for a separate vision encoder. +//! +//! The resize algorithm is a Rust adaptation of PyTorch's CPU uint8 bicubic +//! antialias implementation (which credits Pillow). Smart resize and packing +//! follow Transformers' Qwen2VLImageProcessor. See `../THIRD_PARTY_NOTICES.md`. + +use std::borrow::Cow; + +use anyhow::{Result, ensure}; + +const PATCH_SIZE: usize = 16; +const MERGE_SIZE: usize = 2; +const FACTOR: usize = PATCH_SIZE * MERGE_SIZE; +const PATCH_VALUES: usize = 3 * 2 * PATCH_SIZE * PATCH_SIZE; +const MIN_PIXELS: usize = 65_536; +const MAX_PIXELS: usize = 16_777_216; + +/// Normalized image patches and the spatial metadata used by the vision model. +#[derive(Debug)] +pub struct ProcessedImage { + /// Contiguous row-major `[patches, 1536]` float32 values. + pub pixel_values: Vec, + /// `[1, resized_height / 16, resized_width / 16]`. + pub image_grid_thw: [usize; 3], + pub resized_width: usize, + pub resized_height: usize, +} + +impl ProcessedImage { + /// Number of image tokens after the model's 2-by-2 spatial merge. + pub fn image_tokens(&self) -> usize { + self.pixel_values.len() / PATCH_VALUES / (MERGE_SIZE * MERGE_SIZE) + } +} + +/// Preprocess a decoded RGB8 image using the fixed 4B processor settings. +/// +/// Rejects empty dimensions, sides over 2048, area over 1,048,576 pixels, +/// aspect ratios over 200, and buffers whose length is not `width * height * 3`. +/// Geometry and buffer arithmetic are checked before allocating image buffers. +pub fn preprocess_rgb8(width: usize, height: usize, rgb: &[u8]) -> Result { + ensure!(width > 0 && height > 0, "image dimensions must be nonzero"); + ensure!( + width <= 2048 && height <= 2048, + "image sides must not exceed 2048" + ); + let area = width + .checked_mul(height) + .ok_or_else(|| anyhow::anyhow!("image area overflow"))?; + ensure!( + area <= 1_048_576, + "image area must not exceed 1048576 pixels" + ); + ensure!( + width.max(height) <= width.min(height) * 200, + "image aspect ratio must not exceed 200" + ); + let input_len = area + .checked_mul(3) + .ok_or_else(|| anyhow::anyhow!("RGB buffer length overflow"))?; + ensure!( + rgb.len() == input_len, + "RGB buffer length must be {input_len}, got {}", + rgb.len() + ); + + let (resized_width, resized_height) = smart_resize(width, height); + let resized_area = resized_width + .checked_mul(resized_height) + .ok_or_else(|| anyhow::anyhow!("resized area overflow"))?; + let resized_len = resized_area + .checked_mul(3) + .ok_or_else(|| anyhow::anyhow!("resized buffer length overflow"))?; + let horizontal_len = resized_width + .checked_mul(height) + .and_then(|area| area.checked_mul(3)) + .ok_or_else(|| anyhow::anyhow!("horizontal buffer length overflow"))?; + let output_len = resized_area + .checked_mul(6) + .ok_or_else(|| anyhow::anyhow!("patch buffer length overflow"))?; + output_len + .checked_mul(std::mem::size_of::()) + .ok_or_else(|| anyhow::anyhow!("patch buffer byte length overflow"))?; + + let mut resized = Cow::Borrowed(rgb); + if resized_width != width { + let axis = AxisWeights::new(width, resized_width); + let mut horizontal = vec![0; horizontal_len]; + for y in 0..height { + for (x, kernel) in axis.kernels.iter().enumerate() { + for channel in 0..3 { + horizontal[(y * resized_width + x) * 3 + channel] = + axis.apply(kernel, |source_x| rgb[(y * width + source_x) * 3 + channel]); + } + } + } + resized = Cow::Owned(horizontal); + } + if resized_height != height { + let axis = AxisWeights::new(height, resized_height); + let mut vertical = vec![0; resized_len]; + for (y, kernel) in axis.kernels.iter().enumerate() { + for x in 0..resized_width { + for channel in 0..3 { + vertical[(y * resized_width + x) * 3 + channel] = axis + .apply(kernel, |source_y| { + resized[(source_y * resized_width + x) * 3 + channel] + }); + } + } + } + resized = Cow::Owned(vertical); + } + + let mut pixel_values = Vec::with_capacity(output_len); + for block_y in 0..resized_height / FACTOR { + for block_x in 0..resized_width / FACTOR { + for merge_y in 0..MERGE_SIZE { + for merge_x in 0..MERGE_SIZE { + for channel in 0..3 { + for _temporal in 0..2 { + for patch_y in 0..PATCH_SIZE { + for patch_x in 0..PATCH_SIZE { + let y = block_y * FACTOR + merge_y * PATCH_SIZE + patch_y; + let x = block_x * FACTOR + merge_x * PATCH_SIZE + patch_x; + let pixel = resized[(y * resized_width + x) * 3 + channel]; + // Match the fused float32 torchvision normalization, + // including its operation order (no reciprocal multiply). + pixel_values.push((f32::from(pixel) - 127.5) / 127.5); + } + } + } + } + } + } + } + } + Ok(ProcessedImage { + pixel_values, + image_grid_thw: [1, resized_height / PATCH_SIZE, resized_width / PATCH_SIZE], + resized_width, + resized_height, + }) +} + +fn smart_resize(width: usize, height: usize) -> (usize, usize) { + // Python round uses ties-to-even; Rust's ordinary round does not. + let mut w = (width as f64 / FACTOR as f64).round_ties_even() as usize * FACTOR; + let mut h = (height as f64 / FACTOR as f64).round_ties_even() as usize * FACTOR; + if w * h > MAX_PIXELS { + let beta = ((width * height) as f64 / MAX_PIXELS as f64).sqrt(); + w = ((width as f64 / beta / FACTOR as f64).floor() as usize * FACTOR).max(FACTOR); + h = ((height as f64 / beta / FACTOR as f64).floor() as usize * FACTOR).max(FACTOR); + } else if w * h < MIN_PIXELS { + let beta = (MIN_PIXELS as f64 / (width * height) as f64).sqrt(); + w = (width as f64 * beta / FACTOR as f64).ceil() as usize * FACTOR; + h = (height as f64 * beta / FACTOR as f64).ceil() as usize * FACTOR; + } + (w, h) +} + +struct Kernel { + start: usize, + weights: Vec, +} + +struct AxisWeights { + kernels: Vec, + precision: u32, +} + +impl AxisWeights { + fn new(input: usize, output: usize) -> Self { + let scale = input as f64 / output as f64; + let support = 2.0 * scale.max(1.0); + let invscale = if scale >= 1.0 { 1.0 / scale } else { 1.0 }; + let max_size = support.ceil() as usize * 2 + 1; + let mut maximum = 0.0_f64; + let mut floating = Vec::with_capacity(output); + for index in 0..output { + let center = scale * (index as f64 + 0.5); + // C++ conversion truncates toward zero before clamping the bounds. + let start = ((center - support + 0.5) as isize).max(0) as usize; + let end = ((center + support + 0.5) as usize).min(input); + let count = end.saturating_sub(start).min(max_size); + let mut weights: Vec = (0..count) + .map(|j| cubic((j as f64 + start as f64 - center + 0.5) * invscale)) + .collect(); + let total: f64 = weights.iter().sum(); + if total != 0.0 { + for weight in &mut weights { + *weight /= total; + maximum = maximum.max(*weight); + } + } + floating.push((start, weights)); + } + // One precision for the whole axis, as in PyTorch's int16 path. + let mut precision = 0; + while precision < 22 { + if (0.5 + maximum * f64::from(1 << (precision + 1))) as i32 >= (1 << 15) { + break; + } + precision += 1; + } + let multiplier = f64::from(1 << precision); + let kernels = floating + .into_iter() + .map(|(start, weights)| Kernel { + start, + weights: weights + .into_iter() + .map(|weight| { + let value = weight * multiplier; + (value + if value < 0.0 { -0.5 } else { 0.5 }) as i16 + }) + .collect(), + }) + .collect(); + Self { kernels, precision } + } + + fn apply(&self, kernel: &Kernel, pixel: impl Fn(usize) -> u8) -> u8 { + let mut accumulator = 1_i32 << (self.precision - 1); + for (offset, &weight) in kernel.weights.iter().enumerate() { + accumulator += i32::from(pixel(kernel.start + offset)) * i32::from(weight); + } + (accumulator >> self.precision).clamp(0, 255) as u8 + } +} + +fn cubic(x: f64) -> f64 { + let x = x.abs(); + const A: f64 = -0.5; + if x < 1.0 { + ((A + 2.0) * x - (A + 3.0)) * x * x + 1.0 + } else if x < 2.0 { + ((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A + } else { + 0.0 + } +} diff --git a/src/models/cua_s1/native/src/lib.rs b/src/models/cua_s1/native/src/lib.rs index 0d3aa5f..ff12872 100644 --- a/src/models/cua_s1/native/src/lib.rs +++ b/src/models/cua_s1/native/src/lib.rs @@ -5,5 +5,6 @@ pub mod contract; pub mod cuda; pub mod engine; +pub mod image_preprocess; pub mod json; pub mod model;