纯C语言 不用 OpenCV 开源库 实现ORB特征提取及匹配 可视化
参考:画直线 https://blog.****.net/Bluechalk/article/details/84578197
C语言图像读取及基本操作 https://blog.****.net/zhangquan2015/article/details/80160864
ORB特征 https://blog.****.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;
}