Skip to content

Commit

Permalink
Added another layer of filtering squares by only accepting the most p…
Browse files Browse the repository at this point in the history
…opular orientation.
  • Loading branch information
Daniel L. Lau committed May 28, 2018
1 parent da83bad commit fb969b7
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 66 deletions.
156 changes: 98 additions & 58 deletions laucaltagglwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,17 +489,16 @@ LAUCalTagFilterWidget::LAUCalTagFilterWidget(LAUCalTagGLWidget *glwdgt, QWidget
mednSpinBox->setValue(settings.value(QString("LAUCalTagScanWidget::mednSpinBox"), glWidget->medianRadius()).toInt());
offsetSpinBox->setValue(settings.value(QString("LAUCalTagScanWidget::offsetSpinBox"), glWidget->offset()).toDouble());

// COPY THE PARAMETERS OVER TO THE GLFILTER
glWidget->onSetMedianRadius(mednSpinBox->value());
glWidget->onSetGaussianRadius(gausSpinBox->value());
glWidget->onSetIterations(iterSpinBox->value());
glWidget->onSetFlipCalTagsFlag(flipCalTagsFlag->isChecked());
minRegionArea->setValue(settings.value(QString("LAUCalTagScanWidget::minRegionArea"), glWidget->minRegion()).toInt());
maxRegionArea->setValue(settings.value(QString("LAUCalTagScanWidget::maxRegionArea"), glWidget->maxRegion()).toInt());
minBoxCount->setValue(settings.value(QString("LAUCalTagScanWidget::minBoxCount"), glWidget->minBox()).toInt());
flipCalTagsFlag->setChecked(settings.value(QString("LAUCalTagScanWidget::flipCalTagsFlag"), glWidget->flipCalTags()).toBool());
}

/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
LAUCalTagGLWidget::LAUCalTagGLWidget(QWidget *parent) : LAUVideoGLWidget(parent), displayTextureFlag(true)
LAUCalTagGLWidget::LAUCalTagGLWidget(QWidget *parent) : LAUVideoGLWidget(parent)
{
// INITIALIZE PRIVATE VARIABLES
textureLUT = NULL;
Expand All @@ -518,7 +517,7 @@ LAUCalTagGLWidget::LAUCalTagGLWidget(QWidget *parent) : LAUVideoGLWidget(parent)
minBoxCount = 32; // 2X2 PIXELS TIMES 8X8 BOX SIZE
maxRegionArea = (640 * 480) / 16; // NEEDS IMAGE SIZE AND BYTES PER PIXEL
minRegionArea = maxRegionArea / 10;
flipCalTagsFlag = true;
flipCalTagsFlag = false;
}

/****************************************************************************/
Expand Down Expand Up @@ -738,19 +737,9 @@ void LAUCalTagGLWidget::paint()
if (quadIndexBuffer.bind()) {
// SET THE ACTIVE TEXTURE ON THE GPU
glActiveTexture(GL_TEXTURE0);
#ifndef QT_DEBUG
glBindTexture(GL_TEXTURE_2D, frameBufferObjectB->texture());
#else
videoTexture->bind();
#endif
programK.setUniformValue("qt_texture", 0);

if (displayTextureFlag) {
programK.setUniformValue("qt_mode", 0);
} else {
programK.setUniformValue("qt_mode", 1);
}

// TELL OPENGL PROGRAMMABLE PIPELINE HOW TO LOCATE VERTEX POSITION DATA
programK.setAttributeBuffer("qt_vertex", GL_FLOAT, 0, 4, 4 * sizeof(float));
programK.enableAttributeArray("qt_vertex");
Expand Down Expand Up @@ -1210,8 +1199,6 @@ cv::Mat LAUCalTagGLWidget::detectCalTagGrid(bool *okay)
#endif
cv::vector<cv::RotatedRect> rotatedRects = regionArea(sbImage);

cv::imwrite("/tmp/sbImage.tif", sbImage);

// MAKE SURE WE HAVE ENOUGH DETECTED RECTANGLES
if (rotatedRects.size() > (unsigned long)minBoxCount) {
// GET A GOOD APPROXIMATION OF WHERE THE SADDLE POINTS ARE
Expand Down Expand Up @@ -1288,28 +1275,6 @@ cv::Mat LAUCalTagGLWidget::detectCalTagGrid(bool *okay)
return (localTransform);
}

/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
cv::Mat LAUCalTagGLWidget::cleanStrays(cv::Mat inImage)
{
// MAKE A COPY OF THE INPUT IMAGE TO BE SAME FORMAT AND SIZE
cv::Mat otImage = inImage;

//REMOVE RANDOM NOISE THAT WAS IDENTIFIED AS A CONTOUR
cv::vector<cv::vector<cv::Point>> contours;
cv::vector<cv::Vec4i> hierarchy;

cv::findContours(inImage.clone(), contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cv::Point(0, 0));
cv::Scalar color = cv::Scalar(0);
for (unsigned int i = 0; i < contours.size(); i++) {
if (contours[i].size() < 8) {
cv::floodFill(otImage, contours[i][0], color);
}
}
return (otImage);
}

/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
Expand Down Expand Up @@ -1564,7 +1529,90 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
decodingMatrix.at<int>(i / 4, i % 4) = qRound(pow(2.0, (double)i));
}

// NOW LET'S TRY ALL ORIENTATIONS TO SEE WHICH IS THE MOST LIKELY ORIENTATION
int hist[4] = { 0, 0, 0, 0};
for (unsigned int n = 0; n < squares.size(); n++) {
// DERIVE SQUARE COORDINATES TO IMAGE PIXEL COORDINATES BASED ON CURRENT SQUARE
cv::vector<cv::Point2f> inPoints;
for (int c = 0; c < 4; c++) {
inPoints.push_back(squares[n][c]);
}
cv::Mat localTransform = cv::getPerspectiveTransform(sqPoints, inPoints);

// MAP THE CODE BIT COORDINATES TO IMAGE PIXEL COORDINATES
cv::Mat codeMatrix(4, 4, CV_32S);
cv::transform(gdPoints, kgPoints, localTransform);
for (unsigned int i = 0; i < kgPoints.size(); i++) {
// ACCUMULATE THE SUM OF PIXELS WITHIN A SMALL WINDOW ABOUT CURRENT PIXEL
int row = qRound(kgPoints[i].y / kgPoints[i].z);
int col = qRound(kgPoints[i].x / kgPoints[i].z);

// MAKE SURE THE CURRENT COORDINATE IS WITHIN THE BOUNDS OF THE IMAGE
if (row > 0 && row < image.rows - 1) {
if (col > 0 && col < image.cols - 1) {
int sum = 0;
for (int r = row - 1; r < row + 2; r++) {
for (int c = col - 1; c < col + 2; c++) {
sum += (image.at<unsigned char>(r, c) > 128);
}
}
// COPY THE CURRENT BIT TO THE CODE MATRIX
if (sum > 4) {
codeMatrix.at<int>(i / 4, 3 - i % 4) = 1;
} else {
codeMatrix.at<int>(i / 4, 3 - i % 4) = 0;
}
}
}
}

// FLIP THE CODE MATRIX LEFT-RIGHT IN CASE WE ARE
// LOOKING AT THE TARGET FROM BEHIND WITH BACK LIGHTING
if (flipCalTagsFlag) {
cv::flip(codeMatrix, codeMatrix, 1);
}

cv::Point2f point;
unsigned int code = decodingMatrix.dot(codeMatrix);
if (checkBitCode(code, &point)) {
hist[0]++;
}

cv::Mat matrix = codeMatrix;
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point)) {
hist[1]++;
}

cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point)) {
hist[2]++;
}

cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point)) {
hist[3]++;
}
}

// DETERMINE WHICH ORIENTATION IS THE MOST FREQUENT
int orientation = 3;
if (hist[0] > hist[1] && hist[0] > hist[2] && hist[0] > hist[3]) {
orientation = 0;
} else if (hist[1] > hist[0] && hist[1] > hist[2] && hist[1] > hist[3]) {
orientation = 1;
} else if (hist[2] > hist[0] && hist[2] > hist[1] && hist[2] > hist[3]) {
orientation = 2;
}

int validCodeCounter = 0;
for (unsigned int n = 0; n < squares.size(); n++) {
Expand Down Expand Up @@ -1609,31 +1657,30 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image

cv::Point2f point;
unsigned int code = decodingMatrix.dot(codeMatrix);
if (checkBitCode(code, &point) == false) {
if (orientation != 0 || checkBitCode(code, &point) == false) {
cv::Mat matrix = codeMatrix;
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 1 || checkBitCode(code, &point) == false) {
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 2 || checkBitCode(code, &point) == false) {
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 3 || checkBitCode(code, &point) == false) {
cv::vector<cv::Point2f> square;
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
outputSquares.push_back(square);
} else {
hist[3]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[3] + point);
Expand All @@ -1643,7 +1690,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[2]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[2] + point);
Expand All @@ -1653,7 +1699,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[1]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[1] + point);
Expand All @@ -1663,7 +1708,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[0]++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[0] + point);
square.push_back(sqPoints[3] + point);
Expand All @@ -1674,31 +1718,30 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
} else {
cv::Point2f point;
unsigned int code = decodingMatrix.dot(codeMatrix);
if (checkBitCode(code, &point) == false) {
if (orientation != 0 || checkBitCode(code, &point) == false) {
cv::Mat matrix = codeMatrix;
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 1 || checkBitCode(code, &point) == false) {
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 2 || checkBitCode(code, &point) == false) {
cv::transpose(matrix, matrix);
cv::flip(matrix, matrix, 1);
code = decodingMatrix.dot(matrix);

if (checkBitCode(code, &point) == false) {
if (orientation != 3 || checkBitCode(code, &point) == false) {
cv::vector<cv::Point2f> square;
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
square.push_back(cv::Point2f(NAN, NAN));
outputSquares.push_back(square);
} else {
hist[3]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[0] + point);
Expand All @@ -1708,7 +1751,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[2]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[3] + point);
Expand All @@ -1718,7 +1760,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[1]++;
validCodeCounter++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[2] + point);
Expand All @@ -1728,7 +1769,6 @@ cv::vector<cv::vector<cv::Point2f>> LAUCalTagGLWidget::findPattern(cv::Mat image
outputSquares.push_back(square);
}
} else {
hist[0]++;
cv::vector<cv::Point2f> square;
square.push_back(sqPoints[1] + point);
square.push_back(sqPoints[2] + point);
Expand Down Expand Up @@ -1832,7 +1872,7 @@ bool LAUCalTagGLWidget::checkBitCode(int code, cv::Point2f *pt)
for (int i = 0; i < 20; i++) {
for (int j = 13; j >= 0; j--) {
if (code == realBitCodes[i][j]) {
*pt = cv::Point2f(7.0f - (float)(13 - j), 14.0f - (float)(20 - i));
*pt = cv::Point2f((float)(j - 7), (float)(i - 7));
return (true);
} else if (code > realBitCodes[i][j]) {
return (false);
Expand Down
28 changes: 20 additions & 8 deletions laucaltagglwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ class LAUCalTagGLWidget : public LAUVideoGLWidget
return (iterationCount);
}

int minRegion() const
{
return (minRegionArea);
}

int maxRegion() const
{
return (maxRegionArea);
}

int minBox() const
{
return (minBoxCount);
}

bool flipCalTags() const
{
return (flipCalTagsFlag);
}

LAUMemoryObject grabImage();

public slots:
Expand Down Expand Up @@ -150,12 +170,6 @@ public slots:
processGL();
}

void onEnableTexture(bool state)
{
displayTextureFlag = state;
processGL();
}

protected:
void initialize();
void process();
Expand All @@ -178,7 +192,6 @@ public slots:
int maxRegionArea; // MAXIMUM REGION AREA
int minBoxCount; // MINIMUM BOX AREA
bool flipCalTagsFlag; // IS THE TARGET BACKLIT?
bool displayTextureFlag;

QByteArray memoryObject[2];
#ifdef QT_DEBUG
Expand All @@ -203,7 +216,6 @@ public slots:
void dilationErosion(QOpenGLFramebufferObject *fboA, QOpenGLFramebufferObject *fboB);
void removeOutlierPoints(cv::vector<cv::Point2f> &fmPoints, cv::vector<cv::Point2f> &toPoints);

cv::Mat cleanStrays(cv::Mat inImage);
cv::vector<cv::RotatedRect> regionArea(cv::Mat inImage);
cv::vector<cv::vector<cv::Point2f>> findSaddles(cv::vector<cv::RotatedRect> rotatedRects);
cv::vector<cv::vector<cv::Point2f>> findPattern(cv::Mat image, cv::vector<cv::vector<cv::Point2f>> squares);
Expand Down

0 comments on commit fb969b7

Please sign in to comment.