Skip to content

Instantly share code, notes, and snippets.

@cedricpinson
Last active February 5, 2022 17:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cedricpinson/275ee86baf8a749b215d8992cc61e875 to your computer and use it in GitHub Desktop.
Save cedricpinson/275ee86baf8a749b215d8992cc61e875 to your computer and use it in GitHub Desktop.
stb_resize kernel downsampling
/* Mitchel filter is like that:
{ stbir__filter_mitchell, stbir__support_two }
float stbir__support_two(float s)
{
// scale is not used for mitchell
return 2.0;
}
float stbir__filter_mitchell(float x, float s)
{
STBIR__UNUSED_PARAM(s);
x = (float)fabs(x);
if (x < 1.0f)
return (16 + x*x*(21 * x - 36))/18;
else if (x < 2.0f)
return (32 + x*(-60 + x*(36 - 7*x)))/18;
return (0.0f);
}
*/
//
// This is the maximum number of input samples that can affect an output sample
// with the given filter
static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) {
// false
if (stbir__use_upsampling(scale))
return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2);
else {
// support return 2.0
// ceil( 2 * 2 / scale )
// return 256;
return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 /
scale);
}
}
// This is how much to expand buffers to account for filters seeking outside
// the image boundaries.
static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) {
// return 256/2 = 128
return stbir__get_filter_pixel_width(filter, scale) / 2;
}
// filter = STBIR_FILTER_MITCHELL
// input_size = 4096
// output_size = 64
// scale = 0.015625
static int stbir__get_contributors(float scale, stbir_filter filter,
int input_size, int output_size) {
// stbir__use_upsampling is false because scale < 1.0
if (stbir__use_upsampling(scale))
return output_size;
else {
// 4096 + 128 * 2 = 4352
return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2);
}
}
// in_pixels_radius = 128
// scale_ratio = 0,015625
// out_shift = 0
static void stbir__calculate_sample_range_downsample(
int n, // NO
float in_pixels_radius, float scale_ratio, float out_shift,
int *out_first_pixel, int *out_last_pixel, float *out_center_of_in) {
// eg n = -128 for pixel 0;
// eg n = 0
float in_pixel_center = (float)n + 0.5f;
// -127.5 - 128 = -255.5
// 0.5 - 128 = -127.5
float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius;
// -127.5 + 128 = 0.5
// 0.5 + 128 = 128.5
float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius;
// -255.5 * 0.015625 = -3.9921875
// -127.5 * 0.015625 = -1.9921875
float out_pixel_influence_lowerbound =
in_pixel_influence_lowerbound * scale_ratio - out_shift;
// 0.5 * 0.015625 = 0,0078125
// 128.5 * 0.015625 = 2.0078125
float out_pixel_influence_upperbound =
in_pixel_influence_upperbound * scale_ratio - out_shift;
// 127.5 * 0.015625 = 1.9921875
// 0.5 * 0.015625 = 0.0078125
*out_center_of_in = in_pixel_center * scale_ratio - out_shift;
// floor(-3.9921875 + 0.5) = -4
// floor(-1.9921875 + 0.5) = -2
*out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5));
// floor(0.0078125 - 0.5) = -1
// floor(2.0078125 - 0.5) = 1
*out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5));
}
// input is 4096
// output is 64
// scale_ratio = 64/4096 = 0.015625
// Each scan line uses the same kernel values so we should calculate the kernel
// values once and then we can use them for every scan line.
static void stbir__calculate_filters(stbir__contributors *contributors,
float *coefficients, stbir_filter filter,
float scale_ratio, float shift,
int input_size, int output_size) {
int n;
int total_contributors =
stbir__get_contributors(scale_ratio, filter, input_size, output_size);
// total_contributors = 4352
// false
if (stbir__use_upsampling(scale_ratio)) {
float out_pixels_radius =
stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio;
// Looping through out pixels
for (n = 0; n < total_contributors; n++) {
float in_center_of_out; // Center of the current out pixel in the in pixel
// space
int in_first_pixel, in_last_pixel;
stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio,
shift, &in_first_pixel,
&in_last_pixel, &in_center_of_out);
stbir__calculate_coefficients_upsample(
filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out,
stbir__get_contributor(contributors, n),
stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
}
} else {
float in_pixels_radius =
stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio;
// in_pixels_radius = 2.0 / 0.015625 = 128
// Looping through in pixels
// total_contributors = 4352
for (n = 0; n < total_contributors; n++) {
float out_center_of_in; // Center of the current out pixel in the in pixel
// space
int out_first_pixel, out_last_pixel;
// n_adjusted = n - 128;
int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio);
stbir__calculate_sample_range_downsample(
n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel,
&out_last_pixel, &out_center_of_in);
// I need to dig more into
// stbir__get_contributor & stbir__get_coefficient
stbir__calculate_coefficients_downsample(
filter, scale_ratio, out_first_pixel, out_last_pixel,
out_center_of_in, stbir__get_contributor(contributors, n),
stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
}
stbir__normalize_downsample_coefficients(contributors, coefficients, filter,
scale_ratio, input_size,
output_size);
}
}
// s0 = 0
// t0 = 0
// s1 = 1
// t1 = 1
// transform = null
static void stbir__calculate_transform(stbir__info *info, float s0, float t0,
float s1, float t1, float *transform) {
info->s0 = s0;
info->t0 = t0;
info->s1 = s1;
info->t1 = t1;
// false
if (transform) {
info->horizontal_scale = transform[0];
info->vertical_scale = transform[1];
info->horizontal_shift = transform[2];
info->vertical_shift = transform[3];
} else {
// horizontal_scale = 64/4096 / ( 1.0 - 0.0 );
info->horizontal_scale =
((float)info->output_w / info->input_w) / (s1 - s0);
info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
// horizontal_shift = 0.0 * 64 / ( 1.0 - 0.0 );
info->horizontal_shift = s0 * info->output_w / (s1 - s0);
info->vertical_shift = t0 * info->output_h / (t1 - t0);
}
}
// static int stbir__resize_arbitrary(
// void *alloc_context,
// const void* input_data,
// int input_w,
// int input_h,
// int input_stride_in_bytes,
// void* output_data,
// int output_w,
// int output_h,
// int output_stride_in_bytes,
// float s0,
// float t0,
// float s1,
// float t1,
// float *transform,
// int channels,
// int alpha_channel,
// stbir_uint32 flags,
// stbir_datatype type,
// stbir_filter h_filter,
// stbir_filter v_filter,
// stbir_edge edge_horizontal,
// stbir_edge edge_vertical,
// stbir_colorspace colorspace)
// stbir__resize_arbitrary(NULL,
// input_pixels,
// input_w,
// input_h,
// input_stride_in_bytes,
// output_pixels,
// output_w,
// output_h,
// output_stride_in_bytes,
// 0,
// 0,
// 1,
// 1,
// NULL,
// num_channels,
// -1,0,
// STBIR_TYPE_UINT8,
// STBIR_FILTER_DEFAULT,
// STBIR_FILTER_DEFAULT,
// STBIR_EDGE_CLAMP,
// STBIR_EDGE_CLAMP,
// STBIR_COLORSPACE_LINEAR);
static void stbir__calculate_coefficients_downsample(
stbir_filter filter, // NO_LINT
float scale_ratio, // NO_LINT
int out_first_pixel, // NO_LINT
int out_last_pixel, // NO_LINT
float out_center_of_in, // NO_LINT
stbir__contributors *contributor, // NO_LINT
float *coefficient_group) {
int i;
STBIR_ASSERT(out_last_pixel - out_first_pixel <=
(int)ceil(stbir__filter_info_table[filter].support(scale_ratio) *
2)); // NO_LINT
// Taken directly from stbir__get_coefficient_width() which we can't call
// because we don't know if we're horizontal or vertical.
// [-2; 1]
contributor->n0 = out_first_pixel;
contributor->n1 = out_last_pixel;
STBIR_ASSERT(contributor->n1 >= contributor->n0);
// i < (1+2)
for (i = 0; i <= out_last_pixel - out_first_pixel; i++) {
// = -1.5 to (-2 + 3 + 0.5) = 1.5
// out_center_in = 0.0078125 for the case
float out_pixel_center = (float)(i + out_first_pixel) + 0.5f;
// [-1.5 - 0.0078125; 1.5 - 0.0078125]
float x = out_pixel_center - out_center_of_in;
coefficient_group[i] =
stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
}
// NOTE(fg): Not actually true in general, nor is there any reason to expect
// it should be. It would be true in exact math but is at best approximately
// true in floating-point math, and it would not make sense to try and put
// actual bounds on this here because it depends on the image aspect ratio
// which can get pretty extreme.
// STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel
// + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
for (i = out_last_pixel - out_first_pixel; i >= 0; i--) {
if (coefficient_group[i])
break;
// This line has no weight. We can skip it.
contributor->n1 = contributor->n0 + i - 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment