Skip to content

Commit

Permalink
Gabor-like filter stage
Browse files Browse the repository at this point in the history
  • Loading branch information
tirr-c committed Dec 29, 2024
1 parent ab24d2c commit 64f66ae
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
110 changes: 110 additions & 0 deletions jxl/src/render/stages/gaborish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

use crate::render::{RenderPipelineInOutStage, RenderPipelineStage};

/// Apply Gabor-like filter to a channel.
#[derive(Debug)]
pub struct GaborishStage {
channel: usize,
weight_side: f32,
weight_diag: f32,
}

impl GaborishStage {
// TODO(tirr-c): remove once we use this!
#[allow(unused)]
pub fn new(channel: usize, weight1: f32, weight2: f32) -> Self {
Self {
channel,
weight_side: weight1,
weight_diag: weight2,
}
}
}

impl std::fmt::Display for GaborishStage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Gaborish filter for channel {}", self.channel)
}
}

impl RenderPipelineStage for GaborishStage {
type Type = RenderPipelineInOutStage<f32, f32, 1, 1, 0, 0>;

fn uses_channel(&self, c: usize) -> bool {
c == self.channel
}

fn process_row_chunk(
&mut self,
_position: (usize, usize),
xsize: usize,
row: &mut [(&[&[f32]], &mut [&mut [f32]])],
) {
let (rows_in, ref mut rows_out) = row[0];
let row_out = &mut rows_out[0][..xsize];

let weight_side = self.weight_side;
let weight_diag = self.weight_diag;
let weight_total = 1.0 + weight_side * 4.0 + weight_diag * 4.0;

let kernel_top_bottom = [weight_diag, weight_side, weight_diag].map(|x| x / weight_total);
let kernel_center = [weight_side, 1.0, weight_side].map(|x| x / weight_total);

for (idx, out) in row_out.iter_mut().enumerate() {
let mut sum = 0f32;
let row_and_kernel = std::iter::zip(
rows_in,
[kernel_top_bottom, kernel_center, kernel_top_bottom],
);

for (row_in, kernel) in row_and_kernel {
for (dx, weight) in kernel.into_iter().enumerate() {
sum += row_in[idx + dx] * weight;
}
}

*out = sum;
}
}
}

#[cfg(test)]
mod test {
use test_log::test;

use super::*;
use crate::error::Result;
use crate::image::Image;
use crate::render::test::make_and_run_simple_pipeline;
use crate::util::test::assert_all_almost_eq;

#[test]
fn consistency() -> Result<()> {
crate::render::test::test_stage_consistency::<_, f32, f32>(
GaborishStage::new(0, 0.115169525, 0.061248592),
(500, 500),
1,
)
}

#[test]
fn checkerboard() -> Result<()> {
let mut image = Image::new((2, 2))?;
image.as_rect_mut().row(0).copy_from_slice(&[0.0, 1.0]);
image.as_rect_mut().row(1).copy_from_slice(&[1.0, 0.0]);

let stage = GaborishStage::new(0, 0.115169525, 0.061248592);
let (_, output) =
make_and_run_simple_pipeline::<_, f32, f32>(stage, &[image], (2, 2), 256)?;
let output = output[0].as_rect();

assert_all_almost_eq!(output.row(0), &[0.20686048, 0.7931395], 1e-6);
assert_all_almost_eq!(output.row(1), &[0.7931395, 0.20686048], 1e-6);

Ok(())
}
}
1 change: 1 addition & 0 deletions jxl/src/render/stages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

mod chroma_upsample;
mod convert;
mod gaborish;
mod nearest_neighbor;
mod noise;
mod save;
Expand Down

0 comments on commit 64f66ae

Please sign in to comment.