版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46621272/article/details/126459136
用 C 语言编写的临近缩放算法
前言
- 在我们做 verilog 算法时,输出的结果是否正确?需要一个正确的参考。需要一组正确的输出图片与本仿真结果做比对。采用与本实验中相同的算法,用 C 语言实现,可以帮助阅读”临近缩小 video_scale_down_near.sv“算法的理解。C 语言执行后输出的图片文件与本仿真运行产生的 BMP 用软件二进制比对无任何差异。
1. bmp.h
//bmp.h
#ifndef __BMP__
#define __BMP__
#define COLOR_WHITE 0xffffff ///< 白色
#define COLOR_RED 0xff0000 ///< 红色
#define COLOR_GREEN 0x00ff00 ///< 绿色
#define COLOR_BLUE 0x0000ff ///< 蓝色
#define COLOR_YELLOW 0xffff00 ///< 黄色
#define COLOR_PURPLE 0xff00ff ///< 紫色
#define COLOR_INDIGO 0x00ffff ///< 青色
#define COLOR_BLACK 0x000000 ///< 黑色
//==================================================================================================
/// @brief BMP 文件头
//==================================================================================================
#pragma pack(push, 1)
typedef struct
{
unsigned short int bfType; ///< BM
unsigned int bfSize; ///< BMP 图像文件大小(字节)
unsigned short int bfReserverd1; ///< 保留
unsigned short int bfReserverd2; ///< 保留
unsigned int bfbfOffBits; ///< BMP 图像数据位置
}bmp_file_header;
//==================================================================================================
/// @brief BMP 图像信息头
//==================================================================================================
typedef struct
{
unsigned int biSize; ///< BMP 信息头大小
unsigned int biWidth; ///< BMP 图像宽度
unsigned int biHeight; ///< BMP 图像高度
unsigned short int biPlanes; ///< 固定值1
unsigned short int biBitcount; ///< 每个像素的位数 1-黑白图,4-16色,8-256色,24-真彩色
unsigned int biCompression; ///< 压缩方式,BI_RGB(0)为不压缩
unsigned int biSizeImage; ///< 图像数据大小,单位字节,必须是4的倍数
unsigned int biXPelsPermeter; ///< 水平分辨率(像素/米)
unsigned int biYPelsPermeter; ///< 垂直分辨率(像素/米)
unsigned int biClrUsed; ///< 位图使用的颜色数,如果为0,则颜色数为2的biBitCount次方
unsigned int biClrImportant; ///< 重要的颜色数,0代表所有颜色都重要
}bmp_inf_header;
typedef struct
{
unsigned char b; ///< 像素蓝色
unsigned char g; ///< 像素绿色
unsigned char r; ///< 像素红色
}pixel_dat;
typedef struct
{
unsigned int width; ///< BMP 图像宽度
unsigned int height; ///< 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bmp.h"
void write_bmp_file(bmpSt *b) //创建并写入BMP图片文件
{
FILE *bmp_fp;
bmp_file_header bmp_header;
bmp_inf_header inf_header;
int x,y;
int m;
//初始化 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;y<b->height;y++)
{
for(x=0;x<b->width;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;
int x,y;
int line_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;y<b->height;y++)
{
fseek(bmp_fp,bmp_header.bfbfOffBits+line_size*y,SEEK_SET);
for(x=0;x<b->width;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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bmp.h"
//临近插值缩放,采用浮点乘法计算,速度较慢
void image_scale_near_x1(bmpSt *vin,bmpSt *vout)
{
double scale_width;
double scale_height;
int x,y,sx,sy;
double dsx,dsy;
int wr_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;y<vout->height;y++) //按输出视频的大小,垂直扫描计数
{
for(x=0;x<vout->width;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/120917913
void image_scale_near_x2(bmpSt *vin,bmpSt *vout)
{
int scale_width;
int scale_height;
int x,y,sx,sy;
int dsx,dsy;
int wr_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;y<vout->height;y++) //按输出视频的大小,垂直扫描计数
{
dsx = 0;
for(x=0;x<vout->width;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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bmp.h"
void image_scale_near_x1(bmpSt *vin,bmpSt *vout)
{
double scale_width;
double scale_height;
int x,y,sx,sy;
double dsx,dsy;
int wr_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;y<vout->height;y++) //按输出视频的大小,垂直扫描计数
{
for(x=0;x<vout->width;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)
{
int scale_width;
int scale_height;
int x,y,sx,sy;
int dsx,dsy;
int wr_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;y<vout->height;y++) //按输出视频的大小,垂直扫描计数
{
dsx = 0;
for(x=0;x<vout->width;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 )
{
int i;
int scale_width;
int scale_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