精华内容
下载资源
问答
  • OpenCV遍历图像像素、查找表和时间效率一、本节学习目标二、测试案例三、准备工作四、对比三种遍历算法的性能五、使用查找表的方法六、性能分析七、完整代码八、致谢 一、本节学习目标 学会如何遍历图像的每一个...

    一、本节学习目标

    • 学会如何遍历图像的每一个像素
    • 弄懂OpenCV矩阵值是如何存储的
    • 学会评估和测量算法的性能(使用计时函数)
    • 弄清楚什么是查找表并学会使用他们

    二、测试案例

    让我们考虑一个简单的颜色量化(减少一个颜色分量如R通道的取值数)方法。通过使用unsigned char C和c++类型来存储矩阵元素,一个像素通道可以有多达256个不同的值。对于一个三通道的图像,这会形成太多的颜色(确切地说是1600万)。处理如此多的色度可能会给我们的算法性能带来严重的影响。然而,有时候你只需要使用相对较少的颜色取值便能够获得相同的最终结果。

    在这种情况下,我们通常会对颜色空间进行缩减。这意味着我们用一个新的输入值分割颜色空间的当前值,最终得到更少的颜色取值。例如,0到9之间的每一个值都取新值0,10到19之间的每一个值都取值10,以此类推。

    当你将uchar类型的(无符号char - 其值在0到255之间)值与int值相除时,结果也将是char类型的值。这些值只能是char类型的值。因此,任何分数都会向下舍入(C/C++整除的特性,如3/2=1)。利用这一事实,uchar域上操作可以表示为:
    在这里插入图片描述
    一个简单的颜色空间缩减算法需要遍历图像矩阵的每个像素,在遍历的过程中应用这个公式。值得注意的是,我们做了除法和乘法运算,这些操作对于一个系统来说是很耗时的。如果可能的话,通过使用一些更高效的操作来避免它们是值得的,比如一些减法、加法,或者最好的情况是一个简单的赋值操作。此外,注意我们只有有限数量的输入值用于上操作。在uchar系统中,准确地说是256。

    因此,对于较大的图像,明智的做法是预先计算所有可能输入值对应的输出值,并在赋值过程中使用查找表进行赋值。查找表是简单的数组(具有一个或多个维度),对于给定的输入值,它保存最终的输出值。它的优点是我们不需要做计算,我们只需要读取结果。

    我们的测试用例程序(以及下面的代码示例)将执行以下操作:首先读取命令行参数传递的图像(它可能是彩色图像也可能是灰度图),并使用给定的命令行参数的整数值应用缩减。在OpenCV中,目前有三种主要的逐像素浏览图像的方法。为了让事情变得更有趣一点,我们将使用这些方法中的每一种来处理图像,并打印出处理所需的时间。

    三、准备工作

    1、构建查询表的内容

        #define divideWith 10
        uchar table[256];
        for (int i = 0; i < 256; ++i)
           table[i] = (uchar)(divideWith * (i/divideWith));
    

    2、计时函数统计算法性能
    OpenCV提供了两个简单的函数来实现计时,cv::getTickCount()cv::getTickFrequency()。第一个函数返回执行该函数时系统CPU的计数(比如自启动系统以来)。第二个函数返回的是CPU在一秒钟内发出多少次计数(频率)。因此,测量两个操作之间所花费的时间非常简单:

        double t = (double)getTickCount();
        // do something ...
        t = ((double)getTickCount() - t)/getTickFrequency();
        cout << "Times passed in seconds: " << t << endl;
    

    3、图像矩阵如何存储在内存中?
    你已经在上一节的笔记中中了解了Mat对象,矩阵的大小取决于所使用的颜色空间和图像的尺寸(这儿只讨论颜色系统)。颜色空间即所使用的通道数量。在灰度图像的情况下,图像矩阵为:
    在这里插入图片描述
    上图中,最上一行标识列号,最左一列标识行号。中间的取值分别代表行列索引。

    对于多通道图像,列包含与通道数量相同的子列。例如,BGR颜色空间的图像矩阵为:

    在这里插入图片描述
    注意通道的顺序是BGR而不是RGB。通常情况下,内存足够大,可以以连续的方式存储行,所以行可能一个接一个地出现,从而创建一个长行。因为所有的东西都在一个地方,一个接着一个,这可能有助于加快扫描过程。我们可以使用 cv::Mat::isContinuous() 函数来判断图像矩阵的存储是否连续。

    四、对比三种遍历算法的性能

    1、最有效的方式:C特点的指针操作

    在性能方面,经典的C风格操作符访问是最有效的。

    Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
    {
        CV_Assert(I.depth() == CV_8U);   // 只接受char类型的矩阵
        int channels = I.channels();     // 获取图像的通道数
        int nRows = I.rows;              // 获取图像的行数
        // 这个列不是图像的列数,其值与steps相等,表示图像矩阵中一行的列数。因为图像都是按行存储的,
        // 即一行的元素个数等于 图像的列数 * 通道数
        int nCols = I.cols * channels;  
        // 判断图像矩阵的存储是否连续,如果连续,则可以当作一行处理,那就只需要知道总元素个数即可
        if (I.isContinuous())
        {
            nCols *= nRows;
            nRows = 1;
        }
        int i,j;
        uchar* p;
        // 外层循环遍历行
        for( i = 0; i < nRows; ++i)
        {
            // 指针获取一行的首地址
            p = I.ptr<uchar>(i);
            // 内层循环控制遍历每一个元素
            for ( j = 0; j < nCols; ++j)
            {
                // 利用数组的随机读写的特性快速赋值
                p[j] = table[p[j]];
            }
        }
        return I;
    }
    

    这里我们基本上只获取一个指向每一行开始的指针,然后遍历它直到它结束。在矩阵以连续方式存储的特殊情况下,我们只需要请求一次指针,并一直走到最后。我们需要处理彩色图像:彩色图像有三个通道,所以我们需要在每一行中遍历三倍多的元素。

    还有另一种方法。Mat对象的data数据成员返回指向第一行第一列的指针。如果该指针为空,则该对象中没有有效的输入。检查这是检查图像加载是否成功的最简单的方法。如果存储是连续的,我们可以使用这个来遍历整个数据指针。在灰度图像的情况下,如:

        uchar* p = I.data;
        for( unsigned int i = 0; i < ncol*nrows; ++i)
            *p++ = table[*p];
    

    你会得到相同的结果。然而,这段代码在以后阅读起来会困难得多。如果你在循环中做了更复杂的操作,阅读就更困难了。而且,在实践中,您会得到相同的性能结果(因为大多数现代编译器可能会自动为您实现这个小的优化技巧)。

    2、安全的方式:迭代器
    如果采用指针的方法,则需要确保传递正确数量的uchar字段,并跳过行之间可能出现的间隙。迭代器方法被认为是一种更安全的方法,因为它代替用户进行了判断。您所需要做的就是找到图像矩阵的开始和结束迭代器,然后递增开始迭代器,直到结束。要获取迭代器所指向的值,请使用*操作符(在它前面添加该值)。

    Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
    {
        CV_Assert(I.depth() == CV_8U);
        const int channels = I.channels();
        // 灰度图和彩色图的迭代器类型不一样,需要不同处理
        switch(channels)
        {
        case 1:
            {
                MatIterator_<uchar> it, end;
                // 迭代器的遍历可读性更强
                for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
                    *it = table[*it];
                break;
            }
        case 3:
            {
                MatIterator_<Vec3b> it, end;
                for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
                {
                    (*it)[0] = table[(*it)[0]];
                    (*it)[1] = table[(*it)[1]];
                    (*it)[2] = table[(*it)[2]];
                }
            }
        }
        return I;
    }
    

    对于彩色图像,每个列有三个uchar元素。这可以被认为是uchar元素组成的一个短向量,在OpenCV中它被命名为Vec3b。为了访问第n个子列,我们使用简单的操作符[]访问。重要的是要记住,OpenCV迭代器遍历列并自动跳到下一行。因此,对于彩色图像,如果你使用简单的uchar迭代器,你就只能访问蓝色通道的值。

    3、动态地址计算:返回引用
    最后一种方法不推荐用于遍历图像像素。它是用来获取或修改图像中的随机元素的。它的基本用途是指定要访问的项的行号和列号。在前面两种遍历方法中,你可能已经注意到:我们正在处理的图像的类型是很重要的,这里则不需要区分,因为您需要手动指定在自动查找中使用的类型。你可以在以下处理灰度图像的源代码的观察到这一点(使用 cv::Mat::at() 函数):

    Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
    {
        CV_Assert(I.depth() == CV_8U);
        const int channels = I.channels();
        switch(channels)
        {
        case 1:
            {
                for( int i = 0; i < I.rows; ++i)
                    for( int j = 0; j < I.cols; ++j )
                        I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
                break;
            }
        case 3:
            {
             Mat_<Vec3b> _I = I;
             for( int i = 0; i < I.rows; ++i)
                for( int j = 0; j < I.cols; ++j )
                   {
                       _I(i,j)[0] = table[_I(i,j)[0]];
                       _I(i,j)[1] = table[_I(i,j)[1]];
                       _I(i,j)[2] = table[_I(i,j)[2]];
                }
             I = _I;
             break;
            }
        }
        return I;
    }
    

    该函数获取输入数据类型和坐标,并计算查询项的地址,然后返回对它的引用。在获取值时,这可能是一个常量,而在设置值时,这可能不是常量。作为仅在debug模式中的安全步骤,将执行一个检查,以确认您的输入坐标是有效的,并且确实存在。如果不是这种情况,您将在标准错误输出流上得到一个很好的输出消息。与release模式下的有效方法相比,使用这个方法的唯一区别是,对于图像的每个元素,都将获得一个新的行指针。

    如果您需要使用这种方法对一个图像进行多次查找,那么为每次访问输入数据类型和at关键字可能会很麻烦,而且很耗时。为了解决这个问题,OpenCV使用cv::Mat_ 数据类型,它与Mat相同,只是需要在定义时通过绑定数据矩阵来指定数据类型,但是作为回报,可以使用operator() 来快速访问元素。为了更便捷,这很容易转换为cv::Mat数据类型。您可以在上述函数的对彩色图像的处理中看到它的示例用法。然而要注意,同样的操作(具有相同的运行速度)也可以用cv::Mat::at函数来完成。

    五、使用查找表的方法

    这是在图像中实现查找表修改的附加方法。在图像处理中,将所有给定的图像值修改为其他值是很常见的。OpenCV提供了一个修改图像值的函数,不需要写图像的遍历逻辑。我们使用了core模块的 cv::LUT() 函数。首先,我们构建一个Mat类型的查找表:

        Mat lookUpTable(1, 256, CV_8U);
        uchar* p = lookUpTable.ptr();
        for( int i = 0; i < 256; ++i)
            p[i] = table[i];
    

    最后调用函数(I是我们的输入图像,J是输出图像):

        LUT(I, lookUpTable, J);
    

    六、性能分析

    为了获得最好的结果,请编译程序并自己运行它。为了使不同算法的性能差异扩大,我使用了一个相当大的(3840 X 2160)图像。这里展示的是彩色图像。为了获得更精确的值,我在函数调用中调用100次取平均值。

    算法算法耗时(ms)
    C指针3.27054
    MatIterator_5.09794
    Random Access5.4999
    LUT2.68983

    在这里插入图片描述

    作者使用的PC环境为WIN10 64位操作系统,CPU为2块Intel® Xeon® Silver 4114,OpenCV版本为OpenCV4.5。以上实验数据仅供参考,每次运行可能略微不同。

    七、完整代码

    #include<opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    #define DIVIDEWIDTH  10
    
    Mat scanImageAndReduceC(Mat& src, const uchar* const table);
    Mat scanImageAndReduceIterator(Mat src, const uchar* const table);
    Mat scanAndReduceImageRandomAccess(Mat& src, const uchar* const table);
    Mat scanAndReduceImageLUT(Mat& src, Mat& lookUpTable);
    
    int main(int argc, char** argv)
    {
    	cout << "----->\t\t\tStart scan and reduce image\t\t\t<-----" << endl << endl;
    
    	string fileName = "O:\\CSDN\\2.jpeg";
    	Mat src = imread(fileName, IMREAD_COLOR);
    	if (src.empty())
    	{
    		cout << "failed to load image[" << fileName << "],Please check out the path!" << endl;
    		system("pause");
    		return EXIT_FAILURE;
    	}
    
    	uchar table[256];
    	for (int i = 0; i < 256; ++i)
    		table[i] = (uchar)(DIVIDEWIDTH * (i / DIVIDEWIDTH));
    
    	Mat dst1, dst2, dst3, dst4;
    
    	cout << "Method No1: C point.\tDealing, please wait..." << endl;
    	double t1 = (double)getTickCount();
    	for (int i = 0; i < 100; ++i)
    	{
    		 dst1= scanImageAndReduceC(src, table);
    	}
    	double t1Average = ((double)getTickCount() - t1) / getTickFrequency();
    	cout << "The average cost time of Method No1 is: " << t1Average << "(ms)" << endl << endl;
    
    	cout << "Method No2: C++ MatIterator_.\tDealing, please wait..." << endl;
    	double t2 = (double)getTickCount();
    	for (int i = 0; i < 100; ++i)
    	{
    		dst2 = scanImageAndReduceIterator(src, table);
    	}
    	double t2Average = ((double)getTickCount() - t2) / getTickFrequency();
    	cout << "The average cost time of Method No2 is: " << t2Average << "(ms)" << endl << endl;
    
    	cout << "Method No3: Random Access.\tDealing, please wait..." << endl;
    	double t3 = (double)getTickCount();
    	for (int i = 0; i < 100; ++i)
    	{
    		dst3 = scanAndReduceImageRandomAccess(src, table);
    	}
    	double t3Average = ((double)getTickCount() - t3) / getTickFrequency();
    	cout << "The average cost time of Method No3 is: " << t3Average << "(ms)" << endl << endl;
    	
    	Mat lookUpTable(1, 256, CV_8U);
    	uchar* p = lookUpTable.ptr();
    	for (int i = 0; i < 256; ++i)
    		p[i] = table[i];
    
    	cout << "Method No4: LUT.\tDealing, please wait..." << endl;
    	double t4 = (double)getTickCount();
    	for (int i = 0; i < 100; ++i)
    	{
    		dst4 = scanAndReduceImageLUT(src, lookUpTable);
    	}
    	double t4Average = ((double)getTickCount() - t4) / getTickFrequency();
    	cout << "The average cost time of Method No4 is: " << t4Average << "(ms)" << endl << endl;
    
    	namedWindow("Original", WINDOW_NORMAL);
    	resizeWindow("Original", src.cols / 5, src.rows / 5);
    	imshow("Original", src);
    	namedWindow("C point", WINDOW_NORMAL);
    	resizeWindow("C point", src.cols / 5, src.rows / 5);
    	imshow("C point", dst1);
    	namedWindow("MatIterator_", WINDOW_NORMAL);
    	resizeWindow("MatIterator_", src.cols / 5, src.rows / 5);
    	imshow("MatIterator_", dst2);
    	namedWindow("Random access", WINDOW_NORMAL);
    	resizeWindow("Random access", src.cols / 5, src.rows / 5);
    	imshow("Random access", dst3);
    	namedWindow("LUT", WINDOW_NORMAL);
    	resizeWindow("LUT", src.cols / 5, src.rows / 5);
    	imshow("LUT", dst4);
    	waitKey(0);
    	destroyAllWindows();
    	
    	system("pause");
    	return EXIT_SUCCESS;
    }
    
    Mat scanImageAndReduceC(Mat& src, const uchar* const table)
    {
    	Mat I = src.clone();
    	CV_Assert(I.depth() == CV_8U);
    	int channels = I.channels();
    	int rows = I.rows;
    	int cols = I.cols * channels;
    	if (I.isContinuous())
    	{
    		cols *= rows;
    		rows = 1;
    	}
    	uchar* p;
    	for (int row = 0; row < rows; ++row)
    	{
    		p = I.ptr<uchar>(row);
    		for (int col = 0; col < cols; ++col)
    		{
    			p[col] = table[p[col]];
    		}
    	}
    	return I;
    }
    
    Mat scanImageAndReduceIterator(Mat src, const uchar* const table)
    {
    	Mat I = src.clone();
    	CV_Assert(I.depth() == CV_8U);
    	const int channels = I.channels();
    	switch (channels)
    	{
    	case 1:
    	{
    		MatIterator_<uchar>it, end;
    		for (it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
    		{
    			*it = table[*it];
    		}
    		break;
    	}
    	case 3:
    	{
    		MatIterator_<Vec3b>it, end;
    		for (it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
    		{
    			(*it)[0] = table[(*it)[0]];
    			(*it)[1] = table[(*it)[1]];
    			(*it)[2] = table[(*it)[2]];
    		}
    		break;
    	}
    	}
    	return I;
    }
    
    Mat scanAndReduceImageRandomAccess(Mat& src, const uchar* const table)
    {
    	Mat I = src.clone();
    	CV_Assert(I.depth() == CV_8U);
    	const int channels = I.channels();
    	switch (channels)
    	{
    	case 1:
    	{
    		for (int row = 0; row < I.rows; ++row)
    			for (int col = 0; col < I.cols; ++col)
    				I.at<uchar>(row, col) = table[I.at<uchar>(row, col)];
    		break;
    	}
    	case 3:
    	{
    		Mat_<Vec3b> _I = I;
    		for(int row=0;row<I.rows;++row)
    			for (int col = 0; col < I.cols; ++col)
    			{
    				_I(row, col)[0] = table[_I(row, col)[0]];
    				_I(row, col)[1] = table[_I(row, col)[1]];
    				_I(row, col)[2] = table[_I(row, col)[2]];
    			}
    		break;
    	}
    	}
    	return I;
    }
    
    Mat scanAndReduceImageLUT(Mat& src,Mat &lookUpTable)
    {
    	Mat I = src.clone();
    	CV_Assert(I.depth() == CV_8U);
    	LUT(I, lookUpTable, I);
    	return I;
    }
    

    八、致谢

    1、感谢自己坚持不懈地做好每件事
    2、感谢OpenCV官方团队作出地贡献
    3、感兴趣的小伙伴欢迎入群讨论学习。入群飞机票

    展开全文
  • C++/opencv遍历图像像素值,创建并绘制新的图像 代码的主要功能: 主要是给原图,图上一层颜色,不同的类别对应不同的颜色 ori_img: label_img: 通过读取 label_img生成彩色图: 和原图融合: 1、有 ori.jpg原图...

    C++/opencv遍历图像像素值,创建并绘制新的图像,并和原图融合

    代码的主要功能:
    主要是给原图,图上一层颜色,不同的类别对应不同的颜色
    ori_img:
    在这里插入图片描述
    label_img:
    在这里插入图片描述
    通过读取 label_img生成彩色图:
    在这里插入图片描述
    和原图融合:
    在这里插入图片描述
    1、有 ori.jpg原图 和 label.png对应的灰度图,其中灰度图中像素值从 0-9不等
    2、通过读取 label.png中的像素值,生成对应的彩色图
    3、将生成的彩色图和原图融合

    代码如下:
    1、单张处理程序

    #include <fstream>
    #include <sstream>
    #include <opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	Mat img_in = imread("C:/Users/xujun/Desktop/front_img/002_003003_102837851.png", 0); //灰度图,label图片
    	Mat img_ori = imread("C:/Users/xujun/Desktop/front_img/oriImg/002_003003_102837851.jpg"); //原图像
    	Mat img_out(img_in.rows, img_in.cols, CV_8UC3, Scalar(0, 0, 0));  //输出图形
    	 
    	vector<Vec3b> rgb_pixels;  //创建 rgb像素点列表
    	rgb_pixels = { 
    		{ 0, 0, 0 },
    		{ 165, 42, 42 },
    		{ 0, 192, 0 },
    		{ 255, 130, 71 },
    		{ 90, 120, 150 },
    		{ 102, 102, 156 },
    		{ 128, 64, 255 },
    		{ 255, 255, 0 },
    		{ 220, 20, 60 },
    		{ 244, 35, 232 }
    	};
    
    
    	for (int i = 0; i < img_in.rows; i++) //遍历label图像的每一个像素点,然后相应的生成一个新的彩色图
    	{
    		for (int j = 0; j < img_in.cols; j++)
    		{
    			if (img_in.at<uchar>(i, j) == 0)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[0];
    			if (img_in.at<uchar>(i, j) == 1)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[1];
    			if (img_in.at<uchar>(i, j) == 2)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[2];
    			if (img_in.at<uchar>(i, j) == 3)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[3];
    			if (img_in.at<uchar>(i, j) == 4)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[4];
    			if (img_in.at<uchar>(i, j) == 5)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[5];
    			if (img_in.at<uchar>(i, j) == 6)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[6];
    			if (img_in.at<uchar>(i, j) == 7)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[7];
    			if (img_in.at<uchar>(i, j) == 8)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[8];
    			if (img_in.at<uchar>(i, j) == 9)
    				img_out.at<Vec3b>(i, j) = rgb_pixels[9];
    			
    		}
    
    	}
    
    	Mat result;
    	addWeighted(img_out, 1, img_ori, 0.8, 0, result); //将生成的彩色图和原图融合,得到结果
    	namedWindow("result",0);
    	imshow("result",result);
    	waitKey(0);
    	//imwrite("C:/Users/xujun/Desktop/front_img/0002.png", result);
    
    	return 0;
    }
    
    

    2、多张处理程序

    #include <fstream>
    #include <sstream>
    #include <opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    
    
    //{ 0, 192, 0 },
    //{ 190, 153, 153 },
    //{ 90, 120, 150 },
    //{ 102, 102, 156 },
    //{ 128, 64, 255 },
    //{ 250, 170, 160 },
    //{ 150, 120, 90 },
    //{ 244, 35, 232 }
    int main()
    {
    	vector<Vec3b> rgb_pixels;  //创建 rgb像素点列表
    	rgb_pixels = { 
    		{ 0, 0, 0 },
    		{ 165, 42, 42 },
    		{ 0, 192, 0 },
    		{ 255, 130, 71 },
    		{ 90, 120, 150 },
    		{ 102, 102, 156 },
    		{ 128, 64, 255 },
    		{ 255, 255, 0 },
    		{ 220, 20, 60 },
    		{ 244, 35, 232 }
    	};
    	string path_label = "C:/Users/xujun/Desktop/front_img/label3";
    	string path_ori = "C:/Users/xujun/Desktop/front_img/oriImg";
    	string path_out = "C:/Users/xujun/Desktop/front_img/outpath";
    	vector<cv::String> filenames;
    	glob(path_label, filenames,false);
    	for (int i = 0; i < filenames.size(); i++)
    	{
    		cout << filenames[i] << endl;
    		int str_begin1 = filenames[i].find_last_of('\\');
    		int str_begin2 = filenames[i].find_last_of('.');
    		int str_end = filenames[i].size();
    		std::string filename_base = filenames[i].substr(str_begin1 + 1, str_begin2 - str_begin1 - 1);
    		string oripath = path_ori + "/" + filename_base + ".jpg";
    		string save_path = path_out + "/" + filename_base + ".jpg";
    
    		Mat img_in = imread(filenames[i], 0); //灰度图,label图片
    		Mat img_ori = imread(oripath); //原图像
    		Mat img_out(img_in.rows, img_in.cols, CV_8UC3, Scalar(0, 0, 0));  //输出图形
    
    		for (int i = 0; i < img_in.rows; i++) //遍历label图像的每一个像素点,然后相应的生成一个新的彩色图
    		{
    			for (int j = 0; j < img_in.cols; j++)
    			{
    				if (img_in.at<uchar>(i, j) == 0)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[0];
    				if (img_in.at<uchar>(i, j) == 1)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[1];
    				if (img_in.at<uchar>(i, j) == 2)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[2];
    				if (img_in.at<uchar>(i, j) == 3)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[3];
    				if (img_in.at<uchar>(i, j) == 4)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[4];
    				if (img_in.at<uchar>(i, j) == 5)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[5];
    				if (img_in.at<uchar>(i, j) == 6)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[6];
    				if (img_in.at<uchar>(i, j) == 7)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[7];
    				if (img_in.at<uchar>(i, j) == 8)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[8];
    				if (img_in.at<uchar>(i, j) == 9)
    					img_out.at<Vec3b>(i, j) = rgb_pixels[9];				
    			}
    		}
    		Mat result;
    		addWeighted(img_out, 1, img_ori, 0.7, 0, result); //将生成的彩色图和原图融合,得到结果
    		imwrite(save_path, result);
    	}
    
    	return 0;
    }
    
    
    展开全文
  • OpenCV遍历图像

    2020-10-11 20:48:31
    在图形处理中,遍历每个像素点是最基本的功能,是做算法的基础,这篇文章来总结一下OpenCV遍历图像的几种方法。

    OpenCV遍历图像

    在图形处理中,遍历每个像素点是最基本的功能,是做算法的基础,这篇文章来总结一下OpenCV遍历图像的几种方法。
    本文章参考文档OpenCV tutorials的how_to_scan_images.cpp例子。

    最有效率–指针

    用c语言直接访问是最有效率的,最快的,下面是简单的示例。

    int scan_image_c(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        for (int i = 0; i < I.rows; i++)
        {
            Vec3b *ptr = I.ptr<Vec3b>(i);
            for (int j = 0; j < I.cols; j++)
            {
                ptr[j][0] = 0;
                ptr[j][1] = 0;
                ptr[j][2] = 0;
            }
        }
        return 0;
    }
    

    最安全–迭代器

    迭代器是C++中的一个概念,因为迭代器从用户手中接管了一些工作,它会保证访问的安全,所以必然会导致一些性能上的降低,简单例子如下。

    int scan_image_iterator(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        MatIterator_<Vec3b> it, end;
        for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
        {
            (*it)[0] = 0;
            (*it)[1] = 0;
            (*it)[2] = 0;
        }
        return 0;
    }
    

    最便捷–at方法

    OpenCV的Mat类中有一个at方法,它可以直接返回某个像素点,示例如下。

    int scan_image_random(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        for( int i = 0; i < I.rows; ++i)
        {
            for( int j = 0; j < I.cols; ++j 
            {
                I.at<Vec3b>(i, j)[0] = 0;
                I.at<Vec3b>(i, j)[1] = 0;
                I.at<Vec3b>(i, j)[2] = 0;
            }
        }
    
        return 0;
    }
    

    完整例子

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    int scan_image_c(Mat &I);
    int scan_image_iterator(Mat &I);
    int scan_image_random(Mat &I);
    
    int main( int argc, char* argv[])
    {
        if (argc != 2)
        {
            cout << "input parameters failed!" << endl;
            return -1;
        }
    
        Mat I;
        I = imread(argv[1], IMREAD_COLOR);
    
        if (I.empty())
        {
            cout << "The image" << argv[1] << " could not be loaded." << endl;
            return -1;
        }
    
        const int times = 100;
        double t = 0;
    
        t = (double)getTickCount();
    
        for (int i = 0; i < times; ++i)
        {
            cv::Mat clone_i = I.clone();
            scan_image_c(clone_i);
        }
    
        t = 1000*((double)getTickCount() - t)/getTickFrequency();
        t /= times;
    
        cout << "Time of scan_image_c        (averaged for "
             << times << " runs): " << t << " ms."<< endl;
    
        t = (double)getTickCount();
    
        for (int i = 0; i < times; ++i)
        {
            cv::Mat clone_i = I.clone();
            scan_image_iterator(clone_i);
        }
    
        t = 1000*((double)getTickCount() - t)/getTickFrequency();
        t /= times;
    
        cout << "Time of scan_image_iterator (averaged for "
            << times << " runs): " << t << " ms."<< endl;
    
        t = (double)getTickCount();
    
        for (int i = 0; i < times; ++i)
        {
            cv::Mat clone_i = I.clone();
            scan_image_random(clone_i);
        }
    
        t = 1000*((double)getTickCount() - t)/getTickFrequency();
        t /= times;
    
        cout << "Time of scan_image_random   (averaged for "
            << times << " runs): " << t << " ms."<< endl;
    
        return 0;
    }
    
    int scan_image_c(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        for (int i = 0; i < I.rows; i++)
        {
            Vec3b *ptr = I.ptr<Vec3b>(i);
            for (int j = 0; j < I.cols; j++)
            {
                ptr[j][0] = 0;
                ptr[j][1] = 0;
                ptr[j][2] = 0;
            }
        }
        return 0;
    }
    
    int scan_image_iterator(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        MatIterator_<Vec3b> it, end;
        for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
        {
            (*it)[0] = 0;
            (*it)[1] = 0;
            (*it)[2] = 0;
        }
        return 0;
    }
    
    int scan_image_random(Mat &I)
    {
        int channels = I.channels();
        if (channels != 3)
        {
            printf("test support only three channel.\n");
            return -1;
        }
    
        for( int i = 0; i < I.rows; ++i)
        {
            for( int j = 0; j < I.cols; ++j 
            {
                I.at<Vec3b>(i, j)[0] = 0;
                I.at<Vec3b>(i, j)[1] = 0;
                I.at<Vec3b>(i, j)[2] = 0;
            }
        }
    
        return 0;
    }
    

    运行结果如下:

    Time of scan_image_c        (averaged for 100 runs): 2.04884 ms.
    Time of scan_image_iterator (averaged for 100 runs): 4.77701 ms.
    Time of scan_image_random   (averaged for 100 runs): 3.64237 ms.
    

    从数据上看,c语言的方法确实是最快的,和其他两种方式拉开了一定的差距。而at遍历比迭代器遍历快了不少。
    在平常使用中,我们可以根据每个方法的优点去选择不同的方法。

    与我联系

    QQ:1570553025
    公众号
    在这里插入图片描述

    展开全文
  • OpenCV遍历图片像素点

    千次阅读 2017-09-06 22:29:55
    之前一直用IplImage作为图像的载体,后来觉得Mat使用起来更直观明了,而且不用担心内存的释放问题。也看了许多前辈们关于两种载体的遍历,直接套用总是报错,修修改改之后,在这里做一个小总结。

    之前一直用IplImage作为图像的载体,后来觉得Mat使用起来更直观明了,而且不用担心内存的释放问题。也看了许多前辈们关于两种载体的遍历,直接套用总是报错,修修改改之后,在这里做一个小总结。

    IplImage是一个结构体,储存图像的部分是char* imageData,这个指针指向的是像素数组的首地址(也就是首个像素的地址)。而像素信息在这里是一个按序排列的一维数组。

    Mat是一个类,由两部分数据组成:矩阵头(矩阵的尺寸,储存方法,储存地址)和图像数据的指针。像素信息在这里是一个二维矩阵。

    首先先说说IplImage的遍历,示例如下:

    IplImage yuanshiPic=cvLoadImage("...");
    IplImage img=cvCreateImage(cvSize(yuanshiPic->width, yuanshiPic->height), IPL_DEPTH_8U, 1);//创建一个与yuanshiPic大小一致的IplImage
    cvCvtColor(yuanshiPic, img, CV_BGR2GRAY);//灰度化
    uchar*tmp;//用来临时存放每一个像素点的地址
    uchar*data = (uchar*)img->imageData;//获取图像数据的首地址
    int step = img->widthStep / sizeof(uchar);//每个像素点所占内存
    int height = img->height;
    int width = img->width;
    for (int j = 0; j < height; j++)
       for (int i = 0; i < width; i++) 
          tmp = data + j*step + i;
          if(*tmp>125)
             *tmp=255;
          else
             *tmp=0;

    再来说说Mat的遍历,示例如下:

    Mat yuanshiPic=imread("...");
    Mat img;
    cvtColor(yuanshiPic, img, CV_BGR2GRAY);//灰度化
    const int channels =img.channels();//检测一下通道数,灰度图为1,彩色图为3
    uchar *p;//用于存储每行的首地址
    int nRows = dst.rows;
    int nCols = dst.cols*channels;
    for (int j = 0; j < nRows ; j++)
       for (int i = 0; i < nCols ; i++)
          p = img.ptr<uchar>(i);
          if(p[j]>125)
            p[j]=255;
          else
            p[j]=0;  

    只写了两种方式,因为自己用的就是这两种,总结完毕,撒花。

    展开全文
  • 减少图像中颜色数目 #include #include using namespace cv; void colorReduce(cv::Mat &image, int div=64) { int n1 = image.rows;//行数 //每行的元素个数 int nc = image.cols*image.channels(); for...
  • OpenCV遍历图像性能比较、利用查找表
  • OpenCV学习笔记之遍历图像像素点

    千次阅读 2018-10-22 18:53:15
    通过行地址来逐行遍历像素点的值。   代码: #include&lt;opencv2/opencv.hpp&gt; #include&lt;iostream&gt; using namespace cv; using namespace std; int main() { //读取单通道图像 Mat...
  • opencv逐个遍历图像像素并进行修改

    千次阅读 2018-08-25 16:08:02
    //访问像素点 //cout [0] [1] [2]; pixel.val[ 0 ] = 255 - pixel.val[ 0 ]; // B分量 pixel.val[ 1 ] = 255 - pixel.val[ 1 ]; // G分量 pixel.val[ 2 ] = 255 - pixel.val[ 2 ]; // R分量 ...
  • 例子:在图像中加入白色椒盐噪声二、遍历图像像素1.指针扫描2.迭代器扫描总结 前言 一、访问图像像素 1.访问(j,i)处像素 以8位(0~255)灰度图像和BGR彩色图像为例,用at可以访问图像像素: //灰度图像: image....
  • 颜色压缩(Color Reduction)最简单的理解就是减少表示图像的颜色数目,我们都知道,8位位深的3通道RGB真彩图像包括了1600多万(16777216)的颜色数目,其实在某些应用中用不到这么多数量(例如图像传输...
  • 我们在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不是说低效的方法就不好,不同场景使用不同方法。 方法 下面将一一...
  • 采用at函数获取像素值 for (int i = 0; i ; i++) { for (int j = 0; j ; j++) { img.at(i, j) = 255 - img.at(i, j); } } 方式2.采用.data指针获取像素 uchar * pImg = img.data; for (int i
  • opencv遍历像素输出像素

    千次阅读 2019-05-25 14:28:18
    #include<...opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; void p...
  • opencv遍历像素的方式

    2019-07-20 13:50:46
    OpenCV优化:图像的遍历4种方式 ...我们在实际应用中对图像进行的操作,往往并不是将图像作为一个整体进行操作,而是对图像中的所有或特殊进行运算,所以遍历图像就显得很重要,如何高效的遍历图像是一个很值...
  • 全部像素取反,实现一个反向效果 cv.imshow( " fanxiang " , frame) image = " D:/Image/test.jpg " src = cv.imread(image) cv.imshow( " Picture " , src) access_pixels(src) cv.waitKey(0) ...
  • 在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不同场景使用不同方法。 其中: Mat_对应的是CV_8U,Mat_对应的是CV_8S,Mat...
  • 由于图片尺寸过大,对图像进行resize处理用到的各种差值方法比较耗费时间,于是手动将图片缩小,每8*8个像素取一个组成新的图片。 主要考虑两种方式,第一种at方法取图像中的的用法:image.at(i,j):取出灰度...
  • 对于uint8类型3通道图像,不论是BGR还是HSV,这种图像在内存的排序方式就是(BGR为蓝绿红): BGRBGRBGR..... BGRBGRBGR..... BGRBGRBGR..... ............................... 对于uint8类型的单通道灰度图,排序...
  • opencv IplImage* 遍历图像像素的值

    千次阅读 2018-08-10 16:57:45
    转...  opencv2.1版本之前使用的IplImage *数据结构来表示图像,2.1之后的版本使用图像容器垫来存储.IplImage结构体如下所示。 1 typedef struct _IplImage 2 { 3 int nSize; ...
  • 方法一 使用at<Vec3b>... //读入彩色图像 Mat img = imread("fruits.jpg"); imshow("原图", img); int rows = img.rows; int cols = img.cols; //生成和img同样大小的空白灰度图像 Mat ...
  • 关于opencv遍历像素速度的提高方案

    千次阅读 2018-01-20 00:15:24
    1、 二值化算法经过测试后基本可以,但是在运动中...我在网上查询到可以用opencv 的LUT查表法来简化遍历像素的时间,这个可以比指针快,后来经过阅读了一定网上的资料,才发现利用LUT必须知道原图像与目标图像像素的对
  • 首先遍历图片的所有像素点,利用图片色彩的BGR,来改变图片的色彩,达到改变图像素的目的! 具体代码如下: import cv2 as cv #导入openc包 import numpy as np #科学计数的包 def access_pixels(image): #获取...
  • opencvSharp 遍历Mat像素点的两种方法

    千次阅读 2018-05-06 15:26:37
    在生成中勾选使用不安全的代码,方法和C++完全一样,详情见C++中遍历像素点。 public unsafe static Mat ConvolutionImage(Mat img, double[,] k, int offsetH, int offsetW, int height, int width) { Mat...
  • 分享给java的同学工作需要对图片中的印章进行提取,这就用到了opencv像素遍历不用多废话,直接/** * 遍历图片中全部红色部分 * @author Administrator * */public class Test3 { public static void m...
  • OpenCV图像遍历像素操作

    万次阅读 2016-04-26 22:55:34
    OpenCV中表示图像遍历一般有三种方式,即数组遍历、指针遍历、迭代器遍历
  • opencv遍历图像每个像素点

    千次阅读 2012-06-26 16:08:58
    uchar *image_data = (uchar *)gray_plane->imageData; for (int i=0;iheight;i++) { for (int j=0;jwidth;j++) { value = image_data[gray_plane->widthStep*i+j]; } }
  • //遍历像素点 for (int i = 0; i ; ++i) { for (int j = 0; j ; ++j) { if (img.at(i, j)[2]>180) { img.at(i, j) = Vec3b(255, 255, 255); } } } //创建一个名字为MyWindow的窗口 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,939
精华内容 2,775
关键字:

opencv遍历图像像素点