# 图像功能实现
邮箱:yiyangyu1314@outlook.com
作者:易陽羽
时间:2023-06-08
2
3
4
5
# 1.用C#把Mono12Packed的buffer 保存为16位的tiff 图像
用C#中的System.Drawing.Imaging
命名空间下的Bitmap
、BitmapData
和Encoder
类将
Mono12Packed 的 buffer 保存为 16 位的 tiff 图像。以下是大致的代码流程:
// 假设已经将 Mono12Packed 数据读入到了 byte[] buffer 中
// 设置图像宽度和高度(根据实际情况设置)
int width = 2560;
int height = 2160;
// 创建一个 Bitmap 对象并锁定其内存区域以便于直接访问数据
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
// 写入像素数据(将 Mono12Packed 转换为 16 位灰度像素)
for (int y = 0; y < height; y++)
{
byte* pRow = (byte*)bmpData.Scan0 + y * bmpData.Stride;
for (int x = 0; x < width; x += 2)
{
byte b1 = buffer[(y * width + x) / 2 * 3];
byte b2 = buffer[(y * width + x) / 2 * 3 + 1];
ushort val1 = (ushort)((b1 << 4) | (b2 >> 4));
ushort val2 = (ushort)(((b2 & 0xF) << 8) | buffer[(y * width + x) / 2 * 3 + 2]);
*(ushort*)(pRow + x * 2) = val1;
*(ushort*)(pRow + x * 2 + 2) = val2;
}
}
// 解锁 Bitmap 对象并保存为 tiff 图像
bmp.UnlockBits(bmpData);
EncoderParameters encParams = new EncoderParameters(1);
encParams.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
bmp.Save("output.tiff", GetEncoder(ImageFormat.Tiff), encParams);
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
需要注意的是,上面代码中的 GetEncoder
方法需要自行实现。它通常会在整个应用程序范围内使用,
并将每个图像格式映射到相应的图像编码器。以下是一个简单实现:
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
参考资料:
- [2]
- Bitmap 类 (System.Drawing)
- BitmapData 结构 (System.Drawing.Imaging)
- .Imaging 命名空间 (System.Drawing.Imaging)
- ImageCodecInfo 类 (System.Drawing.Imaging)
# 2.把Mono12Packed的buffer 保存为16位的tiff 图像,封装为动态库dll
以下是一个用C++将Mono12Packed的buffer保存为16位tiff图像的代码,并封装成动态库dll:
#include <fstream>
#include <iostream>
#include <vector>
#include <cstdint>
#include "tiffio.h"
extern "C" {
__declspec(dllexport) void SaveAsTiff(uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
// 打开TIFF文件
TIFF* out = TIFFOpen(path, "w");
if (out == NULL) {
std::cerr << "Failed to create TIFF file." << std::endl;
return;
}
// 配置TIFF文件头信息
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 16);
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, width * sizeof(uint16_t)));
// 写入像素数据(将 Mono12Packed 转换为 16 位灰度像素)
std::vector<uint16_t> data(width * height);
for (uint32_t y = 0; y < height; y++)
{
for (uint32_t x = 0; x < width; x += 2)
{
uint8_t b1 = buffer[(y * width + x) / 2 * 3];
uint8_t b2 = buffer[(y * width + x) / 2 * 3 + 1];
uint16_t val1 = (uint16_t)((b1 << 4) | (b2 >> 4));
uint16_t val2 = (uint16_t)(((b2 & 0xF) << 8) | buffer[(y * width + x) / 2 * 3 + 2]);
data[y * width + x] = val1;
data[y * width + x + 1] = val2;
}
}
TIFFWriteEncodedStrip(out, 0, data.data(), width * height * sizeof(uint16_t));
// 关闭TIFF文件
TIFFClose(out);
}
}
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
代码中使用了 libtiff 库来操作 TIFF 文件,需要在项目中添加 -ltiff
库链接参数。
同时,请注意在编译动态库时添加合适的选项以确保导出函数符号。
这个代码可以编译为 Windows 或 Linux 平台下的动态库,在其他 C++ 项目中引入并
调用 SaveAsTiff 函数即可实现将Mono12Packed数据保存为16位tiff图像的功能。
参考资料:
# 3.用C++ 把Mono12Packed的buffer 保存为16位的tiff 图像,封装为动态库dll,并使用C#调用存图接口
以下是调用 C++ 动态库的示例 C# 代码,用于保存 Mono12Packed 数据为 16 位 TIFF 格式图像:
using System;
using System.Runtime.InteropServices;
namespace SaveAsTiffDemo
{
class Program
{
[DllImport("libMono12PackedToTiff.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveAsTiff(IntPtr buffer, uint width, uint height, string path);
static void Main(string[] args)
{
// Mono12Packed 数据(假设为 2560x2160 分辨率)
byte[] mono12PackedData = new byte[2560 * 2160 / 2 * 3];
// 填充 Mono12Packed 数据(此处略去)
// ......
// 将 Mono12Packed 数据传递给 C++ 动态库
IntPtr ptr = Marshal.AllocHGlobal(mono12PackedData.Length);
Marshal.Copy(mono12PackedData, 0, ptr, mono12PackedData.Length);
// 调用 C++ 存图接口
SaveAsTiff(ptr, 2560, 2160, "output.tif");
// 释放内存
Marshal.FreeHGlobal(ptr);
}
}
}
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
需要注意的是,C++ 动态库中的函数必须按照 C 风格进行导出,即使用 extern "C" { ... }
块来包含函数声明,并且在调用时需要指定 CallingConvention = CallingConvention.Cdecl
参数。同时,需要将 Mono12Packed 数据转换为 IntPtr
类型的指针传递给 C++ 函数。
完成上述代码后,在 C# 项目中添加对动态库文件的引用,并在调用前确保动态库文件已经正确部署到目标系统中即可。
# 4.用C++ 把Mono12的buffer 保存为16位的bmp,tiff,png,raw,jpeg 文件,封装为动态库dll,并使用C#,JAVA Jna调用存图接口
保存 Mono12 数据为 16 位图像的C++代码示例:
#include <cstdint>
#include <cstring>
#include <iostream>
#include <fstream>
#include "libtiff/tiffio.h"
#include "lodepng.h"
// 保存 Mono12 数据为 16 位 TIFF 格式图像
void SaveAsTiff(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
// 打开 TIFF 文件并写入数据
TIFF* tif = TIFFOpen(path, "w");
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, width * sizeof(uint16_t)));
for (uint32_t i = 0; i < height; ++i)
{
TIFFWriteScanline(tif, (uint16_t*)(buffer + i * width), i);
}
TIFFClose(tif);
}
// 保存 Mono12 数据为 16 位 PNG 格式图像(使用 lodepng 库)
void SaveAsPng(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
std::vector<uint16_t> data16(width * height);
for (uint32_t i = 0; i < width * height; ++i)
{
data16[i] = buffer[i] << 8;
}
std::vector<uint8_t> data8(data16.size() * 2);
std::memcpy(data8.data(), data16.data(), data16.size() * sizeof(uint16_t));
lodepng::encode(path, data8, width, height, LCT_GREY_ALPHA, 16);
}
// 保存 Mono12 数据为 16 位 RAW 格式图像
void SaveAsRaw(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
std::ofstream fout(path, std::ios::binary);
fout.write((const char*)(buffer), width * height * sizeof(uint16_t));
fout.close();
}
// 保存 Mono12 数据为 16 位 JPEG 格式图像(使用 libjpeg-turbo 库)
void SaveAsJpeg(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
FILE* outfile = fopen(path, "wb");
if (!outfile)
{
std::cerr << "Failed to open file for writing " << path << std::endl;
return;
}
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 1;
cinfo.in_color_space = JCS_GRAYSCALE;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 90, TRUE);
jpeg_start_compress(&cinfo, TRUE);
JSAMPROW row_pointer[1];
while (cinfo.next_scanline < cinfo.image_height)
{
row_pointer[0] = (JSAMPLE*)(buffer + cinfo.next_scanline * width);
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
}
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
需要注意的是,上述函数中使用的 TIFF 和 JPEG 库需要自行安装并链接到项目中。同时, 在编译 DLL 文件时需要将上述函数导出,例如:
// 定义 DLL 导出宏
#ifdef MAKEDLL
#define DLL __declspec(dllexport)
#else
#define DLL __declspec(dllimport)
#endif
// 导出函数声明
extern "C" {
DLL void SaveAsTiff(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path);
DLL void SaveAsPng(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path);
DLL void SaveAsRaw(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path);
DLL void SaveAsJpeg(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path);
}
// 导出函数定义
DLL void SaveAsTiff(const uint8_t* buffer, uint32_t width, uint32_t height, const char* path)
{
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
完成 DLL 文件编译后,在 C# 项目中可以使用以下方式调用存图接口:
using System;
using System.Runtime.InteropServices;
namespace SaveAsImageDemo
{
class Program
{
// 导入 DLL 中的函数
[DllImport("SaveAsImage.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveAsTiff(IntPtr buffer, uint width, uint height, string path);
[DllImport("SaveAsImage.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveAsPng(IntPtr buffer, uint width, uint height, string path);
[DllImport("SaveAsImage.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveAsRaw(IntPtr buffer, uint width, uint height, string path);
[DllImport("SaveAsImage.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveAsJpeg(IntPtr buffer, uint width, uint height, string path);
static void Main(string[] args)
{
// 假设 Mono12 数据存放在 byte[] buffer 中,分辨率为 2560x2160
byte[] buffer = new byte[2560 * 2160];
// 将 Mono12 数据传递给 C++ DLL 中的函数
IntPtr ptr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, ptr, buffer.Length);
// 调用 C++ DLL 中的存图接口
SaveAsTiff(ptr, 2560, 2160, "output.tif");
SaveAsPng(ptr, 2560, 2160, "output.png");
SaveAsRaw(ptr, 2560, 2160, "output.raw");
SaveAsJpeg(ptr, 2560, 2160, "output.jpg");
// 释放内存
Marshal.FreeHGlobal(ptr);
}
}
}
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
在 JAVA 中,可以使用 JNA(Java Native Access)库调用 C++ DLL 中的函数,具体操作方式略有 不同,可以参考 JNA 的相关文档进行学习。