1 代码
包含三个文件:把bmp.h、bmp.cpp、main.cpp
1.1 bmp.h
#pragma once
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
//位图文件头定义
typedef struct tagBITMAPFILEHEADER {
// WORD bfType;//单独读取,结构体中就不定义了
DWORD bfSize;//文件大小
WORD bfReserved1;//保留字
WORD bfReserved2;//保留字
DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数
}BITMAPFILEHEADER;
//位图信息头定义
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;//信息头大小
DWORD biWidth;//图像宽度
DWORD biHeight;//图像高度
WORD biPlanes;//位平面数,必须为1
WORD biBitCount;//每像素位数
DWORD biCompression; //压缩类型
DWORD biSizeImage; //压缩图像大小字节数
DWORD biXPelsPerMeter; //水平分辨率
DWORD biYPelsPerMeter; //垂直分辨率
DWORD biClrUsed; //位图实际用到的色彩数
DWORD biClrImportant; //本位图中重要的色彩数
}BITMAPINFOHEADER; //位图信息头定义
//彩色图像的像素信息
typedef struct tagRgbImgData
{
BYTE blue;
BYTE green;
BYTE red;
}DATA;
// 灰度图像的像素信息
typedef struct tagGrayBmpData
{
BYTE gray;
}GRAYDATA;
// 调色板的结构定义,灰度图有256个调色板
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色的亮度(值范围为0-255)
BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
BYTE rgbRed; // 红色的亮度(值范围为0-255)
BYTE rgbReserved;// 保留,必须为0
} RGBQUAD;
double xAfterRot(int x, int y, double theta, int cx, int cy);
double yAfterRot(int x, int y, double theta, int cx, int cy);
// 检查图片类型是不是BMP
int checkImgType(const char* filename);
// 彩色BMP图像读取操作
DATA* readRgbBmp(const char* filename, WORD* bfType, BITMAPFILEHEADER* strHead, BITMAPINFOHEADER* strInfo, int* h, int* mw, int* imgSize, int* cx, int* cy);
// 灰色BMP图像读取操作
GRAYDATA* readGrayBmp(const char* filename, WORD* bfType, BITMAPFILEHEADER* strHead, BITMAPINFOHEADER* strInfo, RGBQUAD* ipRGB, int* h, int* mw, int* imgSize, int* cx, int* cy);
int modifyWidth(int biWidth);
int getImageSize(int biWidth, int biHeight, int bitCount);
DATA* scale(const DATA* src, float x, float y, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType);
GRAYDATA* scaleGray(const GRAYDATA* src, float x, float y, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType, RGBQUAD* ipRGB);
DATA* rotation(const DATA* src, double theta, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType);
GRAYDATA* rotationGray(const GRAYDATA* src, double theta, int mh, int mw,
int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo,WORD bfType, RGBQUAD* ipRGB);
1. 2 bmp.cpp
#include <cstdio>
#include <iostream>
#include <vector>
#include<algorithm>
#include <cmath>
#define PI acos(-1)
#include <cstdlib>
#include<exception>
#include<set>
#include "bmp.h"
using namespace std;
int checkImgType(const char* filename) {
FILE* fpi = fopen(filename, "rb");
WORD bfType;
if (fpi == NULL) {
cout << filename << " 文件未能找到" << endl;
return 0;
}
else {
fread(&bfType, 1, sizeof(WORD), fpi);
if (0x4d42 != bfType) {
cout << "Error: The file is not a bmp image!" << endl;
return 0;
}
}
return 1;
}
int modifyWidth(int biWidth) {
if (biWidth % 4 == 0)
return biWidth;
return (biWidth / 4 + 1) * 4;
}
int getImageSize(int biWidth, int biHeight, int bitCount) {
int bytePerPixel = bitCount / 8;
int LineBytes = (biWidth * bytePerPixel + 3) / 4 * 4; //这个其实也相当于是每行的宽度,但是这里的宽度指的是字节数,而不是像素数
return LineBytes * biWidth;
}
DATA * readRgbBmp(const char* filename, WORD * bfType, BITMAPFILEHEADER* strHead, BITMAPINFOHEADER* strInfo, int* h, int* mw, int* imgSize, int * cx, int* cy) {
FILE* fpi = fopen(filename, "rb");
fread(bfType, 1, sizeof(WORD), fpi);
fread(strHead, 1, sizeof(tagBITMAPFILEHEADER), fpi);
fread(strInfo, 1, sizeof(tagBITMAPINFOHEADER), fpi);
*h = (*strInfo).biHeight;
*mw = modifyWidth((*strInfo).biWidth);
*imgSize = (*h) * (*mw);
*cx = (*mw) / 2;
*cy = (*h) / 2;
DATA* imgData = new DATA[*imgSize];
fread(imgData, 1, sizeof(DATA) * (*imgSize), fpi);
return imgData;
}
GRAYDATA* readGrayBmp(const char* filename, WORD* bfType, BITMAPFILEHEADER* strHead, BITMAPINFOHEADER* strInfo, RGBQUAD* ipRGB, int* h, int* mw, int* imgSize, int* cx, int* cy) {
FILE* fpi = fopen(filename, "rb");
fread(bfType, 1, sizeof(WORD), fpi);
fread(strHead, 1, sizeof(tagBITMAPFILEHEADER), fpi);
fread(strInfo, 1, sizeof(tagBITMAPINFOHEADER), fpi);
fread(ipRGB, sizeof(RGBQUAD) * 256, 1, fpi);
*h = (*strInfo).biHeight;
*mw = modifyWidth( (*strInfo).biWidth );
*imgSize = (*h) * (*mw);
*imgSize = (*h) * (*mw);
*cx = (*mw) / 2;
*cy = (*h) / 2;
//GRAYDATA* imgData = new GRAYDATA[*imgSize];
GRAYDATA* imgData = (GRAYDATA*) malloc(sizeof(GRAYDATA) * (*imgSize));
fread(imgData, 1, sizeof(GRAYDATA) * (*imgSize), fpi);
return imgData;
}
DATA* scale(const DATA* src, float x, float y, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType) {
float target_x, target_y;
int int_target_x, int_target_y;
DATA* target = new DATA[imgSize];
int* needItr = new int[imgSize];
memset(target, 0, sizeof(DATA) * imgSize);
memset(needItr, 0, sizeof(int) * imgSize);
for (int i = 0; i < mh; i++)
for (int j = 0; j < mw; j++) {
target_x = (j - cx) / x + cx;
target_y = (i - cy) / y + cy;
int_target_x = (int)target_x;
int_target_y = (int)target_y;
if (int_target_x - target_x < 0 || int_target_y - target_y < 0 || int_target_x <0 || int_target_x > mw || int_target_y < 0 || int_target_y > mh) {
target[i * mw + j].red = 0;
target[i * mw + j].green = 0;
target[i * mw + j].blue = 0;
needItr[i * mw + j] = 1;
}
else {
try {
target[i * mw + j].blue = src[int_target_y * mw + int_target_x].blue;
target[i * mw + j].red = src[int_target_y * mw + int_target_x].red;
target[i * mw + j].green = src[int_target_y * mw + int_target_x].green;
}
catch (...) {
cout << "i: " << i << "j:" << j << endl;
}
}
}
cout << "Doing the interpolation, please wait.......(0\%)" << endl;
for (int i = 0; i < mh; i++) {
int last = -1;
for (int j = 0; j < mw; j++) {
// 这里应该是找到每一行的需要插值的点
// last的值应该是每一行中需要插值的第一个点的前面一个点
if (needItr[i * mw + j] == 0) {
last = j;
continue;
}
else {
// 当找到一个需要插值的点的时候
// 首先期望找到这个插值点右侧的不需要插值的第一个点
int k = j;
//find next position which has value
while (k < mw && needItr[i * mw + k] == 1) {
k++;
}
if (k >= mw) { //in the last position of the line
if (last == -1)
break;
target[i * mw + j] = target[i * mw + last];
}
else {
if (last == -1) { //in the first position of the line
target[i * mw + j] = target[i * mw + k];
}
else { //in the middle
float coe = (j - last) * 1.0 / (k - last);
target[i * mw + j].red = (BYTE)(((int)target[i * mw + k].red - (int)target[i * mw + last].red) * coe + (int)target[i * mw + last].red);
target[i * mw + j].green = (BYTE)(((int)target[i * mw + k].green - (int)target[i * mw + last].green) * coe + (int)target[i * mw + last].green);
target[i * mw + j].blue = (BYTE)(((int)target[i * mw + k].blue - (int)target[i * mw + last].blue) * coe + (int)target[i * mw + last].blue);
}
}
}
needItr[i * mw + j] = 0;
}
}
cout << "Doing the interpolation, please wait.......(50\%)" << endl;
//then do the vertical interpolation
for (int i = 0; i < mw; i++) {
int last = -1;
for (int j = 0; j < mh; j++) {
if (needItr[j * mw + i] == 0) {
last = j;
continue;
}
else {
int k = j;
//find next position which has value
while (k < mh && needItr[k * mw + i] == 1) {
k++;
}
if (k >= mh) { //in the last position of the line
if (last == -1)
break;
target[j * mw + i] = target[last * mw + i];
}
else {
if (last == -1) { //in the first position of the line
target[j * mw + i] = target[k * mw + i];
}
else { //in the middle
float coe = (j - last) * 1.0 / (k - last);
target[j * mw + i].red = (BYTE)(((int)target[k * mw + i].red - (int)target[last * mw + i].red) * coe + (int)target[last * mw + i].red);
target[j * mw + i].green = (BYTE)(((int)target[k * mw + i].green - (int)target[last * mw + i].green) * coe + (int)target[last * mw + i].green);
target[j * mw + i].blue = (BYTE)(((int)target[k * mw + i].blue - (int)target[last * mw + i].blue) * coe + (int)target[last * mw + i].blue);
}
}
}
needItr[j * mw + i] = 0;
}
}
FILE* fpo1;
fpo1 = fopen("scale.bmp", "wb");
BITMAPFILEHEADER newHead = strHead;
BITMAPINFOHEADER newInfo = strInfo;
fwrite(&bfType, 1, sizeof(WORD), fpo1);
fwrite(&newHead, 1, sizeof(tagBITMAPFILEHEADER), fpo1);
fwrite(&newInfo, 1, sizeof(tagBITMAPINFOHEADER), fpo1);
fwrite(target, 1, sizeof(DATA) * (imgSize), fpo1);
fclose(fpo1);
delete[] needItr;
return target;
}
GRAYDATA* scaleGray(const GRAYDATA* src, float x, float y, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType, RGBQUAD* ipRGB) {
float target_x, target_y;
int int_target_x, int_target_y;
GRAYDATA* target = new GRAYDATA[imgSize];
int* needItr = new int[imgSize];
memset(target, 0, sizeof(GRAYDATA) * imgSize);
memset(needItr, 0, sizeof(int) * imgSize);
for (int i = 0; i < mh; i++)
for (int j = 0; j < mw; j++) {
target_x = (j - cx) / x + cx;
target_y = (i - cy) / y + cy;
int_target_x = (int)target_x;
int_target_y = (int)target_y;
if (int_target_x - target_x < 0 || int_target_y - target_y < 0 || int_target_x <0 || int_target_x > mw || int_target_y < 0 || int_target_y > mh) {
target[i * mw + j].gray = 0;
needItr[i * mw + j] = 1;
}
else {
//cout << "i: " << i << "j:" << j << endl;
try {
target[i * mw + j].gray = src[int_target_y * mw + int_target_x].gray;
}
catch (...) {
//cout << "i: " << i << "j:" << j << endl;
}
}
}
cout << "Doing the interpolation, please wait.......(0\%)" << endl;
for (int i = 0; i < mh; i++) {
int last = -1;
for (int j = 0; j < mw; j++) {
// 这里应该是找到每一行的需要插值的点
// last的值应该是每一行中需要插值的第一个点的前面一个点
if (needItr[i * mw + j] == 0) {
last = j;
continue;
}
else {
// 当找到一个需要插值的点的时候
// 首先期望找到这个插值点右侧的不需要插值的第一个点
int k = j;
//find next position which has value
while (k < mw && needItr[i * mw + k] == 1) {
k++;
}
if (k >= mw) { //in the last position of the line
if (last == -1)
break;
target[i * mw + j] = target[i * mw + last];
}
else {
if (last == -1) { //in the first position of the line
target[i * mw + j] = target[i * mw + k];
}
else { //in the middle
float coe = (j - last) * 1.0 / (k - last);
target[i * mw + j].gray = (BYTE)(((int)target[i * mw + k].gray - (int)target[i * mw + last].gray) * coe + (int)target[i * mw + last].gray);
}
}
}
needItr[i * mw + j] = 0;
}
}
cout << "Doing the interpolation, please wait.......(50\%)" << endl;
//then do the vertical interpolation
for (int i = 0; i < mw; i++) {
int last = -1;
for (int j = 0; j < mh; j++) {
if (needItr[j * mw + i] == 0) {
last = j;
continue;
}
else {
int k = j;
//find next position which has value
while (k < mh && needItr[k * mw + i] == 1) {
k++;
}
if (k >= mh) { //in the last position of the line
if (last == -1)
break;
target[j * mw + i] = target[last * mw + i];
}
else {
if (last == -1) { //in the first position of the line
target[j * mw + i] = target[k * mw + i];
}
else { //in the middle
float coe = (j - last) * 1.0 / (k - last);
target[j * mw + i].gray = (BYTE)(((int)target[k * mw + i].gray - (int)target[last * mw + i].gray) * coe + (int)target[last * mw + i].gray);
}
}
}
needItr[j * mw + i] = 0;
}
}
FILE* fpo1;
fpo1 = fopen("scaleGray.bmp", "wb");
BITMAPFILEHEADER newHead = strHead;
BITMAPINFOHEADER newInfo = strInfo;
newHead.bfReserved1 = newHead.bfReserved2 = 0;
newHead.bfOffBits = sizeof(bfType) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
newHead.bfSize = newHead.bfOffBits + imgSize;
newInfo.biHeight = (DWORD)mh;
newInfo.biWidth = (DWORD)mw;
newInfo.biSizeImage = (DWORD)(imgSize);
fwrite(&bfType, 1, sizeof(WORD), fpo1);
fwrite(&newHead, 1, sizeof(tagBITMAPFILEHEADER), fpo1);
fwrite(&newInfo, 1, sizeof(tagBITMAPINFOHEADER), fpo1);
fwrite(ipRGB, sizeof(RGBQUAD), 256, fpo1);
fwrite(target, 1, sizeof(GRAYDATA) * (imgSize), fpo1);
fclose(fpo1);
delete[] needItr;
return target;
}
DATA * rotation(const DATA* src, double theta, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType) {
float target_x = 0, target_y = 0;
int int_target_x, int_target_y;
DATA* target = new DATA[imgSize];
int* needItr = new int[imgSize];
int* noneedItr = new int[imgSize];
memset(target, 0, sizeof(DATA) * imgSize);
memset(needItr, 0, sizeof(int) * imgSize);
memset(noneedItr, 0, sizeof(int) * imgSize);
int count1 = 0;
// 这里面越界的点很少啊
for (int i = 0; i < mh; i++)
for (int j = 0; j < mw; j++) {
/*target_x, target_y = xyAfterRot(j - cx, i - cy, theta);
target_x += cx;
target_y += cy;*/
target_x = (int)xAfterRot(j, i, theta, cx, cy);
target_y = (int)yAfterRot(j, i, theta, cx, cy);
int_target_x = (int)target_x;
int_target_y = (int)target_y;
//int_target_x - target_x < 0 || int_target_y - target_y < 0
if (int_target_x <0 || int_target_x > mw || int_target_y < 0 || int_target_y > mh) {
target[i * mw + j].red = 0;
target[i * mw + j].green = 0;
target[i * mw + j].blue = 0;
needItr[i * mw + j] = 1;
count1 += 1;
}
else {
target[i * mw + j].blue = src[int_target_y * mw + int_target_x].blue;
target[i * mw + j].red = src[int_target_y * mw + int_target_x].red;
target[i * mw + j].green = src[int_target_y * mw + int_target_x].green;
//cout << "here rotate "<<endl;
}
}
cout << "count1" << float(count1) / (mh * mw) << endl;
FILE* fpo1;
fpo1 = fopen("rotation.bmp", "wb");
BITMAPFILEHEADER newHead = strHead;
BITMAPINFOHEADER newInfo = strInfo;
fwrite(&bfType, 1, sizeof(WORD), fpo1);
fwrite(&newHead, 1, sizeof(tagBITMAPFILEHEADER), fpo1);
fwrite(&newInfo, 1, sizeof(tagBITMAPINFOHEADER), fpo1);
fwrite(target, 1, sizeof(DATA) * (imgSize), fpo1);
fclose(fpo1);
delete[] needItr;
return target;
}
GRAYDATA* rotationGray(const GRAYDATA* src, double theta, int mh, int mw, int imgSize, int cx, int cy, BITMAPFILEHEADER strHead, BITMAPINFOHEADER strInfo, WORD bfType, RGBQUAD* ipRGB) {
float target_x = 0, target_y = 0;
int int_target_x, int_target_y;
GRAYDATA* target = new GRAYDATA[imgSize];
int* needItr = new int[imgSize];
int* noneedItr = new int[imgSize];
memset(target, 0, sizeof(GRAYDATA) * imgSize);
memset(needItr, 0, sizeof(int) * imgSize);
memset(noneedItr, 0, sizeof(int) * imgSize);
int count1 = 0;
// 这里面越界的点很少啊
for (int i = 0; i < mh; i++)
for (int j = 0; j < mw; j++) {
/*target_x, target_y = xyAfterRot(j - cx, i - cy, theta);
target_x += cx;
target_y += cy;*/
target_x = (int)xAfterRot(j, i, theta, cx ,cy);
target_y = (int)yAfterRot(j, i, theta, cx, cy);
int_target_x = (int)target_x;
int_target_y = (int)target_y;
//int_target_x - target_x < 0 || int_target_y - target_y < 0
if (int_target_x <0 || int_target_x > mw || int_target_y < 0 || int_target_y > mh) {
target[i * mw + j].gray = 0;
needItr[i * mw + j] = 1;
count1 += 1;
}
else {
target[i * mw + j].gray = src[int_target_y * mw + int_target_x].gray;
//cout << "here rotate "<<endl;
}
}
cout << "count1" << float(count1) / (mh * mw) << endl;
FILE* fpo1;
fpo1 = fopen("rotationGray.bmp", "wb");
BITMAPFILEHEADER newHead = strHead;
BITMAPINFOHEADER newInfo = strInfo;
newHead.bfReserved1 = newHead.bfReserved2 = 0;
newHead.bfOffBits = sizeof(bfType) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
newHead.bfSize = newHead.bfOffBits + imgSize;
newInfo.biHeight = (DWORD)mh;
newInfo.biWidth = (DWORD)mw;
newInfo.biSizeImage = (DWORD)(imgSize);
fwrite(&bfType, 1, sizeof(WORD), fpo1);
fwrite(&newHead, 1, sizeof(tagBITMAPFILEHEADER), fpo1);
fwrite(&newInfo, 1, sizeof(tagBITMAPINFOHEADER), fpo1);
fwrite(ipRGB, sizeof(RGBQUAD), 256, fpo1);
fwrite(target, 1, sizeof(GRAYDATA) * (imgSize), fpo1);
fclose(fpo1);
return target;
}
double xAfterRot(int x, int y, double theta, int cx, int cy) {
x = x - cx;
y = y - cy;
double result = x * cos(theta) - y * sin(theta);
//double result = x * cos(theta) + y * sin(theta);
return result += cx;
}
double yAfterRot(int x, int y, double theta, int cx, int cy) {
x = x - cx;
y = y - cy;
double result = x * sin(theta) + y * cos(theta);
//double result = -x * sin(theta) + y * cos(theta);
return result += cy;
}
1.3 main.cpp
#include "bmp.cpp"
using namespace std;
int main() {
const char* filename1 = (char*)"D:\\TestEverything\\BmpCenteredManipuation\\Debug\\Coman.bmp"; //彩色图像
const char* filename2 = (char*)"D:\\TestEverything\\BmpCenteredManipuation\\Debug\\barbara.bmp"; //灰度图像
BITMAPFILEHEADER strHead1, strHead2;
BITMAPINFOHEADER strInfo1, strInfo2;
int h1, h2, mw1, mw2, imgSize1, imgSize2;
DATA* imgData1, *imgData3;
GRAYDATA* imgData2, *imgData4;
WORD bfType1, bfType2;
RGBQUAD* ipRGB = (RGBQUAD*)malloc(sizeof(RGBQUAD) * 256);
int cx1, cx2, cy1, cy2;
if (checkImgType(filename1)) {
imgData1 = readRgbBmp(filename1, &bfType1, &strHead1, &strInfo1, &h1, &mw1, &imgSize1, &cx1, &cy1);
float mulX, mulY;
cout << "Scale, input the x coefficient (0.1<x<4.0): ";
cin >> mulX;
cout << "Scale, input the y coefficient (0.1<y<4.0): ";
cin >> mulY;
imgData3 = scale(imgData1, mulX, mulY, h1, mw1, imgSize1, cx1, cy1, strHead1, strInfo1, bfType1);
double theta;
cout << "Rotation, input the angle to be rotate (0 < theta < 360): ";
cin >> theta;
theta = theta / 180 * PI;
rotation(imgData3, theta, h1, mw1, imgSize1, cx1, cy1, strHead1, strInfo1, bfType1);
}
if (checkImgType(filename2)) {
imgData2 = readGrayBmp(filename2, &bfType2, &strHead2, &strInfo2, ipRGB, &h2, &mw2, &imgSize2, &cx2, &cy2);
float mulX, mulY;
cout << "Scale, input the x coefficient (0.1<x<4.0): ";
cin >> mulX;
cout << "Scale, input the y coefficient (0.1<y<4.0): ";
cin >> mulY;
imgData4 = scaleGray(imgData2, mulX, mulY, h2, mw2, imgSize2, cx2, cy2, strHead2, strInfo2, bfType2, ipRGB);
const char* filename4 = (char*)"D:\TestEverything\BmpCenteredManipuation\BmpCenteredManipuation\scaleGray.bmp"; //彩色图像
double theta;
cout << "Rotation, input the angle to be rotate (0 < theta < 360): ";
cin >> theta;
theta = theta / 180 * PI;
rotationGray(imgData4, theta, h2, mw2, imgSize2, cx2, cy2, strHead2, strInfo2, bfType2, ipRGB);
}
return 0;
}
2 实现效果
- 1 灰度图
2.2 彩色图
3 实现难点
BMP的存储结构;转化时的矩阵变换操作