cv::Mat转std::string

通过vector进行中转。但memcpy效率还是挺高的.

1
2
3
4
5
6
7
8
9
10
11
cv::Mat mat = cv::imread("a.jpg");
std::vector<unsigned char> buff;
cv::imencode(".jpg", mat, buff);
// 深拷贝
std::string str;
str.resize(buff.size());
memcpy(&str[0], buff.data(), buff.size());

// 浅拷贝
std::string str;
str.assign(buff.begin(), buff.end());

std::string转cv::Mat

把std::string当做1行N列的数据,通过引用数据指针的方式来得到mat2。此时没有发生内存拷贝。

1
2
3
4
5
6
7
cv::Mat mat(1, str.size(), CV_8U, (char *)str.data());
cv::Mat dst = cv::imdecode(mat, cv::IMREAD_UNCHANGED);

// 有拷贝
std::vector<uchar> view_buf(str.begin(), str.end());
cv::Mat view_image = cv::imdecode(view_buf, cv::ImreadModes::IMREAD_COLOR);
view_buf.clear();

cv::Mat按行合并

1
2
3
4
5
6
7
8
9
10
11
cv::Mat mergeRows(cv::Mat A, cv::Mat B)
{
// cv::CV_ASSERT(A.cols == B.cols&&A.type() == B.type());
int totalRows = A.rows + B.rows;
cv::Mat mergedDescriptors(totalRows, A.cols, A.type());
cv::Mat submat = mergedDescriptors.rowRange(0, A.rows);
A.copyTo(submat);
submat = mergedDescriptors.rowRange(A.rows, totalRows);
B.copyTo(submat);
return mergedDescriptors;
}

cv::Mat按列合并

1
2
3
4
5
6
7
8
9
10
11
cv::Mat mergeCols(cv::Mat A, cv::Mat B)
{
// cv::CV_ASSERT(A.cols == B.cols&&A.type() == B.type());
int totalCols = A.cols + B.cols;
cv::Mat mergedDescriptors(A.rows, totalCols, A.type());
cv::Mat submat = mergedDescriptors.colRange(0, A.cols);
A.copyTo(submat);
submat = mergedDescriptors.colRange(A.cols, totalCols);
B.copyTo(submat);
return mergedDescriptors;
}

cv::Mat元素访问方式

这里介绍三种方式:

  • 数组访问方式
  • cv::Vec3b指针访问方式
  • 字节指针访问方式

其中字节指针方式耗时最少,最高效,数组访问方式耗时最多,比较低效。其具体使用方式如下。

数组访问方式

1
2
3
4
5
6
7
8
9
10
11
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
Vec3b bgr = image.at<Vec3b>(row, col);
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
image.at<Vec3b>(row, col) = bgr;
}
}

cv::Vec3b指针访问方式

1
2
3
4
5
6
7
8
9
10
11
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++) {
Vec3b* curr = image.ptr<Vec3b>(row);
for (int col = 0; col < w; col++) {
Vec3b bgr = curr[col];
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
}
}

字节指针方式

1
2
3
4
5
6
7
8
9
10
11
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++) {
uchar* uc_pixel = image.data + row * image.step;
for (int col = 0; col < w; col++) {
uc_pixel[0] = 255 - uc_pixel[0];
uc_pixel[1] = 255 - uc_pixel[1];
uc_pixel[2] = 255 - uc_pixel[2];
uc_pixel += 3;
}
}

图像resize

1
2
3
4
5
double threshold = std::min(rx, ry);
double ratio_x = rx / threshold;
double ratio_y = ry / threshold;
cv::Mat resize_image;
cv::resize(src, resize_image, cv::Size(), ratio_x, ratio_y);

文件二进制转cv::Mat

有时会从文件读取图像文件的所有数据,或者来自网络的带格式的图像数据,转成cv::Mat如下:

1
2
3
4
5
cv::Mat binary2Mat(std::string &&image) {
cv::Mat m(1, image.size(), CV_8U, std::move(image.data()));
cv::Mat img = cv::imdecode(m, cv::IMREAD_UNCHANGED);
return std::move(img);
}

深度图,伪rgb显示

  • 单通道cv_32F测试可用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    cv::Mat depth2rgb(const std::string &vfilepath) {
    cv::Mat img = cv::imread(vfilepath, cv::IMREAD_UNCHANGED);
    cv::Mat img8U;
    cv::convertScaleAbs(img, img8U, 10); // scale: 10, adjust
    cv::mat rgb;
    cv::applyColorMap(img8U, rgb, cv::COLORMAP_JET);
    return std::move(rgb);
    }

    int main(int argc, char *argv[]) {
    cv::Mat img = depth2rgb("test.tiff");
    cv::imshow("tiff", img);
    cv::WaitKey(0);
    return 0;
    }
  • 有时候数据以16位存储CV_16S1,根据实际情况转float,再映射到CV_8UC1,在渲染下:
    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
    cv::Mat comm::depth16U2Mat(std::string &raw)
    {
    float resolution_z = 0.0004f;
    float offset_z = 0.25f;
    cv::Mat data(1, raw.size(), CV_16UC1, std::move(raw.data()));
    cv::Mat img_16u = cv::imdecode(data, cv::IMREAD_ANYDEPTH);
    cv::Mat img_32f(img_16u.size(), CV_32FC1);
    cv::Mat img_8u(img_16u.size(), CV_8UC1);
    for (int r = 0; r < img_16u.rows; r++)
    {
    auto src_ptr = img_16u.ptr<short>(r);
    auto dst_ptr = img_32f.ptr<float>(r);
    for (int c = 0; c < img_16u.cols; c++)
    {
    dst_ptr[c] = offset_z + resolution_z * src_ptr[c];
    }
    }

    double min = -2, max = 2;
    double ratio = 255.0 / (max - min);
    double shift = -min * ratio;
    img_32f.convertTo(img_8u, CV_8U, ratio, shift);
    cv::Mat color_map;
    cv::applyColorMap(img_8u, color_map, cv::COLORMAP_BONE);
    return color_map;
    }

超范围裁图

如果裁图范围超过图像区域,超出的范围以0填充,rect的x, y可以是负数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cv::Mat cropImage(const cv::Mat &src, const cv::Rect &rect)
{
cv::Mat dst = cv::Mat::zeros(rect.height, rect.width, 0);
int crop_x1 = cv::Max(0, rect.x);
int crop_y1 = cv::Max(0, rect.y);
int crop_x2 = cv::min(src.cols - 1, rect.x + rect.width - 1); // 图像坐标为行/列-1
int crop_y2 = cv::min(src.rows - 1, rect.y + rect.height - 1);
cv::Mat = roi_img = src(cv::Range(crop_y1, crop_y2 + 1), cv::Range(crop_x1, crop_x2 + 1)); // 范围左等于,右小于[1, 2)

int x1 = crop_x1 - rect.x;
int y1 = crop_y1 - rect.y;
int x2 = crop_x2 - rect.x;
int y2 = crop_y2 - rect.y;

dst(cv::Range(y1, y2 + 1), cv::Range(x1, x2 + 1)) = dst(cv::Range(y1, y2 + 1), cv::Range(x1, x2 + 1)) + roi_img;
return std::move(dst);
}