访问cv::Mat中的数据时遇到的指针类型问题
在用Opencv的时候由于下图原本的图像尺寸是1111*1111,要进行resize,代码如下:
cv::Mat img = cv::imread("//Users//apple//td3//vase//19201.png",CV_LOAD_IMAGE_GRAYSCALE); cv::Mat img2; cv::resize(img, img2, cv::Size(400,400),0,0, cv::INTER_AREA);
因为我根本不知道img的数据是什么类型(不知道数据类型根本无法通过指针计算来访问每个像素的值,而且我想操作逐个像素来做blur),然后我就用int指针去访问,结果自然是很容易错误的。我将访问的值写入文本之后发现都是些奇怪的数值,例如-1,4294967296(2^32),还以其他奇葩数值。结果如果我利用其opencv自己重载的函数输出:
std::cout << img2.at<int>(0,0); //输出(0,0)的像素值为-1 std::cout << img2; //输出矩阵的所有像素的像素值,结果很正常属于[0,255]
结果我以为矛盾,然后直接访问img2.data成员,写了一些很没道理的代码:
for(int row = 0; row <img2.rows; row++){ uchar *ptr = (img2.data + row*img2.step); for(int col = 0; col < img2.cols; col++){ int *intTmp = (int*)(ptr+col); //结果输出自然是错的,因为img2的数据类型是uchar,一个像素用8位表示 std::cout << *intTmp << ' '; //而强制转换成int*后访问的时候就直接取出32位 } std::cout << std::endl; }
然后设置断点查看了下img2.data里面的数据是'\xff',然后用cout打印出来是'\377',原来是字符串的十六进制和八进制表示,仔细分析为什么'\377'的值是-1,结果得出:
在C中有两种特殊的字符,八进制转义字符和十六进制转义字符,八进制字符的一般形式是'\ddd',d是0-9的数字。十六进制字符的一般形式是'\xhh',h是0-9或A-F内的一个。八进制字符和十六进制字符表示的是字符的ASCII码对应的数值。比如
'\063'表示的是字符'3',因为'3'的ASCII码是30(十六进制),48(十进制),63(八进制)。
'\x41'表示的是字符'A',因为'A'的ASCII码是41(十六进制),65(十进制),101(八进制)。
负数在计算机内部是用补码表示的 例如 -1 1 的原码是 0000 0001 则 -1 的反码是 1111 1110 补码是 1111 1111
因为我强制转换为int指针,所以是指针指向的数是有符号的,并且一次性取得32位,将这4个'\xff'表示为二进制:1111 1111 1111 1111 1111 1111 1111 1111(32个1)所以在有符号整数中的值就是-1,而在无符号中的值又是4294967295。但是均不是我想要的255,然后查了各种资料,结果看到这个网站:
http://stackoverflow.com/questions/23819445/why-is-xff-not-being-recognized
中有一句话剪切过来:
'\xff' is a character constant, not a string literal. String literals have no signedness; they represent arrays. Type char may be either signed or unsigned, depending on the implementation. If plain char is unsigned, then '\xff' has the value 255 of type int. If plain char is signed, the wording of the standard is unclear (at least to me).
结果我试了几组代码:
char *sign_t = new char('\377'); int j = *sign_t; //值是-1 uint *intTest = new uint('\xff\xff\xff\xff'); //值是4294967295 uchar *t = new uchar('\xff'); int *pp = (int*)t; //值是255
结果我明白了很多,错误始终是在一开始我根本不清楚cv::Mat中的数据的深度和通道信息,然后我搜索了到了一个解答:
http://stackoverflow.com/questions/10167534/how-to-find-out-what-type-of-a-mat-object-is-with-mattype-in-opencv
http://answers.opencv.org/question/742/matrix-depth-equals-0/
将其解答剪切出来,我们可以用img2::type(), img2::channels(), img2::depth()来确定mat的类型。但是img2::type与img2::depth()需要区别如下:
So,至此所有问题都明朗了,最后贴上一个正确访问CV_8UC1类型Mat.data数据的代码:
for(int row = 40; row <img2.rows-200; row++){ uchar *ptr = (img2.data + row*img2.step); for(int col = 20; col < img2.cols-200; col++){ uchar *ptrTmp = ptr+col; *ptrTmp = 0; } std::cout << std::endl; }
结果图如下:
还有留下的问题需要解决,继续看opencv,就是如果我要逐个操作每个像素值,我是否应该将CV_8UC1类型转为浮点型计算完成之后再转回来。还有另外一个问题就是负数的二进制表示还需要再看看资料。