added, improved and updated a large set of the OpenCV filter wrappers
This commit is contained in:
parent
2ad5599a98
commit
e627a02a3d
40 changed files with 1171 additions and 314 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
35
filters/derivative.cpp
Normal 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
30
filters/derivative.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
120
filters/paddetect.cpp
Normal 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
41
filters/paddetect.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
103
filters/pathplanner.cpp
Normal 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
37
filters/pathplanner.h
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
91
filters/rectify.cpp
Normal 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
24
filters/rectify.h
Normal 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
36
filters/resize.cpp
Normal 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
20
filters/resize.h
Normal 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
|
|
@ -1,3 +0,0 @@
|
|||
#include "result.h"
|
||||
|
||||
|
|
@ -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
28
filters/rotate.cpp
Normal 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
21
filters/rotate.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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<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<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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue