# 1.工业相机图像处理3
邮箱:yiyangyu1314@outlook.com
作者:易陽羽
时间:2023-06-08
2
3
4
5
# 1.C++实现图像深度转换,并且保存常见的图像格式
用C++开发一份24位的RGB彩色图像buffer转化为8位图像,并且保存图像文件为tiff,bmp,png,raw格式,水平垂直分辨率为96; 以下是C++代码实现:
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <tiffio.h>
#include <png.h>
#include <opencv2/opencv.hpp>
using namespace std;
void saveTIFF(string filename, vector<unsigned char>& data, int width, int height)
{
TIFF* tiff = TIFFOpen(filename.c_str(), "w");
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, height);
TIFFSetField(tiff, TIFFTAG_XRESOLUTION, 96.0);
TIFFSetField(tiff, TIFFTAG_YRESOLUTION, 96.0);
TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
for (int row = 0; row < height; ++row) {
unsigned char* row_data = &data[3 * width * row];
if (TIFFWriteScanline(tiff, row_data, row, 0) < 0) {
cerr << "Error writing TIFF file: " << filename << endl;
break;
}
}
TIFFClose(tiff);
}
void saveBMP(string filename, vector<unsigned char>& data, int width, int height)
{
cv::Mat image(height, width, CV_8UC3, &data[0]);
cv::imwrite(filename, image);
}
void savePNG(string filename, vector<unsigned char>& data, int width, int height)
{
FILE* file = fopen(filename.c_str(), "wb");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
cerr << "Error creating PNG write struct" << endl;
return;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
cerr << "Error creating PNG info struct" << endl;
return;
}
png_init_io(png_ptr, file);
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_set_pHYs(png_ptr, info_ptr, 96 * PNG_RESOLUTION_METER, 96 * PNG_RESOLUTION_METER, PNG_RESOLUTION_METER_UNIT_TYPE);
png_write_info(png_ptr, info_ptr);
vector<png_bytep> rows(height);
for (int row = 0; row < height; ++row) {
rows[row] = &data[3 * width * row];
}
png_write_image(png_ptr, &rows[0]);
png_write_end(png_ptr, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(file);
}
void saveRAW(string filename, vector<unsigned char>& data, int width, int height)
{
ofstream file(filename.c_str(), ios::out | ios::binary);
file.write((char*)&data[0], data.size());
file.close();
}
void convertTo8Bit(vector<unsigned char>& rgb_data, vector<unsigned char>& output_data, int width, int height)
{
output_data.resize(width * height);
for (int i = 0; i < width * height; ++i) {
int r = rgb_data[3 * i];
int g = rgb_data[3 * i + 1];
int b = rgb_data[3 * i + 2];
int gray = int(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
output_data[i] = (gray > 255) ? 255 : gray;
}
}
int main()
{
string input_filename = "test.rgb";
string output_filename_prefix = "test";
ifstream file(input_filename.c_str(), ios::in | ios::binary);
int width = 640;
int height = 480;
vector<unsigned char> rgb_data(width * height * 3);
vector<unsigned char> output_data;
file.read((char*)&rgb_data[0], rgb_data.size());
convertTo8Bit(rgb_data, output_data, width, height);
saveTIFF(output_filename_prefix + ".tif", rgb_data, width, height);
saveBMP(output_filename_prefix + ".bmp", rgb_data, width, height);
savePNG(output_filename_prefix + ".png", rgb_data, width, height);
saveRAW(output_filename_prefix + ".raw", rgb_data, width, height);
cout << "Images saved successfully" << endl;
return 0;
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
上述代码包含了4个函数,分别用于保存tiff、bmp、png和raw格式的图像文件。其中,tiff和png文件 需要使用相关库进行编译,具体为libtiff和libpng。另外,代码中使用了OpenCV库,用于将RGB数据 转换成Mat格式,然后保存成bmp文件。
主函数中,先读取24位RGB彩色图像数据,然后调用convertTo8Bit
函数将其转换成8位灰度图像数据。最
后分别调用保存函数将图像数据保存成不同格式的文件。其中,水平垂直分辨率都为96。
需要注意的是,上述代码中只是做了简单的灰度值计算,如果需要更加精确的转换可以考虑使用其他的算法,如Luma通道计算。
# 2.C#实现图像深度转换,并且保存常见的图像格式
C#开发一份24位的RGB彩色图像buffer转化为8位图像,并且保存图像文件为tiff,bmp,png,raw格式,水平垂直分辨率为480;
以下是C#代码实现:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace ImageConverter
{
class Program
{
static void Main(string[] args)
{
string inputFilename = "test.rgb";
string outputFilenamePrefix = "test";
int width = 640;
int height = 480;
byte[] rgbData = File.ReadAllBytes(inputFilename);
byte[] outputData;
ConvertTo8Bit(rgbData, out outputData, width, height);
SaveTIFF(outputFilenamePrefix + ".tif", outputData, width, height);
SaveBMP(outputFilenamePrefix + ".bmp", outputData, width, height);
SavePNG(outputFilenamePrefix + ".png", outputData, width, height);
SaveRAW(outputFilenamePrefix + ".raw", outputData);
Console.WriteLine("Images saved successfully");
}
static void ConvertTo8Bit(byte[] rgbData, out byte[] outputData, int width, int height)
{
outputData = new byte[width * height];
for (int i = 0; i < width * height; ++i)
{
int r = rgbData[3 * i];
int g = rgbData[3 * i + 1];
int b = rgbData[3 * i + 2];
int gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
outputData[i] = (byte)(gray > 255 ? 255 : gray);
}
}
static void SaveTIFF(string filename, byte[] data, int width, int height)
{
using (var bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
{
ColorPalette grayscalePalette = bitmap.Palette;
for (int i = 0; i < 256; ++i)
{
grayscalePalette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = grayscalePalette;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
IntPtr ptr = bitmapData.Scan0;
int bytes = bitmapData.Stride * height;
byte[] rgbValues = new byte[bytes];
// Copy the data into RGB format.
for (int row = 0; row < height; ++row)
{
int srcOffs = row * width;
int dstOffs = row * bitmapData.Stride;
for (int col = 0; col < width; ++col)
{
rgbValues[dstOffs++] = data[srcOffs++];
}
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bitmap.UnlockBits(bitmapData);
bitmap.SetResolution(480, 480);
bitmap.Save(filename, ImageFormat.Tiff);
}
}
static void SaveBMP(string filename, byte[] data, int width, int height)
{
using (var bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
{
ColorPalette grayscalePalette = bitmap.Palette;
for (int i = 0; i < 256; ++i)
{
grayscalePalette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = grayscalePalette;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
IntPtr ptr = bitmapData.Scan0;
int bytes = bitmapData.Stride * height;
byte[] rgbValues = new byte[bytes];
// Copy the data into RGB format.
for (int row = 0; row < height; ++row)
{
int srcOffs = row * width;
int dstOffs = row * bitmapData.Stride;
for (int col = 0; col < width; ++col)
{
rgbValues[dstOffs++] = data[srcOffs++];
}
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bitmap.UnlockBits(bitmapData);
bitmap.SetResolution(480, 480);
bitmap.Save(filename, ImageFormat.Bmp);
}
}
static void SavePNG(string filename, byte[] data, int width, int height)
{
using (var bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
{
ColorPalette grayscalePalette = bitmap.Palette;
for (int i = 0; i < 256; ++i)
{
grayscalePalette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = grayscalePalette;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
IntPtr ptr = bitmapData.Scan0;
int bytes = bitmapData.Stride * height;
byte[] rgbValues = new byte[bytes];
// Copy the data into RGB format.
for (int row = 0; row < height; ++row)
{
int srcOffs = row * width;
int dstOffs = row * bitmapData.Stride;
for (int col = 0; col < width; ++col)
{
rgbValues[dstOffs++] = data[srcOffs++];
}
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bitmap.UnlockBits(bitmapData);
bitmap.SetResolution(480, 480);
bitmap.Save(filename, ImageFormat.Png);
}
}
static void SaveRAW(string filename, byte[] data)
{
File.WriteAllBytes(filename, data);
}
}
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
上述代码包含了4个函数,分别用于保存tiff、bmp、png和raw格式的图像文件。其中,tiff、bmp和png文件均 需要使用System.Drawing命名空间下的相关类进行编写。
主函数中,先读取24位RGB彩色图像数据,然后调用ConvertTo8Bit
函数将其转换成8位灰度图像数据。
最后分别调用保存函数将图像数据保存成不同格式的文件。其中,水平垂直分辨率都为480。
需要注意的是,上述代码中只是做了简单的灰度值计算,如果需要更加精确的转换可以考虑使用其他的 算法,如Luma通道计算。而在保存tiff、bmp和png文件时,还需要设置对应的256级灰度调色板。
# 3.用C++实现黑白相机,彩色相机的图像深度转换和存储
C++ 把MONO12,Mono8,RGB16,RGB8 ,Mono10Packed,Mono12Packed的图像buffer 分别转化 位8位,16位,24位的图像,并且保存为水平垂直分辨率为300的tiff,bmp,png,jpg,raw文件格式。 以下是C++代码实现:
#include <iostream>
#include <fstream>
using namespace std;
// MONO10Packed和Mono12Packed的像素数据需要解包
void UnpackPixels(const unsigned char* src, unsigned char* dst, const int pixelCount, const int bitsPerPixel) {
for (int i = 0; i < pixelCount; ++i) {
if (bitsPerPixel == 10) {
dst[2 * i] = src[5 * i] << 2 | src[5 * i + 1] >> 6;
dst[2 * i + 1] = (src[5 * i + 1] & 0x3F) << 2 | src[5 * i + 2] >> 6;
} else if (bitsPerPixel == 12) {
dst[2 * i] = src[6 * i] << 4 | src[6 * i + 1] >> 4;
dst[2 * i + 1] = (src[6 * i + 1] & 0x0F) << 8 | src[6 * i + 2];
}
}
}
// 将单通道图像转换为8位或16位图像
void ConvertTo8or16Bit(const unsigned char* src, unsigned char* dst, const int pixelCount, const int bitsPerPixel) {
if (bitsPerPixel == 8) {
for (int i = 0; i < pixelCount; ++i) {
dst[i] = src[i];
}
} else if (bitsPerPixel == 16) {
for (int i = 0; i < pixelCount; ++i) {
dst[2 * i] = src[i] & 0xFF;
dst[2 * i + 1] = src[i] >> 8;
}
}
}
// 将RGB图像转换为24位图像
void ConvertTo24Bit(const unsigned char* src, unsigned char* dst, const int pixelCount) {
for (int i = 0; i < pixelCount; ++i) {
dst[3 * i] = src[3 * i + 2];
dst[3 * i + 1] = src[3 * i + 1];
dst[3 * i + 2] = src[3 * i];
}
}
// 保存为tiff格式
void SaveAsTiff(const string& filename, const unsigned char* data, const int width, const int height, const int bitsPerPixel) {
TIFF* tiff = TIFFOpen(filename.c_str(), "w");
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bitsPerPixel);
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
TIFFSetField(tiff, TIFFTAG_XRESOLUTION, 300.0);
TIFFSetField(tiff, TIFFTAG_YRESOLUTION, 300.0);
TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0));
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
const int bufferSize = width * height * bitsPerPixel / 8;
TIFFWriteEncodedStrip(tiff, 0, (void*)data, bufferSize);
TIFFClose(tiff);
}
// 保存为bmp格式
void SaveAsBmp(const string& filename, const unsigned char* data, const int width, const int height, const int bitsPerPixel) {
BITMAPFILEHEADER bmpFileHeader;
bmpFileHeader.bfType = 0x4D42;
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + width * height * bitsPerPixel / 8;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmpInfoHeader;
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biWidth = width;
bmpInfoHeader.biHeight = -height; // negative height to indicate top-down DIB
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = bitsPerPixel;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biSizeImage = 0;
bmpInfoHeader.biXPelsPerMeter = 300.0 / 0.0254;
bmpInfoHeader.biYPelsPerMeter = 300.0 / 0.0254;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
ofstream ofs(filename, ios::binary);
ofs.write(reinterpret_cast<char*>(&bmpFileHeader), sizeof(BITMAPFILEHEADER));
ofs.write(reinterpret_cast<char*>(&bmpInfoHeader), sizeof(BITMAPINFOHEADER));
ofs.write(reinterpret_cast<const char*>(data), width * height * bitsPerPixel / 8);
ofs.close();
}
// 保存为png格式
void SaveAsPng(const string& filename, const unsigned char* data, const int width, const int height, const int bitsPerPixel) {
LodePNGColorType colorType;
if (bitsPerPixel == 8) {
colorType = LCT_GREY;
} else if (bitsPerPixel == 16) {
colorType = LCT_GREY_ALPHA;
} else if (bitsPerPixel == 24) {
colorType = LCT_RGB;
}
unsigned error = lodepng::encode(filename, data, width, height, colorType, 8);
if (error) {
cerr << "PNG encoding error: " << lodepng_error_text(error) << endl;
}
}
// 保存为jpeg格式
void SaveAsJpeg(const string& filename, const unsigned char* data, const int width, const int height, const int bitsPerPixel) {
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE* outfile;
if ((outfile = fopen(filename.c_str(), "wb")) == NULL) {
cerr << "Cannot open JPEG file" << endl;
return;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = width;
cinfo.image_height = height;
if (bitsPerPixel == 8) {
cinfo.input_components = 1;
cinfo.in_color_space = JCS_GRAYSCALE;
} else if (bitsPerPixel == 24) {
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
}
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 90, true);
jpeg_start_compress(&cinfo, true);
JSAMPROW row_pointer[1];
int row_stride;
if (bitsPerPixel == 8) {
row_stride = width;
} else if (bitsPerPixel == 24) {
row_stride = 3 * width;
}
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = const_cast<JSAMPROW>(&data[cinfo.next_scanline * row_stride]);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
}
// 保存为raw格式
void SaveAsRaw(const string& filename, const unsigned char* data, const int width, const int height, const int bitsPerPixel) {
ofstream ofs(filename, ios::binary);
ofs.write(reinterpret_cast<const char*>(data), width * height * bitsPerPixel / 8);
ofs.close();
}
int main() {
const string inputFilename = "test.rgb";
const string outputFilenamePrefix = "test";
const int width = 640;
const int height = 480;
const int pixelCount = width * height;
// 读取图像数据
ifstream ifs(inputFilename, ios::binary);
unsigned char* rawData = new unsigned char[3 * pixelCount];
ifs.read(reinterpret_cast<char*>(rawData), 3 * pixelCount);
ifs.close();
// 分配内存并转换图像格式
unsigned char* outputData8 = new unsigned char[pixelCount];
unsigned char* outputData16 = new unsigned char[2 * pixelCount];
unsigned char* outputData24 = new unsigned char[3 * pixelCount];
unsigned char* unpackedData = new unsigned char[pixelCount];
UnpackPixels(rawData, unpackedData, pixelCount, 10); // 对于Mono10Packed图像需要解包
ConvertTo8or16Bit(unpackedData, outputData8, pixelCount, 8);
ConvertTo8or16Bit(unpackedData, outputData16, pixelCount, 16);
UnpackPixels(rawData, unpackedData, pixelCount, 12); // 对于Mono12Packed图像需要解包
ConvertTo8or16Bit(unpackedData, outputData8, pixelCount, 8);
ConvertTo8or16Bit(unpackedData, outputData16, pixelCount, 16);
ConvertTo8or16Bit(rawData, outputData8, pixelCount, 8);
ConvertTo8or16Bit(rawData, outputData16, pixelCount, 16);
ConvertTo24Bit(rawData, outputData24, pixelCount);
// 保存为不同格式的图像文件
SaveAsTiff(outputFilenamePrefix + "_8bit.tif", outputData8, width, height, 8);
SaveAsBmp(outputFilenamePrefix + "_8bit.bmp", outputData8, width, height, 8);
SaveAsPng(outputFilenamePrefix + "_8bit.png", outputData8, width, height, 8);
SaveAsJpeg(outputFilenamePrefix + "_8bit.jpg", outputData8, width, height, 8);
SaveAsRaw(outputFilenamePrefix + "_8bit.raw", outputData8, width, height, 8);
SaveAsTiff(outputFilenamePrefix + "_16bit.tif", outputData16, width, height, 16);
SaveAsBmp(outputFilenamePrefix + "_16bit.bmp", outputData16, width, height, 16);
SaveAsPng(outputFilenamePrefix + "_16bit.png", outputData16, width, height, 16);
SaveAsJpeg(outputFilenamePrefix + "_16bit.jpg", outputData16, width, height, 16);
SaveAsRaw(outputFilenamePrefix + "_16bit.raw", outputData16, width, height, 16);
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# 4. 工业相机图像格式的数据存储原理
MONO12图像buffer存储结构和原理、Mono8图像buffer存储结构和原理、RGB16图像buffer存储结构和 原理、RGB8图像buffer存储结构和原理
以下是各种图像buffer存储结构和原理的整理:
- MONO8(单通道8位):每个像素点只有一个通道,通道值的范围是0-255,使用一个字节存储。
- MONO10Packed(单通道10位压缩):每个像素点只有一个通道,通道值的范围是0-1023,使用2个
字节存储。其中,每三个像素点的数据会被压缩为4个字节,即将30位的像素数据压缩为24位进行存储。
- MONO12(单通道12位):每个像素点只有一个通道,通道值的范围是0-4095,使用2个字节存储。
- MONO12Packed(单通道12位压缩):每个像素点只有一个通道,通道值的范围是0-4095,使用3个字节存储。其
中,每两个像素点的数据会被压缩为3个字节,即将24位的像素数据压缩为18位进行存储。
- RGB8(三通道8位):每个像素点有三个通道,分别是红、绿、蓝三个通道,每个通道的像素值范围
是0-255,总共使用三个字节存储。
- RGB16(三通道16位):每个像素点有三个通道,分别是红、绿、蓝三个通道,每个通道的像素值范
围是0-65535,总共使用六个字节存储。
2
3
4
5
6
7
8
9
10
以上是各种图像buffer的存储结构和原理的整理。需要特别说明的是,MONO10Packed和MONO12Packed采用 了压缩算法进行存储,可以在一定程度上减少存储空间的占用。此外,RGB8和RGB16是三通道的图像格 式,分别对应红、绿、蓝三个颜色通道,每个通道的像素值都可以在0-255或0-65535之间变化。
# 5. 24位图像处理
用C# 把24位的彩色相机buffer分别转化为8位,16位,24位的buffer,并且分别保存图 像为tiff,jpg,png,raw,bmp文件,水平垂直分辨率为96
using System;
using System.Drawing;
using System.Drawing.Imaging;
public static class ColorCameraBufferConverter
{
public static void ConvertTo8Bit(byte[] inputBuffer, byte[] outputBuffer)
{
for (int i = 0; i < inputBuffer.Length; i += 3)
{
byte r = inputBuffer[i];
byte g = inputBuffer[i + 1];
byte b = inputBuffer[i + 2];
byte gray = (byte)(0.299 * r + 0.587 * g + 0.114 * b);
outputBuffer[i / 3] = gray;
}
}
public static void ConvertTo16Bit(byte[] inputBuffer, byte[] outputBuffer)
{
for (int i = 0; i < inputBuffer.Length; i += 3)
{
byte r = inputBuffer[i];
byte g = inputBuffer[i + 1];
byte b = inputBuffer[i + 2];
ushort red16 = (ushort)(r << 8);
ushort green16 = (ushort)(g << 8);
ushort blue16 = (ushort)(b << 8);
outputBuffer[i * 2] = (byte)red16;
outputBuffer[i * 2 + 1] = (byte)(red16 >> 8);
outputBuffer[i * 2 + 2] = (byte)green16;
outputBuffer[i * 2 + 3] = (byte)(green16 >> 8);
outputBuffer[i * 2 + 4] = (byte)blue16;
outputBuffer[i * 2 + 5] = (byte)(blue16 >> 8);
}
}
public static void ConvertTo24Bit(byte[] inputBuffer, byte[] outputBuffer)
{
Array.Copy(inputBuffer, outputBuffer, inputBuffer.Length);
}
public static void SaveToTiff(byte[] buffer, int width, int height, string fileName, long compression = EncoderValue.CompressionLZW)
{
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
int stride = bitmapData.Stride; // 行字节宽度
for (int y = 0; y < height; y++)
{
byte[] lineBuffer = new byte[stride];
Array.Copy(buffer, y * stride, lineBuffer, 0, stride);
for (int x = 0; x < width; x++)
{
// RGB顺序
int index1 = x * 3;
int index2 = y * stride + x * 3;
lineBuffer[index1] = buffer[index2 + 2];
lineBuffer[index1 + 1] = buffer[index2 + 1];
lineBuffer[index1 + 2] = buffer[index2];
}
System.Runtime.InteropServices.Marshal.Copy(lineBuffer, 0, (IntPtr)(bitmapData.Scan0 + y * stride), stride);
}
SaveTiff(bitmap, fileName, compression);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
}
public static void SaveToJpeg(byte[] buffer, int width, int height, string fileName, int quality = 80)
{
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
int stride = bitmapData.Stride; // 行字节宽度
for (int y = 0; y < height; y++)
{
byte[] lineBuffer = new byte[stride];
Array.Copy(buffer, y * stride, lineBuffer, 0, stride);
for (int x = 0; x < width; x++)
{
// RGB顺序
int index1 = x * 3;
int index2 = y * stride + x * 3;
lineBuffer[index1] = buffer[index2 + 2];
lineBuffer[index1 + 1] = buffer[index2 + 1];
lineBuffer[index1 + 2] = buffer[index2];
}
System.Runtime.InteropServices.Marshal.Copy(lineBuffer, 0, (IntPtr)(bitmapData.Scan0 + y * stride), stride);
}
SaveJpeg(bitmap, fileName, quality);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
}
public static void SaveToPng(byte[] buffer, int width, int height, string fileName)
{
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
int stride = bitmapData.Stride; // 行字节宽度
for (int y = 0; y < height; y++)
{
byte[] lineBuffer = new byte[stride];
Array.Copy(buffer, y * stride, lineBuffer, 0, stride);
for (int x = 0; x < width; x++)
{
// RGB顺序
int index1 = x * 3;
int index2 = y * stride + x * 3;
lineBuffer[index1] = buffer[index2 + 2];
lineBuffer[index1 + 1] = buffer[index2 + 1];
lineBuffer[index1 + 2] = buffer[index2];
}
System.Runtime.InteropServices.Marshal.Copy(lineBuffer, 0, (IntPtr)(bitmapData.Scan0 + y * stride), stride);
}
bitmap.Save(fileName, ImageFormat.Png);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
}
public static void SaveToRaw(byte[] buffer, string fileName)
{
System.IO.File.WriteAllBytes(fileName, buffer);
}
public static void SaveToBmp(byte[] buffer, int width, int height, string fileName)
{
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
int stride = bitmapData.Stride; // 行字节宽度
for (int y = 0; y < height; y++)
{
byte[] lineBuffer = new byte[stride];
Array.Copy(buffer, y * stride, lineBuffer, 0, stride);
for (int x = 0; x < width; x++)
{
// RGB顺序
int index1 = x * 3;
int index2 = y * stride + x * 3;
lineBuffer[index1] = buffer[index2 + 2];
lineBuffer[index1 + 1] = buffer[index2 + 1];
lineBuffer[index1 + 2] = buffer[index2];
}
System.Runtime.InteropServices.Marshal.Copy(lineBuffer, 0, (IntPtr)(bitmapData.Scan0 + y * stride), stride);
}
bitmap.Save(fileName, ImageFormat.Bmp);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
}
private static void SaveTiff(Bitmap bitmap, string fileName, long compression)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, compression);
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/tiff");
bitmap.Save(fileName, myImageCodecInfo, encoderParameters);
}
private static void SaveJpeg(Bitmap bitmap, string fileName, int quality)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
bitmap.Save(fileName, myImageCodecInfo, encoderParameters);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int i = 0; i < encoders.Length; i++)
{
if (encoders[i].MimeType == mimeType)
{
return encoders[i];
}
}
return null;
}
}
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
调用示例:
// 假设width和height已知并且是24位彩色图像的大小
byte[] inputBuffer = ... // 24位彩色图像buffer
byte[] outputBuffer1 = new byte[width * height];
byte[] outputBuffer2 = new byte[width * height * 2];
byte[] outputBuffer3 = new byte[width * height * 3];
ColorCameraBufferConverter.ConvertTo8Bit(inputBuffer, outputBuffer1);
ColorCameraBufferConverter.ConvertTo16Bit(inputBuffer, outputBuffer2);
ColorCameraBufferConverter.ConvertTo24Bit(inputBuffer, outputBuffer3);
string tiffFileName = "output.tiff";
string jpegFileName = "output.jpg";
string pngFileName = "output.png";
string rawFileName = "output.raw";
string bmpFileName = "output.bmp";
int horizontalResolution = 96;
int verticalResolution = 96;
ColorCameraBufferConverter.SaveToTiff(outputBuffer1, width, height, tiffFileName);
ColorCameraBufferConverter.SaveToJpeg(outputBuffer1, width, height, jpegFileName);
ColorCameraBufferConverter.SaveToPng(outputBuffer1, width, height, pngFileName);
ColorCameraBufferConverter.SaveToRaw(outputBuffer1, rawFileName);
ColorCameraBufferConverter.SaveToBmp(outputBuffer1, width, height, bmpFileName);
// 保存时指定水平垂直分辨率
SaveToTiff(outputBuffer1, width, height, tiffFileName, horizontalResolution, verticalResolution);
SaveToJpeg(outputBuffer1, width, height, jpegFileName, horizontalResolution, verticalResolution);
SaveToPng(outputBuffer1, width, height, pngFileName, horizontalResolution, verticalResolution);
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
来源链接:[1] [2] [3] [4] -LIBTIFF库的使用心得 - 一杯清酒邀明月 - 博客园 (cnblogs.com)