Created
November 20, 2024 16:24
-
-
Save snowclipsed/2588211173a4dae291bb16841d7efcd2 to your computer and use it in GitHub Desktop.
Resize Kernel using Bicubic Interpolation
This file contains hidden or 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
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