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

@@ -32,6 +32,11 @@ namespace nxtar{
* PRIVATE CONSTANTS * * PRIVATE CONSTANTS *
******************************************************************************/ ******************************************************************************/
/**
* Minimal number of points in a contour.
*/
static const int MIN_POINTS = 40;
/** /**
* Size of a square cell in the calibration pattern. * Size of a square cell in the calibration pattern.
*/ */
@@ -66,44 +71,20 @@ namespace nxtar{
* PRIVATE FUNCTION PROTOTYPES * * PRIVATE FUNCTION PROTOTYPES *
******************************************************************************/ ******************************************************************************/
/**
* Calculates the perimeter of a points vector defining a polygon.
*/
float perimeter(points_vector &); float perimeter(points_vector &);
/**
* Calculates the Hamming distance of a 5x5 marker.
*/
int hammDistMarker(cv::Mat); int hammDistMarker(cv::Mat);
/**
* Rotates an OpenCV matrix in place by 90 degrees clockwise.
*/
cv::Mat rotate(cv::Mat); cv::Mat rotate(cv::Mat);
/**
* Returns the code of a 5x5 marker or -1 if the marker is not valid.
*/
int decodeMarker(cv::Mat &); int decodeMarker(cv::Mat &);
/**
* Renders the polygon defined in the input vector on the specified image.
*/
void renderMarkers(markers_vector &, cv::Mat &); void renderMarkers(markers_vector &, cv::Mat &);
/**
* Identifies all possible marker candidates in a polygon vector.
*/
void isolateMarkers(const contours_vector &, markers_vector &); void isolateMarkers(const contours_vector &, markers_vector &);
/**
* Identifies all roughly 4 side figures in the input image.
*/
void findContours(cv::Mat &, contours_vector &, int); void findContours(cv::Mat &, contours_vector &, int);
/**
* Removes the prerspective distortion from a marker candidate image.
*/
void warpMarker(Marker &, cv::Mat &, cv::Mat &); void warpMarker(Marker &, cv::Mat &, cv::Mat &);
/****************************************************************************** /******************************************************************************
@@ -127,12 +108,12 @@ namespace nxtar{
// 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);
@@ -203,7 +184,7 @@ namespace nxtar{
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));
@@ -226,8 +207,15 @@ namespace nxtar{
* PRIVATE HELPER FUNCTIONS * * PRIVATE HELPER FUNCTIONS *
******************************************************************************/ ******************************************************************************/
/**
* Find all contours in the input image and save them to the output
* vector.
*/
void findContours(cv::Mat & img, contours_vector & v, int minP){ void findContours(cv::Mat & img, contours_vector & v, int minP){
contours_vector c; contours_vector c;
// A contour is discarded if it possess less than the specified
// minimum number of points.
cv::findContours(img, c, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); cv::findContours(img, c, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
v.clear(); v.clear();
@@ -238,9 +226,13 @@ namespace nxtar{
} }
} }
/**
* Render the input marker vector onto the output image.
*/
void renderMarkers(markers_vector & v, cv::Mat & dst){ void renderMarkers(markers_vector & v, cv::Mat & dst){
contours_vector cv; contours_vector cv;
// Extract the points that form every marker into a contours vector.
for(size_t i = 0; i < v.size(); i++){ for(size_t i = 0; i < v.size(); i++){
std::vector<cv::Point> pv; std::vector<cv::Point> pv;
for(size_t j = 0; j < v[i].points.size(); ++j) for(size_t j = 0; j < v[i].points.size(); ++j)
@@ -248,19 +240,30 @@ namespace nxtar{
cv.push_back(pv); cv.push_back(pv);
} }
// Render.
cv::drawContours(dst, cv, -1, COLOR, 1, CV_AA); cv::drawContours(dst, cv, -1, COLOR, 1, CV_AA);
} }
/**
* Identify and return all marker candidates.
*/
void isolateMarkers(const contours_vector & vc, markers_vector & vm){ void isolateMarkers(const contours_vector & vc, markers_vector & vm){
std::vector<cv::Point> appCurve; std::vector<cv::Point> appCurve;
markers_vector posMarkers; markers_vector posMarkers;
// For every detected contour.
for(size_t i = 0; i < vc.size(); ++i){ for(size_t i = 0; i < vc.size(); ++i){
double eps = vc[i].size() * 0.05; double eps = vc[i].size() * 0.05;
// Approximate the contour with a polygon.
cv::approxPolyDP(vc[i], appCurve, eps, true); cv::approxPolyDP(vc[i], appCurve, eps, true);
// If the polygon is not a cuadrilateral then this is not a marker
// candidate.
if(appCurve.size() != 4 || !cv::isContourConvex(appCurve)) continue; if(appCurve.size() != 4 || !cv::isContourConvex(appCurve)) 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(); float minD = std::numeric_limits<float>::max();
for(int i = 0; i < 4; i++){ for(int i = 0; i < 4; i++){
@@ -271,6 +274,7 @@ namespace nxtar{
if(minD < MIN_CONTOUR_LENGTH) continue; if(minD < MIN_CONTOUR_LENGTH) continue;
// Save the marker and order it's points counter-clockwise.
Marker m; Marker m;
for(int i = 0; i < 4; i++) for(int i = 0; i < 4; i++)
@@ -285,6 +289,8 @@ namespace nxtar{
posMarkers.push_back(m); posMarkers.push_back(m);
} }
// Identify contours that are to close to each other to eliminate
// possible duplicates.
std::vector<std::pair<int, int> > tooNear; std::vector<std::pair<int, int> > tooNear;
for(size_t i = 0; i < posMarkers.size(); ++i){ for(size_t i = 0; i < posMarkers.size(); ++i){
const Marker & m1 = posMarkers[i]; const Marker & m1 = posMarkers[i];
@@ -305,8 +311,8 @@ namespace nxtar{
} }
} }
// Mark one of every pair of duplicates to be discarded.
std::vector<bool> remMask(posMarkers.size(), false); std::vector<bool> remMask(posMarkers.size(), false);
for(size_t i = 0; i < tooNear.size(); ++i){ for(size_t i = 0; i < tooNear.size(); ++i){
float p1 = perimeter(posMarkers[tooNear[i].first].points); float p1 = perimeter(posMarkers[tooNear[i].first].points);
float p2 = perimeter(posMarkers[tooNear[i].second].points); float p2 = perimeter(posMarkers[tooNear[i].second].points);
@@ -318,27 +324,39 @@ namespace nxtar{
remMask[remInd] = true; remMask[remInd] = true;
} }
// Save the candidates that survided the duplicates test.
vm.clear(); vm.clear();
for(size_t i = 0; i < posMarkers.size(); ++i){ for(size_t i = 0; i < posMarkers.size(); ++i){
if(remMask[i]) vm.push_back(posMarkers[i]); if(!remMask[i]) vm.push_back(posMarkers[i]);
} }
} }
/**
* Warp a marker image to remove it's perspective distortion.
*/
void warpMarker(Marker & m, cv::Mat & in, cv::Mat & out){ void warpMarker(Marker & m, cv::Mat & in, cv::Mat & out){
cv::Mat bin; cv::Mat bin;
cv::Size markerSize(350, 350); cv::Size markerSize(350, 350);
points_vector v; points_vector v;
// Assemble a unitary reference polygon.
v.push_back(cv::Point2f(0,0)); v.push_back(cv::Point2f(0,0));
v.push_back(cv::Point2f(markerSize.width-1,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(markerSize.width-1,markerSize.height-1));
v.push_back(cv::Point2f(0,markerSize.height-1)); v.push_back(cv::Point2f(0,markerSize.height-1));
// Warp the input image's perspective to transform it into the reference
// polygon's perspective.
cv::Mat M = cv::getPerspectiveTransform(m.points, v); cv::Mat M = cv::getPerspectiveTransform(m.points, v);
cv::warpPerspective(in, bin, M, markerSize); cv::warpPerspective(in, bin, M, markerSize);
// Binarize the warped image into the output image.
cv::threshold(bin, out, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); cv::threshold(bin, out, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
} }
/**
* Calculate the hamming distance of a 5x5 bit matrix.
*/
int hammDistMarker(cv::Mat bits){ int hammDistMarker(cv::Mat bits){
int ids[4][5] = { int ids[4][5] = {
{1,0,0,0,0}, {1,0,0,0,0},
@@ -369,8 +387,12 @@ namespace nxtar{
return dist; return dist;
} }
/**
* Rotate a matrix by 90 degrees clockwise.
*/
cv::Mat rotate(cv::Mat in){ cv::Mat rotate(cv::Mat in){
cv::Mat out; cv::Mat out;
in.copyTo(out); in.copyTo(out);
for (int i=0;i<in.rows;i++){ for (int i=0;i<in.rows;i++){
for (int j=0;j<in.cols;j++){ for (int j=0;j<in.cols;j++){
@@ -381,43 +403,51 @@ namespace nxtar{
return out; return out;
} }
/**
* Decode a marker image and return it's code. Returns -1 if the image is
* not a valid marker.
*/
int decodeMarker(cv::Mat & marker){ int decodeMarker(cv::Mat & marker){
bool found = false; bool found = false;
int code = 0; int code = 0;
cv::Mat bits; cv::Mat bits;
// Verify that the outer rim of marker cells are all black.
for(int y = 0; y < 7; y++){ for(int y = 0; y < 7; y++){
int inc = (y == 0 || y == 6) ? 1 : 6; int inc = (y == 0 || y == 6) ? 1 : 6;
for(int x = 0; x < 7; x += inc){ for(int x = 0; x < 7; x += inc){
int cX = x * 50; int cX = x * 50;
int cY = y * 50; int cY = y * 50;
cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50)); cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50));
int nZ = cv::countNonZero(cell); int nZ = cv::countNonZero(cell);
// Not a valid marker. // 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; 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); 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 y = 0; y < 5; y++){
for(int x = 0; x < 5; x++){ for(int x = 0; x < 5; x++){
int cX = (x + 1) * 50; int cX = (x + 1) * 50;
int cY = (y + 1) * 50; int cY = (y + 1) * 50;
cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50)); cv::Mat cell = marker(cv::Rect(cX, cY, 50, 50));
int nZ = cv::countNonZero(cell); int nZ = cv::countNonZero(cell);
if(nZ > (50 * 50) / 2) bits.at<uchar>(y, x) = 1; 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){ if(hammDistMarker(bits) != 0){
for(int r = 1; r < 4; r++){ for(int r = 1; r < 4; r++){
bits = rotate(bits); bits = rotate(bits);
@@ -426,7 +456,7 @@ namespace nxtar{
} }
}else found = true; }else found = true;
// If the code is valid then decode it to an int and return it.
if(found){ if(found){
for(int y = 0; y < 5; y++){ for(int y = 0; y < 5; y++){
code <<= 1; code <<= 1;
@@ -444,6 +474,9 @@ namespace nxtar{
return -1; return -1;
} }
/**
* Calculate the perimeter of a polygon defined as a vector of points.
*/
float perimeter(points_vector & p){ float perimeter(points_vector & p){
float per = 0.0f, dx, dy; float per = 0.0f, dx, dy;
@@ -460,6 +493,9 @@ namespace nxtar{
* CLASS METHODS * * CLASS METHODS *
******************************************************************************/ ******************************************************************************/
/**
* Clear the points vector associated with this marker.
*/
Marker::~Marker(){ Marker::~Marker(){
points.clear(); 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
}
} }