Skip to content

Instantly share code, notes, and snippets.

@snowclipsed
Created November 20, 2024 16:24
Show Gist options
  • Save snowclipsed/2588211173a4dae291bb16841d7efcd2 to your computer and use it in GitHub Desktop.
Save snowclipsed/2588211173a4dae291bb16841d7efcd2 to your computer and use it in GitHub Desktop.
Resize Kernel using Bicubic Interpolation
const std = @import("std");
const c = @cImport({
@cInclude("stb_image.h");
});
fn cubic(x: f32) f32 {
const a = -0.5;
const abs_x = @abs(x);
if (abs_x <= 1.0) {
return (a + 2.0) * abs_x * abs_x * abs_x - (a + 3.0) * abs_x * abs_x + 1.0;
} else if (abs_x < 2.0) {
return a * abs_x * abs_x * abs_x - 5.0 * a * abs_x * abs_x + 8.0 * a * abs_x - 4.0 * a;
}
return 0.0;
}
pub fn resizeBicubic(allocator: std.mem.Allocator, input: []const u8, width: i32, height: i32, channels: i32, new_width: i32, new_height: i32) ![]u8 {
const scale_x = @as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(new_width));
const scale_y = @as(f32, @floatFromInt(height)) / @as(f32, @floatFromInt(new_height));
var output = try allocator.alloc(u8, @intCast(new_width * new_height * channels));
errdefer allocator.free(output);
var y: i32 = 0;
while (y < new_height) : (y += 1) {
const y0 = @floor(@as(f32, @floatFromInt(y)) * scale_y);
var x: i32 = 0;
while (x < new_width) : (x += 1) {
const x0 = @floor(@as(f32, @floatFromInt(x)) * scale_x);
var c_idx: i32 = 0;
while (c_idx < channels) : (c_idx += 1) {
var sum: f32 = 0.0;
var weight_sum: f32 = 0.0;
var i: i32 = -1;
while (i <= 2) : (i += 1) {
const current_y = y0 + @as(f32, @floatFromInt(i));
if (current_y < 0 or current_y >= @as(f32, @floatFromInt(height))) continue;
var j: i32 = -1;
while (j <= 2) : (j += 1) {
const current_x = x0 + @as(f32, @floatFromInt(j));
if (current_x < 0 or current_x >= @as(f32, @floatFromInt(width))) continue;
const pixel_idx = @as(usize, @intFromFloat((current_y * @as(f32, @floatFromInt(width)) + current_x) * @as(f32, @floatFromInt(channels)) + @as(f32, @floatFromInt(c_idx))));
const pixel_value = @as(f32, @floatFromInt(input[pixel_idx]));
const dx = @as(f32, @floatFromInt(x)) * scale_x - current_x;
const dy = @as(f32, @floatFromInt(y)) * scale_y - current_y;
const weight = cubic(dx) * cubic(dy);
sum += pixel_value * weight;
weight_sum += weight;
}
}
const output_idx = @as(usize, @intCast((y * new_width + x) * channels + c_idx));
if (weight_sum != 0.0) {
const pixel_value = @as(u8, @intFromFloat(std.math.clamp(sum / weight_sum, 0.0, 255.0)));
output[output_idx] = pixel_value;
} else {
output[output_idx] = 0;
}
}
}
}
return output;
}
const ImageInfo = struct {
data: []u8,
width: usize,
height: usize,
channels: usize,
};
fn loadImage(path: []const u8, allocator: std.mem.Allocator) !ImageInfo {
var width: i32 = undefined;
var height: i32 = undefined;
var channels: i32 = undefined;
const pixels = c.stbi_load(path.ptr, &width, &height, &channels, 0) orelse return error.ImageLoadFailed;
defer c.stbi_image_free(pixels);
const size = @as(usize, @intCast(@as(u32, @intCast(width)) * @as(u32, @intCast(height)) * @as(u32, @intCast(channels))));
const data = try allocator.alloc(u8, size);
const pixels_slice = @as([*]u8, @ptrCast(pixels))[0..size];
@memcpy(data, pixels_slice);
return ImageInfo{
.data = data,
.width = @intCast(width),
.height = @intCast(height),
.channels = @intCast(channels),
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment