Training Lloyd-Max quatizer with PDF from histogram of sampled data - histogram

I'm trying to code basic scalar Lloy-Max qunatizer... and have the possibility to initialize it by:
range-min, range-max, pdf(x).
I know that LM can be trained directly on given training-set (I already have with) but I want to have also possibility to "train" it via provided probability-density-function.
It seams that I have bug in "approximation" of PDF from histrogram of sampled-data or in training LM via PDF.
I construct histogram from training-set used to train 1st LM, train 2nd LM with PDF approximated "by" historgram and compare the difference (final ranges and theirs reconstruction value).
1)
Function that I pass to initialization of 2nd LM is Histogram.PDF(x):
for 'x' it locates corresponding bin and returns:
bin-counter / ( total-histogram-counter * bin-range-width )
It is alright? If I integrate this function on [0,1] it yields 1.0 as proper PDF(x) should do.
2) I train LM this way... Do you see anybody problem in it?
Func< float,float > x_pdf = (x) => x * pdf( x );
for( var iter = maxIters; iter --> 0; )
{
for( var i = 0; i < levels; ++i ) // update values
{
var r0 = m_Ranges[ i ];
var r1 = m_Ranges[ i+1 ];
var denom = Integrate( pdf, r0, r1 );
if( denom != 0f )
{
var numer = Integrate( x_pdf, r0, r1 );
m_Values[ i ] = numer / denom;
}
else
{
m_Values[ i ] = 0.5f * ( r0 + r1 );
}
}
var finished = true;
for( var i = 1; i < levels; ++i ) // update ranges
{
var r = 0.5f * ( m_Values[ i-1 ] + m_Values[ i ] );
var diff = m_Ranges[ i ] - r;
m_Ranges[ i ] = r;
finished &= Abs( diff ) <= eps;
}
if( finished == true )
break;
}
m_Values : float[n]
m_Ranges : float[n+1] ... initialized before training loop to uniformly divide given range (range-min, range-min + step, range-min + 2*step, ..., range-max)
What wories me is that ranges&values after training 1st and 2nd LM are for some inputs realy quite different. And LM trained with PDF(x) (provided by histogram of training-set) has sometimes reconstruction-values outside corresponding sub-ranges.

Related

How can I get ellipse coefficient from fitEllipse function of OpenCV?

I want to extract the red ball from one picture and get the detected ellipse matrix in picture.
Here is my example:
I threshold the picture, find the contour of red ball by using findContour() function and use fitEllipse() to fit an ellipse.
But what I want is to get coefficient of this ellipse. Because the fitEllipse() return a rotation rectangle (RotatedRect), so I need to re-write this function.
One Ellipse can be expressed as Ax^2 + By^2 + Cxy + Dx + Ey + F = 0; So I want to get u=(A,B,C,D,E,F) or u=(A,B,C,D,E) if F is 1 (to construct an ellipse matrix).
I read the source code of fitEllipse(), there are totally three SVD process, I think I can get the above coefficients from the results of those three SVD process. But I am quite confused what does each result (variable cv::Mat x) of each SVD process represent and why there are three SVD here?
Here is this function:
cv::RotatedRect cv::fitEllipse( InputArray _points )
{
Mat points = _points.getMat();
int i, n = points.checkVector(2);
int depth = points.depth();
CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));
RotatedRect box;
if( n < 5 )
CV_Error( CV_StsBadSize, "There should be at least 5 points to fit the ellipse" );
// New fitellipse algorithm, contributed by Dr. Daniel Weiss
Point2f c(0,0);
double gfp[5], rp[5], t;
const double min_eps = 1e-8;
bool is_float = depth == CV_32F;
const Point* ptsi = points.ptr<Point>();
const Point2f* ptsf = points.ptr<Point2f>();
AutoBuffer<double> _Ad(n*5), _bd(n);
double *Ad = _Ad, *bd = _bd;
// first fit for parameters A - E
Mat A( n, 5, CV_64F, Ad );
Mat b( n, 1, CV_64F, bd );
Mat x( 5, 1, CV_64F, gfp );
for( i = 0; i < n; i++ )
{
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
c += p;
}
c.x /= n;
c.y /= n;
for( i = 0; i < n; i++ )
{
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
p -= c;
bd[i] = 10000.0; // 1.0?
Ad[i*5] = -(double)p.x * p.x; // A - C signs inverted as proposed by APP
Ad[i*5 + 1] = -(double)p.y * p.y;
Ad[i*5 + 2] = -(double)p.x * p.y;
Ad[i*5 + 3] = p.x;
Ad[i*5 + 4] = p.y;
}
solve(A, b, x, DECOMP_SVD);
// now use general-form parameters A - E to find the ellipse center:
// differentiate general form wrt x/y to get two equations for cx and cy
A = Mat( 2, 2, CV_64F, Ad );
b = Mat( 2, 1, CV_64F, bd );
x = Mat( 2, 1, CV_64F, rp );
Ad[0] = 2 * gfp[0];
Ad[1] = Ad[2] = gfp[2];
Ad[3] = 2 * gfp[1];
bd[0] = gfp[3];
bd[1] = gfp[4];
solve( A, b, x, DECOMP_SVD );
// re-fit for parameters A - C with those center coordinates
A = Mat( n, 3, CV_64F, Ad );
b = Mat( n, 1, CV_64F, bd );
x = Mat( 3, 1, CV_64F, gfp );
for( i = 0; i < n; i++ )
{
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
p -= c;
bd[i] = 1.0;
Ad[i * 3] = (p.x - rp[0]) * (p.x - rp[0]);
Ad[i * 3 + 1] = (p.y - rp[1]) * (p.y - rp[1]);
Ad[i * 3 + 2] = (p.x - rp[0]) * (p.y - rp[1]);
}
solve(A, b, x, DECOMP_SVD);
// store angle and radii
rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage
if( fabs(gfp[2]) > min_eps )
t = gfp[2]/sin(-2.0 * rp[4]);
else // ellipse is rotated by an integer multiple of pi/2
t = gfp[1] - gfp[0];
rp[2] = fabs(gfp[0] + gfp[1] - t);
if( rp[2] > min_eps )
rp[2] = std::sqrt(2.0 / rp[2]);
rp[3] = fabs(gfp[0] + gfp[1] + t);
if( rp[3] > min_eps )
rp[3] = std::sqrt(2.0 / rp[3]);
box.center.x = (float)rp[0] + c.x;
box.center.y = (float)rp[1] + c.y;
box.size.width = (float)(rp[2]*2);
box.size.height = (float)(rp[3]*2);
if( box.size.width > box.size.height )
{
float tmp;
CV_SWAP( box.size.width, box.size.height, tmp );
box.angle = (float)(90 + rp[4]*180/CV_PI);
}
if( box.angle < -180 )
box.angle += 360;
if( box.angle > 360 )
box.angle -= 360;
return box;
}
The source code link: https://github.com/Itseez/opencv/blob/master/modules/imgproc/src/shapedescr.cpp
The function fitEllipse returns a RotatedRect that contains all the parameters of the ellipse.
An ellipse is defined by 5 parameters:
xc : x coordinate of the center
yc : y coordinate of the center
a : major semi-axis
b : minor semi-axis
theta : rotation angle
You can obtain these parameters like:
RotatedRect e = fitEllipse(points);
float xc = e.center.x;
float yc = e.center.y;
float a = e.size.width / 2; // width >= height
float b = e.size.height / 2;
float theta = e.angle; // in degrees
You can draw an ellipse with the function ellipse using the RotatedRect:
ellipse(image, e, Scalar(0,255,0));
or, equivalently using the ellipse parameters:
ellipse(res, Point(xc, yc), Size(a, b), theta, 0.0, 360.0, Scalar(0,255,0));
If you need the values of the coefficients of the implicit equation, you can do like (from Wikipedia):
So, you can get the parameters you need from the RotatedRect, and you don't need to change the function fitEllipse.
The solve function is used to solve linear systems or least-squares problems. Using the SVD decomposition method the system can be over-defined and/or the matrix src1 can be singular.
For more details on the algorithm, you can see the paper of Fitzgibbon that proposed this fit ellipse method.
Here is some code that worked for me which I based on the other responses on this thread.
def getConicCoeffFromEllipse(e):
# ellipse(Point(xc, yc),Size(a, b), theta)
xc = e[0][0]
yc = e[0][1]
a = e[1][0]/2
b = e[1][1]/2
theta = math.radians(e[2])
# See https://en.wikipedia.org/wiki/Ellipse
# Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 is the equation
A = a*a*math.pow(math.sin(theta),2) + b*b*math.pow(math.cos(theta),2)
B = 2*(b*b - a*a)*math.sin(theta)*math.cos(theta)
C = a*a*math.pow(math.cos(theta),2) + b*b*math.pow(math.sin(theta),2)
D = -2*A*xc - B*yc
E = -B*xc - 2*C*yc
F = A*xc*xc + B*xc*yc + C*yc*yc - a*a*b*b
coef = np.array([A,B,C,D,E,F]) / F
return coef
def getConicMatrixFromCoeff(c):
C = np.array([[c[0], c[1]/2, c[3]/2], # [ a, b/2, d/2 ]
[c[1]/2, c[2], c[4]/2], # [b/2, c, e/2 ]
[c[3]/2, c[4]/2, c[5]]]) # [d/2], e/2, f ]
return C

what algorithm does cv::arclength use to compute perimeter?

I am currently doing a project which requires me use some structural analysis like finding the perimeter and area. I have successfully obtained the contour of the object.
when I use contour.size() function it return 1108(in this case)
when I used cv::arclength(contour) function it returns 1200.
shouldn't the Perimeter be the number of points of the contour.(the contour is the external boundary of the object)? Which should I trust?
not necessarily, with cv::arclength you summarize the euclidean distances between the consecutive points in the curve.
Here is a code snippet of cv::arclength:
...
const Point2f* ptf = curve.ptr<Point2f>();
...
for( i = 0; i < count; i++ )
{
Point2f p = ptf[i];
float dx = p.x - prev.x, dy = p.y - prev.y;
buf[j] = dx*dx + dy*dy;
if( ++j == N || i == count-1 )
{
Mat bufmat(1, j, CV_32F, buf);
sqrt(bufmat, bufmat);
for( ; j > 0; j-- )
perimeter += buf[j-1];
}
prev = p;
}
return perimeter;

How to use FFTW on deviceMotion.userAcceleration.x

I am working on an app. that its measuring the motion of a device. (xyz direction )
and now i must use fftw to filter the data.
i don't know how to call the data through fftw. hier below is a part of my code trying to execute the X-data ( i am working on each direction separate so X, Y, and then Z)
// FFTW for X-data
int SIZE = 97;
fftw_complex *dataX, *fft_resultX;
fftw_plan plan_X;
int i ;
dataX = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * SIZE);
fft_resultX = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * SIZE);
plan_X = fftw_plan_dft_1d(SIZE, dataX, fft_resultX,
FFTW_FORWARD, FFTW_ESTIMATE); // FFTW_MEASURE
for( i = 0 ; i < SIZE ; i++ ) {
dataX[i][0] = 1.0; // real
dataX[i][1] = 0.0; // complex
for( i = 0 ; i < SIZE ; i++ ) {
fprintf( stdout, "dataX[%d] = { %2.2f, %2.2f }\n",
i, dataX[i][0], dataX[i][1] );
}
fftw_execute( plan_X);
for( i = 0 ; i < SIZE ; i++ ) {
fprintf( stdout, "fft_resultX[%d] = { %2.2f, %2.2f }\n",
i, fft_resultX[i][0], fft_resultX[i][1] );
}
and hier is the userAcceleration:
[[weakSelf.graphViews objectAtIndex:kDeviceMotionGraphTypeUserAcceleration] addX:deviceMotion.userAcceleration.x y:deviceMotion.userAcceleration.y z:deviceMotion.userAcceleration.z];
for example, when i am writing :
dataX= deviceMotion.userAcceleration.x;
i am getting this error:
Assigning to 'fftw_complex *' (aka '_Complex double *') from incompatible type 'double'
any idea how to make fftw work on it ?
thanks for every try
You can't simply convert real data to a real imaginary pair. Each complex number is made up of 2 doubles.
You need to store all your acceleration data into a larger array (256 entries for example) where the x value is assigned to the real part of the complex number and 0 is assigned to the imaginary part.

Gini Impurity, growing random trees in opencv

Goal: To add offset-impurity to the split decision of growing trees in openCV.
Currently in opencv random trees, the split is made as following:
if( !priors )
{
int L = 0, R = n1;
for( i = 0; i < m; i++ )
rsum2 += (double)rc[i]*rc[i];
for( i = 0; i < n1 - 1; i++ )
{
int idx = responses[sorted_indices[i]];
int lv, rv;
L++; R--;
lv = lc[idx]; rv = rc[idx];
lsum2 += lv*2 + 1;
rsum2 -= rv*2 - 1;
lc[idx] = lv + 1; rc[idx] = rv - 1;
if( values[i] + epsilon < values[i+1] )
{
double val = (lsum2*R + rsum2*L)/((double)L*R);
if( best_val < val )
{
best_val = val;
best_i = i;
}
}
}
}
Its using the gini impurity.
Anyone who can explain how the code accomplish this, from what i get it: initially it puts all class counts in the right node, and while moving one instance from right to left and update lsum2 and rsum2 it find the best solution. What i dont get is how p_j^2 is related to lv*2 +1 or rv*2-1.
The real question, if having offsets available and would like to add a split based on the impurity of simularity of offsets. (offsets are direction and distance from center to the current node.
What i have come up with is something like this, and if anyone can point out any flaws it would be good, because atm its not giving good results and im not sure where to start debugging.
//Compute mean
for(i = 0; i<n1;++i)
{
float* point = (float*)(points.data + rstep*sample_idx_src[sorted_indices[i]]);
meanx[responses[sorted_indices[i]]] += point[0];
meany[responses[sorted_indices[i]]] += point[1];
}
for(i = 0;i<m;++i)
{
meanx[i] /= rc0[i];
meany[i] /= rc0[i];
}
if(!priors)
{
int L = 0, R = n1;
for(i=0;i<n1;i++)
{
float* point = (float*)(points.data + rstep*sample_idx_src[sorted_indices[i]]);
double tmp = point[0] - meanx[responses[sorted_indices[i]]];
rsum2 += tmp*tmp;
tmp = point[1] -meany[responses[sorted_indices[i]]];
rsum2 += tmp*tmp;
}
double minDist = DBL_MAX;
for(i=0;i<n1;++i)
{
float* point = (float*)(points.data + rstep*sample_idx_src[sorted_indices[i]]);
++L; --R;
double tmp = point[0] - meanx[responses[sorted_indices[i]]];
lsum2 += tmp*tmp;
tmp = point[1] -meany[responses[sorted_indices[i]]];
lsum2 += tmp*tmp;
tmp = point[0] - meanx[responses[sorted_indices[i]]];
rsum2 -= tmp*tmp;
tmp = point[1] -meany[responses[sorted_indices[i]]];
rsum2 -= tmp*tmp;
if( values[i] + epsilon < values[i+1] )
{
double val = (lsum2 + rsum2)/((double)L*R);
if(val < minDist )
{
minDist = val;
best_val = -val;
best_i = i;
}
}
}
Ok, the Gini coefficient in this case is simple because there are only two groups, left and right. So instead of a large sum 1-sum(pj*pj) we have 1-pl*pl-pr*pr. The proportion of items on the left pl is the number of items on the left lv divided by the total.
Now as we shift the split, pl*pl and pr*pr change, but not because the total number of items changes. So instead of optimizing pr and pl (which are floating-point numbers) we optimize lv and rv (which are simple counts).
Next, the question why 2*lv+1. That's simple: we're increasing lv = lv=1 to optimize lv*lv. Now (lv+1)*(lv+1) - (lv*lv) (the increase) happens to be 2*lv+1 if you write out all the terms. And the decrease (rv-1)*(rv-1) - (rv*rv) happens to be -2*rv+1 or -(r*rv+1).

OpenCV: get Hough accumulator value?

Is it possible to get the accumulator value along with rho and theta from a Hough transform?
I ask because I'd like to differentiate between lines which are "well defined" (ie, which have a high accumulator value) and lines which aren't as well defined.
Thanks!
Ok, so looking at the cvhough.cpp file, the structure CvLinePolar is only defined by rho and angle.
This is all that is passed back as a result of our call to HoughLines. I am in the process of modifying the c++ file and see if i can get the votes out.
Update oct 26: just realized these are not really answers but more like questions. apparently frowned upon. I found some instructions on recompiling OpenCV. I guess we'll have to go in the code and modify it and recompile.
How to install OpenCV 2.0 on win32
update Oct 27: well, i failed at compiling the dlls for OpenCV with my new code so I ended up copy-pasting the specific parts I want to modify into my own files.
I like to add new functions so to avoid overloading the already defined functions.
There are 4 main things you need to copy over:
1- some random defines
#define hough_cmp_gt(l1,l2) (aux[l1] > aux[l2])
static CV_IMPLEMENT_QSORT_EX( icvHoughSortDescent32s, int, hough_cmp_gt, const int* )
2- redefining the struct for line parameters
typedef struct CvLinePolar2
{
float rho;
float angle;
float votes;
}
CvLinePolar2;
3- the main function that was modified
static void
icvHoughLinesStandard2( const CvMat* img, float rho, float theta,
int threshold, CvSeq *lines, int linesMax )
{
cv::AutoBuffer<int> _accum, _sort_buf;
cv::AutoBuffer<float> _tabSin, _tabCos;
const uchar* image;
int step, width, height;
int numangle, numrho;
int total = 0;
float ang;
int r, n;
int i, j;
float irho = 1 / rho;
double scale;
CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );
image = img->data.ptr;
step = img->step;
width = img->cols;
height = img->rows;
numangle = cvRound(CV_PI / theta);
numrho = cvRound(((width + height) * 2 + 1) / rho);
_accum.allocate((numangle+2) * (numrho+2));
_sort_buf.allocate(numangle * numrho);
_tabSin.allocate(numangle);
_tabCos.allocate(numangle);
int *accum = _accum, *sort_buf = _sort_buf;
float *tabSin = _tabSin, *tabCos = _tabCos;
memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );
for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
{
tabSin[n] = (float)(sin(ang) * irho);
tabCos[n] = (float)(cos(ang) * irho);
}
// stage 1. fill accumulator
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for( n = 0; n < numangle; n++ )
{
r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n+1) * (numrho+2) + r+1]++;
}
}
// stage 2. find local maximums
for( r = 0; r < numrho; r++ )
for( n = 0; n < numangle; n++ )
{
int base = (n+1) * (numrho+2) + r+1;
if( accum[base] > threshold &&
accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
sort_buf[total++] = base;
}
// stage 3. sort the detected lines by accumulator value
icvHoughSortDescent32s( sort_buf, total, accum );
// stage 4. store the first min(total,linesMax) lines to the output buffer
linesMax = MIN(linesMax, total);
scale = 1./(numrho+2);
for( i = 0; i < linesMax; i++ )
{
CvLinePolar2 line;
int idx = sort_buf[i];
int n = cvFloor(idx*scale) - 1;
int r = idx - (n+1)*(numrho+2) - 1;
line.rho = (r - (numrho - 1)*0.5f) * rho;
line.angle = n * theta;
line.votes = accum[idx];
cvSeqPush( lines, &line );
}
cvFree( (void**)&sort_buf );
cvFree( (void**)&accum );
cvFree( (void**)&tabSin );
cvFree( (void**)&tabCos);
}
4- the function that calls that function
CV_IMPL CvSeq*
cvHoughLines3( CvArr* src_image, void* lineStorage, int method,
double rho, double theta, int threshold,
double param1, double param2 )
{
CvSeq* result = 0;
CvMat stub, *img = (CvMat*)src_image;
CvMat* mat = 0;
CvSeq* lines = 0;
CvSeq lines_header;
CvSeqBlock lines_block;
int lineType, elemSize;
int linesMax = INT_MAX;
int iparam1, iparam2;
img = cvGetMat( img, &stub );
if( !CV_IS_MASK_ARR(img))
CV_Error( CV_StsBadArg, "The source image must be 8-bit, single-channel" );
if( !lineStorage )
CV_Error( CV_StsNullPtr, "NULL destination" );
if( rho <= 0 || theta <= 0 || threshold <= 0 )
CV_Error( CV_StsOutOfRange, "rho, theta and threshold must be positive" );
if( method != CV_HOUGH_PROBABILISTIC )
{
lineType = CV_32FC3;
elemSize = sizeof(float)*3;
}
else
{
lineType = CV_32SC4;
elemSize = sizeof(int)*4;
}
if( CV_IS_STORAGE( lineStorage ))
{
lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize, (CvMemStorage*)lineStorage );
}
else if( CV_IS_MAT( lineStorage ))
{
mat = (CvMat*)lineStorage;
if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 && mat->cols != 1) )
CV_Error( CV_StsBadArg,
"The destination matrix should be continuous and have a single row or a single column" );
if( CV_MAT_TYPE( mat->type ) != lineType )
CV_Error( CV_StsBadArg,
"The destination matrix data type is inappropriate, see the manual" );
lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize, mat->data.ptr,
mat->rows + mat->cols - 1, &lines_header, &lines_block );
linesMax = lines->total;
cvClearSeq( lines );
}
else
CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" );
iparam1 = cvRound(param1);
iparam2 = cvRound(param2);
switch( method )
{
case CV_HOUGH_STANDARD:
icvHoughLinesStandard2( img, (float)rho,
(float)theta, threshold, lines, linesMax );
break;
default:
CV_Error( CV_StsBadArg, "Unrecognized method id" );
}
if( mat )
{
if( mat->cols > mat->rows )
mat->cols = lines->total;
else
mat->rows = lines->total;
}
else
result = lines;
return result;
}
And i guess you could uninstall opencv so it takes off all those automatic path setting and recompile it yourself using the CMake method and then the OpenCV is really whatever you make it.
Although this is an old question, I had the same problem, so I might as well put up my solution. The threshold in houghlines() returns 1 for any point that cleared the threshold for votes. The solution is to run houghlines() for every threshold value (until there are no more votes) and add up the votes in another array. In python (maybe with other languages too) when you have no more votes, it throws an error, so use try/except.
Here is an example in python. The array I used was for rho values of -199 to 200 with a max vote of less than 100. You can play around with those constants to suit your needs. You may need to add a line to convert the source image to grayscale.
import matplotlib.pyplot as plt
import cv2
import math
############ make houghspace array ############
houghspace = []
c = 0
height = 400
while c <= height:
houghspace.append([])
cc = 0
while cc <= 180:
houghspace[c].append(0)
cc += 1
c+=1
############ do transform ############
degree_tick = 1 #by how many degrees to check
total_votes = 1 #votes counter
highest_vote = 0 #highest vote in the array
while total_votes < 100:
img = cv2.imread('source.pgm')
edges = cv2.Canny(img,50,150,apertureSize = 3)
lines = cv2.HoughLines(edges,1,math.pi*degree_tick/180,total_votes)
try:
for rho,theta in lines[0]:
a = math.cos(theta)
b = math.sin(theta)
x1 = int((a*rho) + 1000*(-b))
y1 = int((b*rho) + 1000*(a))
x2 = int((a*rho) - 1000*(-b))
y2 = int((b*rho) - 1000*(a))
cv2.line(img,(x1,y1),(x2,y2),(50,200,255),2)
#################add votes into the array################
deradian = 180/math.pi #used to convert to degrees
for rho,theta in lines[0]:
degree = int(round(theta*deradian))
rho_pos = int(rho - 200)
houghspace[rho_pos][degree] += 1
#when lines[0] has no votes, it throws an error which is caught here
except:
total_votes = 999 #exit loop
highest_vote = total_votes
total_votes += 1
del lines
########### loop finished ###############################
print highest_vote
#############################################################
################### plot the houghspace ###################
maxy = 200 #used to offset the y-axis
miny = -200 #used to offset the y-axis
#the main graph
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111)
ax.set_title('Houghspace')
plt.imshow(houghspace, cmap='gist_stern')
ax.set_aspect('equal')
plt.yticks([0,-miny,maxy-miny], [miny,0,maxy])
#the legend
cax = fig.add_axes([0, 0.1, 0.78, 0.8])
cax.get_xaxis().set_visible(False)
cax.get_yaxis().set_visible(False)
cax.patch.set_alpha(0)
cax.set_frame_on(False)
plt.colorbar(orientation='vertical')
#plot
plt.show()

Resources