视频处理|用 C 语言编写的临近缩放算法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46621272/article/details/126459136
【视频处理|用 C 语言编写的临近缩放算法】用 C 语言编写的临近缩放算法
前言
  • 在我们做 verilog 算法时,输出的结果是否正确?需要一个正确的参考。需要一组正确的输出图片与本仿真结果做比对。采用与本实验中相同的算法,用 C 语言实现,可以帮助阅读”临近缩小 video_scale_down_near.sv“算法的理解。C 语言执行后输出的图片文件与本仿真运行产生的 BMP 用软件二进制比对无任何差异。
1. bmp.h
//bmp.h #ifndef __BMP__ #define __BMP__#define COLOR_WHITE0xffffff ///< 白色 #define COLOR_RED0xff0000 ///< 红色 #define COLOR_GREEN0x00ff00 ///< 绿色 #define COLOR_BLUE0x0000ff ///< 蓝色 #define COLOR_YELLOW 0xffff00 ///< 黄色 #define COLOR_PURPLE0xff00ff ///< 紫色 #define COLOR_INDIGO 0x00ffff ///< 青色 #define COLOR_BLACK0x000000 ///< 黑色//================================================================================================== /// @brief BMP 文件头 //================================================================================================== #pragma pack(push, 1) typedefstruct { unsigned short int bfType; ///< BM unsigned intbfSize; ///< BMP 图像文件大小(字节) unsigned short int bfReserverd1; ///< 保留 unsigned short int bfReserverd2; ///< 保留 unsigned intbfbfOffBits; ///< BMP 图像数据位置 }bmp_file_header; //================================================================================================== /// @brief BMP 图像信息头 //================================================================================================== typedefstruct { unsigned intbiSize; ///< BMP 信息头大小 unsigned intbiWidth; ///< BMP 图像宽度 unsigned intbiHeight; ///< BMP 图像高度 unsigned short int biPlanes; ///< 固定值1 unsigned short int biBitcount; ///< 每个像素的位数 1-黑白图,4-16色,8-256色,24-真彩色 unsigned intbiCompression; ///< 压缩方式,BI_RGB(0)为不压缩 unsigned intbiSizeImage; ///< 图像数据大小,单位字节,必须是4的倍数 unsigned intbiXPelsPermeter; ///< 水平分辨率(像素/米) unsigned intbiYPelsPermeter; ///< 垂直分辨率(像素/米) unsigned intbiClrUsed; ///< 位图使用的颜色数,如果为0,则颜色数为2的biBitCount次方 unsigned intbiClrImportant; ///< 重要的颜色数,0代表所有颜色都重要 }bmp_inf_header; typedefstruct { unsigned char b; ///< 像素蓝色 unsigned char g; ///< 像素绿色 unsigned char r; ///< 像素红色 }pixel_dat; typedefstruct { unsigned intwidth; ///< BMP 图像宽度 unsigned intheight; ///< BMP 图像高度 unsigned char *bmp_file_name; ///< BMP 文件名 pixel_dat*bmp_dat; }bmpSt; void write_bmp_file(bmpSt *b); ///< 创建并写入BMP图片文件 void read_bmp_file(bmpSt *b); ///< 将BMP图片文件读入内存 bmp_dat void get_bmp_info(bmpSt *b); ///< 获取BMP图片的宽高# endif

2. bmp.c
//bmp.c #include #include #include #include "bmp.h"void write_bmp_file(bmpSt *b) //创建并写入BMP图片文件 { FILE *bmp_fp; bmp_file_header bmp_header; bmp_inf_header inf_header; intx,y; intm; //初始化 BMP 文件头 bmp_header.bfType= 0x4d42; bmp_header.bfSize= sizeof(bmp_header) + sizeof(inf_header) + ((b->width*3+3)&0xfffffffc)*b->height; bmp_header.bfReserverd1= 0; bmp_header.bfReserverd2= 0; bmp_header.bfbfOffBits= sizeof(bmp_header) + sizeof(inf_header); //初始化 BMP 文件头 inf_header.biSize= sizeof(inf_header); inf_header.biWidth= b->width; inf_header.biHeight= b->height; inf_header.biPlanes= 1; inf_header.biBitcount= 24; inf_header.biCompression = 0; inf_header.biSizeImage= 0; //b->width*b->height*3; inf_header.biXPelsPermeter = 0xc40e; inf_header.biYPelsPermeter = 0xc40e; inf_header.biClrUsed= 0; inf_header.biClrImportant = 0; bmp_fp = fopen(b->bmp_file_name,"wb+"); if(bmp_fp == 0) { printf("%s file open error \n",b->bmp_file_name); return; } fwrite((const void *)&bmp_header,1,sizeof(bmp_header),bmp_fp); fwrite((const void *)&inf_header,1,sizeof(inf_header),bmp_fp); m = (b->width*3)&3; for(y=0; yheight; y++) { for(x=0; xwidth; x++) { fwrite((const void *)&b->bmp_dat[y*b->width+x],1,sizeof(pixel_dat),bmp_fp); } ///< bmp 文件,每行图形占用的字节数必须是 4 的倍数 if(m == 1) fprintf(bmp_fp,"%c%c%c",0,0,0); //m==1 补 3 个 0 if(m == 2) fprintf(bmp_fp,"%c%c",0,0); //m==2 补 2 个 0 if(m == 3) fprintf(bmp_fp,"%c",0); //m==3 补 1 个 0 }//m==0 补 0 个 0 fclose(bmp_fp); printf("%s file create ok!\n",b->bmp_file_name); }void read_bmp_file(bmpSt *b)//将BMP图片文件读入内存 bmp_dat { FILE *bmp_fp; bmp_file_header bmp_header; bmp_inf_header inf_header; intx,y; intline_size; bmp_fp = fopen(b->bmp_file_name,"rb+"); if(bmp_fp == 0) { printf("%s file open error \n",b->bmp_file_name); return; } fread((void *)&bmp_header,1,sizeof(bmp_header),bmp_fp); fread((void *)&inf_header,1,sizeof(inf_header),bmp_fp); if(bmp_header.bfType != 0x4d42) { printf("%s file format error \n",b->bmp_file_name); return; } b->width = inf_header.biWidth; b->height = inf_header.biHeight; line_size = (b->width * 3 + 3) & 0xfffffffc; ///< bmp 文件,每行图形占用的字节数必须是 4 的倍数 fseek(bmp_fp,bmp_header.bfbfOffBits,SEEK_SET); for(y=0; yheight; y++) { fseek(bmp_fp,bmp_header.bfbfOffBits+line_size*y,SEEK_SET); for(x=0; xwidth; x++) { fread((void *)&b->bmp_dat[y*b->width+x],1,sizeof(pixel_dat),bmp_fp); } } fclose(bmp_fp); printf("%s file read ok!\n",b->bmp_file_name); }void get_bmp_info(bmpSt *b) //获取BMP图片的宽高 { FILE *bmp_fp; bmp_file_header bmp_header; bmp_inf_header inf_header; bmp_fp = fopen(b->bmp_file_name,"rb+"); if(bmp_fp == 0) { printf("%s file open error \n",b->bmp_file_name); return; } fread((void *)&bmp_header,1,sizeof(bmp_header),bmp_fp); fread((void *)&inf_header,1,sizeof(inf_header),bmp_fp); if(bmp_header.bfType != 0x4d42) { printf("%s file format error \n",b->bmp_file_name); return; } b->width = inf_header.biWidth; b->height = inf_header.biHeight; fclose(bmp_fp); printf("%s file get bmp info ok! width=%d,height=%d\n",b->bmp_file_name,b->width,b->height); }

3. near.c
//near.c #include #include #include #include"bmp.h"//临近插值缩放,采用浮点乘法计算,速度较慢 void image_scale_near_x1(bmpSt *vin,bmpSt *vout) { double scale_width; double scale_height; intx,y,sx,sy; double dsx,dsy; intwr_addr,rd_addr; scale_width= 1.0*vin->width/vout->width + 1.0e-15; scale_height = 1.0*vin->height/vout->height + 1.0e-15; //为了方便阅读算法实现,下面的代码没有做优化。 for(y=0; yheight; y++)//按输出视频的大小,垂直扫描计数 { for(x=0; xwidth; x++)//按输出视频的大小,水平扫描计数 { dsy= scale_height * y; //计算 y 坐标映射到 vin 中的坐标 sy= dsy; //取整 dsx= scale_width* x; //计算 x 坐标映射到 vin 中的坐标 sx= dsx; //取整 rd_addr = sy * vin->width+ sx; //通过 sx,sy 算出 vin读地址 wr_addr = y* vout->width + x; //通过 x ,y算出 vout 写地址 vout->bmp_dat[wr_addr] = vin->bmp_dat[rd_addr]; } } }//临近插值缩放,采用加法计算,速度块 //这部分算法和我文章中采用 verilog 实现的临近缩放算法是一样的。这个代码产生的数据可以用来矫正 verilog 仿真数据 //对 verilog 缩放有兴趣的可以阅读这篇文章 //https://blog.csdn.net/qq_46621272/article/details/126439519 //https://blog.csdn.net/qq_46621272/article/details/120917913void image_scale_near_x2(bmpSt *vin,bmpSt *vout) { intscale_width; intscale_height; intx,y,sx,sy; intdsx,dsy; intwr_addr,rd_addr; dsx = 0; dsy = 0; //用定浮点数的累加取代双精度浮点数的乘法运算,提高运行效率。 scale_width= (vin->width <<16)/vout->width+ 1; //水平缩放步长 scale_height = (vin->height<<16)/vout->height + 1; //垂直缩放步长 for(y=0; yheight; y++)//按输出视频的大小,垂直扫描计数 { dsx= 0; for(x=0; xwidth; x++)//按输出视频的大小,水平扫描计数 { sx= (dsx+0)>>16; //取整 //dsx 是个定浮点数,高16位是整数部分,低16位是小数部分 sy= (dsy+0)>>16; //取整 //dsy 是个定浮点数,高16位是整数部分,低16位是小数部分 rd_addr = sy * vin->width+ sx; //通过 sx,sy 算出 vin读地址 wr_addr = y* vout->width + x; //通过 x ,y算出 vout 写地址vout->bmp_dat[wr_addr] = vin->bmp_dat[rd_addr]; dsx= dsx+ scale_width; //用步长累加的方式取代浮点数的乘法运算 } dsy= dsy + scale_height; //用步长累加的方式取代浮点数的乘法运算 } }

4. scale_main.c
//scale_near.c #include #include #include #include"bmp.h"void image_scale_near_x1(bmpSt *vin,bmpSt *vout) { double scale_width; double scale_height; intx,y,sx,sy; double dsx,dsy; intwr_addr,rd_addr; scale_width= 1.0*vin->width/vout->width + 1.0e-15; scale_height = 1.0*vin->height/vout->height + 1.0e-15; //为了方便阅读算法实现,下面的代码没有做优化。 for(y=0; yheight; y++)//按输出视频的大小,垂直扫描计数 { for(x=0; xwidth; x++)//按输出视频的大小,水平扫描计数 { dsy= scale_height * y; //计算 y 坐标映射到 vin 中的坐标 sy= dsy; //取整 dsx= scale_width* x; //计算 x 坐标映射到 vin 中的坐标 sx= dsx; //取整 rd_addr = sy * vin->width+ sx; //通过 sx,sy 算出 vin读地址 wr_addr = y* vout->width + x; //通过 x ,y算出 vout 写地址 vout->bmp_dat[wr_addr] = vin->bmp_dat[rd_addr]; } } }void image_scale_near_x2(bmpSt *vin,bmpSt *vout) { intscale_width; intscale_height; intx,y,sx,sy; intdsx,dsy; intwr_addr,rd_addr; dsx = 0; dsy = 0; //用定浮点数的累加取代双精度浮点数的乘法运算,提高运行效率。 //这部分算法和我文章中采用 verilog 实现的临近缩放算法是一样的。这个代码产生的数据可以用来矫正 verilog 仿真数据 //对 verilog 缩放有兴趣的可以阅读这篇文章 https://editor.csdn.net/md/?articleId=126439519 scale_width= (vin->width <<16)/vout->width+ 1; //水平缩放步长 scale_height = (vin->height<<16)/vout->height + 1; //垂直缩放步长 for(y=0; yheight; y++)//按输出视频的大小,垂直扫描计数 { dsx= 0; for(x=0; xwidth; x++)//按输出视频的大小,水平扫描计数 { sx= (dsx+0)>>16; //取整 //dsx 是个定浮点数,高16位是整数部分,低16位是小数部分 sy= (dsy+0)>>16; //取整 //dsy 是个定浮点数,高16位是整数部分,低16位是小数部分 rd_addr = sy * vin->width+ sx; //通过 sx,sy 算出 vin读地址 wr_addr = y* vout->width + x; //通过 x ,y算出 vout 写地址vout->bmp_dat[wr_addr] = vin->bmp_dat[rd_addr]; dsx= dsx+ scale_width; //用步长累加的方式取代浮点数的乘法运算 } dsy= dsy + scale_height; //用步长累加的方式取代浮点数的乘法运算 } }void main( void ) { inti; intscale_width; intscale_height; char bmp_file_name[100]; bmpSt vin,vout_x1; vin.bmp_file_name = "../vin.bmp"; ///< 原始图片 get_bmp_info(&vin); ///< 获取BMP图片的宽高 vin.bmp_dat= ( pixel_dat *) malloc( sizeof(pixel_dat) * vin.width*vin.height ); ///< 申请内存 read_bmp_file(&vin); ///< 将BMP图片文件读入内存 bmp_dat for(i=1; i<=14; i++) //实现 14 帧图像的缩放比列 { sprintf(bmp_file_name,"../vouBmpC/vout_%03d.bmp",i); vout_x1.bmp_file_name = bmp_file_name; printf("%s\n",vout_x1.bmp_file_name); switch(i) { case 1:vout_x1.width = vin.width/1 - 0; vout_x1.height = vin.height/1 - 0; break; case 2:vout_x1.width = vin.width/1 - 1; vout_x1.height = vin.height/1 - 1; break; case 3:vout_x1.width = vin.width/2 + 1; vout_x1.height = vin.height/2 + 1; break; case 4:vout_x1.width = vin.width/2 + 0; vout_x1.height = vin.height/2 + 0; break; case 5:vout_x1.width = vin.width/2 - 1; vout_x1.height = vin.height/2 - 1; break; case 6:vout_x1.width = vin.width/3 + 1; vout_x1.height = vin.height/3 + 1; break; case 7:vout_x1.width = vin.width/3 + 0; vout_x1.height = vin.height/3 + 0; break; case 8:vout_x1.width = vin.width/3 - 1; vout_x1.height = vin.height/3 - 1; break; case 9:vout_x1.width = vin.width/5 + 1; vout_x1.height = vin.height/5 + 1; break; case 10:vout_x1.width = vin.width/5 + 0; vout_x1.height = vin.height/5 + 0; break; case 11:vout_x1.width = vin.width/5 - 1; vout_x1.height = vin.height/5 - 1; break; case 12:vout_x1.width = vin.width/7 + 1; vout_x1.height = vin.height/7 + 1; break; case 13:vout_x1.width = vin.width/7 + 0; vout_x1.height = vin.height/7 + 0; break; case 14:vout_x1.width = vin.width/7 - 1; vout_x1.height = vin.height/7 - 1; break; default:vout_x1.width = vin.width/1 - 0; vout_x1.height = vin.height/1 - 0; break; } vout_x1.bmp_dat = ( pixel_dat *) malloc( sizeof(pixel_dat) * vout_x1.width*vout_x1.height ); ///< 申请内存 image_scale_near_x2(&vin,&vout_x1); ///< 临近缩放运算,将结果存入 vout_x1 write_bmp_file(&vout_x1); ///< 创建并写入BMP图片文件 free(vout_x1.bmp_dat); ///< 释放内存 } free(vin.bmp_dat); ///< 释放内存 }

相关连接
  • System Verilog 视频缩放图像缩放 vivado 仿真 https://blog.csdn.net/qq_46621272/article/details/126439519
本仿真工程文件下载,采用 Xilinx vivado 2017.4 版本
  • https://download.csdn.net/download/qq_46621272/86406386

    推荐阅读