纯C语言 不用 OpenCV 开源库 实现ORB特征提取及匹配 可视化

参考:画直线 https://blog.csdn.net/Bluechalk/article/details/84578197

C语言图像读取及基本操作 https://blog.csdn.net/zhangquan2015/article/details/80160864

ORB特征 https://blog.csdn.net/qq_32998593/article/details/79221641

利用C语言,实现一个简单的ORB特征提取、描述子构造及匹配的程序,这是之前完成的一项大作业的初步版本,分享到博客里,供大家交流,实现完整版的ORB特征版本要复杂一些。

这个版本严格来说不算是对ORB特征的复现,如果仔细看代码,ORB中的R(旋转)是没有实现的,因此从可视化效果来看,还是存在一些错误的匹配,这个程序的主要参考价值在于如果不使用OpenCV等开源库,纯用C语言实现一个图像处理算法,各部分参考的博客详见文章开头

程序的输入为两张bmp图片,输出为拼接的bmp图片,以及对应特征点匹配,用线相连

主函数 main.c

#include <stdio.h>
//#include "stdafx.h" 
#include <math.h>
#include "orb_head.h"
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[])
{
	ZqImage* img1 = imread("3.bmp");  //读取原始图像
	ZqImage* img_gray1 = rgb2gray(img1);   // 变为灰度图
	ZqImage* img2 = imread("4.bmp");  //读取原始图像
	ZqImage* img_gray2 = rgb2gray(img2);   // 变为灰度图
	
	FastPix* img1_fast = get_FastPoint(img_gray1,1);
	FastPix* img2_fast = get_FastPoint(img_gray2,2);

	FastPix* img1_brief = get_Brief(img_gray1, img1_fast);
	FastPix* img2_brief = get_Brief(img_gray2, img2_fast);
	
	MatchPix* match = match_Brief(img1_brief, img2_brief);
	ZqImage* finalp = imconcat(img1, img2);

	
	for (int i = 0; i < match->total - 1; i++) //total_point
	{
		int i1, j1, i2, j2;
		i1 = match->i1[i];
		j1 = match->j1[i];
		i2 = match->i2[i];
		j2 = match->j2[i] + img1->width;
		finalp = Line_Bresenham(j1, i1, j2, i2, finalp);//i height j width
	}

	imwrite("ORB.bmp", finalp);
	
	printf("可视化完成\n");
	free(img1);
	free(img2);
	getchar();
	return 0;
}

FastPix* get_FastPoint(ZqImage* img, int index )  // fast特征点
{
	
	ZqImage* img_gray1;
	img_gray1 = (ZqImage*)malloc(sizeof(ZqImage));
	img_gray1->imageData = (unsigned char*)malloc(sizeof(unsigned char)*img->width*img->height);
	img_gray1->height = img->height;
	img_gray1->width = img->width;
	img_gray1->channels = 1;

	int step_gray = img->width * 1;
	int i, j, k;

	for (i = 0; i < img->height; i++)
	{
		for (j = 0; j < img->width; j++)
		{
			int temp = img->imageData[(img->height - 1 - i)*step_gray + j];
			img_gray1->imageData[(img->height - 1 - i)*step_gray + j] = temp;
		}
	}
	int threshold = 70;
	int offset_row[16] = { -3, -3, -2, -1, 0, 1, 2, 3, 3, 3, 2, 1, 0, -1, -2, -3 };//对应height
	int offset_col[16] = { 0, 1, 2, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -2, -1 };
	FastPix* fast = (FastPix*)malloc(sizeof(FastPix));
	fast->i = (int*)malloc(sizeof(int)*img->width*img->height);
	fast->j = (int*)malloc(sizeof(int)*img->width*img->height);
	fast->brief = (unsigned char*)malloc(sizeof(unsigned char)*img->width*img->height*8);
	//fast->PixValue = (int*)malloc(sizeof(int)*img1->width*img1->height);
	int test = img->imageData[(img->height - 1 - (3 + offset_row[0]))*step_gray + (3 + offset_col[0])];
	int fast_total = 0;
	for (i = 3; i < img->height - 3; i++)
	{
		for (j = 3; j < img->width - 3; j++)
		{
			int num = 0;
			for (k = 0; k < 16; k++)
			{
				int center = img->imageData[(img->height - 1 - i)*step_gray + j];
				int neighbor = img->imageData[(img->height - 1 - (i + offset_row[k]))*step_gray + (j + offset_col[k])];

				if (abs(neighbor - center) > threshold)
				{
					num = num + 1;
				}
			}
			if (num > 9) // 超过9个,则center是fast角点
			{
				for (k = 0; k < 16; k++)
				{
					img_gray1->imageData[(img->height - 1 - (i + offset_row[k]))*step_gray + (j + offset_col[k])] = 255;
				}
				if ((i > 16 && i < img->height - 16) && (j>16 && j < img->width - 16))
				{
					fast->i[fast_total] = i;
					fast->j[fast_total] = j;
					fast_total++;
				}
			}
		}
	}
	fast->total = fast_total - 1;

	char str[80];
	sprintf(str, "Result_gray_%d.bmp", index);
	imwrite(str, img_gray1);
	//free(img);
	return fast;
}

FastPix* get_Brief(ZqImage* img_g, FastPix* img1_fast)  // brief描述子
{
	FastPix* fast = (FastPix*)malloc(sizeof(FastPix));
	int size = img1_fast->total;
	fast->total = size;
	fast->i = (int*)malloc(sizeof(int)*size);
	fast->j = (int*)malloc(sizeof(int)*size);
	fast->brief = (unsigned char*)malloc(sizeof(unsigned char)* size * 8);
	int i, j;

	int r1_x[64] = { 8 ,6 ,-7 ,-2 ,-10 ,4 ,12 ,10 ,6 ,-6 ,-5 ,1 ,-1 ,5 ,8 ,-2 ,0 ,7 ,3 ,-10 ,-5 ,\
		- 5 ,-8 ,-1 ,1 ,9 ,4 ,7 ,-5 ,2 ,10 ,-4 ,4 ,8 ,-6 ,3 ,-6 ,-3 ,-4 ,-3 ,-2 ,-5 ,-4 ,3 ,10 ,-10 ,\
		- 2 ,2 ,3 ,6 ,1 ,-11 ,4 ,6 ,-3 ,-2 ,-4 ,-1 ,-6 ,7 ,-8 ,-2 ,8 ,2 };
	int r1_y[64] = { -5 ,3 ,6 ,2 ,-1 ,0 ,-1 ,-9 ,2 ,7 ,0 ,-4 ,4 ,7 ,3 ,3 ,1 ,-4 ,1 ,7 ,0 ,4 ,0 ,4 ,\
		- 4 ,-7 ,-15 ,5 ,2 ,5 ,4 ,-11 ,1 ,12 ,6 ,-1 ,-3 ,-2 ,2 ,1 ,-6 ,-3 ,-2 ,7 ,6 ,2 ,-8 ,1 ,5 ,-1 ,-8 ,\
		- 5 ,5 ,0 ,2 ,2 ,5 ,6 ,3 ,3 ,2 ,-7 ,6 ,6 };
	int r2_x[64] = { -2 ,-3 ,2 ,6 ,4 ,11 ,-6 ,-1 ,-13 ,3 ,5 ,1 ,-10 ,-13 ,11 ,-8 ,6 ,-1 ,0 ,-4 ,4 ,\
		- 1 ,-7 ,-2 ,0 ,-1 ,3 ,2 ,-16 ,-5 ,-1 ,3 ,4 ,3 ,-2 ,9 ,-5 ,-9 ,3 ,6 ,6 ,3 ,5 ,1 ,10 ,\
		8 ,1 ,5 ,7 ,9 ,-1 ,8 ,7 ,6 ,7 ,-1 ,4 ,7 ,-3 ,-8 ,4 ,1 ,3 ,0 };
	int r2_y[64] = { 2 ,-1 ,-4 ,-10 ,0 ,4 ,6 ,-1 ,-3 ,11 ,11 ,15 ,-6 ,-8 ,1 ,2 ,4 ,-3 ,-1 ,\
		- 2 ,-12 ,-5 ,0 ,-3 ,5 ,4 ,2 ,4 ,13 ,-2 ,17 ,12 ,8 ,-12 ,-4 ,-8 ,1 ,-7 ,-3 ,9 ,-8 ,-11 ,\
		- 5 ,-2 ,4 ,1 ,-2 ,1 ,-7 ,2 ,6 ,0 ,7 ,-2 ,6 ,-8 ,0 ,4 ,-6 ,-9 ,-4 ,-2 ,8 ,-6 };
	int count = 0;
	printf(" %d\n", (img_g->height));
	int step = img_g->width;

	for (i = 0; i < size; i++)//
	{
		int i_index = 0;
		int j_index = 0;
		i_index = img1_fast->i[i];
		j_index = img1_fast->j[i];
		fast->i[i] = i_index;
		fast->j[i] = j_index;
		unsigned char brief[8] = {0};
		for (j = 0; j < 64; j++)
		{
			int r1_value = 0;
			int r2_value = 0;
			int index = j / 8;

			r1_value = img_g->imageData[(img_g->height - 1 - (i_index + r1_x[j]))*step + j_index + r1_y[j]];
			r2_value = img_g->imageData[(img_g->height - 1 - (i_index + r2_x[j]))*step + j_index + r2_y[j]];

			brief[index] = brief[index] << 1;
			if (r1_value > r2_value)
			{
				brief[index] += 1;
			}
			int temp_index = i * 8 + index;
			fast->brief[temp_index] = brief[index];
		}

	}
	return fast;
}

MatchPix* match_Brief(FastPix* img1_brief, FastPix* img2_brief)  // brief描述子
{
	int i, j, k, m, n;
	int total_point = 0;
	int match_thres = 8;//15
	if (img1_brief->total > img2_brief->total) {
		total_point = img2_brief->total;
	}
	else {
		total_point = img1_brief->total;
	}
	MatchPix* match = (MatchPix*)malloc(sizeof(MatchPix));
	match->i1 = (int*)malloc(sizeof(int) * total_point);
	match->j1 = (int*)malloc(sizeof(int) * total_point);
	match->i2 = (int*)malloc(sizeof(int) * total_point);
	match->j2 = (int*)malloc(sizeof(int) * total_point);
	total_point = 0;
	for (m = 0; m < img1_brief->total; m++)
	{
		int smallest_count = 64;
		int smallest_index = 0;
		for (i = 0; i < img2_brief->total; i++)
		{
			int count_one = 0;
			for (j = 0; j < 8; j++)
			{
				unsigned char p1, p2, one;
				one = 0;
				p1 = img1_brief->brief[m * 8 + j];
				p2 = img2_brief->brief[i * 8 + j];
				one = p1 ^ p2;
				for (k = 0; k < 8; k++)
				{
					if (one % 2 == 1)
					{
						count_one++;
					}
					one = one >> 1;
				}
			}
			if (count_one < smallest_count)
			{
				smallest_count = count_one;
				smallest_index = i;
			}
		}
		if (smallest_count < match_thres)
		{
			match->i1[total_point] = img1_brief->i[m];
			match->j1[total_point] = img1_brief->j[m];
			match->i2[total_point] = img2_brief->i[smallest_index];
			match->j2[total_point] = img2_brief->j[smallest_index];

			total_point++;
		}
	}
	match->total = total_point - 1;
	printf("匹配%d \n", total_point);
	return match;
}

头文件,orb_head.h,参考了zhangquan2015的博客

#pragma once
#ifndef orb_CV_H  
#define orb_CV_H
#define PI 3.1415926  
/*按字节对齐Start
  如果不按字节对齐,
  第一个struct定义为16bit,
  而BMP文件中实际为14bit,
  故读取会错位。
  --zhangquan
*/
//位图文件头bf
#pragma pack (push ,1)
typedef struct
{
	unsigned short bfType;  //文件标识符BM,0x424D 2bit
	unsigned long    bfSize;//文件的大小 4bit
	unsigned short    bfReserved1; //保留值,必须设置为0 2bit
	unsigned short    bfReserved2; //保留值,必须设置为0 2bit
	unsigned long    bfOffBits;//文件头的最后到图像数据位开始的偏移量 4bit
} BMPFileHeader;

//位图信息头bi  
typedef struct
{
	unsigned long  biSize;//信息头的大小
	long   biWidth;   //图像宽度
	long   biHeight;   //图像高度
	unsigned short   biPlanes; //图像的位面数
	unsigned short   biBitCount;//每个像素的位数
	unsigned long  biCompression;//压缩类型
	unsigned long  biSizeImage;//图像大小,字节
	long   biXPelsPerMeter; //水平分辨率
	long   biYPelsPerMeter; //垂直分辨率
	unsigned long   biClrUsed; //使用的色彩数
	unsigned long   biClrImportant;//重要的颜色数
} BMPInfoHeader;
//颜色表
typedef struct
{
	unsigned char rgbBlue; //蓝色分量
	unsigned char rgbGreen; //绿色分量
	unsigned char rgbRed; //红色分量
	unsigned char rgbReserved; //保留值
} RgbQuad;

typedef struct
{
	int width;
	int height;
	int channels;
	unsigned char* imageData;
}ZqImage;

typedef struct
{
	int total;
	int* i;
	int* j;
	unsigned char* brief;
//	int* PixValue;
}FastPix;

typedef struct
{
	int total;
	int* i1;
	int* j1;
	int* i2;
	int* j2;
}MatchPix;

#pragma pack (pop)
/*按字节对齐End*/
ZqImage* imread(char* path);
int imwrite(char* path, ZqImage* bmpImg);
ZqImage* imrotate(ZqImage* bmpImg, int Angle);
ZqImage* imscale(ZqImage* bmpImg, double dy, double dx);
ZqImage* rgb2gray(ZqImage* img);
FastPix* get_FastPoint(ZqImage* img1, int index);
FastPix* get_Brief(ZqImage* img_gray1, FastPix* img1_fast);
MatchPix* match_Brief(FastPix* img1_brief, FastPix* img2_brief);
ZqImage* imconcat(ZqImage* img1, ZqImage* img2);
ZqImage* Line_Bresenham(int x1, int y1, int x2, int y2, ZqImage* img);
#endif

图像处理基本函数文件,image_process.c,参考zhangquan2015博客以及Bluechalk博客

#include "orb_head.h"  
#include <stdio.h>  
#include <stdlib.h>
#include <math.h>
ZqImage* rgb2gray(ZqImage* img) 
{
	ZqImage* img_gray;
	img_gray = (ZqImage*)malloc(sizeof(ZqImage));
	img_gray->imageData = (unsigned char*)malloc(sizeof(unsigned char)*img->width*img->height);
	img_gray->height = img->height;
	img_gray->width = img->width;
	img_gray->channels = 1;
	int i, j, k;
	int step = img->width * 3;
	int step_gray = img->width;
	float rgb_para[3] = { 0.299, 0.587, 0.114 };
	//float rgb_para[3] = { 0.333, 0.333, 0.334 };
	for (i = 0; i < img->height; i++)
	{
		for (j = 0; j < img->width; j++)
		{
			float gray_value = 0;
			for (k = 0; k < 3; k++)
			{
				gray_value += rgb_para[k] * img->imageData[(img->height - 1 - i)*step + j * 3 + k];
			}
			img_gray->imageData[(img->height - 1 - i)*step_gray + j] = (int)gray_value;
		}
	}
	return img_gray;
}
ZqImage* imconcat(ZqImage* img1, ZqImage* img2)  //图像拼接
{
	int i, j, k;
	ZqImage* combined_image;
	combined_image = (ZqImage*)malloc(sizeof(ZqImage));  //Fast 点集在原图上的表示 
	combined_image->imageData = (unsigned char*)malloc(sizeof(unsigned char)* (img1->width + img2->width) * 3 * img1->height);
	combined_image->width = img1->width + img2->width;
	combined_image->height = img1->height;
	combined_image->channels = 3;

	int step = img1->width * img1->channels;
	int step_combined = combined_image->width * combined_image->channels;
	for (i = 0; i < combined_image->height; i++)
	{
		for (j = 0; j < combined_image->width; j++)
		{
			if (j < img1->width)
			{
				for (k = 0; k < combined_image->channels; k++)
				{
					int img1_value;                            //Img1
					img1_value = img1->imageData[(img1->height - 1 - i)*step + j * 3 + k];
					combined_image->imageData[(combined_image->height - 1 - i)*step_combined + j * 3 + k] = img1_value;
				}
			}
			else
			{
				for (k = 0; k < combined_image->channels; k++)
				{
					int img2_value;                           //Img2
					img2_value = img2->imageData[(img1->height - 1 - i)*step + (j - img1->width) * 3 + k];
					combined_image->imageData[(combined_image->height - 1 - i)*step_combined + j * 3 + k] = img2_value;
				}
			}
		}
	}
	//free(img1);
	//free(img2);
	return combined_image;
}
ZqImage* Line_Bresenham(int x1, int y1, int x2, int y2, ZqImage* img) 
{
	ZqImage* lined = (ZqImage*)malloc(sizeof(ZqImage)); 
	lined->width = (int)malloc(sizeof(int));
	lined->width = img->width;
	lined->height = img->height;
	lined->channels = 3;
	lined->imageData = (unsigned char*)malloc(sizeof(unsigned char)*img->width * 3 * img->height);
	lined = img;
	int step = img->channels*img->width;
	int x = x1;
	int y = y1;
	int dx = abs(x2 - x1);
	int dy = abs(y2 - y1);
	int s1 = x2 > x1 ? 1 : -1;
	int s2 = y2 > y1 ? 1 : -1;

	char interchange = 0;	    
	if (dy > dx)				
	{
		int temp = dx;
		dx = dy;
		dy = temp;
		interchange = 1;
	}

	int p = 2 * dy - dx;
	for (int i = 0; i < dx; i++)
	{

		lined->imageData[(img->height - 1 - y)*step + x * 3] = 215;
		lined->imageData[(img->height - 1 - y)*step + x * 3 + 1] = 50;
		lined->imageData[(img->height - 1 - y)*step + x * 3 + 2] = 0;

		if (p >= 0)
		{
			if (!interchange)		// 当斜率 < 1 时,选取上下象素点
				y += s2;
			else					// 当斜率 > 1 时,选取左右象素点
				x += s1;
			p -= 2 * dx;
		}
		if (!interchange)
			x += s1;				// 当斜率 < 1 时,选取 x 为步长
		else
			y += s2;				// 当斜率 > 1 时,选取 y 为步长
		p += 2 * dy;
	}
	return lined;
}
ZqImage* imread(char* path)
{
	ZqImage* bmpImg;
	FILE* pFile;
	BMPFileHeader bmpFileHeader;
	BMPInfoHeader bmpInfoHeader;
	int channels = 1;
	int width = 0;
	int height = 0;
	int step = 0;
	int offset = 0;
	unsigned char pixVal;//像素指针
	RgbQuad* quad;//BMP图像颜色表
	int i, j, k;

	bmpImg = (ZqImage*)malloc(sizeof(ZqImage));

	if ((pFile = fopen(path, "rb")) == NULL)
	{
		printf("Cann't open the file!\n");
		free(bmpImg);
		return NULL;
	}
	//读取文件头
	fread(&bmpFileHeader, sizeof(BMPFileHeader), 1, pFile);
	printf("=========================================== \n");
	printf("BMP文件头信息:\n");
	printf("文件标识符 :0X%X \n", bmpFileHeader.bfType);
	printf("文件大小:%d \n", bmpFileHeader.bfSize);
	printf("保留字:%d \n", bmpFileHeader.bfReserved1);
	printf("保留字:%d \n", bmpFileHeader.bfReserved2);
	printf("位图数据偏移字节数:%d \n", bmpFileHeader.bfOffBits);
	//读取信息头
	fread(&bmpInfoHeader, sizeof(BMPInfoHeader), 1, pFile);
	printf("=========================================== \n");
	printf("BMP文件信息头\n");
	printf("结构体长度:%d \n", bmpInfoHeader.biSize);
	printf("位图宽度:%d \n", bmpInfoHeader.biWidth);
	printf("位图高度:%d \n", bmpInfoHeader.biHeight);
	printf("位图平面数:%d \n", bmpInfoHeader.biPlanes);
	printf("颜色位数:%d \n", bmpInfoHeader.biBitCount);
	printf("压缩方式:%d \n", bmpInfoHeader.biCompression);
	printf("实际位图数据占用的字节数:%d \n", bmpInfoHeader.biSizeImage);
	printf("X方向分辨率:%d \n", bmpInfoHeader.biXPelsPerMeter);
	printf("Y方向分辨率:%d \n", bmpInfoHeader.biYPelsPerMeter);
	printf("使用的颜色数:%d \n", bmpInfoHeader.biClrUsed);
	printf("重要颜色数:%d \n", bmpInfoHeader.biClrImportant);
	printf("=========================================== \n");

	if (bmpInfoHeader.biBitCount == 8)
	{
		printf("\n该图像为灰度图! \n");
		channels = 1;
		width = bmpInfoHeader.biWidth;
		height = bmpInfoHeader.biHeight;
		//windows规定每一个扫描行为4的倍数,不足补0
		offset = (channels*width) % 4;
		if (offset != 0)
		{
			offset = 4 - offset;
		}
		bmpImg->width = width;
		bmpImg->height = height;
		bmpImg->channels = 1;
		//分配图像空间
		bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
		//迭代步长
		step = channels * width;
		//读取图像颜色表
		quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
		fread(quad, sizeof(RgbQuad), 256, pFile);
		free(quad);
		//读取灰度图像数据
		for (i = 0; i < height; i++)
		{
			for (j = 0; j < width; j++)
			{
				fread(&pixVal, sizeof(unsigned char), 1, pFile);
				bmpImg->imageData[(height - 1 - i)*step + j] = pixVal;
			}
			if (offset != 0)
			{
				for (j = 0; j < offset; j++)
				{
					fread(&pixVal, sizeof(unsigned char), 1, pFile);
				}
			}
		}
	}
	else if (bmpInfoHeader.biBitCount == 24)
	{
		printf("\n该图像为彩色图! \n");
		channels = 3;
		width = bmpInfoHeader.biWidth;
		height = bmpInfoHeader.biHeight;
		bmpImg->width = width;
		bmpImg->height = height;
		bmpImg->channels = 3;
		bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width * 3 * height);
		step = channels * width;
		//windows规定每一个扫描行为4的倍数,不足补0
		offset = (channels*width) % 4;
		if (offset != 0)
		{
			offset = 4 - offset;
		}
		//读取彩色图像数据
		for (i = 0; i < height; i++)
		{
			for (j = 0; j < width; j++)
			{
				for (k = 0; k < 3; k++)
				{
					fread(&pixVal, sizeof(unsigned char), 1, pFile);
					bmpImg->imageData[(height - 1 - i)*step + j * 3 + k] = pixVal;
				}
			}
			if (offset != 0)
			{
				for (j = 0; j < offset; j++)
				{
					fread(&pixVal, sizeof(unsigned char), 1, pFile);
				}
			}
		}
	}
	return bmpImg;
}

int imwrite(char* path, ZqImage* bmpImg)
{
	FILE *pFile;
	BMPFileHeader bmpFileHeader;
	BMPInfoHeader bmpInfoHeader;
	int width = 0;
	int height = 0;
	int step = 0;
	int channels = 1;
	int i, j;
	int offset;
	unsigned char pixVal = '\0';
	RgbQuad* quad;
	width = bmpImg->width;
	height = bmpImg->height;
	channels = bmpImg->channels;
	step = width * channels;
	pFile = fopen(path, "wb");
	if (!pFile)
	{
		return 0;
	}
	//写入文件头标识符
	bmpFileHeader.bfType = 0x4D42;
	if (channels == 1)//8位,灰度图
	{
		//windows规定每一个扫描行为4的倍数,不足补0
		offset = step % 4;
		if (offset != 0)
		{
			offset = 4 - offset;
			step += offset;
		}
		//写入文件头
		bmpFileHeader.bfSize = 54 + 256 * 4 + width;
		bmpFileHeader.bfReserved1 = 0;
		bmpFileHeader.bfReserved2 = 0;
		bmpFileHeader.bfOffBits = 54 + 256 * 4;
		fwrite(&bmpFileHeader, sizeof(BMPFileHeader), 1, pFile);
		//写入信息头
		bmpInfoHeader.biSize = 40;
		bmpInfoHeader.biWidth = width;
		bmpInfoHeader.biHeight = height;
		bmpInfoHeader.biPlanes = 1;
		bmpInfoHeader.biBitCount = 8;
		bmpInfoHeader.biCompression = 0;
		bmpInfoHeader.biSizeImage = height * step;
		bmpInfoHeader.biXPelsPerMeter = 0;
		bmpInfoHeader.biYPelsPerMeter = 0;
		bmpInfoHeader.biClrUsed = 256;
		bmpInfoHeader.biClrImportant = 256;
		fwrite(&bmpInfoHeader, sizeof(BMPInfoHeader), 1, pFile);

		quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
		for (i = 0; i < 256; i++)
		{
			quad[i].rgbBlue = i;
			quad[i].rgbGreen = i;
			quad[i].rgbRed = i;
			quad[i].rgbReserved = 0;
		}
		fwrite(quad, sizeof(RgbQuad), 256, pFile);
		free(quad);

		for (i = height - 1; i > -1; i--)
		{
			for (j = 0; j < width; j++)
			{
				pixVal = bmpImg->imageData[i*width + j];
				fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
			}
			if (offset != 0)
			{
				for (j = 0; j < offset; j++)
				{
					pixVal = 0;
					fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
				}
			}
		}
	}
	else if (channels == 3)//24位,通道,彩图
	{
		//windows规定每一个扫描行为4的倍数,不足补0
		offset = step % 4;
		if (offset != 0)
		{
			offset = 4 - offset;
			step += 4 - offset;
		}
		//写入文件头
		bmpFileHeader.bfSize = height * step + 54;
		bmpFileHeader.bfReserved1 = 0;
		bmpFileHeader.bfReserved2 = 0;
		bmpFileHeader.bfOffBits = 54;
		fwrite(&bmpFileHeader, sizeof(BMPFileHeader), 1, pFile);
		//写入信息头
		bmpInfoHeader.biSize = 40;
		bmpInfoHeader.biWidth = width;
		bmpInfoHeader.biHeight = height;
		bmpInfoHeader.biPlanes = 1;
		bmpInfoHeader.biBitCount = 24;
		bmpInfoHeader.biCompression = 0;
		bmpInfoHeader.biSizeImage = height * step;
		bmpInfoHeader.biXPelsPerMeter = 0;
		bmpInfoHeader.biYPelsPerMeter = 0;
		bmpInfoHeader.biClrUsed = 0;
		bmpInfoHeader.biClrImportant = 0;
		fwrite(&bmpInfoHeader, sizeof(BMPInfoHeader), 1, pFile);

		for (i = bmpImg->height - 1; i > -1; i--)
		{
			for (j = 0; j < bmpImg->width; j++)
			{
				pixVal = bmpImg->imageData[i*width * 3 + j * 3 + 0];
				fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
				pixVal = bmpImg->imageData[i*width * 3 + j * 3 + 1];
				fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
				pixVal = bmpImg->imageData[i*width * 3 + j * 3 + 2];
				fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
			}
			if (offset != 0)
			{
				for (j = 0; j < offset; j++)
				{
					pixVal = 0;
					fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
				}
			}
		}
	}
	fclose(pFile);
	return 1;
}

ZqImage* imrotate(ZqImage* bmpImg, int Angle)
{
	//图片旋转处理
	ZqImage* bmpImgRot;
	double angle;//要旋转的弧度数
	int width = 0;
	int height = 0;
	int step = 0;
	int Rot_step = 0;
	int channels = 1;
	int i, j, k;
	width = bmpImg->width;
	height = bmpImg->height;
	channels = bmpImg->channels;
	int midX_pre, midY_pre, midX_aft, midY_aft;//旋转前后的中心点的坐标
	midX_pre = width / 2;
	midY_pre = height / 2;
	int pre_i, pre_j, after_i, after_j;//旋转前后对应的像素点坐标
	angle = 1.0 * Angle * PI / 180;
	//初始化旋转后图片的信息
	bmpImgRot = (ZqImage*)malloc(sizeof(ZqImage));
	bmpImgRot->channels = channels;
	bmpImgRot->width = bmpImg->width;
	bmpImgRot->height = bmpImg->height;
	midX_aft = bmpImgRot->width / 2;
	midY_aft = bmpImgRot->height / 2;
	step = channels * width;
	Rot_step = channels * bmpImgRot->width;
	bmpImgRot->imageData = (unsigned char*)malloc(sizeof(unsigned char)*bmpImgRot->width*bmpImgRot->height*channels);
	if (channels == 1)
	{
		//初始化旋转图像
		for (i = 0; i < bmpImgRot->height; i++)
		{
			for (j = 0; j < bmpImgRot->width; j++)
			{
				bmpImgRot->imageData[(bmpImgRot->height - 1 - i)*Rot_step + j] = 0;
			}
		}
		//坐标变换
		for (i = 0; i < bmpImgRot->height; i++)
		{
			for (j = 0; j < bmpImgRot->width; j++)
			{
				after_i = i - midX_aft;
				after_j = j - midY_aft;
				pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midX_pre;
				pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midY_pre;
				if (pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内
					bmpImgRot->imageData[i * Rot_step + j] = bmpImg->imageData[pre_i * step + pre_j];
			}
		}
	}
	else if (channels == 3)
	{
		//初始化旋转图像
		for (i = 0; i < bmpImgRot->height; i++)
		{
			for (j = 0; j < bmpImgRot->width; j++)
			{
				for (k = 0; k < 3; k++)
				{
					bmpImgRot->imageData[(bmpImgRot->height - 1 - i)*Rot_step + j * 3 + k] = 0;
				}
			}
		}
		//坐标变换
		for (i = 0; i < bmpImgRot->height; i++)
		{
			for (j = 0; j < bmpImgRot->width; j++)
			{
				after_i = i - midX_aft;
				after_j = j - midY_aft;
				pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midX_pre;
				pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midY_pre;

				if (pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内
					for (k = 0; k < 3; k++)
					{
						bmpImgRot->imageData[i * Rot_step + j * 3 + k] = bmpImg->imageData[pre_i * step + pre_j * 3 + k];
					}
			}
		}
	}
	return bmpImgRot;
}

ZqImage* imscale(ZqImage* bmpImg, double dy, double dx)
{
	//图片缩放处理
	ZqImage* bmpImgSca;
	int width = 0;
	int height = 0;
	int channels = 1;
	int step = 0;
	int Sca_step = 0;
	int i, j, k;
	width = bmpImg->width;
	height = bmpImg->height;
	channels = bmpImg->channels;
	int pre_i, pre_j, after_i, after_j;//缩放前对应的像素点坐标
	//初始化缩放后图片的信息
	bmpImgSca = (ZqImage*)malloc(sizeof(ZqImage));
	bmpImgSca->channels = channels;
	bmpImgSca->width = (int)(bmpImg->width*dy + 0.5);
	bmpImgSca->height = (int)(bmpImg->height*dx + 0.5);
	step = channels * width;
	Sca_step = channels * bmpImgSca->width;
	bmpImgSca->imageData = (unsigned char*)malloc(sizeof(unsigned char)*bmpImgSca->width*bmpImgSca->height*channels);

	if (channels == 1)
	{
		//初始化缩放图像
		for (i = 0; i < bmpImgSca->height; i++)
		{
			for (j = 0; j < bmpImgSca->width; j++)
			{
				bmpImgSca->imageData[(bmpImgSca->height - 1 - i)*Sca_step + j] = 0;
			}
		}
		//坐标变换
		for (i = 0; i < bmpImgSca->height; i++)
		{
			for (j = 0; j < bmpImgSca->width; j++)
			{
				after_i = i;
				after_j = j;
				pre_i = (int)(after_i / dx + 0);
				pre_j = (int)(after_j / dy + 0);
				if (pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内
				{
					bmpImgSca->imageData[i * Sca_step + j] = bmpImg->imageData[pre_i * step + pre_j];
				}
			}
		}
	}
	else if (channels == 3)
	{
		//初始化缩放图像
		for (i = 0; i < bmpImgSca->height; i++)
		{
			for (j = 0; j < bmpImgSca->width; j++)
			{
				for (k = 0; k < 3; k++)
				{
					bmpImgSca->imageData[(bmpImgSca->height - 1 - i)*Sca_step + j * 3 + k] = 0;
				}
			}
		}
		//坐标变换
		for (i = 0; i < bmpImgSca->height; i++)
		{
			for (j = 0; j < bmpImgSca->width; j++)
			{
				after_i = i;
				after_j = j;
				pre_i = (int)(after_i / dx + 0.5);
				pre_j = (int)(after_j / dy + 0.5);
				if (pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内
					for (k = 0; k < 3; k++)
					{
						bmpImgSca->imageData[i * Sca_step + j * 3 + k] = bmpImg->imageData[pre_i * step + pre_j * 3 + k];
					}
			}
		}
	}
	return bmpImgSca;
}

 

  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值