I cannot see what I am doing wrong after checking the code a thousand times.
The algorithm is very simple: I have a CV_16U image with the disparity values called disp, and I am trying to implement the building of the u and v disparities in order to detect obstacles.
Mat v_disparity, u_disparity;
v_disparity=Mat::zeros(disp.rows,numberOfDisparities*16, CV_16U);
u_disparity=Mat::zeros(numberOfDisparities*16,disp.cols, CV_16U);
for(int i = 0; i < disp.rows; i++)
{
d = disp.ptr<ushort>(i); //d[j] is the disparity value
for (int j = 0; j < disp.cols; ++j)
{
v_disparity.at<uchar>(i,(d[j]))++;
u_disparity.at<uchar>((d[j]),j)++;
}
}
The problem is that when I use imshow to print both disparities after converting to 8bit Unsigned. The u-disparity is wrong, since it has the shape it should, but it's half the horizontal dimension, being the right pixels black.
I finally figured it out. It was just that I used a wrong template while accessing to the value of the pixels in u and v-disparities. In the v-disparity I didn't detect it since I thought there was no pixels in disp with high disparity values.
To sum up, the following lines:
v_disparity.at<uchar>(i,(d[j]))++;
u_disparity.at<uchar>((d[j]),j)++;
must be replaced by:
v_disparity.at<ushort>(i,(d[j]))++;
u_disparity.at<ushort>((d[j]),j)++;
since both images are CV_16U, and the type uchar is 8 bit, not 16 bit.
Related
I got a little problem with the depth map to point color map,
i simply threshold the nearst depth (about 70-80cm), then i bitwise and the thresholded depth image to corresponding color map,
Mat depthFilter(Mat depth,Mat color){
Mat I;
depth.convertTo(I, CV_8UC1, 255.0 / 4096.0);
unsigned char *input = (unsigned char*)(I.data);
for (int i = 0; i < I.cols; i++){
for (int j = 0; j < I.rows; j++){
int pixel = input[I.cols*j + i];
if (!(pixel <52 && pixel >42)){
input[I.cols*j + i] = 0;
}
else
{
input[I.cols*j + i] = 255;
}
}
}
cvtColor(color, color, CV_BGR2GRAY);
bitwise_and(I, color, I);
return I;
}
(I'm using OpenCvKinect, which uses OpenNi and OpenCv)
But my problem is, the point's are not the same.. I think i need to find some relation between two images, but how :)!
http://postimg.org/image/hyxt25bwd/
I would see Why kinect color and depth won't align correctly? as they are having a similar problem in matlab. The answer suggest to use OpenNI's AlternateViewCapability class to align the images. This is the documentation from the older version of OpenNI (1.5) as I cannot find the 2.0 documentation for C++, but there is probably a similar method. The images on that answer show the difference the shift made.
The code is essentially
depth.GetAlternativeViewPointCap().SetViewPoint(image); //depth is depth generator
//image is color generator
I am not sure if you have already solved the problem of alignment, however this has been implemented within OpenCVKinect Wrappers that you are already using.
To acquire aligned Depth and Color images from Kinect, you need to use setMode function as follows:
setMode(C_MODE_DEPTH | C_MODE_COLOR | C_MODE_ALIGNED);
I have the histogram for an image which i have calculated. I want to display this as an image so that I can actually see the histogram. I think my problem is to do with scaling although i am slightly confused over the co ordinate system starting with 0,0 in the top left as well.
int rows = channel.rows;
int cols = channel.cols;
int hist[256] = {0};
for(int i = 0; i<rows; i++)
{
for(int k = 0; k<cols; k++ )
{
int value = channel.at<cv::Vec3b>(i,k)[0];
hist[value] = hist[value] + 1;
}
}
Mat histPlot = cvCreateMat(256, 500,CV_8UC1);
for(int i = 0; i < 256; i++)
{
int mag = hist[i];
line(histPlot,Point(i,0),Point(i,mag),Scalar(255,0,0));
}
namedWindow("Hist",1);
imshow("Hist",histPlot);
This is my calculation for creating my histogram and displaying the result. If i do mag/100 in my second loop then i get some resemblance of a plot appearing (although upside down). I call this method whenever i adjust a value of my image, so the histogram should also change shape, which it doesn't appear to do. Any help in scaling the histogram and displaying it properly is appreciated.
please don't use cvCreateMat ( aka, the old c-api ), you also seem to have rows and cols wrong, additionally, if you want a color drawing, you need a color image as well, so make that:
Mat histPlot( 500, 256, CV_8UC3 );
image origin is top-left(0,0), so you've got to put y in reverse:
line(histPlot,Point(i,histPlot.rows-1),Point(i,histPlot.rows-1-mag/100),Scalar(255,0,0));
I have the below image after some conversions.
How can I find a distance between these two lines?
A simple way to do this would be
- Scan across a row until you find a pixel above a threshold.
- Keep scanning until you find a pixel below the threshold.
- Count the pixels until the next pixel above the threshold.
- Take the average across a number of rows sampled from the image (or all rows)
- You'll need to know the image resolution (e.g. dpos per inch) to convert the count to an actual distance
An efficient method to scan across rows can be found in the OpenCV documentation
A more complicated approach would use Houghlines to extract lines. It will give you two points on each line (hopefully you only have two). From that it is possible to work out a distance formula, assuming the lines are parallel.
A skeleton code (not efficient, just readable so that you know how to do it) would be,
cv::Mat source = cv::imread("source.jpg", CV_LOAD_IMAGE_GRAYSCALE);
std::vector<int> output;
int threshold = 35, temp_var; // Change in accordance with data
int DPI = 30; // Digital Pixels per Inch
for (int i=0; i<source.cols; ++i)
{
for (int j=0; j<source.rows; ++j)
{
if (source.at<unsigned char>(i,j) > threshold)
{
temp_var = j;
for (; j<source.rows; ++j)
if (source.at<unsigned char>(i,j) > threshold)
output.push_back( (j-temp_var)/DPI ); // Results are stored in Inch
}
}
}
Afterwards, you could take an average of all the elements in output, etc.
HTH
Assumptions:
You have only two continuous lines without any break in between.
No other pixels (noise) apart from the lines
My proposed solution: Almost same as given above
Mark leftmost line as line 1. Mark rightmost line as line 2.
Scan the image (Mat in OpenCV) from the leftmost column and make a list of points matching the pixel value of line 1
Scan the image (Mat in OpenCV) from the rightmost column and make a list of points matching the pixel value of line 2
Calculate the distance between points from that list using the code below.
public double euclideanDistance(Point a, Point b){
double distance = 0.0;
try{
if(a != null && b != null){
double xDiff = a.x - b.x;
double yDiff = a.y - b.y;
distance = Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff, 2));
}
}catch(Exception e){
System.err.println("Something went wrong in euclideanDistance function in "+Utility.class+" "+e.getMessage());
}
return distance;
}
multiplying each pixel by the average blurring mask *(1/9) but the result is totally different.
PImage toAverageBlur(PImage a)
{
PImage aBlur = new PImage(a.width, a.height);
aBlur.loadPixels();
for(int i = 0; i < a.width; i++)
{
for(int j = 0; j < a.height; j++)
{
int pixelPosition = i*a.width + j;
int aPixel = ((a.pixels[pixelPosition] /9));
aBlur.pixels[pixelPosition] = color(aPixel);
}
}
aBlur.updatePixels();
return aBlur;
}
Currently, you are not applying an average filter, you are only scaling the image by a factor of 1/9, which would make it darker. Your terminology is good, you are trying to apply a 3x3 moving average (or neighbourhood average), also known as a boxcar filter.
For each pixel i,j, you need to take the sum of (i-1,j-1), (i-1,j), (i-1,j+1), (i,j-1), (i,j),(i,j+1),(i+1,j-1),(i+1,j),(i+1,j+1), then divide by 9 (for a 3x3 average). For this to work, you need to not consider the pixels on the image edge, which do not have 9 neighbours (so you start at pixel (1,1), for example). The output image will be a pixel smaller on each side. Alternatively, you can mirror values out to add an extra line to your input image which will make the output image the same size as the original.
There are more efficient ways of doing this, for example using FFT based convolution; these methods are faster because they don't require looping.
In Matlab, If A is a matrix, sum(A) treats the columns of A as vectors, returning a row vector of the sums of each column.
sum(Image); How could it be done with OpenCV?
Using cvReduce has worked for me. For example, if you need to store the column-wise sum of a matrix as a row matrix you could do this:
CvMat * MyMat = cvCreateMat(height, width, CV_64FC1);
// Fill in MyMat with some data...
CvMat * ColSum = cvCreateMat(1, MyMat->width, CV_64FC1);
cvReduce(MyMat, ColSum, 0, CV_REDUCE_SUM);
More information is available in the OpenCV documentation.
EDIT after 3 years:
The proper function for this is cv::reduce.
Reduces a matrix to a vector.
The function reduce reduces the matrix to a vector by treating the
matrix rows/columns as a set of 1D vectors and performing the
specified operation on the vectors until a single row/column is
obtained. For example, the function can be used to compute horizontal
and vertical projections of a raster image. In case of REDUCE_MAX and
REDUCE_MIN , the output image should have the same type as the source
one. In case of REDUCE_SUM and REDUCE_AVG , the output may have a
larger element bit-depth to preserve accuracy. And multi-channel
arrays are also supported in these two reduction modes.
OLD:
I've used ROI method: move ROI of height of the image and width 1 from left to right and calculate means.
Mat src = imread(filename, 0);
vector<int> graph( src.cols );
for (int c=0; c<src.cols-1; c++)
{
Mat roi = src( Rect( c,0,1,src.rows ) );
graph[c] = int(mean(roi)[0]);
}
Mat mgraph( 260, src.cols+10, CV_8UC3);
for (int c=0; c<src.cols-1; c++)
{
line( mgraph, Point(c+5,0), Point(c+5,graph[c]), Scalar(255,0,0), 1, CV_AA);
}
imshow("mgraph", mgraph);
imshow("source", src);
EDIT:
Just out of curiosity, I've tried resize to height 1 and the result was almost the same:
Mat test;
cv::resize(src,test,Size( src.cols,1 ));
Mat mgraph1( 260, src.cols+10, CV_8UC3);
for(int c=0; c<test.cols; c++)
{
graph[c] = test.at<uchar>(0,c);
}
for (int c=0; c<src.cols-1; c++)
{
line( mgraph1, Point(c+5,0), Point(c+5,graph[c]), Scalar(255,255,0), 1, CV_AA);
}
imshow("mgraph1", mgraph1);
cvSum respects ROI, so if you move a 1 px wide window over the whole image, you can calculate the sum of each column.
My c++ got a little rusty so I won't provide a code example, though the last time I did this I used OpenCVSharp and it worked fine. However, I'm not sure how efficient this method is.
My math skills are getting rusty too, but shouldn't it be possible to sum all elements in columns in a matrix by multiplying it by a vector of 1s?
For an 8 bit greyscale image, the following should work (I think).
It shouldn't be too hard to expand to different image types.
int imgStep = image->widthStep;
uchar* imageData = (uchar*)image->imageData;
uint result[image->width];
memset(result, 0, sizeof(uchar) * image->width);
for (int col = 0; col < image->width; col++) {
for (int row = 0; row < image->height; row++) {
result[col] += imageData[row * imgStep + col];
}
}
// your desired vector is in result