Ash*_*win 6 c++ opencv image-processing
我们如何获得输入 RGB 图像(3 维 Mat 对象)的平均值,以便获得灰度图像?cvtColor()
OpenCV的函数根据预先存在的公式将图像转换为灰度。我想获得所有三个通道的平均值并将结果图像存储在另一个矩阵中。cv::mean()
OpenCV 中的函数返回所有输入通道的标量平均值。
如果这个 Pythonimg
是一个 RGB 图像,img.mean(2)
会得到我想要的东西。与 Python 相比,连续调用该addWeighted()
函数并使用gray= blue/3.0 + red/3.0 +green/3.0
[After splitting channels] 会产生不同的结果。
有没有类似于img.mean(2)
C++ 或 C++ 的 OpenCV 库的东西?
有没有类似于 C++ 中的 img.mean(2) 或 C++ 的 OpenCV 库的东西?
不,但你可以很容易地计算出来。有几种方法可以做到:
循环遍历所有图像,并将每个值设置为输入像素值的平均值。请注意计算具有比uchar
(此处我使用的double
)更多容量和准确性的类型的平均值的中间值,否则您最终可能会得到错误的结果。您还可以进一步优化代码,例如查看此问题及其答案。您只需要更改在内部循环中计算的函数即可计算平均值。
使用reduce
. 您可以reshape
将 3 通道大小rows x cols
的矩阵设为形状 ((rows*cols) x 3) 的矩阵,然后您可以使用reduce
带参数的操作REDUCE_AVG
来计算平均行。然后reshape
矩阵来校正大小。reshape
操作非常快,因为您只需修改标题而不会影响存储的数据。
使用矩阵运算对通道求和。您可以使用split
获取每个通道的矩阵,并将它们相加。总结时注意不要使您的值饱和!(感谢烧杯的这一点。)
您可以看到第一种方法对于小矩阵更快,但是一旦大小增加,第二种方法的性能就会好得多,因为您利用了 OpenCV 优化。第三种方法效果出奇地好(感谢矩阵表达式)。
一些数字,以毫秒为单位的时间。根据启用的 OpenCV 优化,时间可能因您的计算机而异。在发布中运行!
Size : 10x10 100x100 1000x1000 10000x10000
Loop : 0.0077 0.3625 34.82 3456.71
Reduce: 1.44 1.42 8.88 716.75
Split : 0.1158 0.0656 2.26304 246.476
Run Code Online (Sandbox Code Playgroud)
代码:
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat3b img(1000, 1000);
randu(img, Scalar(0, 0, 0), Scalar(10, 10, 10));
{
double tic = double(getTickCount());
Mat1b mean_img(img.rows, img.cols, uchar(0));
for (int r = 0; r < img.rows; ++r) {
for (int c = 0; c < img.cols; ++c) {
const Vec3b& v = img(r, c);
mean_img(r, c) = static_cast<uchar>(round((double(v[0]) + double(v[1]) + double(v[2])) / 3.0));
}
}
double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
cout << "Loop: " << toc << endl;
}
{
double tic = double(getTickCount());
Mat1b mean_img2 = img.reshape(1, img.rows*img.cols);
reduce(mean_img2, mean_img2, 1, REDUCE_AVG);
mean_img2 = mean_img2.reshape(1, img.rows);
double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
cout << "Reduce: " << toc << endl;
}
{
double tic = double(getTickCount());
vector<Mat1b> planes;
split(img, planes);
Mat1b mean_img3;
if (img.channels() == 3) {
mean_img3 = (planes[0] + planes[1] + planes[2]) / 3.0;
}
double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
cout << "Split: " << toc << endl;
}
getchar();
return 0;
}
Run Code Online (Sandbox Code Playgroud)