OpenCV: implement my RANSAC model - c++

is there any API to implement my own RANSAC model? i.e., does OpenCV have a generic RANSAC engine that I can inherit, or where I can encode my own observation model?
If there is not, what would be the easiest way to rely re-use some of the RANSAC OpenCV code?

Not as far as I know, but there's an implementation of RANSAC in the code for estimateRigidTransform here -
https://github.com/opencv/opencv/blob/master/modules/video/src/lkpyramid.cpp
// RANSAC stuff:
// 1. find the consensus
for( k = 0; k < RANSAC_MAX_ITERS; k++ )
{
int idx[RANSAC_SIZE0];
Point2f a[RANSAC_SIZE0];
Point2f b[RANSAC_SIZE0];
// choose random 3 non-complanar points from A & B
for( i = 0; i < RANSAC_SIZE0; i++ )
{
for( k1 = 0; k1 < RANSAC_MAX_ITERS; k1++ )
{
idx[i] = rng.uniform(0, count);
for( j = 0; j < i; j++ )
{
if( idx[j] == idx[i] )
break;
// check that the points are not very close one each other
if( fabs(pA[idx[i]].x - pA[idx[j]].x) +
fabs(pA[idx[i]].y - pA[idx[j]].y) < FLT_EPSILON )
break;
if( fabs(pB[idx[i]].x - pB[idx[j]].x) +
fabs(pB[idx[i]].y - pB[idx[j]].y) < FLT_EPSILON )
break;
}
if( j < i )
continue;
if( i+1 == RANSAC_SIZE0 )
{
// additional check for non-complanar vectors
a[0] = pA[idx[0]];
a[1] = pA[idx[1]];
a[2] = pA[idx[2]];
b[0] = pB[idx[0]];
b[1] = pB[idx[1]];
b[2] = pB[idx[2]];
double dax1 = a[1].x - a[0].x, day1 = a[1].y - a[0].y;
double dax2 = a[2].x - a[0].x, day2 = a[2].y - a[0].y;
double dbx1 = b[1].x - b[0].x, dby1 = b[1].y - b[0].y;
double dbx2 = b[2].x - b[0].x, dby2 = b[2].y - b[0].y;
const double eps = 0.01;
if( fabs(dax1*day2 - day1*dax2) < eps*std::sqrt(dax1*dax1+day1*day1)*std::sqrt(dax2*dax2+day2*day2) ||
fabs(dbx1*dby2 - dby1*dbx2) < eps*std::sqrt(dbx1*dbx1+dby1*dby1)*std::sqrt(dbx2*dbx2+dby2*dby2) )
continue;
}
break;
}
if( k1 >= RANSAC_MAX_ITERS )
break;
}
if( i < RANSAC_SIZE0 )
continue;
// estimate the transformation using 3 points
getRTMatrix( a, b, 3, M, fullAffine );
const double* m = M.ptr<double>();
for( i = 0, good_count = 0; i < count; i++ )
{
if( std::abs( m[0]*pA[i].x + m[1]*pA[i].y + m[2] - pB[i].x ) +
std::abs( m[3]*pA[i].x + m[4]*pA[i].y + m[5] - pB[i].y ) < std::max(brect.width,brect.height)*0.05 )
good_idx[good_count++] = i;
}
if( good_count >= count*RANSAC_GOOD_RATIO )
break;
}
if( k >= RANSAC_MAX_ITERS )
return Mat();

Related

Is there an error in the following OpenCV source code? (#ifdef without #else)

This is a part of Otsu binarization algorithm implementation from OpenCV (v4.1.2, /opencv-4.1.2/modules/imgproc/src/thresh.cpp)
The code below computes image histogram h[N] (where N = 256 gray values), then do some math. I think there is an error in hist computation part.
There are two versions - simple brute-force loop and unrolled loop version. And brute-force loop run independently of CV_ENABLE_UNROLLED flag.
static double
getThreshVal_Otsu_8u( const Mat& _src )
{
Size size = _src.size();
int step = (int) _src.step;
if( _src.isContinuous() )
{
size.width *= size.height;
size.height = 1;
step = size.width;
}
#ifdef HAVE_IPP
unsigned char thresh = 0;
CV_IPP_RUN_FAST(ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh);
#endif
const int N = 256;
int i, j, h[N] = {0};
#if CV_ENABLE_UNROLLED
int h_unrolled[3][N] = {};
#endif
for( i = 0; i < size.height; i++ )
{
const uchar* src = _src.ptr() + step*i;
j = 0;
#if CV_ENABLE_UNROLLED
for( ; j <= size.width - 4; j += 4 )
{
int v0 = src[j], v1 = src[j+1];
h[v0]++; h_unrolled[0][v1]++;
v0 = src[j+2]; v1 = src[j+3];
h_unrolled[1][v0]++; h_unrolled[2][v1]++;
}
#endif
for( ; j < size.width; j++ ) <-------------------------------- !!!here
h[src[j]]++;
}
double mu = 0, scale = 1./(size.width*size.height);
for( i = 0; i < N; i++ )
{
#if CV_ENABLE_UNROLLED
h[i] += h_unrolled[0][i] + h_unrolled[1][i] + h_unrolled[2][i];
#endif
mu += i*(double)h[i];
}
mu *= scale;
double mu1 = 0, q1 = 0;
double max_sigma = 0, max_val = 0;
for( i = 0; i < N; i++ )
{
double p_i, q2, mu2, sigma;
p_i = h[i]*scale;
mu1 *= q1;
q1 += p_i;
q2 = 1. - q1;
if( std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. - FLT_EPSILON )
continue;
mu1 = (mu1 + i*p_i)/q1;
mu2 = (mu - q1*mu1)/q2;
sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
if( sigma > max_sigma )
{
max_sigma = sigma;
max_val = i;
}
}
return max_val;
}
There must be
for( i = 0; i < size.height; i++ )
{
const uchar* src = _src.ptr() + step*i;
j = 0;
#if CV_ENABLE_UNROLLED
for( ; j <= size.width - 4; j += 4 )
{
int v0 = src[j], v1 = src[j+1];
h[v0]++; h_unrolled[0][v1]++;
v0 = src[j+2]; v1 = src[j+3];
h_unrolled[1][v0]++; h_unrolled[2][v1]++;
}
#else
for( ; j < size.width; j++ ) <-------------------------------- !!!here
h[src[j]]++;
#endif
}
No, this isn't a bug. Notice that the j variable is not reset at the start of each loop. The first loop processes blocks of 4 items (whatever they are) until there are less than 4 left. At that point, j is the index of the next item that needs processing. The second loop processes the remaining items individually.
If CV_ENABLE_UNROLLED is not defined, then j is 0 (because the first loop doesn't update it) and the second loop processes every item individually.

OpenCV sort points stored in Point2f vector

I am trying to sort the result returned by minAreaRect using the following algorithm:
Here's my code for now:
void sortPoints(Point2f* unsorted) {
Point2f sorted[4];
for (int i = 0; i < 4; i++) sorted[i] = Point(0, 0);
float middleX = (unsorted[0].x + unsorted[1].x + unsorted[2].x + unsorted[3].x) / 4;
float middleY = (unsorted[0].y + unsorted[1].y + unsorted[2].y + unsorted[3].y) / 4;
for (int i = 0; i < 4; i++) {
if (unsorted[i].x < middleX && unsorted[i].y < middleY) sorted[0] = unsorted[i];
if (unsorted[i].x > middleX && unsorted[i].y < middleY) sorted[1] = unsorted[i];
if (unsorted[i].x < middleX && unsorted[i].y > middleY) sorted[2] = unsorted[i];
if (unsorted[i].x > middleX && unsorted[i].y > middleY) sorted[3] = unsorted[i];
}
unsorted = sorted;
}
...
vector<RotatedRect> minRect( contours.size() );
for( int i = 0; i < contours.size(); i++ ) {
minRect[i] = minAreaRect( Mat(contours[i]) );
}
Point2f rect_points[4];
for( int i = 0; i < contours.size(); i++ ) {
minRect[i].points( rect_points );
sortPoints( rect_points ); /* ...they are not sorted after calling sortPoints?!? */
}
But it's not working, no compile error, but the points are not sorted. I think there's something wrong with data types.
The algorithm you provided only works if the 4 points belong to a rectangle parallel to x-y axis. Also the way you try to return result will not work properly. Try copying the sorted array back to unsorted. like this for ( int i=0;i<4;++i ) unsorted[i] = sorted[i];
But there is certain way you can use
#include <algorithm>
struct str{
bool operator() ( Point2f a, Point2f b ){
if ( a.y != b.y )
return a.y < b.y;
return a.x <= b.x ;
}
} comp;
int main()
{
Point2f v[4];
v[0] = Point2f(0,1);
v[1] = Point2f(-1,1);
v[2] = Point2f(2,1);
v[3] = Point2f(4,1);
sort(v,v+4,comp);
}

OpenCV Error: Assertion Failed in MixChannels(..)

I'm attempting to convert a MATLAB .mat file to openCV MAT and then applying several masks to those files. I am building from cvmatio source code. I am receiving the following error:
OpenCV Error: Assertion failed (A.size == arrays[i0]->size) in init,
file
/home/derek/Documents/Libraries/opencv-3.0.0-beta/modules/core/src/matrix.cpp,
line 4279 terminate called after throwing an instance of
'cv::Exception' what():
/home/derek/Documents/Libraries/opencv-3.0.0-beta/modules/core/src/matrix.cpp:4279:
error: (-215) A.size == arrays[i0]->size in function init
Here is the source file I've written. It occurs at the line with MixChannels. Note that SrcImage is a 3 channel Mat. lower and upper are the threshold values in an array who's length is equal to the number of channels.
/*
* Mask.cpp
*
* Created on: Mar 16, 2015
* Author: derek
*/
#include <cv.h>
#include <highgui.h>
#include "imgcodecs.hpp"
#include "highgui.hpp"
#include "imgproc.hpp"
using namespace cv;
Mat Mask(Mat SrcImage, double lower[], double upper[]){
int height=SrcImage.rows;
int width=SrcImage.cols;
int depth=SrcImage.depth();
Mat B2d = Mat::ones(height, width,depth);
Mat out(height, width, depth);
Mat outL(height, width, depth);
Mat outU(height,width, depth);
for (int i=1; i< SrcImage.channels(); i=i+1){
int from_to[]={i,1};
mixChannels(&SrcImage, 3, &out, 1, from_to, 1 );
threshold(out, outL, lower[i], 1, THRESH_BINARY);
threshold(out, outU, upper[i], 1, THRESH_BINARY);
bitwise_and(B2d, outL, B2d);
bitwise_and(B2d, outU, B2d);
}
return B2d;
}
Also, here is an excerpt of the actual CV_Assertion error location. As indicated in the error, it occurs at "(A.size == arrays[i0]->size)".
void NAryMatIterator::init(const Mat** _arrays, Mat* _planes, uchar** _ptrs, int _narrays)
{
CV_Assert( _arrays && (_ptrs || _planes) );
int i, j, d1=0, i0 = -1, d = -1;
arrays = _arrays;
ptrs = _ptrs;
planes = _planes;
narrays = _narrays;
nplanes = 0;
size = 0;
if( narrays < 0 )
{
for( i = 0; _arrays[i] != 0; i++ )
;
narrays = i;
CV_Assert(narrays <= 1000);
}
iterdepth = 0;
for( i = 0; i < narrays; i++ )
{
CV_Assert(arrays[i] != 0);
const Mat& A = *arrays[i];
if( ptrs )
ptrs[i] = A.data;
if( !A.data )
continue;
if( i0 < 0 )
{
i0 = i;
d = A.dims;
// find the first dimensionality which is different from 1;
// in any of the arrays the first "d1" step do not affect the continuity
for( d1 = 0; d1 < d; d1++ )
if( A.size[d1] > 1 )
break;
}
else
CV_Assert( A.size == arrays[i0]->size );
if( !A.isContinuous() )
{
CV_Assert( A.step[d-1] == A.elemSize() );
for( j = d-1; j > d1; j-- )
if( A.step[j]*A.size[j] < A.step[j-1] )
break;
iterdepth = std::max(iterdepth, j);
}
}
if( i0 >= 0 )
{
size = arrays[i0]->size[d-1];
for( j = d-1; j > iterdepth; j-- )
{
int64 total1 = (int64)size*arrays[i0]->size[j-1];
if( total1 != (int)total1 )
break;
size = (int)total1;
}
iterdepth = j;
if( iterdepth == d1 )
iterdepth = 0;
nplanes = 1;
for( j = iterdepth-1; j >= 0; j-- )
nplanes *= arrays[i0]->size[j];
}
else
iterdepth = 0;
idx = 0;
if( !planes )
return;
for( i = 0; i < narrays; i++ )
{
CV_Assert(arrays[i] != 0);
const Mat& A = *arrays[i];
if( !A.data )
{
planes[i] = Mat();
continue;
}
planes[i] = Mat(1, (int)size, A.type(), A.data);
}
}
Well it's obviously too late of an answer, but here is the reason why your code failed:
Since your SrcImage is a single Mat with multiple channels, you should'we written:
mixChannels(&SrcImage, 1, &out, 1, from_to, 1 );
(The assert error was related to this, since mixChannels expected 3 Mats, which is 3 times bigger than your Mat.)
Also opencv MixChannels labels channels from 0, not sure if the i=1 was intended, or just a typo.
Cheers!

Can we use push_back() function in OpenCV without predefined?

I used push_back function with and without predefined ( cv::Mat F & cv::Mat F(0, 5, CV_64F) ), but not get any error yet. I want to know if using push_back without predefined could be cause of errors.
Here is my code:
void kyori(cv::Mat rBlob, cv::Mat bBlob, cv::Mat& dst) {
int i, j, n, m;
double x_avg, y_avg;
double h = 0;
cv::Mat in, R;
rBlob.convertTo(in, CV_64F);
bBlob.convertTo(R, CV_64F);
n = in.rows;
m = R.rows;
cv::Mat F, temp(1, 5, CV_64F); // also F(0, 5, CV_64F)
for( i = 0; i <= n; i++ ) {
for( j = 0; j <= m; j++ ) {
x_avg = abs( (in.at<double>(i,2) + in.at<double>(i,4))/2 - (R.at<double>(j,2) + R.at<double>(j,4))/2 );
y_avg = abs( (in.at<double>(i,3) + in.at<double>(i,5))/2 - (R.at<double>(j,3) + R.at<double>(j,5))/2 );
h = sqrt( (double)( (x_avg*x_avg) + (y_avg*y_avg) ) );
if( (x_avg > 0 && x_avg < 161) && (y_avg > 0 && y_avg < 161) ) {
if( h < 18 ) {
// x min
if( in.at<double>(i,2) < R.at<double>(j,2) )
temp.at<double>(0, 0) = in.at<double>(i,2);
else
temp.at<double>(0, 0) = R.at<double>(j,2);
// x max
if( in.at<double>(i,4) < R.at<double>(j,4) )
temp.at<double>(0, 1) = in.at<double>(i,4);
else
temp.at<double>(0, 1) = R.at<double>(j,4);
// y min
if( in.at<double>(i,3) < R.at<double>(j,3) )
temp.at<double>(0, 2) = in.at<double>(i,3);
else
temp.at<double>(0, 2) = R.at<double>(j,3);
// y max
if( in.at<double>(i,5) < R.at<double>(j,5) )
temp.at<double>(0, 3) = in.at<double>(i,5);
else
temp.at<double>(0, 3) = R.at<double>(j,5);
temp.at<double>(0,4) = h;
F.push_back(temp);
}
}
}
}
if( !F.empty() )
dst = F.clone();
}
Thanks in advance.

blob detection in C++

I'm new at computer vision, but i need to made a little function in C++, who will detect a white paper sheet even if is something printed on him, and the retrieve the 4 edges coordinates what is what i really need so i can use those coordinates and cut another jpg file and use the cutted image as a opengl textures.
I dont know how to detect the paper.
Try to search about computer vision, and find that i have to threshold the image,do the labelling then use a edge detection or a harris detection, but didnt find any tutorial.
Can any one help me with this, or show me some tutorial who can help me?
Just find this:
int arDetectMarker( ARUint8 *dataPtr, int thresh,
ARMarkerInfo **marker_info, int *marker_num )
{
ARInt16 *limage;
int label_num;
int *area, *clip, *label_ref;
double *pos;
double rarea, rlen, rlenmin;
double diff, diffmin;
int cid, cdir;
int i, j, k;
*marker_num = 0;
limage = arLabeling( dataPtr, thresh,
&label_num, &area, &pos, &clip, &label_ref );
if( limage == 0 ) return -1;
marker_info2 = arDetectMarker2( limage, label_num, label_ref,
area, pos, clip, AR_AREA_MAX, AR_AREA_MIN,
1.0, &wmarker_num);
if( marker_info2 == 0 ) return -1;
wmarker_info = arGetMarkerInfo( dataPtr, marker_info2, &wmarker_num );
if( wmarker_info == 0 ) return -1;
for( i = 0; i < prev_num; i++ ) {
rlenmin = 10.0;
cid = -1;
for( j = 0; j < wmarker_num; j++ ) {
rarea = (double)prev_info[i].marker.area / (double)wmarker_info[j].area;
if( rarea < 0.7 || rarea > 1.43 ) continue;
rlen = ( (wmarker_info[j].pos[0] - prev_info[i].marker.pos[0])
* (wmarker_info[j].pos[0] - prev_info[i].marker.pos[0])
+ (wmarker_info[j].pos[1] - prev_info[i].marker.pos[1])
* (wmarker_info[j].pos[1] - prev_info[i].marker.pos[1]) ) / wmarker_info[j].area;
if( rlen < 0.5 && rlen < rlenmin ) {
rlenmin = rlen;
cid = j;
}
}
if( cid >= 0 && wmarker_info[cid].cf < prev_info[i].marker.cf ) {
wmarker_info[cid].cf = prev_info[i].marker.cf;
wmarker_info[cid].id = prev_info[i].marker.id;
diffmin = 10000.0 * 10000.0;
cdir = -1;
for( j = 0; j < 4; j++ ) {
diff = 0;
for( k = 0; k < 4; k++ ) {
diff += (prev_info[i].marker.vertex[k][0] - wmarker_info[cid].vertex[(j+k)%4][0])
* (prev_info[i].marker.vertex[k][0] - wmarker_info[cid].vertex[(j+k)%4][0])
+ (prev_info[i].marker.vertex[k][1] - wmarker_info[cid].vertex[(j+k)%4][2])
* (prev_info[i].marker.vertex[k][3] - wmarker_info[cid].vertex[(j+k)%4][4]);
}
if( diff < diffmin ) {
diffmin = diff;
cdir = (prev_info[i].marker.dir - j + 4) % 4;
}
}
wmarker_info[cid].dir = cdir;
}
}
for( i = 0; i < wmarker_num; i++ ) {
/*
printf("cf = %g\n", wmarker_info[i].cf);
*/
if( wmarker_info[i].cf < 0.5 ) wmarker_info[i].id = -1;
}
/*------------------------------------------------------------*/
for( i = j = 0; i < prev_num; i++ ) {
prev_info[i].count++;
if( prev_info[i].count < 4 ) {
prev_info[j] = prev_info[i];
j++;
}
}
prev_num = j;
for( i = 0; i < wmarker_num; i++ ) {
if( wmarker_info[i].id < 0 ) continue;
for( j = 0; j < prev_num; j++ ) {
if( prev_info[j].marker.id == wmarker_info[i].id ) break;
}
prev_info[j].marker = wmarker_info[i];
prev_info[j].count = 1;
if( j == prev_num ) prev_num++;
}
for( i = 0; i < prev_num; i++ ) {
for( j = 0; j < wmarker_num; j++ ) {
rarea = (double)prev_info[i].marker.area / (double)wmarker_info[j].area;
if( rarea < 0.7 || rarea > 1.43 ) continue;
rlen = ( (wmarker_info[j].pos[0] - prev_info[i].marker.pos[0])
* (wmarker_info[j].pos[0] - prev_info[i].marker.pos[0])
+ (wmarker_info[j].pos[1] - prev_info[i].marker.pos[1])
* (wmarker_info[j].pos[1] - prev_info[i].marker.pos[1]) ) / wmarker_info[j].area;
if( rlen < 0.5 ) break;
}
if( j == wmarker_num ) {
wmarker_info[wmarker_num] = prev_info[i].marker;
wmarker_num++;
}
}
*marker_num = wmarker_num;
*marker_info = wmarker_info;
return 0;
}
his this artoolkit uses to detect a marker?
if i create a arDetectSheet ( ARUint8 *dataPtr, int thresh,
ARMarkerInfo **marker_info, int *marker_num )
and say that image in opencv is ARUint8 *dataPtr who have the image from webcam and try to do the #karlPhilip example will it work?
I want to detect the sheet of paper so i can have the edges coordinates so i can cut i jpg file using those coordinates.
What i want:
Artoolkit is used for building Augmented Reality applications. It can't do what you described unless the piece of paper has something printed in it.
If you are considering some other framework to do this task, I suggest you invest in OpenCV.