OpenCV - gray image coloring based on custom color bar

Author: Zhai Tianbao Steven
Copyright notice: the copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source

Scenario requirements

        Qt has a convenient color bar interface for 2D image display, which allows the gray image to be colored based on its designed color bar. For example, if 1 is set as red, 0.55 as yellow, 0.45 as green and 0 as blue, the gray image will be colored gradually from blue to red (small to large) after normalization. However, sometimes the interface needs too much code to match, which brings some trouble to the development. Therefore, based on its principle, I wrote a function graytocolor that can replace this function_ ColorBar.

        Function principle: firstly, the gray value image needs to be transformed into 8-channel (uchar) gray image of 0-255, which can be realized by normalization function; Then, considering the relationship between color and gray, for example, the lowest color is blue (0,0255) corresponding to gray value 0, and the highest color is red (255,0,0) corresponding to gray value 255. Just find out the law of its change.

        The following is the specific implementation function and test code.

Function code

/**
 * @brief GrayToColor_ColorBar             Use the color bar grayscale image to color (1: red, param1: yellow, param2: green, 0: blue)
 * @param phase                            Gray image input, channel 1
 * @param param1                           Color bar parameter 1
 * @param param2                           Color bar parameter 2
 * @return                                 Colored image
 */
cv::Mat GrayToColor_ColorBar(cv::Mat &phase, float param1, float param2)
{
	CV_Assert(phase.channels() == 1);

    // Color bar parameter 1 must be greater than color bar parameter 2
	if (param2 >= param1)
	{
		return cv::Mat::zeros(10, 10, CV_8UC1);
	}

	cv::Mat temp, result, mask;
	// Normalize the grayscale image to 0-255
	cv::normalize(phase, temp, 255, 0, cv::NORM_MINMAX);
	temp.convertTo(temp, CV_8UC1);
	// The mask is created to isolate the interference of nan value
	mask = cv::Mat::zeros(phase.size(), CV_8UC1);
	mask.setTo(255, phase == phase);

	// Initialize three channel color map
	cv::Mat color1, color2, color3;
	color1 = cv::Mat::zeros(temp.size(), temp.type());
	color2 = cv::Mat::zeros(temp.size(), temp.type());
	color3 = cv::Mat::zeros(temp.size(), temp.type());
	int row = phase.rows;
	int col = phase.cols;

	// Based on the gray level of the gray image, color it. The lowest gray value 0 is blue (255, 0,0), the highest gray value 255 is red (0,0255), and the middle gray value 127 is green (0255,0)
	// Don't be surprised why blue is (255,0,0), because BGR is not RGB in OpenCV
	for (int i = 0; i < row; ++i)
	{
		uchar *c1 = color1.ptr<uchar>(i);
		uchar *c2 = color2.ptr<uchar>(i);
		uchar *c3 = color3.ptr<uchar>(i);
		uchar *r = temp.ptr<uchar>(i);
		uchar *m = mask.ptr<uchar>(i);
		for (int j = 0; j < col; ++j)
		{
			if (m[j] == 255)
			{
				if (r[j] > (param1 * 255) && r[j] <= 255)
				{
					c1[j] = 255;
					c2[j] = uchar((1 / (1 - param1)) * (255 - r[j]));
					c3[j] = 0;
				}
				else if (r[j] <= (param1 * 255) && r[j] > (param2 * 255))
				{
					c1[j] = uchar((1 / (param1 - param2)) * r[j] - (param2 / (param1 - param2)) * 255);
					c2[j] = 255;
					c3[j] = 0;
				}
				else if (r[j] <= (param2 * 255) && r[j] >= 0)
				{
					c1[j] = 0;
					c2[j] = uchar((1 / param2) * r[j]);
					c3[j] = uchar(255 - (1 / param2) * r[j]);
				}
				else {
					c1[j] = 0;
					c2[j] = 0;
					c3[j] = 0;
				}
			}
		}
	}

	// The three channels are combined to obtain the color map
	vector<cv::Mat> images;
	images.push_back(color3);
	images.push_back(color2);
	images.push_back(color1);
	cv::merge(images, result);

	return result;
}

C + + test code

#include<iostream>
#include<opencv2/opencv.hpp>
#include<ctime>
using namespace std;
using namespace cv;
void UnitPolar(int squaresize, cv::Mat& mag, cv::Mat& ang);
void UnitCart(int squaresize, cv::Mat& x, cv::Mat& y);
cv::Mat GrayToColor_ColorBar(cv::Mat &phase, float param1, float param2);

int main(void)
{
	cv::Mat mag, ang, result, result3;
	UnitPolar(2001, mag, ang);
	mag.at<float>(10, 10) = nan("");

	clock_t start, end;
	start = clock();
	result = GrayToColor_ColorBar(mag,0.5,0.3);
	end = clock();
	double diff = end - start;
	cout << "time:" << diff / CLOCKS_PER_SEC << endl;

	system("pause");
	return 0;
}
void UnitPolar(int squaresize, cv::Mat& mag, cv::Mat& ang) {
	cv::Mat x;
	cv::Mat y;
	UnitCart(squaresize, x, y);                //Generate the specified number of points within the specified range, and the adjacent data span is the same
	// The built-in conversion of OpenCV has precision limitations, resulting in certain differences in results
	//cv::cartToPolar(x, y, mag, ang, false); // coordinate transformation 

	mag = cv::Mat(x.size(), x.type());
	ang = cv::Mat(x.size(), x.type());
	int row = mag.rows;
	int col = mag.cols;
	float *m, *a, *xx, *yy;
	for (int i = 0; i < row; ++i)
	{
		m = mag.ptr<float>(i);
		a = ang.ptr<float>(i);
		xx = x.ptr<float>(i);
		yy = y.ptr<float>(i);
		for (int j = 0; j < col; ++j)
		{
			m[j] = sqrt(xx[j] * xx[j] + yy[j] * yy[j]);
			a[j] = atan2(yy[j], xx[j]);
		}
	}
}

void UnitCart(int squaresize, cv::Mat& x, cv::Mat& y) {
	CV_Assert(squaresize % 2 == 1);
	x.create(squaresize, squaresize, CV_32FC1);
	y.create(squaresize, squaresize, CV_32FC1);
	//Set boundary
	x.col(0).setTo(-1.0);
	x.col(squaresize - 1).setTo(1.0f);
	y.row(0).setTo(1.0);
	y.row(squaresize - 1).setTo(-1.0f);

	float delta = 2.0f / (squaresize - 1.0f);  //Interval between two elements

	//Calculate values for other locations
	for (int i = 1; i < squaresize - 1; ++i) {
		x.col(i) = -1.0f + i * delta;
		y.row(i) = 1.0f - i * delta;
	}
}

/**
 * @brief GrayToColor_ColorBar             Use the color bar grayscale image to color (1: red, param1: yellow, param2: green, 0: blue)
 * @param phase                            Gray image input, channel 1
 * @param param1                           Color bar parameter 1
 * @param param2                           Color bar parameter 2
 * @return                                 Colored image
 */
cv::Mat GrayToColor_ColorBar(cv::Mat &phase, float param1, float param2)
{
	CV_Assert(phase.channels() == 1);
	// Color bar parameter 1 must be greater than color bar parameter 2
	if (param2 >= param1)
	{
		return cv::Mat::zeros(10, 10, CV_8UC1);
	}
	cv::Mat temp, result, mask;
	// Normalize the grayscale image to 0-255
	cv::normalize(phase, temp, 255, 0, cv::NORM_MINMAX);
	temp.convertTo(temp, CV_8UC1);
	// The mask is created to isolate the interference of nan value
	mask = cv::Mat::zeros(phase.size(), CV_8UC1);
	mask.setTo(255, phase == phase);

	// Initialize three channel color map
	cv::Mat color1, color2, color3;
	color1 = cv::Mat::zeros(temp.size(), temp.type());
	color2 = cv::Mat::zeros(temp.size(), temp.type());
	color3 = cv::Mat::zeros(temp.size(), temp.type());
	int row = phase.rows;
	int col = phase.cols;

	// Based on the gray level of the gray image, color it. The lowest gray value 0 is blue (255, 0,0), the highest gray value 255 is red (0,0255), and the middle gray value 127 is green (0255,0)
	// Don't be surprised why blue is (255,0,0), because BGR is not RGB in OpenCV
	for (int i = 0; i < row; ++i)
	{
		uchar *c1 = color1.ptr<uchar>(i);
		uchar *c2 = color2.ptr<uchar>(i);
		uchar *c3 = color3.ptr<uchar>(i);
		uchar *r = temp.ptr<uchar>(i);
		uchar *m = mask.ptr<uchar>(i);
		for (int j = 0; j < col; ++j)
		{
			if (m[j] == 255)
			{
				if (r[j] > (param1 * 255) && r[j] <= 255)
				{
					c1[j] = 255;
					c2[j] = uchar((1 / (1 - param1)) * (255 - r[j]));
					c3[j] = 0;
				}
				else if (r[j] <= (param1 * 255) && r[j] > (param2 * 255))
				{
					c1[j] = uchar((1 / (param1 - param2)) * r[j] - (param2 / (param1 - param2)) * 255);
					c2[j] = 255;
					c3[j] = 0;
				}
				else if (r[j] <= (param2 * 255) && r[j] >= 0)
				{
					c1[j] = 0;
					c2[j] = uchar((1 / param2) * r[j]);
					c3[j] = uchar(255 - (1 / param2) * r[j]);
				}
				else {
					c1[j] = 0;
					c2[j] = 0;
					c3[j] = 0;
				}
			}
		}
	}

	// The three channels are combined to obtain the color map
	vector<cv::Mat> images;
	images.push_back(color3);
	images.push_back(color2);
	images.push_back(color1);
	cv::merge(images, result);

	return result;
}

Test effect

Figure 1   Grayscale image
Figure 2   Effect picture 1
Figure 3   Effect drawing 2

        As shown in the above figure, for convenience, I generated a 2001 * 2001 image matrix. Figure 1 is a gray image, figure 2 and figure 3 are color images after color processing, which meet the requirements mentioned above. The corresponding parameters of the two renderings are different.

        If the function can be improved, you are very welcome to point out that why not make progress together~

        If the article helps you, you can point a praise to let me know, I will be very happy ~ come on!

Tags: OpenCV Computer Vision image processing

Posted on Tue, 23 Nov 2021 03:57:27 -0500 by Hatch