opencv-通过添加R+将3个通道合并为1个通道;G+;B
在OpenCV中,是否有一种简单的方法将BGR图像转换为单通道图像,其中每个像素是B、G和R的总和 例如,以下矩阵:opencv-通过添加R+将3个通道合并为1个通道;G+;B,opencv,Opencv,在OpenCV中,是否有一种简单的方法将BGR图像转换为单通道图像,其中每个像素是B、G和R的总和 例如,以下矩阵: [ [1, 2, 3], [4, 5, 6], [10, 20, 30], [40, 50, 60] ] 转换为: [ [6], [15], [60], [150] ] 我知道我可以在3个频道的图像上使用split,然后添加所有三个频道,但我想知道是否有更直接的内容。 thx [编辑] 我要做的是计算图像的结构张量,如中所述。结构张量定义为: 其中Lx、ax和
[
[1, 2, 3], [4, 5, 6],
[10, 20, 30], [40, 50, 60]
]
转换为:
[
[6], [15],
[60], [150]
]
我知道我可以在3个频道的图像上使用split
,然后添加所有三个频道,但我想知道是否有更直接的内容。
thx
[编辑]
我要做的是计算图像的结构张量,如中所述。结构张量定义为:其中Lx、ax和bx是L、a、b通道的索贝尔导数 我当前的实施(如果我正确理解了本文):
//加载并转换为Lab(使用32F矩阵保留原始Lab值)
Mat实验室;
图像转换器(实验室,CV_32F,1/255.0);
CVT颜色(实验室、实验室、颜色实验室);
//计算两个方向的导数
matdx,dy;
Sobel(实验室,dx,CV_32F,1,0,3);
Sobel(实验室,dy,CV_32F,0,1,3);
//现在需要通过添加L^2+a^2+b^2将dx转换为单通道
matdx2;
乘法(dx,dx,dx2);
载体dx2Cnls;
分割(dx2,dx2Cnls);
材料E=dx2Cnls[0]/*Lx^2*/+dx2Cnls[1]/*ax^2*/+dx2Cnls[2]/*bx^2*/;
//对dy也一样
Mat-dy2;
乘法(dy,dy,dy2);
载体dy2Cnls;
分裂(dy2,dy2Cnls);
Mat G=dy2Cnls[0]/*Ly^2*/+dy2Cnls[1]/*ay^2*/+dy2Cnls[2]/*by^2*/;
//现在是交叉点dx*dy
Mat-dxdy;
乘法(dx,dy,dxdy);
矢量dxdyCnls;
拆分(dxdy、dxdyCnls);
Mat F=dxdyCnls[0]/*LxLy*/+dxdyCnls[1]/*axay*/+dxdyCnls[2]/*bxby*/;
< >避免任何代码<拆分< /代码>,以便组合3个通道( C++),可以使用FueCH成员:
cv::Mat img1(200, 200, CV_8UC3, cv::Scalar(10, 50, 150));
cv::Mat img2(img1.size(), CV_32FC1, cv::Scalar(0, 0, 0));
img2.forEach<float>([&](float& pix, const int position[])
{
cv::Vec3b p = img1.at<cv::Vec3b>(position[1], position[0]);
pix = p[0] + p[1] + p[2];
});
cv::Mat img1(200200,cv_8UC3,cv::Scalar(10,50150));
cv::Mat img2(img1.size(),cv_32FC1,cv::Scalar(0,0,0));
img2.forEach([&](float&pix,const int position[])
{
cv::Vec3b p=img1.at(位置[1],位置[0]);
pix=p[0]+p[1]+p[2];
});
对于C++,你可以使用FrACH成员:
cv::Mat img1(200, 200, CV_8UC3, cv::Scalar(10, 50, 150));
cv::Mat img2(img1.size(), CV_32FC1, cv::Scalar(0, 0, 0));
img2.forEach<float>([&](float& pix, const int position[])
{
cv::Vec3b p = img1.at<cv::Vec3b>(position[1], position[0]);
pix = p[0] + p[1] + p[2];
});
cv::Mat img1(200200,cv_8UC3,cv::Scalar(10,50150));
cv::Mat img2(img1.size(),cv_32FC1,cv::Scalar(0,0,0));
img2.forEach([&](float&pix,const int position[])
{
cv::Vec3b p=img1.at(位置[1],位置[0]);
pix=p[0]+p[1]+p[2];
});
一旦计算出导数,进一步的处理将独立处理每个像素位置。这意味着我们可以利用内存中像素数据的布局,并将其转换为更方便的形状
我们可以将dx
和dy
转换为1通道Mat
s,具有3列(以及宽*高行)——每个原始像素位于一个新行上,第一列对应于L
通道,第二列对应于a
,第三列对应于b
。整形是一个O(1)操作,因此成本可以忽略不计
每个元素的乘法仍将以相同的方式工作(尽管我们可以使用a而不是使事情更加简洁)
然后,我们可以使用获得每行(即每像素)和
最后,将E
、F
和G
重塑为原始宽度/高度
示例代码
#include <opencv2/opencv.hpp>
int main()
{
// Make some random data
cv::Mat img(16, 16, CV_8UC3);
cv::randu(img, 0, 256);
// Load and convert to Lab (keeping the raw lab values by using 32F matrix)
cv::Mat lab;
img.convertTo(lab, CV_32F, 1 / 255.0);
cv::cvtColor(lab, lab, cv::COLOR_BGR2Lab);
// Compute derivatives in both directions
cv::Mat dx, dy;
cv::Sobel(lab, dx, CV_32F, 1, 0, 3);
cv::Sobel(lab, dy, CV_32F, 0, 1, 3);
// Reshape the arrays to 1-channel, such that each original pixel is on
// one row, first column is L, second is a, third is b
// NB: This is O(1) operation.
int new_row_count(lab.rows * lab.cols);
dx = dx.reshape(1, new_row_count);
dy = dy.reshape(1, new_row_count);
// Get per-row (i.e. per pixel) sums for dx*dx, dx*dy, and dy*dy
cv::Mat E, F, G;
cv::reduce(dx.mul(dx), E, 1, cv::REDUCE_SUM);
cv::reduce(dx.mul(dy), F, 1, cv::REDUCE_SUM);
cv::reduce(dy.mul(dy), G, 1, cv::REDUCE_SUM);
// Return back to original shape (but now only single channel)
E = E.reshape(1, lab.rows);
F = F.reshape(1, lab.rows);
G = G.reshape(1, lab.rows);
return 0;
}
我们可以看到,这基本上是一个:
因此,我们可以使用cv::Vec3f::dot
优雅地计算结果
与使用重塑
和减少
的方法相比,最终实现所需的时间大约减少40%
示例代码
#include <opencv2/opencv.hpp>
int main()
{
// Make some random data
cv::Mat img(16, 16, CV_8UC3);
cv::randu(img, 0, 256);
// Load and convert to Lab (keeping the raw lab values by using 32F matrix)
cv::Mat lab;
img.convertTo(lab, CV_32F, 1 / 255.0);
cv::cvtColor(lab, lab, cv::COLOR_BGR2Lab);
// Compute derivatives in both directions
cv::Mat dx, dy;
cv::Sobel(lab, dx, CV_32F, 1, 0, 3);
cv::Sobel(lab, dy, CV_32F, 0, 1, 3);
// Reshape the arrays to 1-channel, such that each original pixel is on
// one row, first column is L, second is a, third is b
// NB: This is O(1) operation.
int new_row_count(lab.rows * lab.cols);
dx = dx.reshape(1, new_row_count);
dy = dy.reshape(1, new_row_count);
// Get per-row (i.e. per pixel) sums for dx*dx, dx*dy, and dy*dy
cv::Mat E, F, G;
cv::reduce(dx.mul(dx), E, 1, cv::REDUCE_SUM);
cv::reduce(dx.mul(dy), F, 1, cv::REDUCE_SUM);
cv::reduce(dy.mul(dy), G, 1, cv::REDUCE_SUM);
// Return back to original shape (but now only single channel)
E = E.reshape(1, lab.rows);
F = F.reshape(1, lab.rows);
G = G.reshape(1, lab.rows);
return 0;
}
#包括
孔隙结构张量(cv::Mat const&img,cv::Mat&E,cv::Mat&F,cv::Mat&G)
{
//加载并转换为Lab(使用32F矩阵保留原始Lab值)
cv::Mat实验室;
图像转换器(实验室,CV_32F,1/255.0);
cv::CVT颜色(实验室,实验室,cv::颜色实验室);
//计算两个方向的导数
cv::Mat dx,dy;
cv::Sobel(实验室,dx,cv_32F,1,0,3);
cv::Sobel(实验室,dy,cv_32F,0,1,3);
E.创建(实验室尺寸(),CV_32FC1);
F.创建(实验室尺寸(),CV_32FC1);
G.创建(实验室尺寸(),CV_32FC1);
for(int r(0);rdot(*idx);
*如果++=idx->dot(*idy);
*iG++=idy->dot(*idy);
}
/*或者像这样,选择你更喜欢的。
for(int c(0);c
一旦计算出导数,进一步的处理将独立处理每个像素位置。这意味着我们可以利用内存中像素数据的布局,并将其转换为更方便的形状
我们可以将dx
和dy
转换为1通道Mat
s,具有3列(以及宽*高行)——每个原始像素位于一个新行上,第一列对应于L
通道,第二列对应于a
,第三列对应于b
。整形是一个O(1)操作,因此成本可以忽略不计
每个元素的乘法仍将以相同的方式工作(尽管我们可以使用a而不是使事情更加简洁)
然后,我们可以使用获得每行(即每像素)和
最后,将E
、F
和G
重塑为原始宽度/高度
示例代码
#include <opencv2/opencv.hpp>
int main()
{
// Make some random data
cv::Mat img(16, 16, CV_8UC3);
cv::randu(img, 0, 256);
// Load and convert to Lab (keeping the raw lab values by using 32F matrix)
cv::Mat lab;
img.convertTo(lab, CV_32F, 1 / 255.0);
cv::cvtColor(lab, lab, cv::COLOR_BGR2Lab);
// Compute derivatives in both directions
cv::Mat dx, dy;
cv::Sobel(lab, dx, CV_32F, 1, 0, 3);
cv::Sobel(lab, dy, CV_32F, 0, 1, 3);
// Reshape the arrays to 1-channel, such that each original pixel is on
// one row, first column is L, second is a, third is b
// NB: This is O(1) operation.
int new_row_count(lab.rows * lab.cols);
dx = dx.reshape(1, new_row_count);
dy = dy.reshape(1, new_row_count);
// Get per-row (i.e. per pixel) sums for dx*dx, dx*dy, and dy*dy
cv::Mat E, F, G;
cv::reduce(dx.mul(dx), E, 1, cv::REDUCE_SUM);
cv::reduce(dx.mul(dy), F, 1, cv::REDUCE_SUM);
cv::reduce(dy.mul(dy), G, 1, cv::REDUCE_SUM);
// Return back to original shape (but now only single channel)
E = E.reshape(1, lab.rows);
F = F.reshape(1, lab.rows);
G = G.reshape(1, lab.rows);
return 0;
}
我们可以看到,这基本上是一个:
因此,我们可以使用cv::Vec3f::dot
优雅地计算结果
结果实现所需的时间比使用<
#include <opencv2/opencv.hpp>
void structure_tensor(cv::Mat const& img, cv::Mat& E, cv::Mat& F, cv::Mat& G)
{
// Load and convert to Lab (keeping the raw lab values by using 32F matrix)
cv::Mat lab;
img.convertTo(lab, CV_32F, 1 / 255.0);
cv::cvtColor(lab, lab, cv::COLOR_BGR2Lab);
// Compute derivatives in both directions
cv::Mat dx, dy;
cv::Sobel(lab, dx, CV_32F, 1, 0, 3);
cv::Sobel(lab, dy, CV_32F, 0, 1, 3);
E.create(lab.size(), CV_32FC1);
F.create(lab.size(), CV_32FC1);
G.create(lab.size(), CV_32FC1);
for (int r(0); r < lab.rows; ++r) {
cv::Vec3f const* idx = dx.ptr<cv::Vec3f>(r);
cv::Vec3f const* idy = dy.ptr<cv::Vec3f>(r);
float* iE = E.ptr<float>(r);
float* iF = F.ptr<float>(r);
float* iG = G.ptr<float>(r);
for (int c(0); c < lab.cols; ++c, ++idx, ++idy) {
*iE++ = idx->dot(*idx);
*iF++ = idx->dot(*idy);
*iG++ = idy->dot(*idy);
}
/* Or like this, pick what you like more.
for (int c(0); c < lab.cols; ++c) {
iE[c] = idx[c].dot(idx[c]);
iF[c] = idx[c].dot(idy[c]);
iG[c] = idy[c].dot(idy[c]);
}
*/
}
}
int main()
{
// Make some random data
cv::Mat img(16, 16, CV_8UC3);
cv::randu(img, 0, 256);
cv::Mat E, F, G;
structure_tensor(img, E, F, G);
return 0;
}