Ported camera calibration code.

This commit is contained in:
2014-04-28 10:42:38 -04:30
parent 249e6e30a4
commit b3d678f078
3 changed files with 368 additions and 325 deletions

View File

@@ -3,8 +3,9 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
OPENCV_CAMERA_MODULES:=off OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=STATIC OPENCV_LIB_TYPE:=STATIC #SHARED
include C:\Users\miguel.astor\Documents\OpenCV-2.4.8-android-sdk\sdk\native\jni\OpenCV.mk include C:\Users\miguel.astor\Documents\OpenCV-2.4.8-android-sdk\sdk\native\jni\OpenCV.mk
#include C:\NVPACK\OpenCV-2.4.5-Tegra-sdk-r2\sdk\native\jni\OpenCV-tegra3.mk
LOCAL_MODULE := cvproc LOCAL_MODULE := cvproc
LOCAL_SRC_FILES := cv_proc.cpp marker.cpp LOCAL_SRC_FILES := cv_proc.cpp marker.cpp

View File

@@ -24,443 +24,479 @@
namespace nxtar{ namespace nxtar{
typedef std::vector<cv::Point3f> points_vector_3D; typedef std::vector<cv::Point3f> points_vector_3D;
typedef std::vector<std::vector<cv::Point> > contours_vector; typedef std::vector<std::vector<cv::Point> > contours_vector;
typedef std::vector<Marker> markers_vector; typedef std::vector<Marker> markers_vector;
/****************************************************************************** /******************************************************************************
* PRIVATE CONSTANTS * * PRIVATE CONSTANTS *
******************************************************************************/ ******************************************************************************/
/** /**
* Size of a square cell in the calibration pattern. * Minimal number of points in a contour.
*/ */
static const float SQUARE_SIZE = 1.0f; static const int MIN_POINTS = 40;
/** /**
* Minimal lenght of a contour to be considered as a marker candidate. * Size of a square cell in the calibration pattern.
*/ */
static const float MIN_CONTOUR_LENGTH = 0.1; static const float SQUARE_SIZE = 1.0f;
/** /**
* Flags for the calibration pattern detecion function. * Minimal lenght of a contour to be considered as a marker candidate.
*/ */
static const int PATTERN_DETECTION_FLAGS = cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + cv::CALIB_CB_FAST_CHECK; static const float MIN_CONTOUR_LENGTH = 0.1;
/** /**
* Color for rendering the marker outlines. * Flags for the calibration pattern detecion function.
*/ */
static const cv::Scalar COLOR = cv::Scalar(255, 255, 255); static const int PATTERN_DETECTION_FLAGS = cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + cv::CALIB_CB_FAST_CHECK;
/** /**
* Size of the chessboard pattern image (columns, rows). * Color for rendering the marker outlines.
*/ */
static const cv::Size CHESSBOARD_PATTERN_SIZE(6, 9); static const cv::Scalar COLOR = cv::Scalar(255, 255, 255);
/** /**
* Termination criteria for OpenCV's iterative algorithms. * Size of the chessboard pattern image (columns, rows).
*/ */
static const cv::TermCriteria TERM_CRITERIA = cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1); static const cv::Size CHESSBOARD_PATTERN_SIZE(6, 9);
/**
* Termination criteria for OpenCV's iterative algorithms.
*/
static const cv::TermCriteria TERM_CRITERIA = cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1);
/****************************************************************************** /******************************************************************************
* PRIVATE FUNCTION PROTOTYPES * * PRIVATE FUNCTION PROTOTYPES *
******************************************************************************/ ******************************************************************************/
/** float perimeter(points_vector &);
* Calculates the perimeter of a points vector defining a polygon.
*/
float perimeter(points_vector &);
/** int hammDistMarker(cv::Mat);
* Calculates the Hamming distance of a 5x5 marker.
*/
int hammDistMarker(cv::Mat);
/** cv::Mat rotate(cv::Mat);
* Rotates an OpenCV matrix in place by 90 degrees clockwise.
*/
cv::Mat rotate(cv::Mat);
/** int decodeMarker(cv::Mat &);
* Returns the code of a 5x5 marker or -1 if the marker is not valid.
*/
int decodeMarker(cv::Mat &);
/** void renderMarkers(markers_vector &, cv::Mat &);
* Renders the polygon defined in the input vector on the specified image.
*/
void renderMarkers(markers_vector &, cv::Mat &);
/** void isolateMarkers(const contours_vector &, markers_vector &);
* Identifies all possible marker candidates in a polygon vector.
*/
void isolateMarkers(const contours_vector &, markers_vector &);
/** void findContours(cv::Mat &, contours_vector &, int);
* Identifies all roughly 4 side figures in the input image.
*/
void findContours(cv::Mat &, contours_vector &, int);
/** void warpMarker(Marker &, cv::Mat &, cv::Mat &);
* Removes the prerspective distortion from a marker candidate image.
*/
void warpMarker(Marker &, cv::Mat &, cv::Mat &);
/****************************************************************************** /******************************************************************************
* PUBLIC API * * PUBLIC API *
******************************************************************************/ ******************************************************************************/
void getAllMarkers(std::vector<int> & codes, cv::Mat & img){ void getAllMarkers(std::vector<int> & codes, cv::Mat & img){
cv::Mat gray, thresh, cont, mark; cv::Mat gray, thresh, cont, mark;
contours_vector contours; contours_vector contours;
markers_vector markers; markers_vector markers;
markers_vector valid_markers; markers_vector valid_markers;
#ifdef DESKTOP #ifdef DESKTOP
std::ostringstream oss; std::ostringstream oss;
#endif #endif
codes.clear(); codes.clear();
// Find all marker candidates in the input image. // Find all marker candidates in the input image.
// 1) First, convert the image to grayscale. // 1) First, convert the image to grayscale.
// 2) Then, binarize the grayscale image. // 2) Then, binarize the grayscale image.
// 3) Finally indentify all 4 sided figures in the binarized image. // 3) Finally indentify all 4 sided figures in the binarized image.
cv::cvtColor(img, gray, CV_BGR2GRAY); cv::cvtColor(img, gray, CV_BGR2GRAY);
cv::adaptiveThreshold(gray, thresh, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, 7, 7); cv::adaptiveThreshold(gray, thresh, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, 7, 7);
findContours(thresh, contours, 40); findContours(thresh, contours, MIN_POINTS);
isolateMarkers(contours, markers); isolateMarkers(contours, markers);
// Remove the perspective distortion from the detected marker candidates. // Remove the perspective distortion from the detected marker candidates.
// Then attempt to decode them and push the valid ones into the valid // Then attempt to decode them and push the valid ones into the valid
// markes vector. // markers vector.
for(int i = 0; i < markers.size(); i++){ for(int i = 0; i < markers.size(); i++){
warpMarker(markers[i], gray, mark); warpMarker(markers[i], gray, mark);
int code = decodeMarker(mark); int code = decodeMarker(mark);
if(code != -1){ if(code != -1){
markers[i].code = code; markers[i].code = code;
valid_markers.push_back(markers[i]); valid_markers.push_back(markers[i]);
} }
} }
for(int i = 0; i < valid_markers.size(); i++){ for(int i = 0; i < valid_markers.size(); i++){
#ifdef DESKTOP #ifdef DESKTOP
// Render the detected valid markers with their codes for debbuging // Render the detected valid markers with their codes for debbuging
// purposes. // purposes.
oss << valid_markers[i].code; oss << valid_markers[i].code;
cv::putText(mark, oss.str(), cv::Point(5, 250), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar::all(128), 3, 8); cv::putText(mark, oss.str(), cv::Point(5, 250), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar::all(128), 3, 8);
oss.str(""); oss.str("");
oss.clear(); oss.clear();
oss << "Marker[" << i << "]"; oss << "Marker[" << i << "]";
cv::imshow(oss.str(), mark); cv::imshow(oss.str(), mark);
oss.str(""); oss.str("");
oss.clear(); oss.clear();
#endif #endif
// Fix the detected corners to better approximate the markers. And // Fix the detected corners to better approximate the markers. And
// push their codes to the output vector. // push their codes to the output vector.
cv::cornerSubPix(gray, valid_markers[i].points, cvSize(10, 10), cvSize(-1, -1), TERM_CRITERIA); cv::cornerSubPix(gray, valid_markers[i].points, cvSize(10, 10), cvSize(-1, -1), TERM_CRITERIA);
codes.push_back(valid_markers[i].code); codes.push_back(valid_markers[i].code);
} }
// Render the detected markers on top of the input image. // Render the detected markers on top of the input image.
cont = cv::Mat::zeros(img.size(), CV_8UC3); cont = cv::Mat::zeros(img.size(), CV_8UC3);
renderMarkers(valid_markers, cont); renderMarkers(valid_markers, cont);
img = img + cont; img = img + cont;
// Clear the local vectors. // Clear the local vectors.
markers.clear(); markers.clear();
contours.clear(); contours.clear();
valid_markers.clear(); valid_markers.clear();
} }
bool findCalibrationPattern(points_vector & corners, cv::Mat & img){ bool findCalibrationPattern(points_vector & corners, cv::Mat & img){
bool patternfound; bool patternfound;
cv::Mat gray; cv::Mat gray;
// Convert the input image to grayscale and attempt to find the // Convert the input image to grayscale and attempt to find the
// calibration pattern. // calibration pattern.
cv::cvtColor(img, gray, CV_BGR2GRAY); cv::cvtColor(img, gray, CV_BGR2GRAY);
patternfound = cv::findChessboardCorners(gray, CHESSBOARD_PATTERN_SIZE, corners, PATTERN_DETECTION_FLAGS); patternfound = cv::findChessboardCorners(gray, CHESSBOARD_PATTERN_SIZE, corners, PATTERN_DETECTION_FLAGS);
// If the pattern was found then fix the detected points a bit. // If the pattern was found then fix the detected points a bit.
if(patternfound) if(patternfound)
cv::cornerSubPix(gray, corners, cv::Size(11, 11), cv::Size(-1, -1), TERM_CRITERIA); cv::cornerSubPix(gray, corners, cv::Size(11, 11), cv::Size(-1, -1), TERM_CRITERIA);
// Render the detected pattern. // Render the detected pattern.
cv::drawChessboardCorners(img, CHESSBOARD_PATTERN_SIZE, cv::Mat(corners), patternfound); cv::drawChessboardCorners(img, CHESSBOARD_PATTERN_SIZE, cv::Mat(corners), patternfound);
return patternfound; return patternfound;
} }
double getCameraParameters(cv::Mat & camera_matrix, cv::Mat & dist_coeffs, std::vector<points_vector> & image_points, cv::Size image_size){ double getCameraParameters(cv::Mat & camera_matrix, cv::Mat & dist_coeffs, std::vector<points_vector> & image_points, cv::Size image_size){
std::vector<cv::Mat> rvecs, tvecs; std::vector<cv::Mat> rvecs, tvecs;
std::vector<points_vector_3D> object_points; std::vector<points_vector_3D> object_points;
points_vector_3D corner_points; points_vector_3D corner_points;
// Build the reference object points vector; // Build the reference object points vector.
for(int i = 0; i < CHESSBOARD_PATTERN_SIZE.height; i++){ for(int i = 0; i < CHESSBOARD_PATTERN_SIZE.height; i++){
for(int j = 0; j < CHESSBOARD_PATTERN_SIZE.width; j++){ for(int j = 0; j < CHESSBOARD_PATTERN_SIZE.width; j++){
corner_points.push_back(cv::Point3f(float( j * SQUARE_SIZE ), float( i * SQUARE_SIZE ), 0)); corner_points.push_back(cv::Point3f(float( j * SQUARE_SIZE ), float( i * SQUARE_SIZE ), 0));
} }
} }
object_points.push_back(corner_points); object_points.push_back(corner_points);
object_points.resize(image_points.size(), object_points[0]); object_points.resize(image_points.size(), object_points[0]);
// Build a camera matrix. // Build a camera matrix.
camera_matrix = cv::Mat::eye(3, 3, CV_64F); camera_matrix = cv::Mat::eye(3, 3, CV_64F);
// Build the distortion coefficients matrix. // Build the distortion coefficients matrix.
dist_coeffs = cv::Mat::zeros(8, 1, CV_64F); dist_coeffs = cv::Mat::zeros(8, 1, CV_64F);
// Calibrate and return the reprojection error. // Calibrate and return the reprojection error.
return cv::calibrateCamera(object_points, image_points, image_size, camera_matrix, dist_coeffs, rvecs, tvecs, 0, TERM_CRITERIA); return cv::calibrateCamera(object_points, image_points, image_size, camera_matrix, dist_coeffs, rvecs, tvecs, 0, TERM_CRITERIA);
} }
/****************************************************************************** /******************************************************************************
* PRIVATE HELPER FUNCTIONS * * PRIVATE HELPER FUNCTIONS *
******************************************************************************/ ******************************************************************************/
void findContours(cv::Mat & img, contours_vector & v, int minP){ /**
contours_vector c; * Find all contours in the input image and save them to the output
cv::findContours(img, c, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); * vector.
*/
void findContours(cv::Mat & img, contours_vector & v, int minP){
contours_vector c;
v.clear(); // A contour is discarded if it possess less than the specified
for(size_t i = 0; i < c.size(); i++){ // minimum number of points.
if(c[i].size() > minP){ cv::findContours(img, c, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
v.push_back(c[i]);
}
}
}
void renderMarkers(markers_vector & v, cv::Mat & dst){ v.clear();
contours_vector cv; for(size_t i = 0; i < c.size(); i++){
if(c[i].size() > minP){
v.push_back(c[i]);
}
}
}
for(size_t i = 0; i < v.size(); i++){ /**
std::vector<cv::Point> pv; * Render the input marker vector onto the output image.
for(size_t j = 0; j < v[i].points.size(); ++j) */
pv.push_back(cv::Point2f(v[i].points[j].x, v[i].points[j].y)); void renderMarkers(markers_vector & v, cv::Mat & dst){
cv.push_back(pv); contours_vector cv;
}
cv::drawContours(dst, cv, -1, COLOR, 1, CV_AA); // Extract the points that form every marker into a contours vector.
} for(size_t i = 0; i < v.size(); i++){
std::vector<cv::Point> pv;
for(size_t j = 0; j < v[i].points.size(); ++j)
pv.push_back(cv::Point2f(v[i].points[j].x, v[i].points[j].y));
cv.push_back(pv);
}
void isolateMarkers(const contours_vector & vc, markers_vector & vm){ // Render.
std::vector<cv::Point> appCurve; cv::drawContours(dst, cv, -1, COLOR, 1, CV_AA);
markers_vector posMarkers; }
for(size_t i = 0; i < vc.size(); ++i){ /**
double eps = vc[i].size() * 0.05; * Identify and return all marker candidates.
cv::approxPolyDP(vc[i], appCurve, eps, true); */
void isolateMarkers(const contours_vector & vc, markers_vector & vm){
std::vector<cv::Point> appCurve;
markers_vector posMarkers;
if(appCurve.size() != 4 || !cv::isContourConvex(appCurve)) continue; // For every detected contour.
for(size_t i = 0; i < vc.size(); ++i){
double eps = vc[i].size() * 0.05;
float minD = std::numeric_limits<float>::max(); // Approximate the contour with a polygon.
cv::approxPolyDP(vc[i], appCurve, eps, true);
for(int i = 0; i < 4; i++){ // If the polygon is not a cuadrilateral then this is not a marker
cv::Point side = appCurve[i] - appCurve[(i + 1) % 4]; // candidate.
float sqSideLen = side.dot(side); if(appCurve.size() != 4 || !cv::isContourConvex(appCurve)) continue;
minD = std::min(minD, sqSideLen);
}
if(minD < MIN_CONTOUR_LENGTH) continue; // Calculate the lenght of the perimeter of this candidate. If it
// is too short then discard it.
float minD = std::numeric_limits<float>::max();
Marker m; for(int i = 0; i < 4; i++){
cv::Point side = appCurve[i] - appCurve[(i + 1) % 4];
float sqSideLen = side.dot(side);
minD = std::min(minD, sqSideLen);
}
for(int i = 0; i < 4; i++) if(minD < MIN_CONTOUR_LENGTH) continue;
m.points.push_back(cv::Point2f(appCurve[i].x, appCurve[i].y));
cv::Point v1 = m.points[1] - m.points[0]; // Save the marker and order it's points counter-clockwise.
cv::Point v2 = m.points[2] - m.points[0]; Marker m;
double o = (v1.x * v2.y) - (v1.y * v2.x); for(int i = 0; i < 4; i++)
if(o < 0.0) std::swap(m.points[1], m.points[3]); m.points.push_back(cv::Point2f(appCurve[i].x, appCurve[i].y));
posMarkers.push_back(m); cv::Point v1 = m.points[1] - m.points[0];
} cv::Point v2 = m.points[2] - m.points[0];
std::vector<std::pair<int, int> > tooNear; double o = (v1.x * v2.y) - (v1.y * v2.x);
for(size_t i = 0; i < posMarkers.size(); ++i){ if(o < 0.0) std::swap(m.points[1], m.points[3]);
const Marker & m1 = posMarkers[i];
for(size_t j = i + 1; j < posMarkers.size(); j++){ posMarkers.push_back(m);
const Marker & m2 = posMarkers[j]; }
float dSq = 0.0f; // Identify contours that are to close to each other to eliminate
// possible duplicates.
std::vector<std::pair<int, int> > tooNear;
for(size_t i = 0; i < posMarkers.size(); ++i){
const Marker & m1 = posMarkers[i];
for(int c = 0; c < 4; c++){ for(size_t j = i + 1; j < posMarkers.size(); j++){
cv::Point v = m1.points[c] - m2.points[c]; const Marker & m2 = posMarkers[j];
dSq += v.dot(v);
}
dSq /= 4.0f; float dSq = 0.0f;
if(dSq < 100) tooNear.push_back(std::pair<int, int>(i, j)); for(int c = 0; c < 4; c++){
} cv::Point v = m1.points[c] - m2.points[c];
} dSq += v.dot(v);
}
std::vector<bool> remMask(posMarkers.size(), false); dSq /= 4.0f;
for(size_t i = 0; i < tooNear.size(); ++i){ if(dSq < 100) tooNear.push_back(std::pair<int, int>(i, j));
float p1 = perimeter(posMarkers[tooNear[i].first].points); }
float p2 = perimeter(posMarkers[tooNear[i].second].points); }
size_t remInd; // Mark one of every pair of duplicates to be discarded.
if(p1 > p2) remInd = tooNear[i].second; std::vector<bool> remMask(posMarkers.size(), false);
else remInd = tooNear[i].first; for(size_t i = 0; i < tooNear.size(); ++i){
float p1 = perimeter(posMarkers[tooNear[i].first].points);
float p2 = perimeter(posMarkers[tooNear[i].second].points);
remMask[remInd] = true; size_t remInd;
} if(p1 > p2) remInd = tooNear[i].second;
else remInd = tooNear[i].first;
vm.clear(); remMask[remInd] = true;
for(size_t i = 0; i < posMarkers.size(); ++i){ }
if(remMask[i]) vm.push_back(posMarkers[i]);
}
}
void warpMarker(Marker & m, cv::Mat & in, cv::Mat & out){ // Save the candidates that survided the duplicates test.
cv::Mat bin; vm.clear();
cv::Size markerSize(350, 350); for(size_t i = 0; i < posMarkers.size(); ++i){
points_vector v; if(!remMask[i]) vm.push_back(posMarkers[i]);
v.push_back(cv::Point2f(0,0)); }
v.push_back(cv::Point2f(markerSize.width-1,0)); }
v.push_back(cv::Point2f(markerSize.width-1,markerSize.height-1));
v.push_back(cv::Point2f(0,markerSize.height-1));
cv::Mat M = cv::getPerspectiveTransform(m.points, v); /**
cv::warpPerspective(in, bin, M, markerSize); * Warp a marker image to remove it's perspective distortion.
*/
void warpMarker(Marker & m, cv::Mat & in, cv::Mat & out){
cv::Mat bin;
cv::Size markerSize(350, 350);
points_vector v;
cv::threshold(bin, out, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); // Assemble a unitary reference polygon.
} v.push_back(cv::Point2f(0,0));
v.push_back(cv::Point2f(markerSize.width-1,0));
v.push_back(cv::Point2f(markerSize.width-1,markerSize.height-1));
v.push_back(cv::Point2f(0,markerSize.height-1));
int hammDistMarker(cv::Mat bits){ // Warp the input image's perspective to transform it into the reference
int ids[4][5] = { // polygon's perspective.
{1,0,0,0,0}, cv::Mat M = cv::getPerspectiveTransform(m.points, v);
{1,0,1,1,1}, cv::warpPerspective(in, bin, M, markerSize);
{0,1,0,0,1},
{0,1,1,1,0}
};
int dist = 0; // Binarize the warped image into the output image.
cv::threshold(bin, out, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
}
for (int y = 0; y < 5; y++){ /**
int minSum = 1e5; * Calculate the hamming distance of a 5x5 bit matrix.
*/
int hammDistMarker(cv::Mat bits){
int ids[4][5] = {
{1,0,0,0,0},
{1,0,1,1,1},
{0,1,0,0,1},
{0,1,1,1,0}
};
for (int p = 0; p < 4; p++){ int dist = 0;
int sum = 0;
for (int x = 0; x < 5; x++){ for (int y = 0; y < 5; y++){
sum += bits.at<uchar>(y, x) == ids[p][x] ? 0 : 1; int minSum = 1e5;
}
if(minSum > sum) for (int p = 0; p < 4; p++){
minSum = sum; int sum = 0;
}
dist += minSum; for (int x = 0; x < 5; x++){
} sum += bits.at<uchar>(y, x) == ids[p][x] ? 0 : 1;
}
return dist; if(minSum > sum)
} minSum = sum;
}
cv::Mat rotate(cv::Mat in){ dist += minSum;
cv::Mat out; }
in.copyTo(out);
for (int i=0;i<in.rows;i++){
for (int j=0;j<in.cols;j++){
out.at<uchar>(i,j)=in.at<uchar>(in.cols-j-1,i);
}
}
return out; return dist;
} }
int decodeMarker(cv::Mat & marker){ /**
bool found = false; * Rotate a matrix by 90 degrees clockwise.
int code = 0; */
cv::Mat bits; cv::Mat rotate(cv::Mat in){
cv::Mat out;
for(int y = 0; y < 7; y++){ in.copyTo(out);
int inc = (y == 0 || y == 6) ? 1 : 6; for (int i=0;i<in.rows;i++){
for (int j=0;j<in.cols;j++){
out.at<uchar>(i,j)=in.at<uchar>(in.cols-j-1,i);
}
}
for(int x = 0; x < 7; x += inc){ return out;
int cX = x * 50; }
int cY = y * 50;
cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50)); /**
* Decode a marker image and return it's code. Returns -1 if the image is
* not a valid marker.
*/
int decodeMarker(cv::Mat & marker){
bool found = false;
int code = 0;
cv::Mat bits;
int nZ = cv::countNonZero(cell); // Verify that the outer rim of marker cells are all black.
for(int y = 0; y < 7; y++){
int inc = (y == 0 || y == 6) ? 1 : 6;
// Not a valid marker. for(int x = 0; x < 7; x += inc){
if(nZ > (50 * 50) / 2) return -1; int cX = x * 50;
} int cY = y * 50;
} cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50));
bits = cv::Mat::zeros(5, 5, CV_8UC1); int nZ = cv::countNonZero(cell);
// If one of the rim cells is 50% white or more then this
// is not a valid marker.
if(nZ > (50 * 50) / 2) return -1;
}
}
// Create a 5x5 matrix to hold a simplified representation of this
// marker.
bits = cv::Mat::zeros(5, 5, CV_8UC1);
// For every cell in the marker flip it's corresponding 'bit' in the
// bit matrix if it is at least 50 % white.
for(int y = 0; y < 5; y++){
for(int x = 0; x < 5; x++){
int cX = (x + 1) * 50;
int cY = (y + 1) * 50;
cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50));
int nZ = cv::countNonZero(cell);
if(nZ > (50 * 50) / 2) bits.at<uchar>(y, x) = 1;
}
}
// Calcultate the hamming distance of the bit matrix and each of it's
// 90 degree rotations to determine if this marker has a valid code.
if(hammDistMarker(bits) != 0){
for(int r = 1; r < 4; r++){
bits = rotate(bits);
if(hammDistMarker(bits) != 0) continue;
else{ found = true; break;}
}
}else found = true;
// If the code is valid then decode it to an int and return it.
if(found){
for(int y = 0; y < 5; y++){
code <<= 1;
if(bits.at<uchar>(y, 1))
code |= 1;
code <<= 1;
if(bits.at<uchar>(y, 3))
code |= 1;
}
for(int y = 0; y < 5; y++){ return code;
for(int x = 0; x < 5; x++){ }else
int cX = (x + 1) * 50; return -1;
int cY = (y + 1) * 50; }
cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50)); /**
* Calculate the perimeter of a polygon defined as a vector of points.
*/
float perimeter(points_vector & p){
float per = 0.0f, dx, dy;
int nZ = cv::countNonZero(cell); for(size_t i; i < p.size(); ++i){
dx = p[i].x - p[(i + 1) % p.size()].x;
dy = p[i].y - p[(i + 1) % p.size()].y;
per += sqrt((dx * dx) + (dy * dy));
}
if(nZ > (50 * 50) / 2) bits.at<uchar>(y, x) = 1; return per;
} }
}
if(hammDistMarker(bits) != 0){
for(int r = 1; r < 4; r++){
bits = rotate(bits);
if(hammDistMarker(bits) != 0) continue;
else{ found = true; break;}
}
}else found = true;
if(found){
for(int y = 0; y < 5; y++){
code <<= 1;
if(bits.at<uchar>(y, 1))
code |= 1;
code <<= 1;
if(bits.at<uchar>(y, 3))
code |= 1;
}
return code;
}else
return -1;
}
float perimeter(points_vector & p){
float per = 0.0f, dx, dy;
for(size_t i; i < p.size(); ++i){
dx = p[i].x - p[(i + 1) % p.size()].x;
dy = p[i].y - p[(i + 1) % p.size()].y;
per += sqrt((dx * dx) + (dy * dy));
}
return per;
}
/****************************************************************************** /******************************************************************************
* CLASS METHODS * * CLASS METHODS *
******************************************************************************/ ******************************************************************************/
Marker::~Marker(){ /**
points.clear(); * Clear the points vector associated with this marker.
} */
Marker::~Marker(){
points.clear();
}
} }

View File

@@ -169,4 +169,10 @@ public class MainActivity extends AndroidApplication implements OSFunctionalityP
return null; return null;
} }
} }
@Override
public void calibrateCamera() {
// TODO Auto-generated method stub
}
} }