added, improved and updated a large set of the OpenCV filter wrappers

This commit is contained in:
Steffen Vogel 2015-01-15 11:50:59 +01:00
parent 2ad5599a98
commit e627a02a3d
40 changed files with 1171 additions and 314 deletions

View file

@ -38,18 +38,18 @@ Blur::Blur(enum Type t, Size ks, double sp, double clr, double si, int bdr) :
Result * Blur::applyInternal(Image *img)
{
Mat m = img->filtered.clone();
Mat &n = img->filtered;
Mat &n = img->getMat();
Mat m = n.clone();
switch (type) {
case BILATERAL: bilateralFilter(m, n, -1, space, color); break;
case BILATERAL_ADAPTIVE: break; // FIXME adaptiveBilateralFilter(m, n, size, color, space); break;
case BILATERAL_ADAPTIVE: /*adaptiveBilateralFilter(m, n, size, space, color);*/ break;
case BOX: boxFilter(m, n, -1, size, anchor, true, border); break;
case BOX_NORMALIZED: blur(m, n, size, anchor, border); break;
case GAUSSIAN: GaussianBlur(m, n, size, sigma, 0, border); break;
case MEDIAN: medianBlur(m, n, size.width); break;
}
return NULL;
return new Result;
}

View file

@ -7,16 +7,20 @@ using namespace cv;
Channel::Channel(int ch) :
channel(ch)
{
settings["Channel"] = new IntegerSetting(this, channel);
settings["Channel"] = new IntegerSetting(this, channel, Range<int>(0, 5));
}
Result * Channel::applyInternal(Image *img)
{
Mat channels[3];
split(img->filtered, channels);
Mat &m = img->getMat();
Mat *channels = new Mat[m.channels()];
img->filtered = channels[(int) channel];
if (channel < m.channels()) {
split(m, channels);
m = channels[channel];
}
return NULL;
delete[] channels;
return new Result;
}

View file

@ -19,10 +19,11 @@ Convert::Convert(int c) :
Result * Convert::applyInternal(Image *img)
{
Mat dest;
cvtColor(img->filtered, dest, code);
img->filtered = dest;
Mat &m = img->getMat();
Mat n = m.clone();
return NULL;
cvtColor(n, m, code);
return new Result;
}

35
filters/derivative.cpp Normal file
View file

@ -0,0 +1,35 @@
#include "derivative.h"
Derivative::Derivative(Type t, Size o, int s) :
type(t),
order(o),
size(s),
maxima(false)
{
QMap<int, QString> typeMap;
typeMap[SOBEL] = "Sobel";
typeMap[SCHARR] = "Scharr";
settings["Type"] = new EnumSetting(this, (int&) type, typeMap);
settings["Order"] = new SizeSetting(this, order);
settings["Size"] = new IntegerSetting(this, size, Range<int>(1, 10));
settings["Maxima"] = new BooleanSetting(this, maxima, "Find local maximas in source image.");
settings["Scale"] = new DoubleSetting(this, scale);
settings["Delta"] = new DoubleSetting(this, delta);
}
Result * Derivative::applyInternal(Image *img)
{
Mat &n = img->getMat();
switch (type) {
case SOBEL: Sobel(n, n, n.depth(), order.width, order.height, size, scale, delta); break;
case SCHARR: Scharr(n, n, n.depth(), order.width, order.height, scale, delta); break;
}
if (maxima)
compare(n, 0, n, CMP_EQ);
return new Result;
}

30
filters/derivative.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef DERIVATIVE_H
#define DERIVATIVE_H
#include "filter.h"
class Derivative : public Filter
{
public:
enum Type {
SOBEL,
SCHARR
};
Derivative(enum Type t, Size order = Size(1, 1), int size = 3);
QString getName() const { return "Derivative"; }
protected:
Result * applyInternal(Image *img);
enum Type type;
Size order;
int size;
double scale;
double delta;
bool maxima;
};
#endif // DERIVATIVE_H

View file

@ -12,7 +12,11 @@ EdgeDetect::EdgeDetect(double t, double r) :
Result * EdgeDetect::applyInternal(Image *img)
{
Canny(img->filtered, img->filtered, threshold, threshold * ratio);
Mat &m = img->getMat();
CV_Assert(m.channels() == 1);
Canny(m, m, threshold, threshold * ratio);
return NULL;
}

View file

@ -7,11 +7,13 @@
#include <QVector>
#include <QPainter>
#include <QWidget>
#include <QMouseEvent>
#include "painter.h"
#include "image.h"
#include "result.h"
#include "setting.h"
#include "range.h"
using namespace cv;
@ -39,6 +41,7 @@ class Filter : public QObject
public slots:
virtual void reset() { }
virtual bool clicked(Point, QMouseEvent * = 0) { return false; }
signals:
void filterChanged();

View file

@ -2,7 +2,9 @@
Result * HistEqualize::applyInternal(Image *img)
{
equalizeHist(img->filtered, img->filtered);
Mat &m = img->getMat();
return NULL;
equalizeHist(m, m);
return new Result;
}

View file

@ -1,60 +1,101 @@
#include <QtDebug>
#include <QBitArray>
#include "kmeans.h"
using namespace std;
KMeans::KMeans(int km) :
colorFilterRange(1, km),
k(km),
low(0),
high(k)
iterations(3),
iterative(false)
{
settings["K"] = new IntegerSetting(this, k, 1, 2*k);
settings["Low"] = new IntegerSetting(this, low, 0, 2*k-1,
"Only set pixels with a label higher or equal to this value.");
settings["High"] = new IntegerSetting(this, high, 0, 2*k,
"Only set Pixels with a label lower or equal to this value.");
settings["K"] = new IntegerSetting(this, k, Range<int>(1, 10));
settings["Iterations"] = new IntegerSetting(this, iterations, Range<int>(1, 10));
settings["Iterative"] = new BooleanSetting(this, iterative, "Use labels from last run as starting point.");
settings["Color"] = new RangeSetting(this, colorFilterRange, Range<int>(1, 10));
}
void KMeans::reset()
{
labels.release();
colorFilterPoints.clear();
}
Result * KMeans::applyInternal(Image *img)
{
Mat m = img->filtered.clone();
Mat &m = img->getMat();
Mat reshaped_image, reshaped_image32f;
Mat centers, centers_u8;
int flags = KMEANS_RANDOM_CENTERS; //KMEANS_PP_CENTERS;
TermCriteria criteria = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 4, 0.1);
int flags = KMEANS_PP_CENTERS;
if (iterative && labels.data)
flags |= KMEANS_USE_INITIAL_LABELS;
reshaped_image = m.reshape(1, m.cols * m.rows);
reshaped_image.convertTo(reshaped_image32f, CV_32FC1, 1.0 / 255.0);
if (labels.data)
flags |= KMEANS_USE_INITIAL_LABELS;
TermCriteria criteria = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 4, 0.1);
kmeans(reshaped_image32f, k, labels, criteria, iterations, flags, centers);
kmeans(reshaped_image32f, k, labels, criteria, 1, flags, centers);
/* Filter single color */
if (colorFilterPoints.size() || colorFilterRange.min > 1 || colorFilterRange.max < k) {
Mat bw = Mat(m.size(), CV_8UC1);
QBitArray mask(k);
MatIterator_<Vec3b> first = img->filtered.begin<Vec3b>();
MatIterator_<Vec3b> last = img->filtered.end<Vec3b>();
MatConstIterator_<int> label = labels.begin<int>();
/* Calculate label histogram */
if (colorFilterRange.min > 1 || colorFilterRange.max < k) {
QList<QPair<int, int>> hist;
for (int i = 0; i < k; i++)
hist.push_back(qMakePair(0, i));
centers.convertTo(centers_u8, CV_8UC1, 255.0);
Mat centers_u8c3 = centers_u8.reshape(3);
for (auto it = labels.begin<int>();
it != labels.end<int>();
it++)
hist[*it].first++;
while (first != last) {
if (*label >= low && *label <= high)
*first = centers_u8c3.ptr<Vec3b>(*label)[0];
else
*first = Vec3b(0, 0, 0);
qSort(hist);
++first;
++label;
for (QPair<int,int> p : hist)
qDebug() << "Label = " << p.second << "Count =" << p.first;
for (int i = colorFilterRange.min-1; i < colorFilterRange.max-1; i++)
mask.setBit(hist[i].second);
}
/* Create label mask */
Mat labelShape = labels.reshape(1, m.rows);
for (Point p : colorFilterPoints) {
int label = labelShape.at<int>(p);
mask.setBit(label);
}
qDebug() << "mask = " << mask << "labelShape = " << labelShape.cols << labelShape.rows;
/* Filter labels */
MatIterator_<uchar> pix = bw.begin<uchar>();
for (auto lbl = labels.begin<int>(); lbl != labels.end<int>(); lbl++, ++pix)
*pix = mask.testBit(*lbl) ? 0xff : 0;
m = bw;
}
else {
centers.convertTo(centers_u8, CV_8UC1, 255.0);
Mat centers_u8c3 = centers_u8.reshape(3);
MatIterator_<Vec3b> pixel = m.begin<Vec3b>();
for (auto label = labels.begin<int>();
label != labels.end<int>();
label++, ++pixel)
*pixel = centers_u8c3.ptr<Vec3b>(*label)[0];
}
return NULL;
return new Result;
}
bool KMeans::clicked(Point p, QMouseEvent *)
{
colorFilterPoints.append(p);
return true;
}

View file

@ -1,6 +1,7 @@
#ifndef KMEANS_H
#define KMEANS_H
#include "range.h"
#include "filter.h"
class KMeans : public Filter
@ -12,15 +13,19 @@ class KMeans : public Filter
QString getName() const { return "KMeans"; }
public slots:
void reset();
bool clicked(Point, QMouseEvent *);
protected:
Result * applyInternal(Image *img);
int k;
int low, high;
QList<Point> colorFilterPoints;
Range<int> colorFilterRange;
int k, iterations;
Mat labels;
bool iterative;
};
#endif // KMEANS_H

View file

@ -2,7 +2,7 @@
Result * MaxChannel::applyInternal(Image *img)
{
Mat &m = img->filtered;
Mat &m = img->getMat();
CV_Assert(m.type() == CV_8UC3);
@ -15,6 +15,6 @@ Result * MaxChannel::applyInternal(Image *img)
}
}
return NULL;
return new Result;
}

View file

@ -24,7 +24,7 @@ Morph::Morph(int o, int t, Size s, Point a, int i) :
settings["Iterations"] = new IntegerSetting(this, iterations);
settings["Operation"] = new EnumSetting(this, op, mapOp);
settings["Shape"] = new EnumSetting(this, shape, mapShape);
settings["Size"] = new SizeSetting(this, size, 1);
settings["Size"] = new SizeSetting(this, size, Range<int>(1, 32));
reset();
}
@ -36,7 +36,7 @@ void Morph::reset()
Result * Morph::applyInternal(Image *img)
{
Mat &m = img->filtered;
Mat &m = img->getMat();
switch (op) {
case MORPH_DILATE: dilate(m, m, kernel, anchor, iterations); break;
@ -50,5 +50,5 @@ Result * Morph::applyInternal(Image *img)
CV_Error(100, "Invalid morph operation");
}
return NULL;
return new Result;
}

View file

@ -2,7 +2,7 @@
Result * Normalize::applyInternal(Image *img)
{
Mat &m = img->filtered;
Mat &m = img->getMat();
CV_Assert(m.type() == CV_8UC3);
@ -18,5 +18,5 @@ Result * Normalize::applyInternal(Image *img)
}
}
return NULL;
return new Result;
}

120
filters/paddetect.cpp Normal file
View file

@ -0,0 +1,120 @@
#include "paddetect.h"
static double angle(Point pt1, Point pt2, Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
PadDetect::PadDetect(double e, double cos) :
epsilon(e),
areaRatio(0.85),
cosine(cos),
mode(AREA)
{
QMap<int, QString> mapMode;
mapMode[AREA] = "Area";
mapMode[COSINE] = "Cosine";
settings["Mode"] = new EnumSetting(this, (int&) mode, mapMode);
settings["Epsilon"] = new DoubleSetting(this, epsilon, Range<double>(0, 1));
settings["Area Ratio"] = new DoubleSetting(this, areaRatio, Range<double>(0, 1));
settings["Cosine"] = new DoubleSetting(this, cosine, Range<double>(0, 1));
}
Result * PadDetect::applyInternal(Image *img)
{
Mat &m = img->getMat();
std::vector<std::vector<Point>> contours;
PadResult *result = new PadResult;
findContours(m.clone(), contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
for (std::vector<Point> contour : contours) {
std::vector<Point> approx;
bool closed = isContourConvex(contour);
double arc = arcLength(contour, closed);
switch (mode) {
case PadDetect::COSINE:
approxPolyDP(contour, approx, epsilon * arc, closed);
if (approx.size() == 4) {
double maxCosine = 0;
for (int j = 2; j < 5; j++) {
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = std::max(maxCosine, cosine);
}
if (maxCosine < cosine) {
Pad pad = minAreaRect(approx);
//RotatedRect rect = minAreaRect(contour);
result->push_back(pad);
}
}
break;
case PadDetect::AREA: {
Pad pad = minAreaRect(contour);
double areaContour = contourArea(contour);
double areaRect = pad.getArea();
if (areaContour / areaRect > areaRatio)
result->push_back(pad);
break;
}
}
}
return result;
}
void PadResult::drawResult(Painter *p) const
{
QPen pe;
QBrush br;
QColor clr(Qt::magenta);
clr.setAlpha(32);
br.setStyle(Qt::SolidPattern);
br.setColor(clr);
pe.setColor(Qt::magenta);
pe.setWidth(2);
p->setBrush(br);
p->setPen(pe);
for (auto pad : *this)
p->drawPad(pad);
}
QString PadResult::getResult() const
{
return QString("count = %1").arg(size());
}
QList<Pad>::iterator PadResult::getNearest(const Point &p)
{
float minDist = FLT_MAX;
QList<Pad>::iterator nearest = end();
for (auto it = begin(); it != end(); it++) {
float dist = norm(it->center - Point2f(p));
if (dist < minDist) {
nearest = it;
minDist = dist;
}
}
return nearest;
}

41
filters/paddetect.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef PADDETECT_H
#define PADDETECT_H
#include <vector>
#include "pad.h"
#include "filter.h"
using namespace std;
class PadDetect : public Filter
{
public:
enum Mode {
AREA,
COSINE
};
PadDetect(double epsilon = .1, double cosine = 0.3);
QString getName() const { return "PadDetect"; };
protected:
Result * applyInternal(Image *img);
double epsilon;
double areaRatio;
double cosine;
enum Mode mode;
};
class PadResult : public Result, public QList<Pad>
{
public:
void drawResult(Painter *p) const;
QString getResult() const;
QList<Pad>::iterator getNearest(const Point &p);
};
#endif // PADDETECT_H

View file

@ -1,7 +1,55 @@
#include "padfilter.h"
#include "paddetect.h"
Result * PadFilter::applyInternal(Image *)
PadFilter::PadFilter(Filter *s) :
source(s),
areaRange(35, 6000),
ratioRange(1, 3)
{
return NULL;
settings["Area Range"] = new DoubleRangeSetting(this, areaRange, Range<double>(1, 50000));
settings["Ratio Range"] = new DoubleRangeSetting(this, ratioRange, Range<double>(1, 10));
}
Result * PadFilter::applyInternal(Image *img)
{
PadResult *pads = dynamic_cast<PadResult*>(img->getResult(source));
if (pads) {
PadResult *result = new PadResult;
for (Pad &pad : *pads) {
double ratio = pad.getRatio();
double area = pad.getArea();
if (areaRange.contains(area) &&
ratioRange.contains(ratio))
result->push_back(pad);
}
for (Point p : addPoints)
result->append(Pad(p));
for (Point p : delPoints)
result->erase(result->getNearest(p));
return result;
}
else
qWarning() << getName() << "Missing pad list!";
return new Result;
}
void PadFilter::reset()
{
addPoints.clear();
delPoints.clear();
}
bool PadFilter::clicked(Point p, QMouseEvent *me)
{
if (me->modifiers() & Qt::AltModifier)
delPoints.append(p);
else
addPoints.append(p);
return true;
}

View file

@ -6,11 +6,21 @@
class PadFilter : public Filter
{
public:
QString getName() const { return "PadFilter"; };
PadFilter(Filter *source);
QString getName() const { return "PadFilter"; }
bool clicked(Point p, QMouseEvent *me);
void reset();
protected:
Result * applyInternal(Image *img);
Filter *source;
Range<double> areaRange, ratioRange;
QList<Point> addPoints, delPoints;
};
#endif // PADFILTER_H

103
filters/pathplanner.cpp Normal file
View file

@ -0,0 +1,103 @@
#include "pathplanner.h"
#include "range.h"
PathPlanner::PathPlanner(Filter *s, enum Algorithm a) :
algo(a),
source(s)
{
QMap<int, QString> algoMap;
algoMap[NEAREST_NEIGHBOUR] = "Nearest Neighbour";
algoMap[REPETETIVE_NEAREST_NEIGHBOUR] = "Repetitive Nearest Neighbour";
//settings["Algorithm"] = new EnumSetting(this, (int &) algo, algoMap);
}
Result * PathPlanner::applyInternal(Image *img)
{
PadResult *list = dynamic_cast<PadResult*>(img->getResult(source));
if (list) {
PathResult *path = new PathResult;
PadResult vertices = *list; /* create a copy */
if (vertices.size() < 1 || vertices.size() > 1000) {
qWarning() << "Found too much/less pads (" << vertices.size() << ")! Skipping PathPlanner";
return path;
}
switch (algo) {
case NEAREST_NEIGHBOUR:
path->append(vertices.takeFirst());
while (!vertices.isEmpty()) {
auto nearest = vertices.getNearest(path->last().center);
path->append(*nearest);
vertices.erase(nearest);
}
break;
case REPETETIVE_NEAREST_NEIGHBOUR:
// FIXME: implement
break;
}
return path;
}
else
qWarning() << getName() << "Missing pad list!";
return new Result;
}
#if 0
Mat PathPlanner::calcDistanceMat(QList<Point2f> vertices)
{
int size = vertices.size();
Mat distance = Mat::zeros(size, size, CV_32F);
for (int i = 0; i < size; i++) {
for (int j = i+1; j < size; j++) {
distance.at<float>(i, j) = Pad::distance(pads[i], pads[j]);
}
}
return distance;
}
#endif
void PathResult::drawResult(Painter *p) const
{
if (size()) {
QPen pe = p->pen();
QBrush br(Qt::SolidPattern);
pe.setWidth(2);
for (int i = 1; i < size(); i++) {
QColor c = QColor::fromHsl(360.0 / size() * i, 255, 128);
pe.setColor(c);
br.setColor(c);
p->setPen(pe);
p->setBrush(br);
p->drawLine(toQt(at(i-1)), toQt(at(i)));
p->drawEllipse(toQt(at(i)), 1, 1);
}
p->drawMarker(toQt((Point2i) (Point2f) first()));
p->drawMarker(toQt((Point2i) (Point2f) last()));
}
}
float PathResult::getLength() const
{
float length = 0;
for (int i = 1; i < size(); i++)
length += norm(at(i-1).center - at(i).center);
return length;
}
QString PathResult::getResult() const
{
return QString("pads = %1, length = %2").arg(size()).arg(getLength());
}

37
filters/pathplanner.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef PATHPLANNER_H
#define PATHPLANNER_H
#include "paddetect.h"
#include "filter.h"
class PathPlanner : public Filter
{
public:
enum Algorithm {
NEAREST_NEIGHBOUR,
REPETETIVE_NEAREST_NEIGHBOUR
};
PathPlanner(Filter *source, enum Algorithm algo = NEAREST_NEIGHBOUR);
QString getName() const { return "PathPlanner"; }
protected:
enum Algorithm algo;
Filter *source;
Result * applyInternal(Image *img);
//Mat calcDistanceMat(QList<Pad> pads);
};
class PathResult : public PadResult
{
public:
void drawResult(Painter *p) const;
float getLength() const;
QString getResult() const;
};
#endif // PATHPLANNER_H

View file

@ -8,37 +8,53 @@
#include "image.h"
#include "convert.h"
Pattern::Pattern(Size si, double sp, Type t) :
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)
spacing(sp),
areaRange(4000, 40000),
ratioRange(1, 1.7),
showThresh(false)
{
//QMap<int, QString> mapType
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, 1, 20);
settings["Spacing"] = new DoubleSetting(this, spacing, 1e-3, 1e2);
//settings["Type"] = new EnumSetting(this, type, mapType);
settings["Size"] = new SizeSetting(this, size, Range<int>(1, 16));
settings["Spacing"] = new SizeSetting(this, spacing, Range<int>(0, 300));
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;
std::vector<Point2f> points;
bool found = false;
std::vector<Point2f> points, allPoints;
std::vector<std::vector<Point>> contours;
Mat gray;
if (img->original.channels() > 1)
cvtColor(img->original, gray, COLOR_RGB2GRAY);
Mat gray, thresh, &m = img->getMat();
if (m.channels() > 1)
cvtColor(m, gray, COLOR_RGB2GRAY);
else
gray = img->original;
gray = m.clone();
switch(type) {
case CHESSBOARD:
found = findChessboardCorners(gray, size, points, flags);
if(found) {
if (found)
cornerSubPix(gray, points, Size(11, 11), Size(-1, -1),
TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.1));
}
break;
case CIRCLES_GRID:
@ -49,6 +65,59 @@ Result * Pattern::applyInternal(Image *img)
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;
}
@ -56,7 +125,6 @@ Result * Pattern::applyInternal(Image *img)
return new PatternResult(found, points);
}
std::vector<Point3f> Pattern::getPoints() const
{
std::vector<Point3f> points;
@ -66,17 +134,24 @@ std::vector<Point3f> Pattern::getPoints() const
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, i*spacing, 0));
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, i*spacing, 0));
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;
}
@ -99,15 +174,19 @@ PatternResult::PatternResult(bool f, std::vector<Point2f> p) :
points(p)
{ }
void PatternResult::draw(Painter *p) const
void PatternResult::drawResult(Painter *p) const
{
QPen pen = p->pen();
QPen pe = p->pen();
pe.setWidth(2);
for (unsigned i = 0; i < points.size(); i++) {
QColor clr = found ? QColor::fromHsl(360.0 / points.size() * i, 255, 128) : Qt::gray;
if (found)
pe.setColor(QColor::fromHsl(360.0 / points.size() * i, 255, 128));
else
pe.setColor(Qt::gray);
pen.setColor(clr);
p->setPen(pen);
p->setPen(pe);
p->drawMarker(toQt((Point2i) points[i]));
if (i != 0)

View file

@ -19,10 +19,12 @@ class Pattern : public Filter
enum Type {
CHESSBOARD,
CIRCLES_GRID,
ASYMMETRIC_CIRCLES_GRID
ASYMMETRIC_CIRCLES_GRID,
QUADRILINEAR_MARKERS
} type;
Pattern(Size si, double sp, Type t = Type::CHESSBOARD);
Pattern(Size si, int sp, Type t = Type::CHESSBOARD);
Pattern(Size si, Size sp, Type t = Type::CHESSBOARD);
Result * applyInternal(Image *img);
@ -33,8 +35,13 @@ class Pattern : public Filter
protected:
Size size;
Size spacing;
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK;
double spacing;
/* QUADRILINEAR Settings */
Range<double> areaRange;
Range<double> ratioRange;
bool showThresh;
};
class PatternResult : public Result
@ -42,7 +49,7 @@ class PatternResult : public Result
public:
PatternResult(bool found, std::vector<Point2f> op);
void draw(Painter *p) const;
void drawResult(Painter *p) const;
QString getResult() const;
/* Getter */

View file

@ -12,10 +12,11 @@ Perspective::Perspective(Camera *c, Pattern *p) :
Result * Perspective::applyInternal(Image *img)
{
Mat &m = img->filtered;
Mat &m = img->getMat();
PatternResult *presult = (PatternResult *) img->getResult(pattern);
if (presult->isFound()) {
if (presult && presult->isFound()) {
Mat map;
std::vector<Point2f> src, dest, tmp;
std::vector<Point2f> srcPoints = presult->getPoints();
std::vector<Point3f> destPoints = pattern->getPoints();
@ -25,16 +26,18 @@ Result * Perspective::applyInternal(Image *img)
tmp.push_back(Point2f(destPoints[i].x, destPoints[i].y));
}
int destWidth = 0.75 * m.cols;
int destWidth = 0.9 * m.cols;
int destHeight = tmp[2].y * (destWidth / tmp[1].x);
dest.push_back( Point2f((m.cols - destWidth) / 2, (m.rows - destHeight) / 2) );
dest.push_back( dest[0] + Point2f(destWidth, 0) );
dest.push_back( dest[0] + Point2f(0, destHeight) );
dest.push_back( dest[0] + Point2f(destWidth, destHeight) );
dest.push_back(Point2f((m.cols - destWidth) / 2, (m.rows - destHeight) / 2));
dest.push_back(dest[0] + Point2f(destWidth, 0));
dest.push_back(dest[0] + Point2f(0, destHeight));
dest.push_back(dest[0] + Point2f(destWidth, destHeight));
map = getPerspectiveTransform(src, dest);
warpPerspective(img->filtered, img->filtered, map, img->filtered.size());
warpPerspective(m, m, map, m.size());
return new Result(toQTransform(map));
}
return NULL;

View file

@ -14,7 +14,6 @@ class Perspective : public Filter
protected:
Result * applyInternal(Image *img);
Mat map;
Camera *cam;
Pattern *pattern;
};

91
filters/rectify.cpp Normal file
View file

@ -0,0 +1,91 @@
#include "rectify.h"
Rectify::Rectify(double t) :
threshold(t),
thresholdHough(100)
{
settings["Threshold Degree"] = new DoubleSetting(this, threshold, Range<double>(0, 360));
settings["Threshold Hough"] = new IntegerSetting(this, thresholdHough, Range<int>(0, 500));
}
Result * Rectify::applyInternal(Image *img)
{
Mat &m = img->getMat();
Mat affine;
Point2f center;
double angle, degrees;
center = Point2f(m.cols / 2.0, m.rows / 2.0);
angle = getHoughRotation(m);
degrees = angle * 180.0 / CV_PI;
affine = getRotationMatrix2D(center, degrees, 1);
warpAffine(m, m, affine, m.size());
return new DoubleResult("angle", degrees, toQTransform(affine));
}
double Rectify::getHoughRotation(Mat &m)
{
Mat bw;
int dim, maxVal = 0;
float angle;
std::vector<Vec2f> lines;
Canny(m, bw, 60, 180, 3);
HoughLines(bw, lines, 1, CV_PI / 180.0, thresholdHough);
dim = lines.size();
for (int i = 0; i < dim; i++) {
int val = 0;
float pi = mapQuadrant(lines[i][1]);
for (int j = 0; j < dim; j++) {
float pj = mapQuadrant(lines[j][1]);
if (pi < 0) pi += CV_PI / 2.0;
if (pj < 0) pj += CV_PI / 2.0;
float diff = fabs(pi - pj);
if (diff <= threshold * CV_PI / 180.0)
val++;
}
if (val > maxVal) {
maxVal = val;
angle = pi;
}
}
#if 1
for (int i = 0; i < dim; i++) {
Point pt1, pt2;
float rho = lines[i][0], theta = lines[i][1];
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
if (fabs(rho - angle) < threshold * CV_PI / 180.0) {
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
line(m, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
}
#endif
return angle;
}
double Rectify::mapQuadrant(double angle)
{
double result = fmod(angle, CV_PI/2);
if (result < 0)
result += 90;
return result;
}

24
filters/rectify.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef RECTIFY_H
#define RECTIFY_H
#include "filter.h"
class Rectify : public Filter
{
public:
Rectify(double threshold);
QString getName() const { return "Rotate"; }
protected:
double threshold;
int thresholdHough;
Result * applyInternal(Image *img);
double getHoughRotation(Mat &m);
double mapQuadrant(double angle);
};
#endif // RECTIFY_H

36
filters/resize.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "resize.h"
Resize::Resize(Range<int> r, int i) :
range(r),
interpolation(i)
{
QMap<int, QString> interMap;
interMap[INTER_NEAREST] = "Nearest Neighbour";
interMap[INTER_LINEAR] = "Linear";
interMap[INTER_AREA] = "Pixel Area Relation";
interMap[INTER_CUBIC] = "Bi-Cubic (4x4)";
interMap[INTER_LANCZOS4] = "Lanczos4 (8x8)";
settings["Range"] = new RangeSetting(this, range, Range<int>(200, 3000));
settings["Interpolation"] = new EnumSetting(this, (int &) interpolation, interMap);
}
Result * Resize::applyInternal(Image *img)
{
Mat &m = img->getMat();
if (!range.contains(m.cols)) {
double f = 1;
if (m.cols < range.min)
f = (double) range.min / m.cols;
else if (m.cols > range.max)
f = (double) range.max / m.cols;
resize(m, m, Size(), f, f, interpolation);
return new Result(QTransform::fromScale(f, f));
}
else
return new Result;
}

20
filters/resize.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef RESIZE_H
#define RESIZE_H
#include "filter.h"
class Resize : public Filter
{
public:
Resize(Range<int> r = Range<int>(320, 1024), int inter = INTER_LINEAR);
QString getName() const { return "Resize"; }
protected:
Range<int> range;
int interpolation;
Result * applyInternal(Image *img);
};
#endif // RESIZE_H

View file

@ -1,3 +0,0 @@
#include "result.h"

View file

@ -1,23 +1,38 @@
#ifndef RESULT_H
#define RESULT_H
#include <opencv2/core.hpp>
#include <QTransform>
#include <QString>
using namespace cv;
class Painter;
class Result
{
public:
Result(QTransform t = QTransform()) :
transform(t)
{ }
virtual ~Result() { };
virtual void draw(Painter *) const { };
virtual QString getResult() const = 0;
QTransform getTransform() { return transform; }
virtual QString getResult() const { return ""; };
virtual void drawResult(Painter *) const { };
protected:
QTransform transform;
};
class IntegerResult : public Result
{
public:
IntegerResult(QString n, int v) :
IntegerResult(QString n, int v, QTransform t = QTransform()) :
Result(t),
name(n),
value(v)
{ }
@ -34,7 +49,8 @@ class IntegerResult : public Result
class DoubleResult : public Result
{
public:
DoubleResult(QString n, double v) :
DoubleResult(QString n, double v, QTransform t = QTransform()) :
Result(t),
name(n),
value(v)
{ }

28
filters/rotate.cpp Normal file
View file

@ -0,0 +1,28 @@
#include "rotate.h"
Rotate::Rotate(double a, int i = INTER_LINEAR, double s) :
angle(a),
interpolation(i),
scale(s)
{
QMap<int, QString> interMap;
interMap[INTER_NEAREST] = "Nearest Neighbour";
interMap[INTER_LINEAR] = "Linear";
interMap[INTER_AREA] = "Pixel Area Relation";
interMap[INTER_CUBIC] = "Bi-Cubic (4x4)";
interMap[INTER_LANCZOS4] = "Lanczos4 (8x8)";
settings["Angle"] = new DoubleSetting(this, angle, Range<double>(0, 360));
settings["Scale"] = new DoubleSetting(this, scale, Range<double>(0.5, 2));
settings["Interpolation"] = new EnumSetting(this, (int &) interpolation, interMap);
}
Result * Rotate::applyInternal(Image *img)
{
Mat &m = img->getMat();
Mat affine = getRotationMatrix2D(Point2f(m.cols/2, m.rows/2), angle, 1);
warpAffine(m, m, affine, m.size(), interpolation);
return new Result(toQTransform(affine));
}

21
filters/rotate.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef ROTATE_H
#define ROTATE_H
#include "filter.h"
class Rotate : public Filter
{
public:
Rotate(double a, int inter, double scale = 1.0);
QString getName() const { return "Rotate"; }
protected:
Result * applyInternal(Image *img);
double angle;
int interpolation;
double scale;
};
#endif // ROTATE_H

View file

@ -2,7 +2,6 @@
#include <QtDebug>
#include <QLabel>
#include "ui_doublesetting.h"
#include "setting.h"
#include "viewer.h"
#include "filter.h"
@ -46,19 +45,20 @@ QWidget * IntegerSetting::createWidget(QWidget *parent)
layout->setMargin(0);
layout->addWidget(slider);
layout->addWidget(spinner);
spinner->setRange(limits.min, limits.max);
slider->setRange(limits.min, limits.max);
spinner->setValue(value);
slider->setValue(value);
slider->setMinimum(min);
slider->setMaximum(max);
slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
slider->setTracking(false);
connect(slider, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged), [&](int v) {
value = v;
spinner->setValue(v);
emit valueChanged();
});
connect(spinner, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [&](int v) {
value = v;
connect(slider, static_cast<void (QSlider::*)(int)>(&QSlider::sliderMoved), spinner, &QSpinBox::setValue);
connect(spinner, static_cast<void (QSpinBox::*)()>(&QSpinBox::editingFinished), [&]() {
value = spinner->value();
slider->setValue(value);
emit valueChanged();
});
@ -66,6 +66,17 @@ QWidget * IntegerSetting::createWidget(QWidget *parent)
return widget;
}
double DoubleSetting::getSliderValue(int v)
{
double value = ((double) v / slider->maximum());
return value * (limits.max - limits.min) + limits.min;
}
void DoubleSetting::setSliderValue(double value)
{
slider->setValue(slider->maximum() * (value - limits.min) / (limits.max - limits.min));
}
QWidget * DoubleSetting::createWidget(QWidget *parent)
{
QWidget *widget = new QWidget(parent);
@ -79,20 +90,127 @@ QWidget * DoubleSetting::createWidget(QWidget *parent)
layout->addWidget(slider);
layout->addWidget(spinner);
layout->setMargin(0);
spinner->setRange(limits.min, limits.max);
slider->setRange(0, SCALE);
spinner->setValue(value);
slider->setValue(value * SCALE);
slider->setMinimum(min * SCALE);
slider->setMaximum(max * SCALE);
setSliderValue(value);
slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
slider->setTracking(false);
connect(slider, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged), [&](int v) {
value = (double) v / SCALE;
spinner->setValue(value);
value = getSliderValue(v);
emit valueChanged();
});
connect(spinner, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [&](double v) {
value = v;
slider->setValue(value * SCALE);
connect(slider, static_cast<void (QSlider::*)(int)>(&QSlider::sliderMoved), [&](int v) {
value = getSliderValue(v);
spinner->setValue(value);
});
connect(spinner, static_cast<void (QDoubleSpinBox::*)()>(&QDoubleSpinBox::editingFinished), [&]() {
value = spinner->value();
setSliderValue(value);
emit valueChanged();
});
return widget;
}
QWidget * RangeSetting::createWidget(QWidget *parent)
{
QWidget *widget = new QWidget(parent);
QHBoxLayout *layout = new QHBoxLayout(widget);
spinnerL = new QSpinBox(widget);
spinnerU = new QSpinBox(widget);
slider = new RangeSlider(Qt::Horizontal, widget);
layout->setMargin(0);
layout->addWidget(spinnerL);
layout->addWidget(slider);
layout->addWidget(spinnerU);
spinnerL->setRange(limits.min, limits.max);
spinnerU->setRange(limits.min, limits.max);
slider->setRange(limits.min, limits.max);
spinnerL->setValue(range.min);
spinnerU->setValue(range.max);
slider->setValues(range.min, range.max);
slider->setTracking(false);
connect(slider, static_cast<void (RangeSlider::*)(int, int)>(&RangeSlider::positionsChanged), [&](int l, int u) {
spinnerL->setValue(l);
spinnerU->setValue(u);
});
connect(slider, static_cast<void (RangeSlider::*)(int, int)>(&RangeSlider::valuesChanged), [&](int l, int u) {
range.min = l;
range.max = u;
emit valueChanged();
});
connect(spinnerL, static_cast<void (QSpinBox::*)()>(&QSpinBox::editingFinished), [&]() {
range.min = spinnerL->value();
slider->setMinimumValue(range.min);
emit valueChanged();
});
connect(spinnerU, static_cast<void (QSpinBox::*)()>(&QSpinBox::editingFinished), [&]() {
range.max = spinnerU->value();
slider->setMaximumValue(range.max);
emit valueChanged();
});
return widget;
}
Range<double> DoubleRangeSetting::getSliderRange(Range<int> r)
{
Range<double> range = {
((double) r.min / slider->maximum()) * (limits.max - limits.min) + limits.min,
((double) r.max / slider->maximum()) * (limits.max - limits.min) + limits.min };
return range;
}
void DoubleRangeSetting::setSliderRange(Range<double> range)
{
slider->setMinimumValue(slider->maximum() * (range.min - limits.min) / (limits.max - limits.min));
slider->setMaximumValue(slider->maximum() * (range.max - limits.min) / (limits.max - limits.min));
}
QWidget * DoubleRangeSetting::createWidget(QWidget *parent)
{
QWidget *widget = new QWidget(parent);
QHBoxLayout *layout = new QHBoxLayout(widget);
spinnerL = new QDoubleSpinBox(widget);
spinnerU = new QDoubleSpinBox(widget);
slider = new RangeSlider(Qt::Horizontal, widget);
layout->setMargin(0);
layout->addWidget(spinnerL);
layout->addWidget(slider);
layout->addWidget(spinnerU);
spinnerL->setRange(limits.min, limits.max);
spinnerU->setRange(limits.min, limits.max);
slider->setRange(0, SCALE);
spinnerL->setValue(range.min);
spinnerU->setValue(range.max);
slider->setTracking(false);
setSliderRange(range);
connect(slider, static_cast<void (RangeSlider::*)(int, int)>(&RangeSlider::positionsChanged), [&](int mi, int ma) {
range = getSliderRange(Range<int>(mi, ma));
spinnerL->setValue(range.min);
spinnerU->setValue(range.max);
});
connect(slider, static_cast<void (RangeSlider::*)(int, int)>(&RangeSlider::valuesChanged), [&](int mi, int ma) {
range = getSliderRange(Range<int>(mi, ma));
emit valueChanged();
});
connect(spinnerL, static_cast<void (QDoubleSpinBox::*)()>(&QDoubleSpinBox::editingFinished), [&]() {
range.min = spinnerL->value();
setSliderRange(range);
emit valueChanged();
});
connect(spinnerU, static_cast<void (QDoubleSpinBox::*)()>(&QDoubleSpinBox::editingFinished), [&]() {
range.max = spinnerU->value();
setSliderRange(range);
emit valueChanged();
});
@ -132,10 +250,8 @@ QWidget * SizeSetting::createWidget(QWidget *parent)
layout->addWidget(spinnerH);
spinnerH->setValue(value.height);
spinnerW->setValue(value.width);
spinnerH->setMinimum(min);
spinnerH->setMaximum(max);
spinnerW->setMinimum(min);
spinnerW->setMaximum(max);
spinnerH->setRange(limits.min, limits.max);
spinnerW->setRange(limits.min, limits.max);
connect(spinnerH, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [&](int v) {
value.height = v;

View file

@ -9,6 +9,8 @@
#include <QMap>
#include <opencv2/core.hpp>
#include "rangeslider.h"
#include "range.h"
using namespace cv;
@ -33,22 +35,6 @@ class Setting : public QObject
QString toolTip;
};
class IntegerSetting : public Setting
{
Q_OBJECT
public:
IntegerSetting(Filter *f, int &v, int mi = 0, int ma = 100, QString tip = "") :
Setting(f, tip), value(v), min(mi), max(ma) { }
protected:
QWidget * createWidget(QWidget *parent = 0);
int &value, min, max;
QSpinBox *spinner;
QSlider *slider;
};
class BooleanSetting : public Setting
{
Q_OBJECT
@ -64,22 +50,84 @@ class BooleanSetting : public Setting
QCheckBox *box;
};
class IntegerSetting : public Setting
{
Q_OBJECT
public:
IntegerSetting(Filter *f, int &v, Range<int> l = Range<int>(0, 100), QString tip = "") :
Setting(f, tip), value(v), limits(l) { }
protected:
QWidget * createWidget(QWidget *parent = 0);
int &value;
Range<int> limits;
QSpinBox *spinner;
QSlider *slider;
};
class DoubleSetting : public Setting
{
Q_OBJECT
public:
DoubleSetting(Filter *f, double &v, double mi = 0, double ma = 100, QString tip = "") :
Setting(f, tip), value(v), min(mi), max(ma) { }
DoubleSetting(Filter *f, double &v, Range<double> l = Range<double>(0, 100), QString tip = "") :
Setting(f, tip), value(v), limits(l) { }
protected:
static const int SCALE = 1000;
QWidget * createWidget(QWidget *parent = 0);
double &value, min, max;
double &value;
Range<double> limits;
QDoubleSpinBox *spinner;
QSlider *slider;
double getSliderValue(int v);
void setSliderValue(double value);
};
class RangeSetting : public Setting
{
Q_OBJECT
public:
RangeSetting(Filter *f, Range<int> &r, Range<int> l = Range<int>(0, 100), QString tip = "") :
Setting(f, tip), range(r), limits(l) { }
protected:
QWidget * createWidget(QWidget *parent);
Range<int> &range;
Range<int> limits;
QSpinBox *spinnerL, *spinnerU;
RangeSlider *slider;
};
class DoubleRangeSetting : public Setting
{
Q_OBJECT
public:
DoubleRangeSetting(Filter *f, Range<double> &r, Range<double> l = Range<double>(0, 100), QString tip = "") :
Setting(f, tip), range(r), limits(l) { }
protected:
static const int SCALE = 1000;
QWidget * createWidget(QWidget *parent);
Range<double> &range;
Range<double> limits;
QDoubleSpinBox *spinnerL, *spinnerU;
RangeSlider *slider;
Range<double> getSliderRange(Range<int> range);
void setSliderRange(Range<double> value);
};
class EnumSetting : public Setting
@ -103,14 +151,14 @@ class SizeSetting : public Setting
Q_OBJECT
public:
SizeSetting(Filter *f, Size &v, int mi = 1, int ma = 100, QString tip = "") :
Setting(f, tip), value(v), min(mi), max(ma) { }
SizeSetting(Filter *f, Size &v, Range<int> l = Range<int>(1, 100), QString tip = "") :
Setting(f, tip), value(v), limits(l) { }
protected:
QWidget * createWidget(QWidget *parent);
Size &value;
int min, max;
Range<int> limits;
QSpinBox *spinnerH, *spinnerW;
};

View file

@ -1,113 +0,0 @@
#include "cast.h"
#include "shapedetect.h"
static double angle(Point pt1, Point pt2, Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
static double area(RotatedRect rect)
{
Point2f p[4];
rect.points(p);
double dxa = p[0].x - p[1].x;
double dya = p[0].y - p[1].y;
double dxb = p[1].x - p[2].x;
double dyb = p[2].y - p[2].y;
return sqrt( (dxa*dxa + dya*dya) * (dxb*dxb + dyb*dyb) );
}
ShapeDetect::ShapeDetect(double e, double cos) :
epsilon(e),
cosine(cos)
{
QMap<int, QString> mapMode;
mapMode[AREA] = "Area";
mapMode[COSINE] = "Cosine";
settings["Mode"] = new EnumSetting(this, (int&) mode, mapMode);
settings["Epsilon"] = new DoubleSetting(this, epsilon, 0, 1);
settings["Cosine"] = new DoubleSetting(this, cosine, 0, 1);
settings["Shape no"]= new IntegerSetting(this, shape, 0, 1000);
}
Result * ShapeDetect::applyInternal(Image *img)
{
Mat &m = img->filtered;
int i = 0;
std::vector<std::vector<Point>> contours;
ShapeResult *result = new ShapeResult;
findContours(m.clone(), contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
for (std::vector<Point> contour : contours) {
//std::vector<Point> contour = contours[shape];
std::vector<Point> approx;
bool closed = isContourConvex(contour);
double arc = arcLength(contour, closed);
approxPolyDP(contour, approx, epsilon * arc, closed);
switch (approx.size()) {
case 4:
if (mode == ShapeDetect::COSINE) {
double maxCosine = 0;
for (int j = 2; j < 5; j++) {
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = std::max(maxCosine, cosine);
}
if (maxCosine < cosine) {
RotatedRect rect = minAreaRect(approx);
//RotatedRect rect = minAreaRect(contour);
if (i++ == shape)
result->rects.push_back(rect);
}
}
else if (mode == ShapeDetect::AREA) {
RotatedRect rect = minAreaRect(contour);
double areaContour = contourArea(contour);
double areaRect = area(rect);
if (areaContour / areaRect > epsilon) {
if (i++ == shape)
result->rects.push_back(rect);
}
}
break;
}
}
return result;
}
void ShapeResult::draw(Painter *p) const
{
QPen pen = p->pen();
pen.setWidth(2);
p->setPen(pen);
for (auto rect : rects) {
Point2f v[4];
rect.points(v);
for (int i = 0; i < 4; i++)
p->drawLine(toQt((Point2i) v[i]), toQt((Point2i) v[(i+1)%4]));
}
}
QString ShapeResult::getResult() const
{
return QString("rects = %1").arg(rects.size());
}

View file

@ -1,40 +0,0 @@
#ifndef SHAPEDETECT_H
#define SHAPEDETECT_H
#include <vector>
#include "filter.h"
using namespace std;
class ShapeDetect : public Filter
{
public:
enum Mode {
AREA,
COSINE
};
ShapeDetect(double epsilon = 1, double cosine = 0.3);
QString getName() const { return "ShapeDetect"; };
protected:
Result * applyInternal(Image *img);
double epsilon;
double cosine;
int shape = 0;
enum Mode mode;
};
class ShapeResult : public Result
{
public:
void draw(Painter *p) const;
QString getResult() const;
QList<RotatedRect> rects;
};
#endif // SHAPEDETECT_H

View file

@ -3,6 +3,7 @@
Threshold::Threshold(enum Threshold::Type t, double v, int sz, double d) :
Filter(),
type(t),
method(THRESH_BINARY),
size(sz),
value(v),
c(d)
@ -14,33 +15,43 @@ Threshold::Threshold(enum Threshold::Type t, double v, int sz, double d) :
typeMap[OTSU] = "Otsu's method";
typeMap[TRIANGLE] = "Triangle";
QMap<int, QString> methodMap;
methodMap[THRESH_BINARY] = "Binary";
methodMap[THRESH_TOZERO] = "To Zero";
methodMap[THRESH_TRUNC] = "Truncate";
settings["Type"] = new EnumSetting(this, (int&) type, typeMap);
settings["Threshold"] = new DoubleSetting(this, value, 0, 0xff);
settings["C"] = new DoubleSetting(this, c, 0, 0xff);
settings["Size"] = new IntegerSetting(this, size, 1, 100);
settings["Method"] = new EnumSetting(this, (int&) method, methodMap);
settings["Threshold"] = new DoubleSetting(this, value, Range<double>(0, 0xff));
settings["C"] = new DoubleSetting(this, c, Range<double>(0, 0xff));
settings["Size"] = new IntegerSetting(this, size, Range<int>(0, 100));
settings["Inverted"] = new BooleanSetting(this, invert);
}
Result * Threshold::applyInternal(Image *img)
{
Mat &m = img->filtered;
Mat &m = img->getMat();
int mode = (invert) ? THRESH_BINARY_INV : THRESH_BINARY;
int threshMethod;
switch (method) {
case THRESH_BINARY: threshMethod = invert ? THRESH_BINARY_INV : THRESH_BINARY; break;
case THRESH_TOZERO: threshMethod = invert ? THRESH_TOZERO_INV : THRESH_TOZERO; break;
default: threshMethod = method;
}
if (type == ADAPTIVE_GAUSS)
adaptiveThreshold(m, m, 0xff, ADAPTIVE_THRESH_GAUSSIAN_C, mode, size, c);
adaptiveThreshold(m, m, 0xff, ADAPTIVE_THRESH_GAUSSIAN_C, method, size, c);
else if (type == ADAPTIVE_MEAN)
adaptiveThreshold(m, m, 0xff, ADAPTIVE_THRESH_MEAN_C, mode, size, c);
adaptiveThreshold(m, m, 0xff, ADAPTIVE_THRESH_MEAN_C, method, size, c);
else {
int method;
int mode;
switch (type) {
case OTSU: method = THRESH_OTSU; break;
case TRIANGLE: method = THRESH_TRIANGLE; break;
default: method = 0;
case OTSU: mode = THRESH_OTSU; break;
case TRIANGLE: mode = THRESH_TRIANGLE; break;
default: mode = 0;
}
double ret = threshold(img->filtered, img->filtered, value, 0xff, mode | method);
double ret = threshold(m, m, value, 0xff, mode | method);
return new DoubleResult("tresh", ret);
}

View file

@ -24,6 +24,7 @@ class Threshold : public Filter
Result * applyInternal(Image *img);
enum Threshold::Type type;
int method;
bool invert = false;
int size;
double value, c;

View file

@ -16,13 +16,15 @@ void Undistort::reset()
Result * Undistort::applyInternal(Image *img)
{
Mat &m = img->getMat();
if (cam->isCalibrated()) {
#if 1
remap(img->filtered, img->filtered, map1, map2, INTER_LINEAR);
remap(m, m, map1, map2, INTER_LINEAR);
#else
Mat dest;
undistort(mat, dest, cam->getMatrix(), cam->getDistCoeffs());
mat = dest;
undistort(m, dest, cam->getMatrix(), cam->getDistCoeffs());
m = dest;
#endif
}

View file

@ -1,21 +1,43 @@
#include "watershed.h"
Watershed::Watershed()
bool Watershed::clicked(Point pos, QMouseEvent *me)
{
qDebug() << getName() << ": Marker " << ((me->modifiers() & Qt::AltModifier) ? "deleted" : "added") << " at:" << toQt(pos);
if (me && me->modifiers() & Qt::AltModifier)
delMarkers.push_back(pos);
else
addMarkers.push_back(pos);
return true;
}
void Watershed::reset()
{
qDebug() << getName() << ": Markers cleared";
delMarkers.clear();
addMarkers.clear();
}
Result * Watershed::applyInternal(Image *img)
{
Mat dist, dist_8u, &m = img->filtered;
Mat dist, dist_8u, &m = img->getMat();
Mat kernel, max, mask;
CV_Assert(m.channels() == 1);
distanceTransform(m, dist, DIST_L2, 3);
normalize(m, m, 0, 0xff, NORM_MINMAX);
normalize(dist, dist, 0, 0xff, NORM_MINMAX);
dist.convertTo(dist_8u, CV_8U);
m = dist_8u;
kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
dilate(dist_8u, max, kernel);
mask = (dist_8u > 0);
m = (max == dist_8u);
m &= mask;
return NULL;
}

View file

@ -6,13 +6,18 @@
class Watershed : public Filter
{
public:
Watershed();
QString getName() const { return "Watershed"; }
bool clicked(Point pos, QMouseEvent *me = 0);
public slots:
void reset();
protected:
Result * applyInternal(Image *img);
QList<Point> addMarkers;
QList<Point> delMarkers;
};
#endif // WATERSHED_H