2. 图像缩放与裁剪
任务目标
实现一个支持双线性插值缩放与中心裁剪的图像尺寸调整模块,输入任意尺寸的RGB图像,输出指定目标尺寸的图像。缩放与裁剪可独立调用,也可组合使用。
核心实现
// image_resize.h
#pragma once
#include <cstdint>
#include <vector>
struct Image {
int width, height, channels;
std::vector<uint8_t> data; // 按行优先存储,RGB/RGBA交错
};
// 双线性插值缩放
Image resize_bilinear(const Image& src, int dst_w, int dst_h);
// 中心裁剪
Image center_crop(const Image& src, int crop_w, int crop_h);
// image_resize.cpp
#include "image_resize.h"
#include <algorithm>
#include <cmath>
static uint8_t clamp_u8(float v) {
return static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, v)));
}
Image resize_bilinear(const Image& src, int dst_w, int dst_h) {
Image dst;
dst.width = dst_w;
dst.height = dst_h;
dst.channels = src.channels;
dst.data.resize(dst_w * dst_h * dst.channels);
float scale_x = static_cast<float>(src.width) / dst_w;
float scale_y = static_cast<float>(src.height) / dst_h;
for (int y = 0; y < dst_h; ++y) {
float src_y = (y + 0.5f) * scale_y - 0.5f;
src_y = std::max(0.0f, src_y);
int y0 = static_cast<int>(src_y);
int y1 = std::min(y0 + 1, src.height - 1);
float fy = src_y - y0;
for (int x = 0; x < dst_w; ++x) {
float src_x = (x + 0.5f) * scale_x - 0.5f;
src_x = std::max(0.0f, src_x);
int x0 = static_cast<int>(src_x);
int x1 = std::min(x0 + 1, src.width - 1);
float fx = src_x - x0;
for (int c = 0; c < src.channels; ++c) {
float top = src.data[(y0 * src.width + x0) * src.channels + c] * (1 - fx) +
src.data[(y0 * src.width + x1) * src.channels + c] * fx;
float bottom = src.data[(y1 * src.width + x0) * src.channels + c] * (1 - fx) +
src.data[(y1 * src.width + x1) * src.channels + c] * fx;
float val = top * (1 - fy) + bottom * fy;
dst.data[(y * dst_w + x) * dst.channels + c] = clamp_u8(val);
}
}
}
return dst;
}
Image center_crop(const Image& src, int crop_w, int crop_h) {
crop_w = std::min(crop_w, src.width);
crop_h = std::min(crop_h, src.height);
int start_x = (src.width - crop_w) / 2;
int start_y = (src.height - crop_h) / 2;
Image dst;
dst.width = crop_w;
dst.height = crop_h;
dst.channels = src.channels;
dst.data.resize(crop_w * crop_h * src.channels);
for (int y = 0; y < crop_h; ++y) {
const uint8_t* src_row = &src.data[((start_y + y) * src.width + start_x) * src.channels];
uint8_t* dst_row = &dst.data[y * crop_w * src.channels];
std::copy(src_row, src_row + crop_w * src.channels, dst_row);
}
return dst;
}
运行与验证
// main.cpp —— 最小验证入口
#include "image_resize.h"
#include <cstdio>
int main() {
// 构造 4x4 灰度测试图(channels=1),像素值 = 行号*10 + 列号
Image src;
src.width = 4; src.height = 4; src.channels = 1;
for (int y = 0; y < 4; ++y)
for (int x = 0; x < 4; ++x)
src.data.push_back(static_cast<uint8_t>(y * 10 + x));
// 测试1:缩放 4x4 → 2x2
Image scaled = resize_bilinear(src, 2, 2);
printf("缩放结果 2x2:\n");
for (int y = 0; y < 2; ++y) {
for (int x = 0; x < 2; ++x)
printf("%3d ", scaled.data[y * 2 + x]);
printf("\n");
}
// 测试2:中心裁剪 4x4 → 2x2
Image cropped = center_crop(src, 2, 2);
printf("裁剪结果 2x2:\n");
for (int y = 0; y < 2; ++y) {
for (int x = 0; x < 2; ++x)
printf("%3d ", cropped.data[y * 2 + x]);
printf("\n");
}
return 0;
}
编译运行:
g++ -O2 -o resize_test main.cpp image_resize.cpp
./resize_test
预期输出与判定标准
由于输入为确定性构造数据,可精确计算预期结果:
- 缩放 4x4→2x2:双线性插值采样点位于原图 (0.75,0.75)、(2.25,0.75)、(0.75,2.25)、(2.25,2.25) 附近,预期输出约为:
3 5
13 15(四舍五入后) - 中心裁剪 4x4→2x2:取原图中心区域,即行1~2、列1~2,预期输出:
11 12
21 22
通过条件:程序输出与上述预期一致(允许缩放结果因浮点舍入有±1偏差)。若需验证彩色图像,将 channels 改为 3 并填充 RGB 数据即可,算法逻辑不变。
说明:本实现为纯CPU端C++代码,不依赖第三方库,可直接编译运行。若需在嵌入式GPU上部署,可将核心循环替换为OpenCL/OpenGL着色器实现,但插值与裁剪的坐标映射逻辑完全一致。