CUDA实战:图像缩放最近邻算法实现
创作时间:
作者:
@小白创作中心
CUDA实战:图像缩放最近邻算法实现
引用
CSDN
1.
https://m.blog.csdn.net/qq_43448134/article/details/142001271
本文介绍如何使用CUDA实现图像缩放的最近邻算法。通过详细讲解算法原理和提供完整的CUDA代码实现,帮助读者理解CUDA编程和图像处理算法。
项目背景
本项目旨在通过一系列图像算法的CUDA实现,帮助读者弄清楚算法原理和对应的CUDA程序编程代码。
最近邻算法原理
最近邻算法的核心思想是根据目标图像的像素坐标映射回原始图像,选择离映射坐标最近的一个像素作为结果。假设源图像的宽高为H,W,通道为C,目标图像的长宽为h,w,通道数为c。则目标图像的在像素坐标(x,y)的像素值P(x,y)的计算方式:
- 横坐标缩放比例scale_X = W / w,纵坐标缩放比例scale_Y = H / h
- 目标图像取得原图像的横坐标值x1=round(x * scale_X) ,目标图像取得原图像的横坐标值y1= round(y * scale_Y)
- 因此 P(x,y) = Ori(x1,y1)
代码实现
1. 头文件
#include <cuda_runtime.h>
#include <iostream>
// 读取和写入图像
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
using namespace std;
typedef unsigned char uchar;
2. CPU读取图像
unsigned char* read_image(const char* filename ){
int width, height, channels;
// 读取图像文件
unsigned char* imageData = stbi_load(filename, &width, &height, &channels, 0);
if (imageData == nullptr) {
std::cerr << "Error: Could not load image " << filename << std::endl;
}
std::cout << "Image loaded: " << filename << std::endl;
std::cout << "Width: " << width << " Height: " << height << " Channels: " << channels << std::endl;
return imageData;
}
3. 最近邻算法CPU实现
void resize_cpu(unsigned char* ori_img,
unsigned char* dst_img,
int input_w,int input_h,
int out_w,int out_h){
const int channel =3;
// 计算缩放比例
float scale_w = static_cast<float>(input_w / out_w);
float scale_h = static_cast<float>(input_h / out_h);
size_t size_total = out_w * out_h * 3;
for(int y = 0; y < out_h ; y++){
for(int x = 0; x < out_w; x++){
// 计算源图像中最接近的像素位置
int srcX = static_cast<int> (x * scale_w);
int srcY = static_cast<int> (y * scale_w);
// 防止越界
srcX = min( srcX, input_w -1 );
srcY = min( srcY, input_h -1 );
// 计算原图像和目标图像中的像素索引
int srcIndex = (srcY * input_w + srcX) * channel;
int dstIndex = (y * out_w + x) * channel;
for( int c = 0; c < channel; c++ ){
dst_img[dstIndex + c] = ori_img[srcIndex + c];
}
}
}
}
4. CUDA核函数
__global__ void nearestNeighborKernel(unsigned char* d_inputImage,
unsigned char* d_outputImage,
int inputWidth, int inputHeight,
int outputWidth, int outputHeight){
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if( x < outputWidth && y < outputHeight ){
// 计算在输入图像中位置
int srcX = static_cast<int>(x / (float)outputWidth * inputWidth);
int srcY = static_cast<int>(y / (float)outputHeight *inputHeight );
// rgb三个通道分别取对应的像素值,rgb三个数据相邻
d_outputImage[(y *outputWidth + x )*3] = d_inputImage[(srcY * inputWidth + srcX) *3];
d_outputImage[(y *outputWidth + x )*3 + 1] = d_inputImage[(srcY * inputWidth + srcX) *3 + 1];
d_outputImage[(y *outputWidth + x )*3 + 2] = d_inputImage[(srcY * inputWidth + srcX) *3 + 2];
}
}
5. CUDA核函数调用
void nearestNeighborInterpolation_launch(unsigned char* h_inputImage,
unsigned char* h_outputImage,
int inputWidth, int inputHeight,
int outputWidth, int outputHeight){
unsigned char * d_inputImage;
unsigned char * d_outputImage;
size_t inputImageSize = inputWidth * inputHeight * 3 * sizeof(unsigned char);
size_t outputImageSize = outputWidth * outputHeight * 3 * sizeof(unsigned char);
cout << "sizeof(unsigned char) = " << sizeof(unsigned char) << endl;
// cuda malloc && memset
cudaMalloc(&d_inputImage, inputImageSize);
cudaMalloc(&d_outputImage, outputImageSize);
cudaMemset(d_inputImage, 0, inputImageSize);
cudaMemset(d_outputImage, 0, outputImageSize);
// h2d
auto status = cudaMemcpy( d_inputImage, h_inputImage, inputImageSize, cudaMemcpyHostToDevice );
cout << "h2d status = " << status << endl;
cout << "outputWidth = " << outputWidth << ",outputHeight = " << outputHeight <<endl;
// cuda block/grid size
dim3 blockSize(16,16,1);
dim3 gridSize( (outputWidth + blockSize.x -1) /blockSize.x, \
(outputHeight + blockSize.y -1) /blockSize.y,1 );
cout << "blockSize: x =" << blockSize.x <<",y = " << blockSize.y <<",z ="<< blockSize.z << endl;
cout << "gridSize: x = " << gridSize.x <<",y="<< gridSize.y <<",z = "<< gridSize.z<< endl;
// launch cuda kernel
// 最近邻插值
nearestNeighborKernel<<<gridSize,blockSize >>>(d_inputImage,d_outputImage,inputWidth, inputHeight,outputWidth, outputHeight );
// 同步设备
cudaDeviceSynchronize();
// 复制输出图像数据回主机
cudaMemcpy(h_outputImage, d_outputImage, outputImageSize, cudaMemcpyDeviceToHost);
// 释放设备内存
cudaFree(d_inputImage);
cudaFree(d_outputImage);
}
6. 主函数
int main(){
int inputWidth = 640;
int inputHeight = 427;
int outputWidth = 320;
int outputHeight = 213;
const char* image_path = "../det_427_640.png";
// malloc host
unsigned char* h_inputImage = read_image(image_path );
unsigned char* h_outputImage = new unsigned char[outputWidth * outputHeight * 3];
// gpu impl(最近邻)
nearestNeighborInterpolation_launch(h_inputImage, h_outputImage, inputWidth, inputHeight, outputWidth, outputHeight);
// cpu impl
// resize_cpu( h_inputImage, h_outputImage, inputWidth, inputHeight, outputWidth, outputHeight );
// save img
const char* output_filename = "../det_427_640_gpu_out.png";
stbi_write_png( output_filename, outputWidth, outputHeight, 3, h_outputImage, outputWidth * 3);
// free cpu
delete[] h_inputImage;
delete[] h_outputImage;
return 0;
}
最终结果
源图像:尺寸=427*640
目标图像:尺寸=213 * 320
热门推荐
植物奶大PK:谁才是健康之王?
红虫怎么养?人工养殖红虫理论上很简单,但管理上非常麻烦!
红虫养殖:打造高效生态农业的新选择
古人观星术,竟然这么牛?
十三行购物攻略:广州时尚达人的秘密基地
全国首个服装行业智慧服务平台在广州发布,AI数字人直播+VR逛店成新趋势
北方小年夜,教你包正宗东北饺子
北方小年夜:饺子飘香迎新年
岳飞的官职——南宋抗金名将的荣誉与职责
那曲至乌鲁木齐火车旅行全指南:购票、时刻表及行程建议
运用人工智能(AI)于大数据分析
老人吃地黄的功效与作用,老年人健康的守护者
Steam恐怖游戏推荐 恐怖游戏排行榜前十名盘点
赵本山新作《走马上任》:68岁重返荧幕,这部民国喜剧能成爆款吗?
新生儿黄疸不容忽视,这些关键点父母要知道
赵本山:从春晚舞台到全球巡演,一个喜剧大师的艺术人生
赵本山春晚趣事:从意外睡着到经典永存
赵本山小品表演技巧揭秘:从语言艺术到角色塑造
唐朝宫廷贵族的奶茶秘密:从宫廷专享到大众饮品的历史变迁
新生儿黄疸治疗新进展:从光疗创新到药物应用
新生儿黄疸,你家宝宝中招了吗?
新生儿黄疸预防与判断全攻略
岳飞枪挑小梁王的故事
罗伯特·布鲁斯:真正的“勇敢的心”
揭秘《勇敢的心》:诺曼人夹枪冲锋的真相与传奇
十宗罪!法国正式调查“电报”创始人 同时寻觅家暴证据
奥运篮球比赛规则概述
提升400米跑成绩的全面策略与训练方法总结
这5种"老花眼"可能是失明前兆!看不清要警惕这些眼病,45岁以上的人注意了!
老人瞳孔放大还有救吗