C++ opencv Image visual enhancement

一世执手 提交于 2020-08-13 23:17:12

今天,抽短暂时间实现了一个新的功能,那就是16位图像的读取以及伪彩色视觉效果增强。个人感觉各种语言还是C++好用,PY虽然很火,但是项目中执行效率是真的难受。

  • --->Today, a new feature has been briefly implemented, which is the ability to read 16-bit images and enhance false-color visuals.I personally feel that various languages are still good for C++,python is very popular, but the execution efficiency in the project is really uncomfortable.

如果你真的了解opencv等,你会发现,opencv默认读取的是8位图像数据,如果需要读取16位深度的图像,你需要进行相应的处理。你会奇怪位深度对于一个图像有什么用,位深度用于指定图像中的每个像素可以使用的颜色信息数量。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就更逼真。例如,位深度为 1 的图像的像素有两个可能的值:黑色和白色。位深度为 8 的图像有 28(即 256)个可能的值。位深度为 8 的灰度模式图像有 256 个可能的灰色值。RGB 图像由三个颜色通道组成。8 位/像素的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道 (bpc) 的 RGB 图像称作 24 位图像(8 位 x 3 通道 = 24 位数据/像素)。除了 8 位/通道的图像之外,Photoshop 还可以处理包含 16 位/通道或 32 位/通道的图像。包含 32 位/通道的图像也称作高动态范围 (HDR) 图像。

  • --->If you really understand opencv and so on, you will find that opencv reads 8-bit image data by default, and if you need to read 16-bit depth image, you need to process accordingly.You wonder what bit depth is good for an image. Bit depth is used to specify the amount of color information that each pixel in the image can use.The more bits of information each pixel USES, the more colors are available and the more realistic the color presentation.For example, the pixels of an image with a bit depth of 1 have two possible values: black and white.An image with a bit depth of 8 has 28(256) possible values.A grayscale image with a bit depth of 8 has 256 possible gray values.The RGB image consists of three color channels.Each channel in an 8-bit/pixel RGB image has 256 possible values, which means that the image has more than 16 million possible color values.RGB images with 8 bit/channel (BPC) are sometimes referred to as 24 bit images (8 bit x 3 channel = 24 bit data/pixel).In addition to 8-bit/channel images, Photoshop can also process images containing 16-bit/channel or 32-bit/channel.Images containing 32 bits/channels are also known as high dynamic range (HDR) images.

灰度是描述灰度图像内容的最直接的视觉特征。它指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0,故黑白图像也称灰度图像。灰度图像矩阵元素的取值通常为[0,255],因此其数据类型一般为8位无符号整数,这就是人们通常所说的256级灰度。将灰度图像转换为彩色图像,称为灰度图像的伪彩色处理。伪彩色处理技术的实现方式有很多,如:灰度分割法、灰度级-彩色变换法、滤波法等等。以下采用的是灰度级-彩色变换法,这是将来自传感器的灰度图像送入三个不同特征的R、G、B变换器,然后将三种变换器的不同输出分别送到彩色显示器进行显示的技术。

  • ---->Grayscale is the most direct visual feature to describe the content of grayscale image.It refers to the color depth of the point in the black and white image, ranging from 0 to 255, white is 255, black is 0, so the black and white image is also called grayscale image.The value of the gray-scale image matrix element is usually [0,255], so its data type is generally an 8-bit unsigned integer, which is commonly referred to as the 256-level gray-scale.The transformation of grayscale image into color image is called pseudo-color processing of grayscale image.There are many ways to realize the pseudo-color processing technology, such as: gray segmentation method, gray grade-color transformation method, filtering method and so on.The following is the graygrade-color transformation method, which is used to feed the grayscale image from the sensor into the R, G, and B converters with three different features, and then send the different outputs of the three converters to the color display for display.

你能在图像属性查看位深--->

  • ---->You can see the bit depth in the image properties -->

 了解这些之后,你可以进行opencv加载16位图像数据,其实,准确来说,不管以哪一种形式进行加载,对于人的视觉来说差距不是特别大,对于windows来说会进行优化,最后都转换为8位进行显示,参考我的代码,你能实现16位图像的读取。

  • ---->After understanding these, you can use opencv to load 16-bit image data, in fact, precisely, no matter in what kind of form to load, for human visual difference is not particularly big, for Windows will be optimized, finally converted to 8 bits for display, refer to my code, you can read the 16-bit image.
Mat img = imread("./1.tif", cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH);
//-------------------------------------------------------------
cout << "源文件位深度:"<<img.depth() << endl;
//-------------------------------------------------------------
//-------------------------------------------------------------
Mat image(img.rows,img.cols,CV_16U);
for (int i = 0;i < image.rows;i++)
{
	for (int j = 0;j < image.cols;j++)
	{
		image.at<short>(i, j) = img.at<short>(i, j);
		//注意这里.at<>尖括号里的类型是16位图对应的short类型不是8位图对应的uchar类型
	}
}
//------------------------------------------------

这样的话输出的源文件的位深度就是16位,如果直接进行读取,获取到的就是8位深度。

  • ---->In this case, the bit depth of the output source file is 16 bit, and if it is read directly, it will get 8 bit.

接下来定义三个单通道RGB,定义为16位深度。参考我下面的代码:

  • ---->Then, i define three single-channel RGB, a 16-bit depth,channel.Refer to my code below:
//------------------------------------------------
Mat R(image.rows,image.cols,CV_16U);
Mat G(image.rows,image.cols,CV_16U);
Mat B(image.rows,image.cols,CV_16U);
R = image.clone();
G = image.clone();
B = image.clone();
cout << "R文件位深度:"<< R.depth() << endl;
cout << "G文件位深度:"<< G.depth() << endl;
cout << "B文件位深度:"<< B.depth() << endl;
//------------------------------------------------

那么,如何将16位数据进行伪彩色的转换呢,这是个问题,值得思考。 

映射关系如下,其中R(x,y)、G(x,y)、B(x,y)分别表示R、G、B通道的颜色值,f(x,y)表示特定点灰度图像的灰度值,f是所选灰度图像的灰度值。但是,你得明白,这里的关系是8位图对应的转换。

  • ---->So, how to convert 16-bit data to pseudo-color is a problem worth thinking about.
  • The mapping relationship is as follows, where R(x,y), G(x,y) and B(x,y) represent the color values of channels R, G and B, respectively; f(x,y) represents the gray value of the grayscale image of a specific point; f is the gray value of the selected gray image.However, you have to understand that the relationship here is the transformation of 8 bitmaps.

 我进行了简单的对应处理,这个很简单的,就是一个线性对应而已,但是你必须得知道,这个是不好的,但是是可以这么做的。

---->I did a simple correspondence, this is a very simple one, it's just a linear correspondence, but you have to know, this is bad, but you can do it this way.

int rows = image.rows;
int cols = image.cols;
for (int i = 0; i < rows; i++)
{
	for (int j = 0; j < cols; j++)
	{
		int current = image.at<short>(i, j);//uchar 8位
		if ( current  >= 0 && current <= 16448)
		{
			R.at<short>(i, j) = 0;
			G.at<short>(i, j) = 4 * current;
			B.at<short>(i, j) = 65535;
		}
		else if (16448 < current && current <= 32896)
		{
			R.at<short>(i, j) = 0;
			G.at<short>(i, j) = 65535;
			B.at<short>(i, j) = (-4)*(current-32896);
		}
		else if (32896 < current && current <= 49344)
		{
			R.at<short>(i, j) = 4 * (current - 32896);
			G.at<short>(i, j) = 65535;
			B.at<short>(i, j) = 0;
		}
		else
		{
			R.at<short>(i, j) = 65535;
			G.at<short>(i, j) = (-4)*(current - 65535);
			B.at<short>(i, j) = 0;
		}

	}
}

最后,就是三个通道的合并,使用函数merge()。

merge()函数:split()函数的逆向操作——将多个数组合并成一个多通道数组,将给定的这些孤立的单通道数组合并成一个多通道数组,从而创建一个由多个单通道阵列组成的多通道阵列。

原型:<1>C++: void merge(const Mat* mv, size_tcount, OutputArray dst);
          <2>C++: void merge(InputArrayofArrays mv,OutputArray dst);

 变量介绍如下:
        第一个参数:mv,填需要被合并的输入矩阵 或 vector容器的阵列;
        第二个参数:count,当mv为一空白C数组时,代表输入矩阵的个数,显然必须大于1;
        第三个参数:dst,即输出矩阵,和mv[0]具有相同的尺寸和深度,并且通道的数量是矩阵阵列中通道的总数。


  • ---->Finally, it's a merge of three channels, using the function merge().

    Merge () function: the reverse of the split() function, which merges multiple arrays into a multichannel array, merges a given set of isolated single-channel arrays into a multichannel array to create a multichannel array consisting of multiple single-channel arrays.

    Prototype: <1>C++: void merge(const Mat* mv, size_tcount, OutputArray dst);
                     <2>C++: void merge(InputArrayofArrays mv,OutputArray dst);

Variables are introduced as follows:

(1)The first parameter: mv, filling the array of input matrix or vector containers that need to be merged;

        (2)The second parameter: count, when mv is a blank C array, represents the number of input matrix, which must be greater than 1 obviously;

       (3)The third parameter: DST, the output matrix, has the same size and depth as mv[0], and the number of channels is the total number of channels in the matrix array.

Mat imgArray[3];
imgArray[0] = R;
imgArray[1] = G;
imgArray[2] = B;
Mat newImg;
merge(imgArray, 3, newImg);
cout << "out文件位深度:"<< newImg.depth() << endl;
imwrite("./16_24output.tif",newImg);

结果:

  • ---->result:

 my main code:

//---------------------------------------------------------------------
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    /*
    例如:一幅画的尺寸是1024*768,深度为16,则它的数据量为1.5M。
    计算如下:102476816bit=(102476816)/8字节=[(102476816)/8]/1024KB={[(102476816)/8]/1024}/1024MB。

    深度组合
    输入深度(src.depth()) 输出深度(ddepth)
    CV_8U -1 / CV_16S / CV_32F / CV_64F
    CV_16U / CV_16S -1 / CV_32F / CV_64F
    CV_32F -1 / CV_32F / CV_64F
    CV_64F -1 / CV_64F
    注意
    当ddepth = -1时,输出图像将具有与源相同的深度。
    */
    /*depth 图像元素的位深度,可以是下面的其中之一:
                 位深度                                      取值范围
    IPL_DEPTH_8U - 无符号8位整型                               0--255
    IPL_DEPTH_8S - 有符号8位整型                             -128--127
    IPL_DEPTH_16U - 无符号16位整型                             0--65535
    IPL_DEPTH_16S - 有符号16位整型                       -32768--32767
    IPL_DEPTH_32S - 有符号32位整型                              0--65535
    IPL_DEPTH_32F - 单精度浮点数                               0.0--1.0
    IPL_DEPTH_64F - 双精度浮点数                                0.0--1.0
    */
    /*
    Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,
    Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,
    Mat_<double>对应的是CV_64F,对应的数据深度如下:
    • CV_8U - 8-bit unsigned integers ( 0..255 )
    • CV_8S - 8-bit signed integers ( -128..127 )
    • CV_16U - 16-bit unsigned integers ( 0..65535 )
    • CV_16S - 16-bit signed integers ( -32768..32767 )
    • CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
    • CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
    • CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
    */
    /*
        #define CV_8U   0
        #define CV_8S   1
        #define CV_16U  2
        #define CV_16S  3
        #define CV_32S  4
        #define CV_32F  5
        #define CV_64F  6
        #define CV_USRTYPE1 7
    */
    /*
   merge()函数:split()函数的逆向操作——将多个数组合并成一个多通道数组,将给定的这些孤立的单通道数组合并成一个多通道数组,从而创建一个由多个单通道阵列组成的多通道阵列。
     原型:<1>C++: void merge(const Mat* mv, size_tcount, OutputArray dst);
          <2>C++: void merge(InputArrayofArrays mv,OutputArray dst);
    变量介绍如下:
        第一个参数:mv,填需要被合并的输入矩阵 或 vector容器的阵列;
        第二个参数:count,当mv为一空白C数组时,代表输入矩阵的个数,显然必须大于1;
        第三个参数:dst,即输出矩阵,和mv[0]具有相同的尺寸和深度,并且通道的数量是矩阵阵列中通道的总数。
    */
    //===================================================================================================
    //===================================================================================================
    //_8bit_24bit();
    //===================================================================================================
    //===================================================================================================
    _16bit_48bit();
    return a.exec();
}

 ​​​I hope I can help you,If you have any questions, please  comment on this blog or send me a private message. I will reply in my free time.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!