Last active
February 5, 2022 17:28
-
-
Save cedricpinson/275ee86baf8a749b215d8992cc61e875 to your computer and use it in GitHub Desktop.
stb_resize kernel downsampling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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