/** Author: Francois Fleuret
This heuristic is similar to ChamferZk but computes edges at three
different tolerances (1, 2, 4). Hence, there are 8x3 features per
pixel of the area of interest, each telling the distance to the
closer edge of that orientation in the image.
The distance is limited to 10 pixels for computational reasons.
*/
#include <mash/heuristic.h>
#include <stdlib.h>
#include <iostream>
using namespace Mash;
using namespace std;
#define MIN(a, b) ((a) >= (b) ? (b) : (a))
#define PIXEL_DELTA(a, b) ((a) >= (b) ? (a) - (b) : (b) - (a))
bool edge( byte_t v0, byte_t v1,
byte_t v2, byte_t v3, byte_t v4, byte_t v5,
byte_t v6, byte_t v7) {
byte_t g = PIXEL_DELTA(v3, v4);
return
g > PIXEL_DELTA(v0, v3) && g > PIXEL_DELTA(v1, v4) &&
g > PIXEL_DELTA(v2, v3) && g > PIXEL_DELTA(v4, v5) &&
g > PIXEL_DELTA(v3, v6) && g > PIXEL_DELTA(v4, v7);
}
//////////////////////////////////////////////////////////////////////
class TolerantChamferZk: public Heuristic
{
public:
TolerantChamferZk();
virtual ~TolerantChamferZk();
public:
virtual unsigned int dim();
virtual void prepareForImage();
virtual void finishForImage();
virtual void prepareForCoordinates();
virtual void finishForCoordinates();
virtual scalar_t computeFeature(unsigned int feature_index);
protected:
static const int nb_edge_orientations = 8;
static const int nb_edge_tolerances = 3;
static const int nb_edge_types = nb_edge_orientations * nb_edge_tolerances;
static const int chamfer_thickness = 10;
unsigned char *_chamfer_maps;
int _image_width, _image_height;
void compute_edge_maps(byte_t **pixels, unsigned int *edge_maps);
void compute_chamfer_maps(unsigned int *edge_maps, unsigned char *chamfer_maps);
};
extern "C" Heuristic* new_heuristic()
{
return new TolerantChamferZk();
}
//////////////////////////////////////////////////////////////////////
TolerantChamferZk::TolerantChamferZk() { }
TolerantChamferZk::~TolerantChamferZk() { }
unsigned int TolerantChamferZk::dim() {
unsigned int roi_size = roi_extent * 2 + 1;
return nb_edge_types * roi_size * roi_size;
}
void TolerantChamferZk::compute_edge_maps(byte_t **pixels, unsigned int *edge_maps) {
int d00, d01, d02, d03, d04, d05, d06, d07;
int d08, d09, d10, d11, d12, d13, d14, d15;
for(int y = 0; y < _image_height; y++) {
for(int x = 0; x < _image_width; x++) {
for(int e = 0; e < nb_edge_orientations; e++) {
edge_maps[e + nb_edge_types * (x + _image_width * y)] = 0;
}
if(x > 1 && x < _image_width - 2 && y > 1 && y < _image_height - 2) {
d00 = pixels[(x - 1)][(y - 1)];
d01 = pixels[(x + 0)][(y - 1)];
d02 = pixels[(x + 1)][(y - 1)];
d03 = pixels[(x + 2)][(y - 1)];
d04 = pixels[(x - 1)][(y + 0)];
d05 = pixels[(x + 0)][(y + 0)];
d06 = pixels[(x + 1)][(y + 0)];
d07 = pixels[(x + 2)][(y + 0)];
d08 = pixels[(x - 1)][(y + 1)];
d09 = pixels[(x + 0)][(y + 1)];
d10 = pixels[(x + 1)][(y + 1)];
d11 = pixels[(x + 2)][(y + 1)];
d12 = pixels[(x - 1)][(y + 2)];
d13 = pixels[(x + 0)][(y + 2)];
d14 = pixels[(x + 1)][(y + 2)];
d15 = pixels[(x + 2)][(y + 2)];
/*
XXXXXX .XXXXX ...XXX .....X
XXXXXX ..XXXX ...XXX ....XX
...... ...XXX ...XXX ...XXX
...... ....XX ...XXX ..XXXX
#0 #1 #2 #3
...... X..... XXX... XXXXX.
...... XX.... XXX... XXXX..
XXXXXX XXX... XXX... XXX...
XXXXXX XXXX.. XXX... XX....
#4 #5 #6 #7
*/
if(edge(d04, d08, d01, d05, d09, d13, d06, d10)) {
if(d05 < d09)
edge_maps[0 + nb_edge_types * (x + _image_width * y)]++;
else
edge_maps[4 + nb_edge_types * (x + _image_width * y)]++;
}
if(edge(d02, d07, d00, d05, d10, d15, d08, d13)) {
if(d05 < d10)
edge_maps[7 + nb_edge_types * (x + _image_width * y)]++;
else
edge_maps[3 + nb_edge_types * (x + _image_width * y)]++;
}
if(edge(d01, d02, d04, d05, d06, d07, d09, d10)) {
if(d05 < d06)
edge_maps[6 + nb_edge_types * (x + _image_width * y)]++;
else
edge_maps[2 + nb_edge_types * (x + _image_width * y)]++;
}
if(edge(d01, d04, d03, d06, d09, d12, d11, d14)) {
if(d06 < d09)
edge_maps[1 + nb_edge_types * (x + _image_width * y)]++;
else
edge_maps[5 + nb_edge_types * (x + _image_width * y)]++;
}
}
// Tolerance 2
for(int o = 0; o < nb_edge_orientations; o++) {
edge_maps[o + 1 * nb_edge_orientations + nb_edge_types * (x + _image_width * y)] =
edge_maps[(o+0)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)] +
edge_maps[(o+1)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)];
}
// Tolerance 4
for(int o = 0; o < nb_edge_orientations; o++) {
edge_maps[o + 2 * nb_edge_orientations + nb_edge_types * (x + _image_width * y)] =
edge_maps[(o+0)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)] +
edge_maps[(o+1)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)] +
edge_maps[(o+2)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)] +
edge_maps[(o+3)%nb_edge_orientations + nb_edge_types * (x + _image_width * y)];
}
}
}
}
void TolerantChamferZk::compute_chamfer_maps(unsigned int *edge_maps,
unsigned char *chamfer_maps) {
for(int y = 0; y < _image_height; y++) {
for(int x = 0; x < _image_width; x++) {
for(int e = 0; e < nb_edge_types; e++) {
if(edge_maps[e + nb_edge_types * (x + _image_width * y)]) {
chamfer_maps[e + nb_edge_types * (x + _image_width * y)]
= 0;
} else {
chamfer_maps[e + nb_edge_types * (x + _image_width * y)]
= chamfer_thickness + 1;
}
}
}
}
unsigned int d;
int changed = 1;
for(int t = 0; changed && t < chamfer_thickness; t++) {
changed = 0;
for(int y = 0; y < _image_height; y++) {
for(int x = 0; x < _image_width; x++) {
for(int e = 0; e < nb_edge_types; e++) {
d = _image_width + _image_height;
if(x > 0) {
d = MIN(d,
_chamfer_maps[e + nb_edge_types * ((x-1) + _image_width * y)]);
}
if(x < _image_width - 1) {
d = MIN(d,
_chamfer_maps[e + nb_edge_types * ((x+1) + _image_width * y)]);
}
if(y > 0) {
d = MIN(d,
_chamfer_maps[e + nb_edge_types * (x + _image_width * (y-1))]);
}
if(y < _image_height - 1) {
d = MIN(d,
_chamfer_maps[e + nb_edge_types * (x + _image_width * (y+1))]);
}
if(d + 1 < _chamfer_maps[e + nb_edge_types * (x + _image_width * y)]) {
_chamfer_maps[e + nb_edge_types * (x + _image_width * y)] = d+1;
changed = 1;
}
}
}
}
}
}
void TolerantChamferZk::prepareForImage() {
byte_t **pixels = image->grayLines();
_image_width = image->width();
_image_height = image->height();
_chamfer_maps = new unsigned char[_image_width * _image_height * nb_edge_types];
unsigned int *edge_maps;
edge_maps = new unsigned int[_image_width * _image_height * nb_edge_types];
compute_edge_maps(pixels, edge_maps);
compute_chamfer_maps(edge_maps, _chamfer_maps);
delete[] edge_maps;
}
void TolerantChamferZk::finishForImage() {
delete[] _chamfer_maps;
}
void TolerantChamferZk::prepareForCoordinates() { }
void TolerantChamferZk::finishForCoordinates() { }
scalar_t TolerantChamferZk::computeFeature(unsigned int feature_index)
{
// This weird way of picking dx, dy and e so that the
// output-as-an-image of testheuristic looks like something
unsigned int x0 = coordinates.x - roi_extent;
unsigned int y0 = coordinates.y - roi_extent;
unsigned int roi_size = roi_extent * 2 + 1;
unsigned int dx = feature_index % roi_size;
feature_index /= roi_size;
int e = feature_index % nb_edge_types;
feature_index /= nb_edge_types;
unsigned int dy = feature_index;
return scalar_t(_chamfer_maps[e + nb_edge_types * ((x0 + dx) + _image_width * (y0 + dy))]);
}