forked from rosh/MQL5Book
227 lines
5.9 KiB
MQL5
227 lines
5.9 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| LibHoughTransform.mq5 |
| |
//| Copyright (c) 2015-2022, Marketeer |
| |
//| https://www.mql5.com/en/users/marketeer |
| |
//+------------------------------------------------------------------+
| |
#property library
| |
| |
#include "..\Include\LibHoughTransform.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| Aux 2D-array class |
| |
//+------------------------------------------------------------------+
| |
template<typename T>
| |
class Plain2DArray
| |
{
| |
protected:
| |
T subarray[];
| |
int sizex, sizey;
| |
| |
public:
| |
Plain2DArray() {};
| |
Plain2DArray(int x, int y)
| |
{
| |
allocate(x, y);
| |
}
| |
| |
void allocate(int x, int y)
| |
{
| |
sizex = x;
| |
sizey = y;
| |
ArrayResize(subarray, x * y);
| |
zero();
| |
}
| |
| |
T get(int x, int y) const
| |
{
| |
return subarray[x + y * sizex];
| |
}
| |
| |
void set(int x, int y, T v)
| |
{
| |
subarray[x + y * sizex] = v;
| |
}
| |
| |
void inc(int x, int y, T a = (T)1)
| |
{
| |
subarray[x + y * sizex] += a;
| |
}
| |
| |
void getSizes(int &x, int &y) const
| |
{
| |
x = sizex;
| |
y = sizey;
| |
}
| |
| |
bool isAllocated() const
| |
{
| |
return ArraySize(subarray) > 0;
| |
}
| |
| |
void zero()
| |
{
| |
ZeroMemory(subarray);
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Main worker class for Linear Hough Transfrom |
| |
//+------------------------------------------------------------------+
| |
template<typename T>
| |
class LinearHoughTransform: public HoughTransformConcrete<T>
| |
{
| |
protected:
| |
int size;
| |
Plain2DArray<T> data;
| |
Plain2DArray<double> trigonometric;
| |
| |
void init()
| |
{
| |
data.allocate(size, size);
| |
trigonometric.allocate(2, size);
| |
double t, d = M_PI / size;
| |
int i;
| |
for(i = 0, t = 0; i < size; i++, t += d)
| |
{
| |
trigonometric.set(0, i, MathCos(t));
| |
trigonometric.set(1, i, MathSin(t));
| |
}
| |
}
| |
| |
virtual bool findMax(int &x, int &y)
| |
{
| |
T max = (T)0;
| |
bool done = false;
| |
for(int r = 0; r < size; r++)
| |
{
| |
for(int i = 0; i < size; i++)
| |
{
| |
if(data.get(r, i) > max)
| |
{
| |
max = data.get(r, i);
| |
x = r;
| |
y = i;
| |
done = true;
| |
}
| |
}
| |
}
| |
| |
if(done)
| |
for(int r = -1; r < 2; r++)
| |
{
| |
for(int i = -1; i < 2; i++)
| |
{
| |
if(x + r >= 0 && y + i >= 0 && x + r < size && y + i < size)
| |
{
| |
data.set(x + r, y + i, 0);
| |
}
| |
}
| |
}
| |
return done;
| |
}
| |
| |
public:
| |
LinearHoughTransform(const int quants): size(quants)
| |
{
| |
init();
| |
}
| |
| |
// 2 params per line, 8 lines means 16 parameters
| |
virtual int extract(const HoughImage<T> &image, double &result[], const int lines = 8) override
| |
{
| |
ArrayResize(result, lines * 2);
| |
ArrayInitialize(result, 0);
| |
data.zero();
| |
| |
const int w = image.getWidth();
| |
const int h = image.getHeight();
| |
const double d = M_PI / size; // 180 / 36 = 5 degree, for example
| |
const double rstep = MathSqrt(w * w + h * h) / size;
| |
double r, t;
| |
int i;
| |
| |
// find straight lines
| |
for(int x = 0; x < w; x++)
| |
{
| |
for(int y = 0; y < h; y++)
| |
{
| |
T v = image.get(x, y);
| |
if(v == (T)0) continue;
| |
| |
for(i = 0, t = 0; i < size; i++, t += d) // t < Math.PI
| |
{
| |
r = (x * trigonometric.get(0, i) + y * trigonometric.get(1, i));
| |
r = MathRound(r / rstep); // range is [-size, +size]
| |
r += size; // [0, +2size]
| |
r /= 2;
| |
| |
if((int)r < 0) r = 0;
| |
if((int)r >= size) r = size - 1;
| |
if(i < 0) i = 0;
| |
if(i >= size) i = size - 1;
| |
| |
data.inc((int)r, i, v);
| |
}
| |
}
| |
}
| |
| |
// y = a * x + b
| |
// y = (-cos(t)/sin(t)) * x + (r/sin(t))
| |
| |
// save 8 lines as features (2 params per line)
| |
for(i = 0; i < lines; i++)
| |
{
| |
int x, y;
| |
if(!findMax(x, y))
| |
{
| |
return i;
| |
}
| |
| |
double a = 0, b = 0;
| |
if(MathSin(y * d) != 0)
| |
{
| |
a = -1.0 * MathCos(y * d) / MathSin(y * d);
| |
b = (x * 2 - size) * rstep / MathSin(y * d);
| |
}
| |
if(fabs(a) < DBL_EPSILON && fabs(b) < DBL_EPSILON)
| |
{
| |
i--;
| |
continue;
| |
}
| |
result[i * 2 + 0] = a;
| |
result[i * 2 + 1] = b;
| |
}
| |
| |
return i;
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Fabric function for Hough transform objects |
| |
//+------------------------------------------------------------------+
| |
HoughTransform *createHoughTransform(const int quants, const ENUM_DATATYPE type = TYPE_INT) export
| |
{
| |
switch(type)
| |
{
| |
case TYPE_INT:
| |
return new LinearHoughTransform<int>(quants);
| |
case TYPE_DOUBLE:
| |
return new LinearHoughTransform<double>(quants);
| |
// ... more types can be supported
| |
}
| |
return NULL;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Built-in meta-information about implemented transform |
| |
//+------------------------------------------------------------------+
| |
HoughInfo getHoughInfo() export
| |
{
| |
#ifdef LIB_HOUGH_IMPL_DEBUG
| |
Print("inline library (debug)");
| |
#else
| |
Print("standalone library (production)");
| |
#endif
| |
return HoughInfo(2, "Line: y = a * x + b; a = p[0]; b = p[1];");
| |
}
| |
//+------------------------------------------------------------------+
|