I have a type 6 (double-valued, single channel) mat with data ranging from 0 to 255. I can print out the data using the following code:
double* data = result.ptr<double>();
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
std::cout<<data[i*step+j]<<"\t";
And this appears perfectly normal--in the range from 0 to 255 and the size that I'd expect. However, when I try to show the image:
imshow(window_name, result);
waitKey();
I just get a white image. Just white pixels. Nothing else.
Loading other images from files and displaying in the window works fine.
Using Windows 7, OpenCV 233
cv::imshow works in following ways -
If the image is 8-bit unsigned, it is displayed as is.
If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the value range [0,255*256] is mapped to [0,255].
If the image is 32-bit floating-point, the pixel values are multiplied by 255. That is, the value range [0,1] is mapped to [0,255].
Your matrix lies in the 3rd category where imshow is expecting the values to be between 0 and 1 and so it multiplies it by 255. Since your values are already between 0 and 255, you are getting unwanted result. So normalizing the pixels between 0 and 1 will work.
You need to normalize your floating point image so that the values are between 0.0 - 1.0 if you're using imshow. I bet your values are over 1.0 and thus those pixels are all set to 255, giving you the white image.
Related
I'm trying to output a png grayscale image using values from an array of intensity values using ImageMagick.
I've used the Image constructor to try to do this, but the image it is creating does not exactly match the given array.
Image grayscaleImage(256, 256, "I", DoublePixel, inputPtr);
grayscaleImage.write("test.png");
The image that's being created has the correct values for all of the black pixels (intensity of 0) but for the non-zero pixels, I'm getting only white; no gray. How can I correct this issue? Or am I using the constructor incorrectly? Thank you!
As emcconville stated, the numbers in the array of integers need to be scaled to be between 0.0 and 1.0 for Magick::DoublePixel. I achieved this by using the following function:
(b-a)(x - min)
f(x) = -------------- + a
max - min
Where a == 0, b == 1, x == inputPtr[index], min == 0, and max == 255.
i have 100 image, each one is 598 * 598 pixels, and i want to remove the pictorial and noise by taking the average of pixels, but if i want to use Adding for "pixel by pixel"then dividing i will write a loop until 596*598 repetitions for one image, and 598*598*100 for hundred of image.
is there a method to help me in this operation?
You need to loop over each image, and accumulate the results. Since this is likely to cause overflow, you can convert each image to a CV_64FC3 image, and accumualate on a CV_64FC3 image. You can use also CV_32FC3 or CV_32SC3 for this, i.e. using float or integer instead of double.
Once you have accumulated all values, you can use convertTo to both:
make the image a CV_8UC3
divide each value by the number of image, to get the actual mean.
This is a sample code that creates 100 random images, and computes and shows the
mean:
#include <opencv2\opencv.hpp>
using namespace cv;
Mat3b getMean(const vector<Mat3b>& images)
{
if (images.empty()) return Mat3b();
// Create a 0 initialized image to use as accumulator
Mat m(images[0].rows, images[0].cols, CV_64FC3);
m.setTo(Scalar(0,0,0,0));
// Use a temp image to hold the conversion of each input image to CV_64FC3
// This will be allocated just the first time, since all your images have
// the same size.
Mat temp;
for (int i = 0; i < images.size(); ++i)
{
// Convert the input images to CV_64FC3 ...
images[i].convertTo(temp, CV_64FC3);
// ... so you can accumulate
m += temp;
}
// Convert back to CV_8UC3 type, applying the division to get the actual mean
m.convertTo(m, CV_8U, 1. / images.size());
return m;
}
int main()
{
// Create a vector of 100 random images
vector<Mat3b> images;
for (int i = 0; i < 100; ++i)
{
Mat3b img(598, 598);
randu(img, Scalar(0), Scalar(256));
images.push_back(img);
}
// Compute the mean
Mat3b meanImage = getMean(images);
// Show result
imshow("Mean image", meanImage);
waitKey();
return 0;
}
Suppose that the images will not need to undergo transformations (gamma, color space, or alignment). The numpy package lets you do this quickly and succinctly.
# List of images, all must be the same size and data type.
images=[img0, img1, ...]
avg_img = np.mean(images, axis=0)
This will auto-promote the elements to float. If you want the as BGR888, then:
avg_img = avg_img.astype(np.uint8)
Could also do uint16 for 16 bits per channel. If you are dealing with 8 bits per channel, you almost certainly won't need 100 images.
Firstly- convert images to floats. You have N=100 images. Imagine that a single image is an array of average pixel values of 1 image. You need to calculate an array of average pixel values of N images.
Let A- array of average pixel values of X images, B - array of average pixel values of Y images. Then C = (A * X + B * Y) / (X + Y) - array of average pixel values of X + Y images. To get better accuracy in floating point operations X and Y should be approximately equal
You may merge all you images like subarrays in merge sort. In you case merge operation is C = (A * X + B * Y) / (X + Y) where A and B are arrays of average pixel values of X and Y images
I am writing my own implementation of the sobel egde detection. My function's interface is
void sobel_filter(volatile PIXEL * pixel_in, FLAG *EOL, volatile PIXEL * pixel_out, int rows, int cols)
(PIXEL being an 8bit greyscale pixel)
For testing I changed the interface to:
void sobel_filter(PIXEL pixels_in[MAX_HEIGHT][MAX_WIDTH],PIXEL
pixels_out[MAX_HEIGHT][MAX_WIDTH], int rows,int cols);
But Still, the thing is I get to read one pixel at a time, which brings me to the problem of managing the output values of sobel when they are bigger then 255 or smaller then 0. If I had the whole picture from the start, I could normalize all sobel output values with their min and max values. But this is not possible for me.
This is my sobel operator code, ver1:
PIXEL sobel_op(PIXEL_CH window[KERNEL_SIZE][KERNEL_SIZE]){
const char x_op[KERNEL_SIZE][KERNEL_SIZE] = { {-1,0,1},
{-2,0,2},
{-1,0,1}};
const char y_op[KERNEL_SIZE][KERNEL_SIZE] = { {1,2,1},
{0,0,0},
{-1,-2,-1}};
short x_weight=0;
short y_weight=0;
PIXEL ans;
for (short i=0; i<KERNEL_SIZE; i++){
for(short j=0; j<KERNEL_SIZE; j++){
x_weight+=window[i][j]*x_op[i][j];
y_weight+=window[i][j]*y_op[i][j];
}
}
short val=ABS(x_weight)+ABS(y_weight);
//make sure the pixel value is between 0 and 255 and add thresholds
if(val>200)
val=255;
else if(val<100)
val=0;
ans=255-(unsigned char)(val);
return ans;
}
this is ver 2, changes are made only after summing up the weights:
short val=ABS(x_weight)+ABS(y_weight);
unsigned char char_val=(255-(unsigned char)(val));
//make sure the pixel value is between 0 and 255 and add thresholds
if(char_val>200)
char_val=255;
else if(char_val<100)
char_val=0;
ans=char_val;
return ans;
Now, for a 3x3 sobel both seem to be giving OK results:
;
But when I try with a 5x5 sobel
const char x_op[KERNEL_SIZE][KERNEL_SIZE] = { {1,2,0,-2,-1},
{4,8,0,-8,-4},
{6,12,0,-12,-6},
{4,8,0,-8,-4},
{1,2,0,-2,-1}};
const char y_op[KERNEL_SIZE][KERNEL_SIZE] = { {-1,-4,-6,-4,-1},
{-2,-8,-12,-8,-2},
{0,0,0,0,0},
{2,8,12,8,2},
{1,4,6,4,1}};
it gets tricky:
As you can see, for the 5x5 the results are quite bad and I don't know how to normalize the values. Any ideas?
Think about the range of values that your filtered values can take.
For the Sobel 3x3, the highest X/Y value is obtained when the pixels with a positive coefficient are white (255), and the ones with a negative coefficient are black (0), which gives a total of 1020. Symmetrically, the lowest value is -1020. After taking the absolute value, the range is from 0 to 1020 = 4 x 255.
For the magnitude, Abs(X)+Abs(Y), the computation is a little more complicated as the two components cannot reach 1020 at the same time. If I am right, the range is from 0 to 1530 = 6 x 255.
Similar figures for the 5x5 are 48 x 255 and 66 x 255.
Knowing that, you should rescale the values to a smaller range (apply a reduction coefficient), and adjust the thresholds. Logically, if you apply a coefficient 3/66 to the Sobel 5x5, you will return to similar conditions.
It all depends on the effect that you want to achieve.
Anyway, the true question is: how are the filtered values statistically distributed for typical images ? Because it is unnecessary to keep the far tails of the distribution.
You have to normalize the results of your computation. For that you have to find out how "big" is the filter with all absoltue values. So I do this:
for(int i = 0; i < mask.length; i++)
for(int j = 0; j < mask[i].length; j++)
size += Math.abs(mask[i][j]);
Where mask is my sobel filter of each size. So after apply your sobel filter you have to normalize your value in your code it should look like:
for (short i=0; i<KERNEL_SIZE; i++){
for(short j=0; j<KERNEL_SIZE; j++){
x_weight+=window[i][j]*x_op[i][j];
y_weight+=window[i][j]*y_op[i][j];
}
}
x_weight /= size;
y_weight /= size;
After that for visualization you have to shift the values about 128. Just do that if you want to visualize the image. Otherwise you get problems with later calculations (gradient for example).
x_weight += 128;
y_weight += 128;
Hope it works and help.
I am trying to convert a given Mat representing an RGB image with 8-bit depth to Lab using the function provided in the documentation:
cvtColor(source, destination, <conversion code>);
I have tried the following conversion codes:
CV_RGB2Lab
CV_BGR2Lab
CV_LBGR2Lab
I have received bizarre results each time around, with an "L" value of greater than 100 for some samples, literally <107, 125, 130>.
I am also using Photoshop to check the results - but given that 107 is beyond the accepted range of 0 ≤ L ≤ 100, I can not comprehend what my error is.
Update:
I'll post my overall results here:
Given an image (Mat) represented by 8-bit BGR, the image can be converted by the following:
cvtColor(source, destination, CV_BGR2Lab);
The pixel values can then be accessed in the following manner:
int step = destination.step;
int channels = destination.channels();
for (int i = 0; i < destination.rows(); i++) {
for (int j = 0; j < destination.cols(); j++) {
Point3_<uchar> pixelData;
//L*: 0-255 (elsewhere is represented by 0 to 100)
pixelData.x = destination.data[step*i + channels*j + 0];
//a*: 0-255 (elsewhere is represented by -127 to 127)
pixelData.y = destination.data[step*i + channels*j + 1];
//b*: 0-255 (elsewhere is represented by -127 to 127)
pixelData.z = destination.data[step*i + channels*j + 2];
}
}
If anyone is interested in the range of the other variables a and b I made a small program to test their range.
If you convert all the colors that are represented with RGB to the CieLab used in OpenCV the ranges are:
0 <=L<= 255
42 <=a<= 226
20 <=b<= 223
And if you're using RGB values in the float mode instead of uint8 the ranges will be:
0.0 <=L<= 100.0
-86.1813 <=a<= 98.2352
-107.862 <=b<= 94.4758
P.S. If you want to see how distinguishable (regarding human perception) is a LAB value from another LAB value, you should use the floating point. The scale used to keep the lab values in the uint8 ranges messes up with their euclidean distance.
This is the code I used (python):
L=[0]*256**3
a=[0]*256**3
b=[0]*256**3
i=0
for r in xrange(256):
for g in xrange(256):
for bb in xrange(256):
im = np.array((bb,g,r),np.uint8).reshape(1,1,3)
cv2.cvtColor(im,cv2.COLOR_BGR2LAB,im) #tranform it to LAB
L[i] = im[0,0,0]
a[i] = im[0,0,1]
b[i] = im[0,0,2]
i+=1
print min(L), '<=L<=', max(L)
print min(a), '<=a<=', max(a)
print min(b), '<=b<=', max(b)
That's because L value is in range [0..255] in OpenCV. You can simply scale this value to needed interval ([0..100] in your case).
I am not sure about João Abrantes's range on A and B.
The opencv documentation has clearly mentioned the CIE L*a*b*range.
8 bit images
Thus leading to a range of
0 <= L <= 255
0 <= a <= 255
0 <= b <= 255
In case anyone runs into the same issue:
Please note that in OpenCV (2.4.13), you can not convert CV_32FC3 BGR images into the Lab color space. That is to say:
//this->xImage is CV_8UC3
this->xImage.convertTo(FloatPrecisionImage, CV_32FC3);
Mat result;
cvtColor(FloatPrecisionImage, result, COLOR_BGR2Lab);
this->xImage = result;
will not work
while
Mat result;
cvtColor(this->xImage, result, COLOR_BGR2Lab);
result.convertTo(this->xImage, CV_32FC3);
works like a charm.
I did not track down the reason for said behavior; however it seems off to me, because this in effect puts limits on the image's quality.
I got a number between 0-255 and need to convert it to a RGB grayscale color. And how do I convert a RGB-color to a grayscale value between 0-255?
The common formula is luminosity = 0.30 * red + 0.59 * green + 0.11 * blue. Matches the human eye's color perception, doesn't otherwise correct for display device gamma.
If you have a number 0 <= x <= 255 representing a grayscale value, the corresponding RGB tuple is simply (x,x,x).