网上有很多关于bmp图像的资料,但是很多都是存成灰度图,和彩色图,我这个代码是把Mat存成二值图
首先我肯定是站在前人的基础上,一些关于bmp的介绍操作
不错的代码:
http://www.cnblogs.com/skyseraph/archive/2011/03/10/1979532.html
各种blog都说:
一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。
但是对于按位存储图像大家都忽略了一个很重要的东西:
对于一行来说左边的像素点存在内存的低位,右边的像素点存在内存的高位。
但是对于每一个byte中左边的像素点存在byte的高位,右边的像素点存在byte的低位。。
假设这里是一行
低地址
左边像素
|
|
|
|
|
|
|
高地址
右边像素
|
假设这里是一个Byte
高地址
左边像素
|
|
|
|
|
|
|
低地址
右边像素
|
正常的图像应该是--------------------》
1.用位操作把Mat存到一段类存中,注意每个像素占一个bit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
int
mat_to_binary(
const
cv::Mat img,
int
line_byte,
char
* data)
{
int
width = img.cols;
int
height = img.rows;
size_t
line_size = line_byte * 8;
size_t
bit_size = line_size * height;
char
*p = data;
int
offset, v; unsigned
char
temp;
for
(
int
row=height-1; row>=0; row-- ) {
for
(
int
col=0; col<width; col++ ) {
offset = col % 8;
v = img.at<uchar>(row, col);
temp = 1;
temp = temp << (8 - offset -1);
if
(v == 255 ) {
*(p + col/8) |= temp;
}
else
{
temp = ~temp;
*(p + col/8) &= temp;
}
}
for
(
int
j = width/8 ; j < line_byte; j++)
p[j] = 0;
p = p + line_byte;
}
return
0;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
int
save_bmp_image(
const
cv::Mat img, std::string dst)
{
int
width = img.cols;
int
height = img.rows;
const
int
biBitCount = 1;
//颜色表大小,以字节为单位,灰度图像颜色表为256*4字节,彩色图像颜色表大小为0,二值图为2*4
int
color_type_num = 2;
int
colorTablesize = color_type_num *
sizeof
(RGBQUAD);
RGBQUAD *pColorTable =
new
RGBQUAD[color_type_num];
for
(
int
i = 0; i < color_type_num; i++) {
pColorTable[i].rgbBlue = i*255;
pColorTable[i].rgbRed = i*255;
pColorTable[i].rgbGreen= i*255;
pColorTable[i].rgbReserved = 0;
}
//待存储图像数据每行字节数为4的倍数
int
line_byte = (width * biBitCount/8+3)/4*4;
char
* p_data = (
char
*)
malloc
(line_byte*height);
mat_to_binary(img, line_byte, p_data);
std::ofstream fp(dst.c_str(), std::ios::binary | std::ios::out);
if
(!fp.is_open()) {
std::cout <<
"open "
<< dst <<
" failed!"
<< std::endl;
return
-1;
}
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;
//bmp类型
fileHead.bfSize=
sizeof
(BITMAPFILEHEADER) +
sizeof
(BITMAPINFOHEADER)\
+ colorTablesize + line_byte*height;
//bfSize是图像文件4个组成部分之和
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
fileHead.bfOffBits = 54+colorTablesize;
//bfOffBits是图像文件前3个部分所需空间之和
fp.write((
char
*)&fileHead,
sizeof
(BITMAPFILEHEADER));
//写文件头进文件
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount = biBitCount;
head.biClrImportant = 0;
head.biClrUsed = 0;
head.biCompression = 0;
head.biHeight = height;
head.biPlanes = 1;
head.biSize = 40;
head.biSizeImage = line_byte*height;
head.biWidth = width;
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;
//写位图信息头进内存
fp.write((
char
*)&head,
sizeof
(BITMAPINFOHEADER));
//颜色表,写入文件
fp.write((
char
*)pColorTable,
sizeof
(RGBQUAD)*color_type_num);
//写位图数据进文件pBmpBuf
fp.write((
char
*)p_data, height*line_byte);
fp.close();
delete
[]pColorTable;
delete
[]p_data;
return
0;
}
|
- 后记:
关于把Mat转为二进制的函数
mat_to_binary()。
我试了很多个版本:
首先是boost::dynamic_bitset
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
int
mat_to_binary_boost(
const
cv::Mat img,
int
lineByte, boost::dynamic_bitset<> &data)
{
int
width = img.cols;
int
height = img.rows;
size_t
line_size = lineByte * 8;
size_t
bit_size = line_size * height;
data.resize(bit_size);
int
channel = img.channels();
int
type = img.type();
size_t
id = 0;
for
(
int
row=height-1; row>=0; row-- ) {
//for(int row=0; row < height; row++ ) {
for
(
int
col=0; col<width; col++ ) {
unsigned
char
v = img.at<uchar>(row, col);
if
(v == 255 ) {
data[id + col/8*8 + 8 - col%8 - 1] = 1;
}
else
{
data[id + col/8*8 + 8 - col%8 - 1] = 0;
}
}
id += line_size;
}
return
0;
}
|
1
2
3
4
5
6
7
|
boost::dynamic_bitset<> img_data;
mat_to_binary_boost(img, line_byte, img_data);
boost::dynamic_bitset<>::block_type* p_data = (boost::dynamic_bitset<>::block_type*)
malloc
(
sizeof
(
char
)*img_data.bits_per_block * img_data.num_blocks()/8 +1);
int
temp =
sizeof
(
char
)*img_data.bits_per_block * img_data.num_blocks();
int
t1 = img_data.bits_per_block;
int
t2 = img_data.num_blocks();
boost::to_block_range(img_data, p_data);
//这里的p_data不能是一个char*, 一定要是boost::dynamic_bitset<>::block_type 类型.
//因为这个函数的实现是调用std::copy(),类型不一样可能会出错
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int
mat_to_binary_bit_set(
const
cv::Mat img,
int
lineByte, std::bitset<11200>& data)
{
int
width = img.cols;
int
height = img.rows;
size_t
line_size = lineByte * 8;
size_t
bit_size = line_size * height;
size_t
id = 0;
for
(
int
row=height-1; row>=0; row-- ) {
for
(
int
col=0; col<width; col++ ) {
unsigned
char
v = img.at<uchar>(row, col);
if
(v == 255 ) {
data[id + col/8*8 + 8 - col%8 - 1] = 1;
}
else
{
data[id + col/8*8 + 8 - col%8 - 1] = 0;
}
}
id += line_size;
}
return
0;
}
|
错位 -------------------------->>>>
后来把代码改成这样(这也是毫无道理的)得到的图片就
1
2
3
|
std::bitset<11200> data;
mat_to_binary_bit_set(img, line_byte, data);
char
* p_data = (
char
*)&data[0];
|
正常了 ------------->>>>>>>>>>>
看了一下bitset关于operator [] 的源码:
我还试了一把vector<bool>,方式和bitset差不多,这个就更扯淡了,出来的全是一堆乱码。
1
2
3
|
std::bitset<11200> data;
mat_to_binary_bit_set(img, line_byte, data);
char
* p_data = (
char
*)&data[0] + 28*17 + 16;
|
1
2
3
4
5
6
|
{
// test if bit at _Pos is set
if
(_Bits <= _Pos)
_Xran();
// _Pos off end
return
((_Array[_Pos / _Bitsperword]
& ((_Ty)1 << _Pos % _Bitsperword)) != 0);
}
|
这明明返回的是一个临时变量嘛,我把 p_data指针绑在一个
临时值上面, 这个值加上 28×17 +16 正好处于bitset 那一段内存。
这有可能就是以上
正常和不正常的解释吧。。。。其实 data[id] 返回的是某一个bit的值,然而c++ 并没有指向bit 的指针,里面必然会做转化,这就注定了 &data[0]不能取到data的内存地址
vector<bool>
's elements aren't addressable because C++ doesn't have pointers and references to bits.
也是差不错的道理
关于vector<bool>: