Copying two images pixel by pixel - c++

I am trying to work with each pixel from depth map. (I am implementing image segmentation.) I don't know how to work with pixels from image with depth higher than 1.
This sample code copies depth map to another cv::Mat pixel by pixel. It works fine, if I normalize it (depth of normalized image = 1). But it doesn't work with depth = 3, because .at<uchar> is wrong operation for this depth.
cv::Mat res;
cv::StereoBM bm(CV_STEREO_BM_NORMALIZED_RESPONSE);
bm(left, right, res);
std::cout<<"type "<<res.type()<<" depth "<<res.depth()<<" channels "<<res.channels()<<"\n";// type 3 depth 3 channels 1
cv::Mat tmp = cv::Mat::zeros(res.rows, res.cols, res.type());
for(int i = 0; i < res.rows; i++)
{
for(int j = 0; j < res.cols; j++)
{
tmp.at<uchar>(i, j) = res.at<uchar>(i, j);
//std::cout << (int)res.at<uchar>(i, j) << " ";
}
//std::cout << std::endl;
}
cv::imshow("tmp", normalize(tmp));
cv::imshow("res", normalize(res));
normilize function
cv::Mat normalize(cv::Mat const &depth_map)
{
double min;
double max;
cv::minMaxIdx(depth_map, &min, &max);
cv::Mat adjMap;
cv::convertScaleAbs(depth_map, adjMap, 255 / max);
return adjMap;
}
left image - tmp, right image - res
How can I get the pixel from image with depth equal to 3?

Mat::depth() returns value equal to a constant symbolising bit depth of the image. If You get depth equal to for example CV_32F, to get to the pixels You would need to use float instead of uchar.
CV_8S -> char
CV_8U -> uchar
CV_16U -> unsigned int
CV_16S -> int
CV_32F -> float
CV_64F -> double
Mat::channels() tells You how many values of that type are assigned to a coordinate. These multiple values can be extracted as cv::Vec. So if You have a two channel Mat with depth CV_8U, instead using Mat.at<uchar> You would need to go with Mat.at<Vec2b>, or Mat.at<Vec2f> for CV_32F one.

When your images are of depth 3, do this for copying pixel by pixel:
tmp.at<Vec3b>(i,j) = res.at<Vec3b>(i,j);
However, if you are copying the whole image , I do not understand the point of copying each pixel individually, unless you want to do different processing with different pixels.
You can just copy the whole image res to tmp by this:
res.copyTo(tmp);

Related

image.rows and image.cols are less than real image size (c++)

I am learning open cv, I wrote a code which is supposed to add salt and paper noise to the image(it turns some random pixel's value to 255), my image size is 256256. The problem is I can't access to all pixels and the code just changes half of the image's pixels. For example, my image size as I said is 256256 and when I change pixel value which is located in 256*256 it changes the center image's pixel.
inline cv::Mat salt(cv::Mat, int); //my function
int main()
{
int present, imagepixel;
cv::Mat image = cv::imread("sample.jpg");
imagepixel = image.rows * image.cols;
std::cout << "enter the value of salt and paper :";
std::cin >> percent;
present = (present * imagepixel) / 100; //image pixel is number of image's pixels
if (image.empty())
{
std::cout << "image is not valid" << std::endl;
}
image = salt(image, present);
cv::imshow("salt&paper", image);
cv::waitKey(0);
return 0;
}
cv::Mat salt(cv::Mat image, int present)
{
int row, col, j = 0;
srand(time(NULL));
for (int i = 0; i < present; i++)
{
row = rand() % image.rows;
col = rand() % image.cols;
image.at<unsigned char>(row, col) = 255;
}
return image;
}
Why does the image have more rows and columns than calculated with function like image.rows and image.cols (I give the value to image, in debugging mode and image, had value out of image.rows and image.cols range)?
Why is the output set to unsigned char? Why can't the value of pixels be an integer? (I supposed that inside image.at<unsigned char>, <unsigned char> is output kind (int, char, float))
Why can't this code access all pixels? (screenshot has added look at the output)
Second argument of imread specifies how an image is read. Default value is IMREAD_COLOR what means that:
In the case of color images, the decoded images will have the channels
stored in B G R order. (from
reference)
so your result mat has 256x256 resolution and each pixel is described by 3 values: to store Blue,Green and Red component of space color. These components are stored on uchar type (range is [0,255)).
2D images in OpenCV are stored row-by-row. The formula below can be used to calculate address of M(row,col) element:
addr(M[row,col]) = data + step1 * row + step2 * col
where data is first byte of array where the data of image is stored. Your mat has 3 channels and image's width is 256, so step1 equals to 256 * 3 (it is number of columns in row multiplied by number of channels per pixel) and step2 is 3.
Now, let's open source code of at member function:
template<typename _Tp> inline
_Tp& Mat::at(int i0, int i1) {
return ((_Tp*)(data + step.p[0] * i0))[i1];
}
// i0 means row, i1 means col
in your code you specify _Tp as uchar. Now just rules of pointer arithmetic work which cause you can access only 1/3 of input image. For example when you call <uchar>(0,1) you are accessing Green component of (0,0) pixel.
To resolve your issue you have to pass Vec3b in template argument list:
image.at<cv::Vec3b>(row, col) = cv::Vec3b(255,255,255);
(each component of Vec3b has uchar type).

Iterating through Mat

I've got a problem iterating through coordinates of a OpenCV Mat:
cv::Mat picture = cv::Mat(depth.rows, depth.cols, CV_32F);
for (int y = 0; y < depth.rows; ++y)
{
for (int x = 0; x < depth.cols; ++x)
{
float depthValue = (float) depth.at<float>(y,x);
picture.at<float>(y, x) = depthValue;
}
}
cv::namedWindow("picture", cv::WINDOW_AUTOSIZE);
cv::imshow("picture", picture);
cv::waitKey(0);
Resulting pictures:
before (depth)
after (picture)
It looks like it's
1. scaled and
2. stopped at about a third of the width. Any ideas?
Looks like your depth image have 3 channels.
All channels values are the same for BW image (B=G=R), so you have BGRBGRBGR instead of GrayGrayGray, and you trying to access it as it is 1 channel, that is why image is stretched 3 times horizontally.
Try to cv::cvtColor(depth,depth,COLOR_BGR2GRAY) before running loop.
Your iteration code is right.
What's wrong instead is the cv::Mat depth type assumption.
As suggested, this could be CV_U8C3 according to the distorsion.
To get the pixel value of such a CV_8UC3 matrix you can use :
cv::Vec3i depthValue = depth.at<cv::Vec3i>(y,x);
Then do whatever you want with this scalar.
For example, if your depth is of type CV_8UC3 with distance encoded in the two first bytes (MSB first) you can get the distance with :
float distance = depthValue[0] * 255 + depthValue[1];

Converting to Floating Point Image from .tif

I am relatively new to C++ and coding in general and have run into a problem when attempting to convert an image to a floating point image. I am attempting to do this to eliminate round off errors with calculating the mean and standard deviation of pixel intensity for images as it starts to effect data quite substantially. My code is below.
Mat img = imread("Cells2.tif");
cv::namedWindow("stuff", CV_WINDOW_NORMAL);
cv::imshow("stuff",img);
CvMat cvmat = img;
Mat dst = cvCreateImage(cvGetSize(&cvmat),IPL_DEPTH_32F,1);
cvConvertScale(&cvmat,&dst);
cvScale(&dst,&dst,1.0/255);
cvNamedWindow("Test",CV_WINDOW_NORMAL);
cvShowImage("Test",&dst);
And I am running into this error
OpenCV Error: Bad argument (Array should be CvMat or IplImage) in an unknown function, file ......\modules\core\src\array.cpp, line 1238
I've looked everywhere and everyone was saying to convert img to CvMat which I attempted above.
When I did that as above code shows I get
OpenCV Error: Bad argument (Unknown array type) in unknown function, file ......\modules\core\src\matrix.cpp line 697
Thanks for your help in advance.
Just use C++ OpenCV interface instead of C interface and use convertTo function to convert between data types.
Mat img = imread("Cells2.tif");
cv::imshow("source",img);
Mat dst; // destination image
// check if we have RGB or grayscale image
if (img.channels() == 3) {
// convert 3-channel (RGB) 8-bit uchar image to 32 bit float
src.convertTo(dst, CV_32FC3);
}
else if (img.channels() == 1) {
// convert 1-chanel (grayscale) 8-bit uchar image to 32 bit float
img1.convertTo(dst, CV_32FC1);
}
// display output, note that to display dst image correctly
// we have to divide each element of dst by 255 to keep
// the pixel values in the range [0,1].
cv::imshow("output",dst/255);
waitKey();
Second part of the question To calculate the mean of all elements in dst
cv::Salar avg_pixel;
double avg;
// note that Scalar is a vector.
// If your image is RGB, Scalar will contain 3 values,
// representing color values for each channel.
avg_pixel = cv::mean(dst);
if (dst.channels() == 3) {
//if 3 channels
avg = (avg_pixel[0] + avg_pixel[1] + avg_pixel[2]) / 3;
}
if(dst.channels() == 1) {
avg = avg_pixel[0];
}
cout << "average element of m: " << avg << endl;
Here is my code for calculating the average in C++ OpenCV.
int NumPixels = img.total();
double avg;
double c;
for(int y = 0; y <= img.cols; y++)
for(int x = 0; x <= dst.rows; x++)
c+=img.at<uchar>(x,y);
avg = c/NumPixels;
cout << "Avg Value\n" << 255*avg;
For MATLAB I just load the image and take Q = mean(img(:)); which returns 1776.23
And for the return of 1612.36 I used cv:Scalar z = mean(dst);

color depth reduction with opencv and LUT

I'd like to perform a color reduction via color depth scaling.
Like this example:
the first image is CGA resolution, the second is EGA, the third is HAM.
I'd like to do it with cv::LUT because i think it is the betterway to do it.
I can do with greyscale with this code:
Mat img = imread("test1.jpg", 0);
uchar* p;
Mat lookUpTable(1, 256, CV_8U);
p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = 16 * (i/16)
LUT(img, lookUpTable, reduced);
original:
color reduced:
but if i try to do it with color I get strange result..
with this code:
imgColor = imread("test1.jpg");
Mat reducedColor;
int n = 16;
for (int i=0; i<256; i++) {
uchar value = floor(i/n) * n;
cout << (int)value << endl;
lut.at<Vec3b>(i)[2]= (value >> 16) & 0xff;
lut.at<Vec3b>(i)[1]= (value >> 8) & 0xff;
lut.at<Vec3b>(i)[0]= value & 0xff;
}
LUT(imgColor, lut, reducedColor);
You'll probably have moved on by now, but the root of the problem is that you are doing a 16-bit shift to uchar value, which is just 8-bits long. Even an 8-bit shift in this case is too much, as you'll erase all the bits in the uchar. Then there is the fact that the cv::LUT documentation explicitly states that src must be an "input array of 8-bit elements", which clearly isn't the case in your code. The net result is that only the first channel of the color image (the Blue channel) is transformed by cv::LUT.
The best way to work around these limitations is to split color images across channels, transform each channel separately, and then merge the transformed channels into a new color image. See the code below:
/*
Calculates a table of 256 assignments with the given number of distinct values.
Values are taken at equal intervals from the ranges [0, 128) and [128, 256),
such that both 0 and 255 are always included in the range.
*/
cv::Mat lookupTable(int levels) {
int factor = 256 / levels;
cv::Mat table(1, 256, CV_8U);
uchar *p = table.data;
for(int i = 0; i < 128; ++i) {
p[i] = factor * (i / factor);
}
for(int i = 128; i < 256; ++i) {
p[i] = factor * (1 + (i / factor)) - 1;
}
return table;
}
/*
Truncates channel levels in the given image to the given number of
equally-spaced values.
Arguments:
image
Input multi-channel image. The specific color space is not
important, as long as all channels are encoded from 0 to 255.
levels
The number of distinct values for the channels of the output
image. Output values are drawn from the range [0, 255] from
the extremes inwards, resulting in a nearly equally-spaced scale
where the smallest and largest values are always 0 and 255.
Returns:
Multi-channel images with values truncated to the specified number of
distinct levels.
*/
cv::Mat colorReduce(const cv::Mat &image, int levels) {
cv::Mat table = lookupTable(levels);
std::vector<cv::Mat> c;
cv::split(image, c);
for (std::vector<cv::Mat>::iterator i = c.begin(), n = c.end(); i != n; ++i) {
cv::Mat &channel = *i;
cv::LUT(channel.clone(), table, channel);
}
cv::Mat reduced;
cv::merge(c, reduced);
return reduced;
}
Both i and n are integers, therefore i/n is an integer. Perhaps you want it converted to double ((double)i/n) before taking the floor and multiplying by n?

OpenCV: How to visualize a depth image

I am using a dataset in which it has images where each pixel is a 16 bit unsigned int storing the depth value of that pixel in mm. I am trying to visualize this as a greyscale depth image by doing the following:
cv::Mat depthImage;
depthImage = cv::imread("coffee_mug_1_1_1_depthcrop.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR ); // Read the file
depthImage.convertTo(depthImage, CV_32F); // convert the image data to float type
namedWindow("window");
float max = 0;
for(int i = 0; i < depthImage.rows; i++){
for(int j = 0; j < depthImage.cols; j++){
if(depthImage.at<float>(i,j) > max){
max = depthImage.at<float>(i,j);
}
}
}
cout << max << endl;
float divisor = max / 255.0;
cout << divisor << endl;
for(int i = 0; i < depthImage.rows; i++){
for(int j = 0; j < depthImage.cols; j++){
cout << depthImage.at<float>(i,j) << ", ";
max = depthImage.at<float>(i,j) /= divisor;
cout << depthImage.at<float>(i,j) << endl;
}
}
imshow("window", depthImage);
waitKey(0);
However, it is only showing two colours this is because all of the values are close together i.e. in the range of 150-175 + the small values which show up black (see below).
Is there a way to normalize this data such that it will show various grey levels to highlight these small depth differences?
According to the documentation, the function imshow can be used with a variety of image types. It support 16-bit unsigned images, so you can display your image using
cv::Mat map = cv::imread("image", CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH);
cv::imshow("window", map);
In this case, the image value range is mapped from the range [0, 255*256] to the range [0, 255].
If your image only contains values on the low part of this range, you will observe an obscure image. If you want to use the full display range (from black to white), you should adjust the image to cover the expected dynamic range, one way to do it is
double min;
double max;
cv::minMaxIdx(map, &min, &max);
cv::Mat adjMap;
cv::convertScaleAbs(map, adjMap, 255 / max);
cv::imshow("Out", adjMap);
Adding to samg' answer, you can expand even more the range of your displayed image.
double min;
double max;
cv::minMaxIdx(map, &min, &max);
cv::Mat adjMap;
// expand your range to 0..255. Similar to histEq();
map.convertTo(adjMap,CV_8UC1, 255 / (max-min), -min);
// this is great. It converts your grayscale image into a tone-mapped one,
// much more pleasing for the eye
// function is found in contrib module, so include contrib.hpp
// and link accordingly
cv::Mat falseColorsMap;
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_AUTUMN);
cv::imshow("Out", falseColorsMap);
The result should be something like the one below
Ifimshow input has floating point data type then the function assumes that pixel values are in [0; 1] range. As result all values higher than 1 are displayed white.
So you need not divide your divisor by 255.
Adding to Sammy answer, if the original range color is [-min,max] and you want to perform histogram equalization and display the Depth color, the code should be like below:
double min;
double max;
cv::minMaxIdx(map, &min, &max);
cv::Mat adjMap;
// Histogram Equalization
float scale = 255 / (max-min);
map.convertTo(adjMap,CV_8UC1, scale, -min*scale);
// this is great. It converts your grayscale image into a tone-mapped one,
// much more pleasing for the eye
// function is found in contrib module, so include contrib.hpp
// and link accordingly
cv::Mat falseColorsMap;
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_AUTUMN);
cv::imshow("Out", falseColorsMap);