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];");
|
|
}
|
|
//+------------------------------------------------------------------+
|