Automatic Dial Reader

This is an explanation of how I created an automatic dial reader with Matlab and a webcam.

First I took a picture of the dial indicator.
Dial indicator 1, color photo

Then I converted it to a single color. (This is necessary for the contour extraction).
Dial indicator 2, black and white

After that, I extracted the contours. There are a fair number of contour extraction methods, but I used the Matlab default: Sobel. It seemed to have the best performance for my needs. I wanted it to extract only the most important information, i.e. the longest lines.
Dial indicator 3, contours

After that, I thickened the image in order to increase the weight of the lines, and thus increase the significance of the two long lines that form the edge of the need
Dial indicator 4, thickened

Finally, I performed a Hough transform on the image, filtering all but the longest two lines. These two remaining lines were (usually) the left- and right-hand side of the needle. Where the lines intersect is where the needle points.
Dial indicator 5, Hough transformed

This is the Matlab code. Notice, I used the Hough algorithm from Matlab. This is only available on Matlab 7 SP2 and later. If you do not have this, you can use this
Hough transformation code (although it's not quite as nice) or download a free image analysis toolbox.

function m=dial_reader(Image,lightThresh)
%hough_analysis performs a hough transformation on each image in a movie
%file. The two longest lines are then stored in (x1,y1) and (x2,y2)
%coordinate fashion in cal_lines.

global circle rectCrop BandWect

thickener = strel('square',4);
cropImage=imcrop(Image,rectCrop);
% BandW=uint8(im2bw(Image,lightThresh-0.05));
BandW=uint8(im2bw(cropImage,lightThresh-0.05));
[BandWe,thresh]=edge(BandW,'sobel');
BandWec=BandWe.*imcrop(circle,rectCrop);
% BandWec=BandWe.*circle;
BandWect=imdilate(BandWec,thickener);

% image(BandW);
% drawnow

imagesc(BandWect);
colormap(jet)
drawnow

[TransH,theta,rho]=hough(BandWect);
peaksH=houghpeaks(TransH,2,'Threshold',0.3);
linesH=houghlines(BandWect, theta, rho, peaksH,'MinLength',150,'Fillgap',20);
if length(linesH) < 2 %If we've only found one line for some reason or another.
% m=0
else
[u1,v1]=linesH.point1;
[u2,v2]=linesH.point2;

cal_lines(1:4, 1)=[[u1(1) u2(1)] [u1(2) u2(2)]];
cal_lines(1:4, 2)=[[v1(1) v2(1)] [v1(2) v2(2)]];

v=cal_lines(1:4,1);
w=cal_lines(1:4,2);

a1 = v(4) - v(3);
b1 = v(1) - v(2);
c1 = v(1)*v(4) - v(2)*v(3);

a2 = w(4) - w(3);
b2 = w(1) - w(2);
c2 = w(1)*w(4) - w(2)*w(3);
if (-a1*b2+b1*a2)==0
m=1/0;
%Does nothing. This triggers an error and a catch in the calling
%loop.
else
x1 = (-b2*c1+c2*b1)/(-a1*b2+b1*a2);
y1 = (-a1*c2+c1*a2)/(-a1*b2+b1*a2);
m=[x1 y1];
% line(cal_lines(1:2,1),cal_lines(3:4,1),'Color',[0 0 1]) %Blue line
% line(cal_lines(1:2,2),cal_lines(3:4,2),'Color',[1 0 0]) %Red line
hold on;
for k = 1:length(linesH)
xy = [linesH(k).point1; linesH(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','magenta');
% Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
end
plot(x1,y1,'g-x');
hold off;

end
drawnow
end

save calibration_transforms.mat cal_lines rates