C++ 下 Halcon 与 OpenCV 图像的转换

本文最后更新于:2022年12月27日 晚上

Halcon 中的图像数据结构为 HImage, OpenCV 中的图像为 Mat,使用中经常需要相互转换的情况,本文记录转换方式。

转换规则

halcon、opencv 和 C++图像内存数据处理机制有差异,在进行相互转换的时候需要注意内存数据排列问题,否则可能出现花图或者多出黑边等现象。

  • Halcon 的 HImage 和 OpenCV 的 Mat 都是连续存储图像数据的,HImage 存储数据是每个通道的数据存在一起的, Mat 的数据是一个像素点中的多个通道数据连续存在一起的。
  • 单通道图像如果位深度一致可以直接复制内存,如果多通道则需要按照图像的存储规则重新整理。

HImage to Mat

8 bit 深度图像

  • 当图像为 8 bit 单通道普通图像时,Mat 图像在内存中各个像素连续排列,像素存在的顺序和 HImage 一致,可以直接拷贝内存:
1
2
3
4
5
6
HalconCpp::HTuple pointer;
GetImagePointer1(H_img, &pointer, nullptr, &w, &h);
int width = w.I(), height = h.I();
int size = width * height;
cv_img = cv::Mat::zeros(height, width, CV_8UC1);
memcpy(cv_img.data, (void*)(pointer.L()), size);
  • 当图像为多通道图像时,OpenCV 的 Mat 图像内存仍然是连续的,HImage 是多个单通道图像的组合,因此内存组织上有些区别,需要逐个像素整理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HalconCpp::HTuple pointerR, pointerG, pointerB;
HalconCpp::GetImagePointer3(H_img, &pointerR, &pointerG, &pointerB, nullptr, &w, &h);
int width = w.I(), height = h.I();
int size = width * height;
cv_img = cv::Mat::zeros(height, width, CV_8UC3);
uchar* R = (uchar*)(pointerR.L());
uchar* G = (uchar*)(pointerG.L());
uchar* B = (uchar*)(pointerB.L());
for (int i = 0; i < height; ++i)
{
uchar *p = cv_img.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
p[3 * j] = B[i * width + j];
p[3 * j + 1] = G[i * width + j];
p[3 * j + 2] = R[i * width + j];
}
}
示例代码
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
#include<stdio.h>
#include<opencv2/opencv.hpp>
#include "HalconCpp.h"
#include <string>

using namespace std;
using namespace cv;
using namespace HalconCpp;

cv::Mat HImageToMat(HalconCpp::HObject &H_img)
{
cv::Mat cv_img;
HalconCpp::HTuple channels, w, h;

HalconCpp::ConvertImageType(H_img, &H_img, "byte");
HalconCpp::CountChannels(H_img, &channels);

if (channels.I() == 1)
{
HalconCpp::HTuple pointer;
GetImagePointer1(H_img, &pointer, nullptr, &w, &h);
int width = w.I(), height = h.I();
int size = width * height;
cv_img = cv::Mat::zeros(height, width, CV_8UC1);
memcpy(cv_img.data, (void*)(pointer.L()), size);
cout << "Gray" << endl;
}

else if (channels.I() == 3)
{
HalconCpp::HTuple pointerR, pointerG, pointerB;
HalconCpp::GetImagePointer3(H_img, &pointerR, &pointerG, &pointerB, nullptr, &w, &h);
int width = w.I(), height = h.I();
int size = width * height;
cv_img = cv::Mat::zeros(height, width, CV_8UC3);
uchar* R = (uchar*)(pointerR.L());
uchar* G = (uchar*)(pointerG.L());
uchar* B = (uchar*)(pointerB.L());
for (int i = 0; i < height; ++i)
{
uchar *p = cv_img.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
p[3 * j] = B[i * width + j];
p[3 * j + 1] = G[i * width + j];
p[3 * j + 2] = R[i * width + j];
}
}
cout << "RGB" << endl;
}
return cv_img;
}

int main()
{
string data_path = "1.jpg";
HalconCpp::HImage Image(data_path.c_str()), ImageGray;
Rgb1ToGray(Image, &ImageGray);

Mat cv_img_gray = HImageToMat(ImageGray);
Mat cv_img_rgb = HImageToMat(Image);

return 0;
}

其他深度图像

  • 当图像深度不为 8 bits 时,Mat 图像为连续的内存,HImage 图像仍为多个通道单独处理内存的情况,内存拷贝时需要注意转换前后的图像的位深度一致
示例代码
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
#include<stdio.h>
#include<opencv2/opencv.hpp>
#include "HalconCpp.h"
#include <string>

using namespace std;
using namespace cv;
using namespace HalconCpp;


int main()
{
string data_path = "1.jpg";
HalconCpp::HImage Image(data_path.c_str()), ImageGray;
Rgb1ToGray(Image, &ImageGray);

ConvertImageType(ImageGray, &ImageGray, "int4");
HTuple channels, w, h;
HTuple pointer;

CountChannels(ImageGray, &channels);
GetImagePointer1(ImageGray, &pointer, nullptr, &w, &h);

int width = w.I(), height = h.I();
int size = width * height;

Mat cv_img, cv_res;

cv_img = cv::Mat::zeros(height, width, CV_32S);
memcpy(cv_img.data, (void*)(pointer.L()), size * 4);

cv_img.convertTo(cv_res, CV_8UC1);

return 0;
}

Mat to HImage

  • 图像转换逻辑和 HImage 转 Mat 是一致的,直接上示例代码
示例代码
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
#include<stdio.h>
#include<opencv2/opencv.hpp>
#include "HalconCpp.h"
#include <string>

using namespace std;
using namespace cv;
using namespace HalconCpp;

HalconCpp::HObject MatToHImage(cv::Mat& cv_img)
{
HalconCpp::HObject H_img;

if (cv_img.channels() == 1)
{
int height = cv_img.rows, width = cv_img.cols;
int size = height * width;
uchar *temp = new uchar[size];

memcpy(temp, cv_img.data, size);
HalconCpp::GenImage1(&H_img, "byte", width, height, (Hlong)(temp));

delete[] temp;
}
else if (cv_img.channels() == 3)
{
int height = cv_img.rows, width = cv_img.cols;
int size = height * width;
uchar *B = new uchar[size];
uchar *G = new uchar[size];
uchar *R = new uchar[size];

for (int i = 0; i < height; i++)
{
uchar *p = cv_img.ptr<uchar>(i);
for (int j = 0; j < width; j++)
{
B[i * width + j] = p[3 * j];
G[i * width + j] = p[3 * j + 1];
R[i * width + j] = p[3 * j + 2];
}
}
HalconCpp::GenImage3(&H_img, "byte", width, height, (Hlong)(R), (Hlong)(G), (Hlong)(B));

delete[] R;
delete[] G;
delete[] B;
}
return H_img;
}

int main()
{
string data_path = "1.jpg";
cv::Mat cv_img = cv::imread(data_path);
HImage H_img_new = MatToHImage(cv_img);

Mat cv_img_gray;
cvtColor(cv_img, cv_img_gray, COLOR_RGB2GRAY);
HImage H_img_gray = MatToHImage(cv_img_gray);
return 0;
}

参考资料


C++ 下 Halcon 与 OpenCV 图像的转换
https://www.zywvvd.com/notes/coding/halcon/halcon-opencv-conv/halcon-opencv-conv/
作者
Yiwei Zhang
发布于
2022年12月27日
许可协议