pastie/filters/pattern.cpp

222 lines
5.5 KiB
C++

/*
* This file is part of Pastie
*
* Pastie is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Pastie is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pastie. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @copyright 2015 Steffen Vogel
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <steffen.vogel@rwth-aachen.de>
* @link http://www.steffenvogel.de
*/
#include <opencv2/imgproc.hpp>
#include <QPainter>
#include <QtDebug>
#include "cast.h"
#include "pattern.h"
#include "image.h"
#include "convert.h"
Pattern::Pattern(Size si, int sp, Type t) :
Pattern(si, Size(sp, sp), t)
{ }
Pattern::Pattern(Size si, Size sp, Type t) :
type(t),
size(si),
spacing(sp),
areaRange(4000, 40000),
ratioRange(1, 1.7),
showThresh(false)
{
QMap<int, QString> mapType;
mapType[CHESSBOARD] = "Chessboard";
mapType[CIRCLES_GRID] = "Circles Grid";
mapType[ASYMMETRIC_CIRCLES_GRID] = "Asymmetric Circles Grid";
mapType[QUADRILINEAR_MARKERS] = "Quadrilinear Markers";
settings["Size"] = new SizeSetting(this, size, Range<int>(1, 16));
settings["Spacing"] = new SizeSetting(this, spacing, Range<int>(0, 5000));
settings["Type"] = new EnumSetting(this, (int&) type, mapType);
if (type == QUADRILINEAR_MARKERS) {
settings["Show Threshold"] = new BooleanSetting(this, showThresh);
settings["Area Range"] = new DoubleRangeSetting(this, areaRange, Range<double>(0000, 50000));
settings["Ratio Range"] = new DoubleRangeSetting(this, ratioRange, Range<double>(1, 2));
}
}
Result * Pattern::applyInternal(Image *img)
{
bool found = false;
std::vector<Point2f> points, allPoints;
std::vector<std::vector<Point>> contours;
Mat gray, thresh, &m = img->getMat();
if (m.channels() > 1)
cvtColor(m, gray, COLOR_RGB2GRAY);
else
gray = m.clone();
switch(type) {
case CHESSBOARD:
found = findChessboardCorners(gray, size, points, flags);
if (found)
cornerSubPix(gray, points, Size(11, 11), Size(-1, -1),
TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.1));
break;
case CIRCLES_GRID:
found = findCirclesGrid(gray, size, points);
break;
case ASYMMETRIC_CIRCLES_GRID:
found = findCirclesGrid(gray, size, points, CALIB_CB_ASYMMETRIC_GRID);
break;
case QUADRILINEAR_MARKERS:
blur(gray, gray, Size(13, 13));
threshold(gray, thresh, 0, 0xff, THRESH_BINARY_INV | THRESH_OTSU);
if (showThresh) {
m = thresh.clone();
cvtColor(m, m, COLOR_GRAY2BGR);
}
findContours(thresh, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
for (int idx = 0; idx < contours.size(); idx++) {
std::vector<Point> &contour = contours[idx];
std::vector<Point> approx;
double arc = arcLength(contour, true);
approxPolyDP(contour, approx, arc * 0.1, true);
if (contour.size() > 5) {
RotatedRect e = fitEllipse(contour);
int minSide = std::min(e.size.width, e.size.height);
int maxSide = std::max(e.size.width, e.size.height);
double ratio = (double) maxSide / minSide;
Mat roi = thresh(boundingRect(contour));
double nz = countNonZero(roi);
if (showThresh)
ellipse(m, e, Scalar(0,0xff,0), 3);
if (areaRange.contains(nz) &&
ratioRange.contains(ratio) &&
isContourConvex(approx)) {
allPoints.push_back(e.center);
if (showThresh)
ellipse(m, e, Scalar(0,0,0xff), 3);
}
}
}
if (allPoints.size()) {
convexHull(allPoints, points);
found = (points.size() == 4);
if (found)
std::swap(points[2], points[3]);
}
break;
default:
return NULL;
}
return new PatternResult(found, points);
}
std::vector<Point3f> Pattern::getPoints() const
{
std::vector<Point3f> points;
switch(type) {
case CHESSBOARD:
case CIRCLES_GRID:
for (int i = 0; i < size.height; ++i) {
for (int j = 0; j < size.width; ++j)
points.push_back(Point3f(j*spacing.width, i*spacing.height, 0));
}
break;
case ASYMMETRIC_CIRCLES_GRID:
for (int i = 0; i < size.height; i++) {
for (int j = 0; j < size.width; j++)
points.push_back(Point3f((2*j+i%2)*spacing.width, i*spacing.height, 0));
}
break;
case QUADRILINEAR_MARKERS:
points.push_back(Point3f(0, 0, 0));
points.push_back(Point3f(spacing.width, 0, 0));
points.push_back(Point3f(0, spacing.height, 0));
points.push_back(Point3f(spacing.width, spacing.height, 0));
break;
default:
break;
}
return points;
}
std::vector<int> Pattern::getCornerIndizes() const
{
return {
0, size.width - 1,
size.area() - size.width,
size.area() - 1
};
}
PatternResult::PatternResult(bool f, std::vector<Point2f> p) :
found(f),
points(p)
{ }
void PatternResult::drawResult(Painter *p) const
{
QPen pe = p->pen();
pe.setWidth(2);
for (unsigned i = 0; i < points.size(); i++) {
if (found)
pe.setColor(QColor::fromHsl(360.0 / points.size() * i, 255, 128));
else
pe.setColor(Qt::gray);
p->setPen(pe);
p->drawMarker(toQt((Point2i) points[i]));
if (i != 0)
p->drawLine(toQt((Point2i) points[i-1]), toQt((Point2i) points[i]));
}
}
QString PatternResult::getResult() const
{
return QString("found = %1, points = %2").arg(found).arg(points.size());
}