How to get OpenCV mat unique value quickly

According to stackflow’s answer: Obtaining list of unique pixel values in OpenCV Mat, we can get the unique value list of OpeCV Mat, but the speed is too slow. Is there a faster way?

Thank you

please clarify. why do you want to do this?

use a std::set or a std::map to collect pixel values, not vector

(so you dont need to iterate in the most inner loop)

1 Like

Yes! Using std::set really speeds things up

std::vector<float> unique1(const cv::Mat& input, bool sort = false)
{
    if (input.channels() > 1 || input.type() != CV_8U) 
    {
        std::cerr << "unique !!! Only works with CV_8U 1-channel Mat" << std::endl;
        return std::vector<float>();
    }

    std::vector<float> out;
    for (int y = 0; y < input.rows; ++y)
    {
        const float* row_ptr = input.ptr<float>(y);
        for (int x = 0; x < input.cols; ++x)
        {
            float value = row_ptr[x];

            if ( std::find(out.begin(), out.end(), value) == out.end() )
                out.push_back(value);
        }
    }

    if (sort)
        std::sort(out.begin(), out.end());

    return out;
}

to

std::set<int> Unique2(const cv::Mat& input)
{

	if (input.channels() > 1 || input.type() != CV_8U)
	{
		std::cerr << "unique !!! Only works with CV_8U 1-channel Mat" << std::endl;
		return std::set<int>();
	}

	std::set<int> out;
	for (int y = 0; y < input.rows; y++)
	{
		for (int x = 0; x < input.cols; x++)
		{
			int value = input.at<uchar>(y, x);
			std::set<int>::iterator iter;
			if ((iter = out.find(value)) == out.end())
			{
				out.insert(value);
			}
		}
	}

	return out;
}

Test code:

#include <vector>
#include "opencv2/opencv.hpp"

int main()
{
     cv::Mat1b gt(6087, 18744);
     cv::randu(gt, 0, 2); 
    
     std::vector<int> vUnique = unique1(gt, true);
     std::set<int> setUnique = unique2(gt);

     return 0;
}

1 Like