用 C 语言编写的临近缩放算法


版权声明:本文为博主原创文章,遵循 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);							///< 释放内存
}




相关连接

本仿真工程文件下载,采用 Xilinx vivado 2017.4 版本

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老皮芽子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值