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