版权声明:本文为博主原创文章,遵循 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 用软件二进制比对无任何差异。
//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
- https://download.csdn.net/download/qq_46621272/86406386
推荐阅读
- 视频处理|FPGA verilog 临近插值任意比例视频缩小代码
- 视频处理|Syetem Verilog 将视频流输出写入 BMP 图片文件 testbench 激励代码
- 视频处理|Syetem Verilog 用BMP图片文件产生视频流 testbench 激励代码
- 视频处理|FPGA 处理视频SDRAM带宽计算
- 数学建模|matlab层次分析法
- 机器学习|机器学习 K-Means(++)算法
- 机器学习|【进阶版】机器学习之决策树知识与易错点总结(06)
- 嵌入式开发|一款简单好用的日志系统
- Python|在Anaconda中用pip安装Pytorch后无法用pycharm打开