I'm not certain about my mean function. In Matlab, the mean of my image is 135.3565 by using mean2; however, my function gives 140.014 and OpenCV built-in cv::mean gives me [137.67, 152.467, 115.933, 0]. This is my code.
double _mean(const cv::Mat &image)
{
double N = image.rows * image.cols;
double mean;
for (int rows = 0; rows < image.rows; ++rows)
{
for (int cols = 0; cols < image.cols; ++cols)
{
mean += (float)image.at<uchar>(rows, cols);
}
}
mean /= N;
return mean;
}
My guess is that you are feeding one type of image to Matlab and another type to your algoritm and to the opencv built-in function.
The mean2 function of Matlab takes a 2D image (grayscale) . Your function assumes that the image is 2D matrix of unsigned chars (grayscale too), and when you do this:
mean += (float)image.at<uchar>(rows, cols);
and you pass a color image to the function, an incorrect value is retrieved. Try to convert your image to grayscale before passing to your function and compare the result with Matlab.
For a color image, modify your function to this:
double _mean(const cv::Mat &image)
{
double N = image.rows * image.cols * image.channels();
double mean;
for (int rows = 0; rows < image.rows; ++rows)
{
for (int cols = 0; cols < image.cols; ++cols)
{
for(int channels = 0; channels < image.channels(); ++channels)
{
mean += image.at<cv::Vec3b>(rows, cols)[channels];
}
}
}
mean /= N;
return mean;
}
and in Matlab compute the mean with
mean(image(:))
which will vectorize your image before compute the mean. Compare the results.
The opencv function computes the mean of each channel of the image separately, so the result is a vector of the means of each channel.
I hope this will help!
Related
I came across this sample code on openCV library. What does the line p[j] = table[p[j]] do? I have come across multi dimensional arrays but not something like this before.
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
It is doing color replacement by using a table where each pixel intensity maps to some other value. Commonly used for techniques like color grading, histogram adjustment, or even thresholding.
Here, the table contains unsigned char values and is being indexed by the value of the pixel. The pixel's intensity p[i] is used as an index into the table, and the value at that index is then written to that pixel, replacing its original value.
It is a lookup table conversion.
The pixels of image(I) would be converted by means of table.
For example, the pixel with value 100 would be changed to 10 if table[100]=10.
Your sample code is introduced in OpenCV tutorial which is well explained of what the code does.
https://docs.opencv.org/master/db/da5/tutorial_how_to_scan_images.html
I am trying to make a fast image threshold function. Currently what I do is:
void threshold(const cv::Mat &input, cv::Mat &output, uchar threshold) {
int rows = input.rows;
int cols = input.cols;
// cv::Mat for result
output.create(rows, cols, CV_8U);
if(input.isContinuous()) { //we have to make sure that we are dealing with a continues memory chunk
const uchar* p;
for (int r = 0; r < rows; ++r) {
p = input.ptr<uchar>(r);
for (int c = 0; c < cols; ++c) {
if(p[c] >= threshold)
//how to access output faster??
output.at<uchar>(r,c) = 255;
else
output.at<uchar>(r,c) = 0;
}
}
}
}
I know that the at() function is quite slow. How can I set the output faster, or in other words how to relate the pointer which I get from the input to the output?
You are thinking of at as the C++ standard library documents it for a few containers, performing a range check and throwing if out of bounds, however this is not the standard library but OpenCV.
According to the cv::Mat::at documentation:
The template methods return a reference to the specified array element. For the sake of higher performance, the index range checks are only performed in the Debug configuration.
So there's no range check as you may be thinking.
Comparing both cv::Mat::at and cv::Mat::ptr in the source code we can see they are almost identical.
So cv::Mat::ptr<>(row) is as expensive as
return (_Tp*)(data + step.p[0] * y);
While cv::Mat::at<>(row, column) is as expensive as:
return ((_Tp*)(data + step.p[0] * i0))[i1];
You might want to take cv::Mat::ptr directly instead of calling cv::Mat::at every column to avoid further repetition of the data + step.p[0] * i0 operation, doing [i1] by yourself.
So you would do:
/* output.create and stuff */
const uchar* p, o;
for (int r = 0; r < rows; ++r) {
p = input.ptr<uchar>(r);
o = output.ptr<uchar>(r); // <-----
for (int c = 0; c < cols; ++c) {
if(p[c] >= threshold)
o[c] = 255;
else
o[c] = 0;
}
}
As a side note you don't and shouldn't check for cv::Mat::isContinuous here, the gaps are from one row to another, you are taking pointers to a single row, so you don't need to deal with the matrix gaps.
I want to do an operation like this however I cannot get the values of the vector Mat and change them. table is a 1 dimensional array by the way. Thanks.
vector<Mat> orjchannel;
vector<Mat> refchannel;
// There are some functions here
for (int i = 0; i < 512; i++){
for (int j = 0; j < 512; j++){
double value = refchannel[i][j]; // This part does not work
orjchannel[i][j] = tables[value];
With OpenCV, you typically access the values of a Mat with the at<DATATYPE>(r,c) command. For example...
// Mat constructor
Mat data(4, 1, CV_64FC1);
// Set Value
data.at<double>(0,0) = 4;
// Get Value
double value = data.at<double>(0,0);
I load an image in grayscale mode into Mat image. I use image.convertTo(image, CV_32F);
to convert the data type to double. I would like to convert the image into a vector<double>, so I iterate through the matrix in the following way:
int channels = image.channels();
int nRows = image.rows;
int nCols = image.cols;
vector<double> vectorizedMatrix (nRows*nCols);
if (image.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
double* pI;
int k = 0;
for (int i=0; i<nRows; i++) {
pI = image.ptr<double>(i);
for (int j=0;j<nCols;j++) {
vectorizedMatrix.at(k) = pI[j];
k++;
}
}
return vectorizedMatrix;
When checking the data I get, I see huge values in the area of 10^10, which cannot be. Am I iterating wrongly through the matrix or does the function convertTo do something I'm not aware of?
"I use image.convertTo(image, CV_32F); to convert the data type to double"
no, that will convert to float. if you want double, instead use:
image.convertTo(image, CV_64F);
I have to get information about the scalar value of a lot of pixels on a gray-scale image using OpenCV. It will be traversing hundreds of thousands of pixels so I need the fastest possible method. Every other source I've found online has been very cryptic and hard to understand. Is there a simple line of code that should just hand a simple integer value representing the scalar value of the first channel (brightness) of the image?
for (int row=0;row<image.height;row++) {
unsigned char *data = image.ptr(row);
for (int col=0;col<image.width;col++) {
// then use *data for the pixel value, assuming you know the order, RGB etc
// Note 'rgb' is actually stored B,G,R
blue= *data++;
green = *data++;
red = *data++;
}
}
You need to get the data pointer on each new row because opencv will pad the data to 32bit boundary at the start of each row
With regards to Martin's post, you can actually check if the memory is allocated continuously using the isContinuous() method in OpenCV's Mat object. The following is a common idiom for ensuring the outer loop only loops once if possible:
#include <opencv2/core/core.hpp>
using namespace cv;
int main(void)
{
Mat img = imread("test.jpg");
int rows = img.rows;
int cols = img.cols;
if (img.isContinuous())
{
cols = rows * cols; // Loop over all pixels as 1D array.
rows = 1;
}
for (int i = 0; i < rows; i++)
{
Vec3b *ptr = img.ptr<Vec3b>(i);
for (int j = 0; j < cols; j++)
{
Vec3b pixel = ptr[j];
}
}
return 0;
}