(opencv) imread with CV_LOAD_IMAGE_GRAYSCALE yields a 4 channels Mat - opencv

The following code reads an image from a file into a cv::Mat object.
#include <string>
#include <opencv2/opencv.hpp>
cv::Mat load_image(std::string img_path)
{
cv::Mat img = cv::imread(img_path, CV_LOAD_IMAGE_GRAYSCALE);
cv::Scalar intensity = img.at<uchar>(0, 0);
std::cout << intensity << std::endl;
return img;
}
I would expect the cv::Mat to have only one channel (namely, the intensity of the image) but it has 4.
$ ./test_load_image
[164, 0, 0, 0]
I also tried converting the image with
cv::Mat gray(img.size(), CV_8UC1);
img.convertTo(gray, CV_8UC1);
but the gray matrix is also a 4 channels one.
I'd like to know if it's possible to have a single channel cv::Mat. Intuitively, that's what I would expect to have when dealing with a grayscale (thus, single channel) image.

The matrix is single channel. You're just reading the values in the wrong way.
Scalar is a struct with 4 values. Constructing a Scalar with a single value will result in a Scalar with the first value set, and the remaining at zero.
In your case, only the first values make sense. The zeros are as default for Scalar.
However, you don't need to use a Scalar:
uchar intensity = img.at<uchar>(0, 0);
std::cout << int(intensity) << std::endl; // Print the value, not the ASCII character

Related

How to convert any image to CV_32FC1

I want to convert any image I use as input to CV_32FC1 type. My code is below:
char fname[MAX_PATH];
while (openFileDlg(fname))
{
Mat img = imread(fname, CV_LOAD_IMAGE_UNCHANGED);
Mat src(img.rows, img.cols, CV_32FC1);
Mat dst(img.rows, img.cols, CV_32FC1);
if (img.type() == 0)
{
img.convertTo(img, CV_32FC1, 1.0f / 255.0f);
src = img.clone();
}
else
{
img.convertTo(img, CV_32FC1);
}
std::cout << img.type() << ' ' << src.type();
}
For grayscale images it works, but when I use a color image, the conversion doesn't work. For example: for CV_32FC1 the value is 5. When I upload a color image it gives me the value 16, and after conversion is 21. Any ideas?
You see inconsistent results for Gray and RGB images to the difference in channel numbers in both cases, 32FC1 can be broken down as:
32F - 32 bit floating value
C1 - Single channel
But RGB image or BGRA images have 3, 4 channels respectively, so we can't use C1 for them, hence we need to use 32FC3 for 3-channel image and 32FC4 for 4-channel image.

Is it possible to recognize so minimal changes between noisy images in OpenCV?

I want to detect the very minimal movement of a conveyor belt using image evaluation (Resolution: 31x512, image rate: 1000 per second.). The moment of belt-start is important for me.
If I do cv::absdiff between two subsequent images, I obtain very noisy result:
According to the mechanical rotation sensor of the motor, the movement starts here:
I tried to threshold the abs-diff image with a cascade of erosion and dilation, but I could detect the earliest change more than second too late in this image:
Is it possible to find the change earlier?
Here is the sequence of the Images without changes (according to motor sensor):
In this sequence the movement begins in the middle image:
Looks like I've found a solution which works in MY case.
Instead of comparing the image changes in space-domain, the cross-correlation should be applied:
I convert both images to DFT, multiply DFT-Mats and convert back. The max pixel value is the center of the correlation. As long as the images are same, the max-pix remains in the same position and moves otherwise.
The actual working code uses 3 images, 2 DFT multiplication result between images 1,2 and 2,3:
Mat img1_( 512, 32, CV_16UC1 );
Mat img2_( 512, 32, CV_16UC1 );
Mat img3_( 512, 32, CV_16UC1 );
//read the data in the images wohever you want. I read from MHD-file
//Set ROI (if required)
Mat img1 = img1_(cv::Rect(0,200,32,100));
Mat img2 = img2_(cv::Rect(0,200,32,100));
Mat img3 = img3_(cv::Rect(0,200,32,100));
//Float mats for DFT
Mat img1f;
Mat img2f;
Mat img3f;
//DFT and produtcts mats
Mat dft1,dft2,dft3,dftproduct,dftproduct2;
//Calculate DFT of both images
img1.convertTo(img1f, CV_32FC1);
cv::dft(img1f, dft1);
img2.convertTo(img3f, CV_32FC1);
cv::dft(img3f, dft3);
img3.convertTo(img2f, CV_32FC1);
cv::dft(img2f, dft2);
//Multiply DFT Mats
cv::mulSpectrums(dft1,dft2,dftproduct,true);
cv::mulSpectrums(dft2,dft3,dftproduct2,true);
//Convert back to space domain
cv::Mat result,result2;
cv::idft(dftproduct,result);
cv::idft(dftproduct2,result2);
//Not sure if required, I needed it for visualizing
cv::normalize( result, result, 0, 255, NORM_MINMAX, CV_8UC1);
cv::normalize( result2, result2, 0, 255, NORM_MINMAX, CV_8UC1);
//Find maxima positions
double dummy;
Point locdummy; Point maxLoc1; Point maxLoc2;
cv::minMaxLoc(result, &dummy, &dummy, &locdummy, &maxLoc1);
cv::minMaxLoc(result2, &dummy, &dummy, &locdummy, &maxLoc2);
//Calculate products simply fot having one value to compare
int maxlocProd1 = maxLoc1.x*maxLoc1.y;
int maxlocProd2 = maxLoc2.x*maxLoc2.y;
//Calculate absolute difference of the products. Not 0 means movement
int absPosDiff = std::abs(maxlocProd2-maxlocProd1);
if ( absPosDiff>0 )
{
std::cout << id<< std::endl;
break;
}

Opencv imshow Y from YUV

I'm trying to extract and display
Y channel from YUV converted image
My code is as follows:
Mat src, src_resized, src_gray;
src = imread("11.jpg", 1);
resize(src, src_resized, cvSize(400, 320));
cvtColor(src_resized, src_resized, cv::COLOR_BGR2RGB);
/*
I've tried both with and without the upper conversion
(mentioned here as bug
http://stackoverflow.com/questions/7954416/converting-yuv-into-bgr-or-rgb-in-opencv
in an opencv 2.4.* version - mine is 2.4.10 )
*/
cvtColor(src_resized, src_gray, CV_RGB2YUV); //YCrCb
vector<Mat> yuv_planes(3);
split(src_gray,yuv_planes);
Mat g, fin_img;
g = Mat::zeros(Size(src_gray.cols, src_gray.rows),0);
// same result withg = Mat::zeros(Size(src_gray.cols, src_gray.rows), CV_8UC1);
vector<Mat> channels;
channels.push_back(yuv_planes[0]);
channels.push_back(g);
channels.push_back(g);
merge(channels, fin_img);
imshow("Y ", fin_img);
waitKey(0);
return 0;
As result I was expecting a Gray image showing luminescence.
Instead I get a B/G/R channel image depending on the position of (first/second/third)
channels.push_back(yuv_planes[0]);
as shown here:
What am I missing? (I plan to use the luminance to do a sum of rows/columns and extracting the License Plate later using the data obtained)
The problem was displaying the luminescence only in one channel instead of filling all channels with it.
If anyone else hits the same problem just change
Mat g, fin_img;
g = Mat::zeros(Size(src_gray.cols, src_gray.rows),0);
vector<Mat> channels;
channels.push_back(yuv_planes[0]);
channels.push_back(g);
channels.push_back(g);
to
(fill all channels with desired channel)
Mat fin_img;
vector<Mat> channels;
channels.push_back(yuv_planes[0]);
channels.push_back(yuv_planes[0]);
channels.push_back(yuv_planes[0]);

Preventing information loss on image subtraction

I have two images that I am subtracting from one another quite simply:
Mat foo, a, b;
...//imread onto a and b or somesuch
foo = a - b;
Now, as I understand it, any pixel value that goes into the negatives (or over 255 for that matter) will be set to zero instead. If that is so, I'd like to know if there is any way to permit it to go under zero so that I may adjust the image later without information loss.
I'm working with greyscale images if that simplifies things.
This is how a simple convert => substract => convertAndScaleBack application would look like:
input:
and
int main()
{
cv::Mat input = cv::imread("../inputData/Lenna.png", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat input2 = cv::imread("../inputData/Lenna_edges.png", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat input1_16S;
cv::Mat input2_16S;
input.convertTo(input1_16S, CV_16SC1);
input2.convertTo(input2_16S, CV_16SC1);
// compute difference of 16 bit signed images
cv::Mat diffImage = input1_16S-input2_16S;
// now you have a 16S image that has some negative values
// find minimum and maximum values:
double min, max;
cv::minMaxLoc(diffImage, &min, &max);
std::cout << "min pixel value: " << min<< std::endl;
cv::Mat backConverted;
// scale the pixel values so that the smalles value is 0 and the largest one is 255
diffImage.convertTo(backConverted,CV_8UC1, 255.0/(max-min), -min);
cv::imshow("backConverted", backConverted);
cv::waitKey(0);
}
output:

OpenCV: How to convert CV_8UC1 mat to CV_8UC3

How to convert CV_8UC1 Mat to CV_8UC3 with OpenCV?
Mat dst;
Mat src(height, width, CV_8UC1, (unsigned char*) captureClient->data());
src.convertTo(dst, CV_8UC3);
but dst.channels() = 1
I've found that the best way to do this is:
cvtColor(src, dst, COLOR_GRAY2RGB);
The image will look the same as when it was grayscale CV_8UC1 but it will be a 3 channel image of type CV_8UC3.
From the documentation on convertTo
void Mat::convertTo(Mat& m, int rtype, double alpha=1, double beta=0) const
rtype – The desired destination matrix type, or rather, the depth (since the number of channels will be the same with the source one). If rtype is negative, the destination matrix will have the same type as the source.
You want to create a matrix for each of the 3 channels you want to create and then use the merge function. See the answers to this question
The convention is, that for the type CV_8UC3, the pixels values range from 0 to 255, and for type CV_32FC3 from 0.0 to 1.0. Thus you need to use a scaling factor of 255.0, instead of 1.0:
Mat::convertTo(newImage, CV_32FC1, 255.0);

Resources