Problems while rectifying stereo images in matlab - matlab

I am trying to use stereo imaging for 3D reconstruction, however when I use the tutorials and tools in matlab for stereo vision, I get erroneous results. I use a Loreo 3D macro lens to take images of small instruments at a distance of around 23mm. Then after cropping the images to create left and right images I use the stereo calibration app (I have also used code from the matlab tutorial which does pretty much the same thing). These are the kinds of results I get.
Stereo calibration using matlab's app
I am aware that the reprojection errors are quite high, but have tried a lot of things like changes in image quantity, illumination, checkerboard size and the skew, tangential distortion, and coefficients in the app to reduce this value without any luck. At a first glance, the Extrinsic reconstruction in the bottom right looks accurate, as the dimensions are quite correct. Therefore when I use the exported stereoParameters with a new image and the next code:
Isv = imread('IMG_0036.JPG');
I1 = imcrop(Isv, [0 0 2592 3456]);
I2 = imcrop(Isv, [2593 0 2592 3456]);
% Rectify the images.
[J1, J2] = rectifyStereoImages(I1, I2, stereoParams, 'OutputView', 'valid');
% Display the images before rectification.
figure;
imshow(stereoAnaglyph(I1, I2), 'InitialMagnification', 30);
title('Before Rectification');
% Display the images after rectification.
figure;
imshow(stereoAnaglyph(J1, J2), 'InitialMagnification', 30);
title('After Rectification');
disparityRange = [0, 64];
disparityMap = disparity(rgb2gray(J1), rgb2gray(J2), 'DisparityRange', ...
disparityRange);
figure;
imshow(disparityMap, disparityRange, 'InitialMagnification', 30);
colormap('jet');
colorbar;
title('Disparity Map');
point3D = reconstructScene(disparityMap, stereoParams);
% Convert from millimeters to meters.
point3D = point3D / 1000;
% Plot points between 3 and 7 meters away from the camera.
z = point3D(:, :, 3);
maxZ = 7;
minZ = 3;
zdisp = z;
zdisp(z < minZ | z > maxZ) = NaN;
point3Ddisp = point3D;
point3Ddisp(:,:,3) = zdisp;
figure
pcshow(point3Ddisp, J1, 'VerticalAxis', 'Y', 'VerticalAxisDir', 'Down' );
xlabel('X');
ylabel('Y');
zlabel('Z');
I get these erroneous rectification, disparity and 3D reconstruction.
Rectification, disparity and erroneous 3D reconstruction
As it can be seen, the rectification looks bad, as it the objects are too separated in my opinion; also the disparity results look very random, and finally the 3D reconstruction simply has no discernible outcome.
Please, I ask for any possible help, comments or recommendations regarding this issue.

Your reprojection errors are indeed high... Leaving that aside for the moment, your most immediate problem is that the disparityRange is too small.
The rectified images look fine. Corresponding points appear to be on the same pixel rows in both images, which is what you want. Display the anaglyph of the rectified images using imtool, and use the ruler widget to measure the distances between some of the corresponding points. That should give you an idea of what your disparity range should be. [0 64] is definitely too small.
To improve the reprojection errors, normally, I would say get more images. But you already have 30 pairs, which is a good number. You can specify the initial intrinsics and distortion, if you can get them off the camera manufacturer's spec, but I doubt they would help here. Try turning on tangential distortion estimation, and try using 3 radial distortion coefficients instead of two.
Also, see if you can more your cameras farther away from the scene. It may be that at such a short distance the pinhole camera model starts to break down.
There are also things you can do to improve your disparity and 3D reconstruction:
Try varying the BlockSize parameter of the disparity function
Try applying histogram equalization and/or low-pass filtering to your rectified images before computing disparity
Try median filtering the resulting disparity map to reduce the noise
Another tip: since you know approximately where the objects of interest are relative to the cameras, you can simply exclude the 3D points whose z-coordinate is too big or two small (or negative). This should give you a much cleaner 3D plot. You already have this in your code, but you should modify it to have appropriate units and thresholds for Z.

Related

Detect Centre of Target in MATLAB

Can anyone suggest alternative means of detecting the centre of each of the targets in the following image using MATLBAB:
My current approach uses regionprops and centroid detection.
clc, clear all, close all
format long
beep off
rng('default')
I=imread('WP_20160811_13_38_26_Pro.jpg');
BW=im2bw(I);
BW=imcomplement(BW);
s = regionprops(BW, 'area','Centroid');
centroids = cat(1, s.Centroid);
imshow(BW)
hold on
plot(centroids(:,1), centroids(:,2), 'b*')
hold off
Is there a more precise way of detecting the centre as this approach seems sensitive to noise, perspective distortion, etc. Is there a way to find the intersection of each of the two quarter circles.
Another type of target I am considering is:
Can anyone suggest a way for detecting the centre of a crosshair? Thanks
My modification works 100% efficient for your image:
I = imadjust(imcomplement(rgb2gray(imread('WP_20160811_13_38_26_Pro.jpg'))));
filtered_BW = bwareaopen(im2bw(I), 500, 4);
% 500 is the area of ignored objects
final_BW = imdilate(filtered_BW, strel('disk', 5));
s = regionprops(final_BW, 'area','Centroid');
centroids = cat(1, s([s.Area] < 10000).Centroid);
% the condition leaves out the big areas on both sides
figure; imshow(final_BW)
hold on
plot(centroids(:,1), centroids(:,2), 'b*')
hold off
The functions I am adding:
rgb2gray to have one dimension of values!
imadjust to automatically optimize the brightness and contrast,
bwareaopen to get rid of small islands,
imdilate and strel to grow the regions and connect disconnected regions.

Matlab Stereo Camera Calibration Scene Reconstruction Error

I am trying to use the Computer Vision System Toolbox to calibrate the pair of cameras below in order to be able to generate a 3-D point cloud of a vehicle at a range between 1 to 5m. The output image size was approximately 1 MB per image for the checkerboard calibration images and the checkerboard square size was 25 mm. The cameras used were a pair of SJ4000 HD1080P cameras. The cameras were placed as parallel to each other as possible with no angle in the vertical axis. The checkboard calibration was done with the aid of a bright light and whiteboard. The mean error per pixel using the stereo camera calibrator code was 3.31 with 31/32 successful pairings. The approximate distance to the checkerboard was 30 cm and the distance between the cameras was 20 cm.
The problem I am encountering upon rectification is during the 3D Reconstruction of the scene. The figure below is what was outputted. I’m not sure if there is a parameter that is missing in the camera setup or if there is something that is missing / needs to be added in the script. Below is the code that was used for the Stereo anaglyph and scene reconstruction that was adapted from the Matlab Stereo Camera Calibration tutorial.
% Read in the stereo pair of images.
I1 = imread('left.jpg');
I2 = imread('right.jpg');
% Rectify the images.
[J1, J2] = rectifyStereoImages(I1, I2, stereoParams);
% Display the images before rectification.
figure;
imshow(stereoAnaglyph(I1, I2), 'InitialMagnification', 50);
title('Before Rectification');
% Display the images after rectification.
figure;
imshow(stereoAnaglyph(J1, J2), 'InitialMagnification', 50);
title('After Rectification');
%
% Compute Disparity for 3-D Reconstruction
% The distance in pixels between corresponding points in the rectified images is called disparity.
% The disparity is used for 3-D reconstruction, because it is proportional to the distance between the cameras and the 3-D world point.
disparityMap = disparity(rgb2gray(J1), rgb2gray(J2));
figure;
imshow(disparityMap, [0, 64], 'InitialMagnification', 50);
colormap('jet');
colorbar;
title('Disparity Map');
%Reconstruct the 3-D Scene
%Reconstruct the 3-D world coordinates of points corresponding to each pixel from the disparity map.
point3D = reconstructScene(disparityMap, stereoParams);
% Convert from millimeters to meters.
point3D = point3D / 1000;
% Visualize the 3-D Scene
% Plot points between 3 and 7 meters away from the camera.
z = point3D(:, :, 3);
zdisp = z;
point3Ddisp = point3D;
point3Ddisp(:,:,3) = zdisp;
showPointCloud(point3Ddisp, J1, 'VerticalAxis', 'Y',...
'VerticalAxisDir', 'Down' );
xlabel('X');
ylabel('Y');
zlabel('Z');
I have included the images of the Scene Reconstruction, Disparity Map, Mean Error Per Pixel and After Rectification. The version of Matlab I am using is R2014b Student Edition purchased from the Matlab Website.
I think that the most clear problem here is that the re-projection
error (more than 3 pixels) you got in the stereo calibration, points
to a calibration issue. I would advise you to re-calibrate to get a
smaller re-projection error (should be significantly below 1 pixel
for good reconstruction results).
Another question regarding your calibration: what lens distortion
model do you use? I believe you've got fish-eye lenses there - I
am not sure that the Matlab toolbox knows how to handle these.
You have two problems here. One, as #ezfn pointed out, is that the lens distortion may simply be too severe. The best thing to try here is to take even more calibration images so that you have the checkerboard close to the edges and corners of the field of view. Also, try placing the checkerboard at varying distances from the camera. See if you can get those reprojection errors down.
The second issue here is that you need to change the 'DisparityRange' parameter of the disparity function. Display the anaglyph image using imtool, and use the ruler widget to measure distances between some pairs of corresponding points. That should give you the idea of what the disparity range should be. Just looking at the image I can see that [0 64] is way too small.

Subpixel edge detection for almost vertical edges

I want to detect edges (with sub-pixel accuracy) in images like the one displayed:
The resolution would be around 600 X 1000.
I came across a comment by Mark Ransom here, which mentions about edge detection algorithms for vertical edges. I haven't come across any yet. Will it be useful in my case (since the edge isn't strictly a straight line)? It will always be a vertical edge though. I want it to be accurate till 1/100th of a pixel at least. I also want to have access to these sub-pixel co-ordinate values.
I have tried "Accurate subpixel edge location" by Agustin Trujillo-Pino. But this does not give me a continuous edge.
Are there any other algorithms available? I will be using MATLAB for this.
I have attached another similar image which the algorithm has to work on:
Any inputs will be appreciated.
Thank you.
Edit:
I was wondering if I could do this:
Apply Canny / Sobel in MATLAB and get the edges of this image (note that it won't be a continuous line). Then, somehow interpolate this Sobel edges and get the co-ordinates in subpixel. Is it possible?
A simple approach would be to project your image vertically and fit the projected profile with an appropriate function.
Here is a try, with an atan shape:
% Load image
Img = double(imread('bQsu5.png'));
% Project
x = 1:size(Img,2);
y = mean(Img,1);
% Fit
f = fit(x', y', 'a+b*atan((x0-x)/w)', 'Startpoint', [150 50 10 150])
% Display
figure
hold on
plot(x, y);
plot(f);
legend('Projected profile', 'atan fit');
And the result:
I get x_0 = 149.6 pix for your first image.
However, I doubt you will be able to achieve a subpixel accuracy of 1/100th of pixel with those images, for several reasons:
As you can see on the profile, your whites are saturated (grey levels at 255). As you cut the real atan profile, the fit is biased. If you have control over the experiments, I suggest you do it again again with a smaller exposure time for instance.
There are not so many points on the transition, so there is not so many information on where the transition is. Typically, your resolution will be the square root of the width of the atan (or whatever shape you prefer). In you case this limits the subpixel resolution at 1/5th of a pixel, at best.
Finally, your edges are not stricly vertical, they are slightly titled. If you choose to use this projection method, to increase the accuracy you should look for a way to correct this tilt before projecting. This won't increase your accuracy by several orders of magnitude, though.
Best,
There is a problem with your image. At pixel level, it seems like there are four interlaced subimages (odd and even rows and columns). Look at this zoomed area close to the edge.
In order to avoid this artifact, I just have taken the even rows and columns of your image, and compute subpixel edges. And finally, I look for the best fitting straight line, using the function clsq whose code is in this page:
%load image
url='http://i.stack.imgur.com/bQsu5.png';
image = imread(url);
imageEvenEven = image(1:2:end,1:2:end);
imshow(imageEvenEven, 'InitialMagnification', 'fit');
% subpixel detection
threshold = 25;
edges = subpixelEdges(imageEvenEven, threshold);
visEdges(edges);
% compute fit line
A = [ones(size(edges.x)) edges.x edges.y];
[c n] = clsq(A,2);
y = [1,200];
x = -(n(2)*y+c) / n(1);
hold on;
plot(x,y,'g');
When executing this code, you can see the green line that best aproximate all the edge points. The line is given by the equation c + n(1)*x + n(2)*y = 0
Take into account that this image has been scaled by 1/2 when taking only even rows and columns, so the right coordinates must be scaled.
Besides, you can try with the other tree subimages (imageEvenOdd, imageOddEven and imageOddOdd) and combine the four straigh lines to obtain the best solution.

Visualization of (Gaussian, Laplacian, etc.) filters in Matlab

How is it possible to visualize filters the way it is presented in the following website:
http://www.robots.ox.ac.uk/~vgg/research/texclass/filters.html
For example, in Fig.1 the last four square subimages visualize four different Gaussian filters.
They're just images after all, the only trick is to use the right scale. Fortunately this is pretty easy in MATLAB.
So if your filter is G, then to visualize it you can just do
imshow(G,[])
The [ ] will set the right scale for you (min_value, max_value).
So for example, for a Gaussian of sigma 30 (that high for visualization purposes here) these lines:
G1=fspecial('gauss',[round(6*sigma), round(6*sigma)], sigma);
imshow(G1,[]);
Lead to this image:
Same principle for any image you want to display that has an unusual range (i.e. different from [0 255]).
To visualize multiple images at once you have to use subplot
This is just a little bonus, but because the filter is a 2D function, we can also map the amplitude of the function in the Z direction as well. You would basically take the intensity that is shown in imshow and just map it to the third dimension. In other words, replicating what #cifz has done, we can also define a 2D grid, then use mesh or surf to visualize it in 3D.
As such:
sigma = 30;
G1=fspecial('gauss',[round(6*sigma), round(6*sigma)], sigma);
[X,Y] = meshgrid(1:size(G1,2), 1:size(G1,1));
mesh(X, Y, G1);
xlabel('X'); ylabel('Y'); zlabel('Amplitude');
title('3D visualization of the Gaussian filter');
colorbar;
You'd get:
This is obviously not what the OP wanted, but I figured I'd add this bonus in case anyone was curious.

How to improve image quality in Matlab

I'm building an "Optical Character Recognition" system.
so far the system is capable to identify licence plates in good quality without any noise.
what I want in the next level is to be able to identify licence plates in poor quality beacuse of different reasons.
for example, let's look at the next plate:
as you see, the numbers are not look clearly, because of light returns or something else.
for my question: how can I improve the image quality, so when I move to binary image the numbers will not fade away?
thanks in advance.
We can try to correct for lighting effect by fitting a linear plane over the image intensities, which will approximate the average level across the image. By subtracting this shading plane from the original image, we can attempt to
normalize lighting conditions across the image.
For color RGB images, simply repeat the process on each channel separately,
or even apply it to a different colorspace (HSV, Lab*, etc...)
Here is a sample implementation:
function img = correctLighting(img, method)
if nargin<2, method='rgb'; end
switch lower(method)
case 'rgb'
%# process R,G,B channels separately
for i=1:size(img,3)
img(:,:,i) = LinearShading( img(:,:,i) );
end
case 'hsv'
%# process intensity component of HSV, then convert back to RGB
HSV = rgb2hsv(img);
HSV(:,:,3) = LinearShading( HSV(:,:,3) );
img = hsv2rgb(HSV);
case 'lab'
%# process luminosity layer of L*a*b*, then convert back to RGB
LAB = applycform(img, makecform('srgb2lab'));
LAB(:,:,1) = LinearShading( LAB(:,:,1) ./ 100 ) * 100;
img = applycform(LAB, makecform('lab2srgb'));
end
end
function I = LinearShading(I)
%# create X-/Y-coordinate values for each pixel
[h,w] = size(I);
[X Y] = meshgrid(1:w,1:h);
%# fit a linear plane over 3D points [X Y Z], Z is the pixel intensities
coeff = [X(:) Y(:) ones(w*h,1)] \ I(:);
%# compute shading plane
shading = coeff(1).*X + coeff(2).*Y + coeff(3);
%# subtract shading from image
I = I - shading;
%# normalize to the entire [0,1] range
I = ( I - min(I(:)) ) ./ range(I(:));
end
Now lets test it on the given image:
img = im2double( imread('http://i.stack.imgur.com/JmHKJ.jpg') );
subplot(411), imshow(img)
subplot(412), imshow( correctLighting(img,'rgb') )
subplot(413), imshow( correctLighting(img,'hsv') )
subplot(414), imshow( correctLighting(img,'lab') )
The difference is subtle, but it might improve the results of further image processing and OCR task.
EDIT: Here is some results I obtained by applying other contrast-enhancement techniques IMADJUST, HISTEQ, ADAPTHISTEQ on the different colorspaces in the same manner as above:
Remember you have to fine-tune any parameter to fit your image...
It looks like your question has been more or less answered already (see d00b's comment); however, here are a few basic image processing tips that might help you here.
First, you could try a simple imadjust. This simply maps the pixel intensities to a "better" value, which often increases the contrast (making it easier to view/read). I have had a lot of success with it in my work. It is easy to use too! I think its worth a shot.
Also, this looks promising if you simply want a higher resolution image.
Enjoy the "pleasure" of image-processing in MATLAB!
Good luck,
tylerthemiler
P.S. If you are flattening the image to binary tho, you are most likely ruining the image to start with, so don't do that if you can avoid it!
As you only want to find digits (of which there are only 10), you can use cross-correlation.
For this you would Fourier transform the picture of the plate. You also Fourier transform a pattern you want to match a good representation of a picture of the digit 1. Then you multiply in fourier space and inversely Fourier transform the result.
In the final cross-correlation, you will see pronounced peaks, where the pattern overlaps nicely with your image.
You do this 10 times and know where each digit is. Note that you must correct the tilt before you do the cross correlation.
This method has the advantage that you don't have to threshold your image.
There are certainly much more sophisticated algorithms in the literature for assigning number plates. One could for example use Bayes theory to estimate which digit would most likely occur (this helps a lot if you already have a databases of possible numbers).