图像处理(RGB分离)

图像处理技术(RGB分离)


最近学习了图像处理技术,第一个小工程做的事将一张图片的rgb分离,存为三张图片,就像PS中的RGB通道的三张图片一样。

我们先准备两张24位真彩色图片,一张宽度像素为4的倍数,一张则不是。

我们来看下它的文件头和信息头都储存了什么信息:

位图文件头BITMAPFILEHEADER
这是一个结构,其定义如下:

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数),各个域的说明如下:
bfType
指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。
bfSize
指定文件大小,包括这14个字节。
bfReserved1,bfReserved2
为保留字,不用考虑
bfOffBits
为从文件头到实际的位图数据的偏移字节数

位图信息头BITMAPINFOHEADER
这也是一个结构,其定义如下:

typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;

这个结构的长度是固定的,为40个字节(LONG为32位整数),各个域的说明如下:
biSize
指定这个结构的长度,为40。
biWidth
指定图象的宽度,单位是像素。
biHeight
指定图象的高度,单位是像素。
biPlanes
必须是1,不用考虑。
biBitCount
指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)(新的.bmp格式支持32位色,这里就不做讨论了)。
biCompression
指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定义好的常量)。要说明的是,Windows位图可以采用RLE4,和RLE8的压缩格式,但用的不多。我们今后所讨论的只有第一种不压缩的情况,即biCompression为BI_RGB的情况。
biSizeImage
指定实际的位图数据占用的字节数,其实也可以从以下的公式中计算出来:
biSizeImage=biWidth’ × biHeight
要注意的是:上述公式中的biWidth’必须是4的整倍数(所以不是biWidth,而是biWidth’,表示大于或等于biWidth的,最接近4的整倍数。举个例子,如果biWidth=240,则biWidth’=240;如果biWidth=241,biWidth’=244)。
如果biCompression为BI_RGB,则该项可能为零
biXPelsPerMeter
指定目标设备的水平分辨率,单位是每米的象素个数,关于分辨率的概念。
biYPelsPerMeter
指定目标设备的垂直分辨率,单位同上。
biClrUsed
指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount。
biClrImportant
指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。

一开始我想将原图的每个像素点取出来,然后将RGB分量取出来分别赋值,这样处理对于宽度为4的倍数的图片是没有问题的。但是到了不是4倍数的图片就出问题了。

后来了解到图片的读取和储存都是按字节来进行,而且是必须要是4 的倍数,那我们的解决方案就明确了,把所有字节读出来后再赋值。

那么我们怎么样读出所有的字节来呢?

if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}

利用这个公式转换宽度,再乘以高度就是所有字节的数量了。

下面附上代码:

#pragma once
#include
#include
#include

using namespace std;



bool readBmp(char *bmpName) {
	FILE *fb = fopen(bmpName, "rb");
	FILE* pfoutr = fopen("r.bmp", "wb");
	FILE* pfoutg = fopen("g.bmp", "wb");
	FILE* pfoutb = fopen("b.bmp", "wb");
	if (fb == 0) {
		return 0;
	}
	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER infoHeader;
	int bmpWidth =0;//图像的宽
	int bmpHeight=0;//图像的高
	int bmpOffset = 0;

	fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fb);
	fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fb);

	bmpHeight = infoHeader.biHeight;
	bmpWidth = infoHeader.biWidth;
	bmpOffset = fileHeader.bfOffBits;
	
	if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}
	
	if (infoHeader.biBitCount >= 1) {
		
		int size1 = bmpHeight * bmpWidth;
		BYTE *img = new BYTE[size1];
		BYTE *img1 = new BYTE[size1];
		BYTE *img2 = new BYTE[size1];
		BYTE *img3 = new BYTE[size1];
		
		fseek(fb, bmpOffset, 0);
		fread(img, sizeof(BYTE), size1, fb);

		for (int i = 0;i < bmpHeight ;i++) {
			for (int j = 0;j < bmpWidth;j++) {
					switch (j % 3) {
					case 0:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = img[i*bmpWidth + j];
						break;
					case 1:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = img[i*bmpWidth + j];
						img3[i*bmpWidth + j] = 0;
						break;
					case 2:
						img1[i*bmpWidth + j] = img[i*bmpWidth + j];
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = 0;
						break;
					}
				
				
			}
		}
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutr);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutr);
		fwrite(img1, sizeof(BYTE), size1, pfoutr);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutg);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutg);
		fwrite(img2, sizeof(BYTE), size1, pfoutg);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutb);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutb);
		fwrite(img3, sizeof(BYTE), size1, pfoutb);
	}


	
	fclose(fb);
	fclose(pfoutr);
	fclose(pfoutg);
	fclose(pfoutb);
	return 1;
}

int main() {
	char bmpName[] = "sea.bmp";
	readBmp(bmpName);
	
}


这代码我就是测试用,写代码时要注意规范,提取函数哈!

原图:
图像处理(RGB分离)_第1张图片

R通道:图像处理(RGB分离)_第2张图片

G通道:
图像处理(RGB分离)_第3张图片

你可能感兴趣的:(程序员之路,c++,图像处理)