Files
AdvancedVision/NeuralDetector/avansvisionlib20.cpp
T
2017-12-20 12:21:17 +01:00

1052 lines
32 KiB
C++

// avansvisionlib - Growing Visionlibrary of Avans based on OpenCV 2.4.10
// Goal: deep understanding of vision algorithms by means of developing own (new) algorithms.
// deep understanding of neural networks
//
// Copyright Jan Oostindie, version 2.0 dd 5-12-2016 (= Neural Network (BPN) added to version 1.0 dd 5-11-2016.)
// Contains basic functions to perform calculations on matrices/images of class Mat. Including BLOB labeling functions
// Contains a BPN neural network.
// Note: Students of Avans are free to use this library in projects and for own vision competence development. Others may ask permission to use it by means
// of sending an email to Jan Oostindie, i.e. jac.oostindie@avans.nl
#include "avansvisionlib20.h"
#include <math.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
// pre: (i < m.rows) & (j < m.cols)
// Mat is call by reference
void setEntry(Mat m, int i, int j, double value) {
int index = i * m.cols + j;
double * p = m.ptr<double>(0);
p[index] = value;
} // setEntry
// pre: (i < m.rows) & (j < m.cols)
double getEntry(Mat m, int i, int j) {
int index = i * m.cols + j;
double * p = m.ptr<double>(0);
return *(p + index);
} // getEntry
// func: calculate product of a row and column of equal length
// pre: (row.cols == col.rows)
double inproduct(Mat row, Mat col) {
double * p1 = row.ptr<double>(0);
double * p2 = col.ptr<double>(0);
double sumproducts = 0;
for (int i = 0; i < row.cols; i++)
sumproducts += p1[i] * p2[i];
return sumproducts;
} // inproduct
void printMatrix(Mat m) {
for (int i = 0; i < m.rows; i++) {
for (int j = 0; j < m.cols; j++)
cout << getEntry(m, i, j) << " ";
cout << endl;
}
cout << endl;
} // printMatrix
Mat getRow(Mat m, int rowNr) {
Mat result = Mat_<double>(1, m.cols);
double entry;
for (int colNr = 0; colNr < m.cols; colNr++) {
entry = getEntry(m, rowNr, colNr);
// cout << m << endl;
// cout << " ** getRow ** " << endl;
// cout << " entry = " << entry << endl;
setEntry(result, 0, colNr, entry);
}
return result;
} // getRow
Mat getCol(Mat m, int colNr) {
Mat result = Mat_<double>(m.rows, 1);
double entry;
for (int rowNr = 0; rowNr < m.rows; rowNr++) {
entry = getEntry(m, rowNr, colNr);
// cout << " ** getColumn ** " << endl;
// cout << " entry = " << entry << endl;
setEntry(result, rowNr, 0, entry);
}
return result;
} // getCol
// pre: (a.cols == b.rows)
Mat multiply(Mat a, Mat b)
{
assert(a.cols == b.rows);
Mat result = Mat_<double>(a.rows, b.cols);
double inprod;
for (int arow = 0; arow < a.rows; arow++) {
for (int bcol = 0; bcol < b.cols; bcol++) {
inprod = inproduct(getRow(a, arow), getCol(b, bcol));
setEntry(result, arow, bcol, inprod);
}
}
return result;
} // multiply
// pre: matrices have equal dimensions i.e. (a.cols == b.cols) & (a.rows == b.rows)
Mat add(Mat a, Mat b)
{
Mat result = Mat_<double>(a.rows, a.cols);
double entrysum;
for (int row = 0; row < a.rows; row++) {
for (int col = 0; col < a.cols; col++) {
entrysum = getEntry(a, row, col) + getEntry(b, row, col);
setEntry(result, row, col, entrysum);
}
}
return result;
} // add
Mat transpose(Mat m) {
Mat result = Mat_<double>(m.cols, m.rows);
for (int row = 0; row < m.rows; row++)
for (int col = 0; col < m.cols; col++)
setEntry(result, col, row, getEntry(m, row, col));
return result;
} // transpose
// func: sets all entries of a matrix to a value
// pre: true
void setValue(Mat m, double value) {
for (int row = 0; row < m.rows; row++)
for (int col = 0; col < m.cols; col++)
setEntry(m, row, col, value);
} // setValue
double generateRandomValue(double min, double max) {
int steps = rand() % 100 + 1;
double dx = (max - min) / 100;
return min + dx * steps;
} // generateRandomValue
// func: sets all entries of a matrix to a random value in interval [min,max]
// pre: true
void setRandomValue(Mat m, double min, double max) {
srand(time(NULL));
for (int row = 0; row < m.rows; row++)
for (int col = 0; col < m.cols; col++)
setEntry(m, row, col, generateRandomValue(min, max));
} // randomValue
/*********************************** Image operaties ****************************************/
// NB images are supposed to have 1 channel (B/W image) and depth 16 bits signed (CV_16S)
/********************************************************************************************/
void setEntryImage(Mat m, int i, int j, _int16 value) {
int index = i * m.cols + j;
_int16 * p = m.ptr<_int16>(0);
p[index] = value;
} // setEntry
// pre: (i < m.rows) & (j < m.cols)
_int16 getEntryImage(Mat m, int i, int j) {
int index = i * m.cols + j;
_int16 * p = m.ptr<_int16>(0);
return *(p + index);
} // getEntryImage
// func: calculate product of a row and column of equal length
// pre: (row.cols == col.rows)
_int16 inproductImage(Mat row, Mat col) {
_int16 * p1 = row.ptr<_int16>(0);
_int16 * p2 = col.ptr<_int16>(0);
_int16 sumproducts = 0;
for (int i = 0; i < row.cols; i++)
sumproducts += p1[i] * p2[i];
return sumproducts;
} // inproductImage
Mat getRowImage(Mat m, int rowNr) {
Mat result = Mat_<_int16>(1, m.cols);
_int16 entry;
for (int colNr = 0; colNr < m.cols; colNr++) {
entry = getEntryImage(m, rowNr, colNr);
setEntryImage(result, 0, colNr, entry);
}
return result;
} // getRow
Mat getColImage(Mat m, int colNr) {
Mat result = Mat_<_int16>(m.rows, 1);
_int16 entry;
for (int rowNr = 0; rowNr < m.rows; rowNr++) {
entry = getEntryImage(m, rowNr, colNr);
setEntryImage(result, rowNr, 0, entry);
}
return result;
} // getColImage
Mat multiplyImage(Mat a, Mat b)
{
Mat result = Mat_<_int16>(a.rows, b.cols);
_int16 inprod;
for (int arow = 0; arow < a.rows; arow++) {
for (int bcol = 0; bcol < b.cols; bcol++) {
inprod = inproductImage(getRow(a, arow), getColImage(b, bcol));
setEntry(result, arow, bcol, inprod);
}
}
return result;
} // multiplyImage
// pre: matrices have equal dimensions i.e. (a.cols == b.cols) & (a.rows == b.rows)
Mat addImage(Mat a, Mat b)
{
Mat result = Mat_<_int16>(a.rows, a.cols);
_int16 entrysum;
for (int row = 0; row < a.rows; row++) {
for (int col = 0; col < a.cols; col++) {
entrysum = getEntryImage(a, row, col) + getEntryImage(b, row, col);
setEntryImage(result, row, col, entrysum);
}
}
return result;
} // addImage
// func: searches the maximum pixel value in the image
// return: maximum pixel
_int16 maxPixelImage(Mat m) {
_int16 max = getEntryImage(m, 0, 0);
_int16 next;
for (int row = 0; row < m.rows; row++) {
for (int col = 0; col < m.cols; col++) {
next = getEntryImage(m, row, col);
if (next > max) max = next;
}
}
return max;
} // maxPixelImage
// func: searches the minimum pixel value in the image
// return: minimum pixel value
_int16 minPixelImage(Mat m) {
_int16 min = getEntryImage(m, 0, 0);
_int16 next;
for (int row = 0; row < m.rows; row++) {
for (int col = 0; col < m.cols; col++) {
next = getEntryImage(m, row, col);
if (next < min) min = next;
}
}
return min;
} // minPixelImage
// func: determines the range of the image, i.e. the minimum
// and maximum pixel value in the image
// post: range = minPixelValue, maxPixelValue
void getPixelRangeImage(Mat m, _int16 &minPixelValue, _int16 &maxPixelValue) {
_int16 max = getEntryImage(m, 0, 0);
_int16 min = getEntryImage(m, 0, 0);
_int16 next;
for (int row = 0; row < m.rows; row++) {
for (int col = 0; col < m.cols; col++) {
next = getEntryImage(m, row, col);
if (next > max) max = next;
else
if (next < min) min = next;
}
}
minPixelValue = min;
maxPixelValue = max;
} // getPixelRangeImage
// func: stretches the image to a specified range
void stretchImage(Mat m, _int16 minPixelValue, _int16 maxPixelValue) {
_int16 min, max, oldValue, newValue;
getPixelRangeImage(m, min, max);
double scale = maxPixelValue - minPixelValue;
scale /= (max - min);
for (int row = 0; row < m.rows; row++) {
for (int col = 0; col < m.cols; col++) {
oldValue = getEntryImage(m, row, col);
newValue = scale * (oldValue - min) + minPixelValue;
setEntryImage(m, row, col, newValue);
}
}
} // stretchImage
// func: shows a 16S image on the screen
// pre: m is a 16S image (depth 16 bits, signed)
void show16SImageStretch(Mat m, string windowTitle) {
Mat mCopy;
m.copyTo(mCopy);
stretchImage(mCopy, 0, 255);
mCopy.convertTo(mCopy, CV_8U);
// namedWindow(windowTitle, CV_WINDOW_AUTOSIZE);
imshow(windowTitle, mCopy);
waitKey(0);
} // show16SImage
// func: shows a 16S image on the screen. All values clipped to the interval 0-255
// i.e. value < 0 => 0; 0 <= value <= 255 => value ; value > 255 => 255
/// pre: m is a 16S image (depth 16 bits, signed)
void show16SImageClip(Mat m, string windowTitle) {
Mat mCopy;
m.copyTo(mCopy);
mCopy.convertTo(mCopy, CV_8U);
// namedWindow("show16SImageClip", CV_WINDOW_AUTOSIZE);
imshow(windowTitle, mCopy);
waitKey(0);
} // show16SImage
// func: histogram gamma correction
// pre: image has depth 8 bits unsigned and 1 or 3 channels
// post: entry(i,j) = 255*power(entry@pre(i,j)/255)^gamma
void gammaCorrection(Mat image, float gamma) {
unsigned char lut[256];
for (int i = 0; i < 256; i++) {
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), gamma) * 255.0f);
}
// dst = src.clone();
const int channels = image.channels();
switch (channels) {
case 1: {
MatIterator_<uchar> it, end;
for (it = image.begin<uchar>(), end = image.end<uchar>(); it != end; it++)
*it = lut[(*it)];
break;
}
case 3: {
MatIterator_<Vec3b> it, end;
for (it = image.begin<Vec3b>(), end = image.end<Vec3b>(); it != end; it++) {
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
} // switch
} // gammaCorrection
// func: makes a administration used for labeling blobs.
// the function adds a edge of 1 pixel wide tot a binary image, all with value 0.
// All 1's are made -1. The result is returned.
// This function is used by function labelBLOBs
// pre : binaryImage has depth 16 bits signed int. Contains only values 0 and 1.
// return_matrix: All "1" are made "-1" meaning value 1 and unvisited.
Mat makeAdmin(Mat binaryImage) {
Mat result = Mat_<_int16>(binaryImage.rows + 2, binaryImage.cols + 2);
// eerste rij 0 maken
for (int col = 0; col < result.cols; col++)
setEntryImage(result, 0, col, 0);
// binaryImage copieren naar admin waarbij een 1 steeds wordt omgezet naar -1.
for (int row = 1; row < (result.rows - 1); row++) {
// 0 vooraan de rij zetten
setEntryImage(result, row, 0, 0);
// rij binaryImage copieren
_int16 value;
for (int col = 1; col < result.cols - 1; col++) {
value = getEntryImage(binaryImage, row - 1, col - 1);
if (value == 1) value = -1;
setEntryImage(result, row, col, value);
}
// 0 achteraan de rij zetten
setEntryImage(result, row, result.cols - 1, 0);
} // for
// laatste rij 0 maken
for (int col = 0; col < result.cols; col++)
setEntryImage(result, result.rows - 1, col, 0);
return result;
} // makeAdmin
// func: Searches the next blob after position (row,col) i.e. searches
// the next -1 in admin
// post: if return_value == 1 then (row,col) contains the position
// where the next blob starts.
// return_value: 1 next blob found ; starting position is (row,col)
// 0 no blob found ; (row, col) == (-1, -1)
bool findNextBlob(Mat admin, int & row, int & col) {
bool found = false;
// zoeken in de huidige rij
for (int currCol = col; (currCol < (admin.cols - 1)) & !found; currCol++)
if (getEntryImage(admin, row, currCol) == -1) {
found = true;
// row unchanged
col = currCol;
}
// zoeken vanaf de volgende rij
for (int currRow = row + 1; (currRow < (admin.rows - 1)) &!found; currRow++)
for (int currCol = 1; (currCol < (admin.cols - 1)) & !found; currCol++)
if (getEntryImage(admin, currRow, currCol) == -1) {
found = true;
row = currRow;
col = currCol;
}
if (!found) {
row = -1;
col = -1;
}
return found;
}; // findNextBlob
// func: gets the entry of a neighbour pixel with relative position nr.
// Definition of relative positions nr:
// 7 0 1
// 6 X 2
// 5 4 3
_int16 getEntryNeighbour(const Mat & admin, int x, int y, int nr) {
switch (nr) {
case 0: return getEntryImage(admin, x - 1, y); break;
case 1: return getEntryImage(admin, x - 1, y + 1); break;
case 2: return getEntryImage(admin, x, y + 1); break;
case 3: return getEntryImage(admin, x + 1, y + 1); break;
case 4: return getEntryImage(admin, x + 1, y); break;
case 5: return getEntryImage(admin, x + 1, y - 1); break;
case 6: return getEntryImage(admin, x, y - 1); break;
case 7: return getEntryImage(admin, x - 1, y - 1); break;
default: cout << "ERROR getEntryNeighbour " << endl;
}
} // getEntryNeighbour
// func: determines if there are more than 1 adjacent 1's
bool moreNext1(const Mat & admin, int x, int y) {
int cnt1 = 0;
bool more = false;
for (int nr = 0; (nr <= 7) & !more; nr++)
if (getEntryNeighbour(admin, x, y, nr) == -1) {
cnt1++;
if (cnt1 > 1) more = true;
}
return more;
} // moreNext1
// func: searches the first 1 when rotating around the pixel (currX,currY),
// starting at position 0. Definition of relative positions:
// 7 0 1
// 6 X 2
// 5 4 3
void findNext1(Mat admin, int & currX, int & currY, int & next1) {
int rotX, rotY;
rotX = currX - 1; rotY = currY; //0
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 0;
else {
rotX = currX - 1; rotY = currY + 1; //1
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 1;
else {
rotX = currX; rotY = currY + 1; //2
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 2;
else {
rotX = currX + 1; rotY = currY + 1; //3
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 3;
else {
rotX = currX + 1; rotY = currY; //4
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 4;
else {
rotX = currX + 1; rotY = currY - 1; //5
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 5;
else {
rotX = currX; rotY = currY - 1; //6
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 6;
else {
rotX = currX - 1; rotY = currY - 1; //7
if (getEntryImage(admin, rotX, rotY) == -1) next1 = 7;
else next1 = -99;
} // 6
} // 5
} // 4
} // 3
} // 2
} // 1
} //0
if (next1 >= 0) {
currX = rotX;
currY = rotY;
}
} // findNext1
// func: labels all pixels of one blob which starts at position (row,col) with blobNr.
// This function is used by function labelBLOB's which labels all blobs.
// return_value: area of the blob
// Evaluation: This function uses a iterative algorithm in which a special labeling technique is
// is used which gives the opportunity to trace all individiual pixels. This makes it
// possible for example to save only these pixels on disk or to translate the object in
// in the image.
// The disadvantagae however is that the algorithm is more complicated an maybe a little bit
// slower than the recursive variant.
int labelIter(Mat & admin, int row, int col, int blobNr) {
// Every visited pixel is labeled with:
// blobNr*10 + <relative position to the parent >
//
// definition of relative positions
// 7 0 1
// 6 X 2
// 5 4 3
//
// The first visited pixel, i.e. (row,col), is labeled with:
// blobNr * 10 + 8
int x = row, y = col;
setEntryImage(admin, x, y, blobNr * 10 + 8);
int next1 = -999;
int area = 1;
// flag more is set when any entry in the path has two or more
// unvisited neighbours because we visit only one at a time.
bool allLabeledFlag = true;
while (allLabeledFlag) {
allLabeledFlag = false;
bool pathLabeled = false;
while (!pathLabeled) {
if (!allLabeledFlag) allLabeledFlag = moreNext1(admin, x, y);
findNext1(admin, x, y, next1);
if (next1 >= 0) {
setEntryImage(admin, x, y, blobNr * 10 + next1);
area++;
}
else {
//findprevious
switch (getEntryImage(admin, x, y) % 10) {
case 0: x += 1; break;
case 1: x += 1; y -= 1; break;
case 2: y -= 1; break;
case 3: x -= 1; y -= 1; break;
case 4: x -= 1; break;
case 5: x -= 1; y += 1; break;
case 6: y += 1; break;
case 7: x += 1; y += 1; break;
case 8: pathLabeled = true; break; // currIndex should be 0 now
default: cout << "Error func labelIter!";
} // case
} // else
} // while
} // while (more)
return area;
} // labelIter
// func: labels all pixels of one blob which starts at position (topX,topY) with blobNr.
// During the labeling proces the centre of gravity is calculated.
// This function is used by function labelBLOBInfo
// return_value: area of the blob
// Evaluation: This function uses a iterative algorithm in which a special labeling technique is
// is used which gives the opportunity to trace all individiual pixels. This makes it
// possible for example to save only these pixels on disk or to translate the object in
// in the image.
// The disadvantagae however is that the algorithm is more complicated an maybe a little bit
// slower than the recursive variant.
int labelIterInfo(Mat & admin, int topX, int topY, int blobNr,
int & xGravity, int & yGravity) {
// Every visited pixel is labeled with:
// blobNr*10 + <relative position to the parent >
//
// definition of relative positions
// 7 0 1
// 6 X 2
// 5 4 3
//
// The first visited pixel, i.e. (row,col), is labeled with:
// blobNr * 10 + 8
xGravity = topX;
yGravity = topY;
int x = topX, y = topY;
setEntryImage(admin, topX, topY, blobNr * 10 + 8);
int next1 = -999;
int area = 1;
// allLabeledFlag is set when any entry in the path has two or more
// unvisited neighbours because we visit only one at a time.
// This algorithm good be speeded up by using a stack (future improvement)
bool allLabeledFlag = true;
while (allLabeledFlag) {
allLabeledFlag = false;
bool pathLabeled = false;
while (!pathLabeled) {
if (!allLabeledFlag) allLabeledFlag = moreNext1(admin, x, y);
findNext1(admin, x, y, next1);
if (next1 >= 0) {
setEntryImage(admin, x, y, blobNr * 10 + next1);
area++;
xGravity += x;
yGravity += y;
}
else {
//findprevious
switch (getEntryImage(admin, x, y) % 10) {
case 0: x += 1; break;
case 1: x += 1; y -= 1; break;
case 2: y -= 1; break;
case 3: x -= 1; y -= 1; break;
case 4: x -= 1; break;
case 5: x -= 1; y += 1; break;
case 6: y += 1; break;
case 7: x += 1; y += 1; break;
case 8: pathLabeled = true; break; // currIndex should be 0 now
default: cout << "Error func labelIter!";
} // case
} // else
} // while
} // while (more)
xGravity /= area;
yGravity /= area;
return area;
} // labelIterInfo
// func: labels all pixels of one blob which starts at position (row,col) with blobNr.
// return_value: area of the blob
// Evaluation: This function uses a recursive algorithm which has the advantage that it is easy and trasparent.
// The disadvantagae however is that it claims a lot of spacee on the stack. I.e. every found
// pixel results in a function call which in case of large blobs causes a stack overflow.
int labelRecursive(Mat & admin, int row, int col, int blobNr) {
int area = 0;
// bij waarde -1 is het pixel nog niet bezocht
if (getEntryImage(admin, row, col) == -1) {
//cout << "(row,col) = " << "(" << row << "," << col << ")" << endl;
// pixel labelen met het volgnummer van de blob
setEntryImage(admin, row, col, blobNr);
area = 1;
// alle pixels rondom huidige pixel bezoeken
// (row-1,col-1) (row-1,col ) (row-1,col+1)
// (row ,col-1) (row, col ) (row ,col+1)
// (row+1,col-1) (row, col ) (row+1,col+1)
area += labelRecursive(admin, row - 1, col, blobNr);
area += labelRecursive(admin, row - 1, col + 1, blobNr);
area += labelRecursive(admin, row, col + 1, blobNr);
area += labelRecursive(admin, row + 1, col + 1, blobNr);
area += labelRecursive(admin, row, col, blobNr);
area += labelRecursive(admin, row + 1, col - 1, blobNr);
area += labelRecursive(admin, row, col - 1, blobNr);
area += labelRecursive(admin, row - 1, col - 1, blobNr);
}
return area;
} // label
// func: retrieves a labeledImage from the labeling administration
// pre : admin is contains labeled pixels with neighbour number information.
// post: labeledImage: binary 8-connected pixels with value 1 in binaryImage are
// labeled with the number of the object they belong to.
void retrieveLabeledImage(const Mat & admin, Mat & labeledImage) {
labeledImage = Mat_<_int16>(admin.rows - 2, admin.cols - 2);
for (int row = 1; row < admin.rows - 1; row++) {
for (int col = 1; col < admin.cols - 1; col++) {
setEntryImage(labeledImage, row - 1, col - 1,
getEntryImage(admin, row, col) / 10);
}
}
} // retrieveLabeledImage
// func: labeling of all blobs in a binary image
// pre : binaryImage has depth 16 bits signed int. Contains only values 0 and 1.
// post: labeledImage: binary 8-connected pixels with value 1 in binaryImage are
// labeled with the number of the object they belong to.
// return_value: the total number of objects.
int labelBLOBs(Mat binaryImage, Mat & labeledImage) {
// admin contains the administration of the recursive process.
// meaning of the entry values:
// -1: a "1" which is not visited yet. Changes to 1 when visited.
// 0: always a "0"
// 1, 2, 3,... : a "1" which is visited and is labeled with the object number.
Mat admin = makeAdmin(binaryImage);
int row = 1;
int col = 1;
// init volgnummer
int blobNr = 0;
// label alle BLOBs met een volgnummer
while ((row > 0) & (row < (admin.rows - 1)) &
(col > 0) & (col < (admin.cols - 1)))
if (findNextBlob(admin, row, col)) labelIter(admin, row, col, ++blobNr);
retrieveLabeledImage(admin, labeledImage);
// laatste volgnummer is gelijk aan het aantal gevonden blobs
return blobNr;
} // labelBLOBs
// func: removes a BLOB from the labeling administration
// pre: (posx,posy) is the position of the BLOB, blobNr the number
// of the blob to be removed.
void removeBLOB(Mat & admin, int blobNr) {
_int16 value;
for (int row = 1; row < admin.rows - 2; row++)
for (int col = 1; col < admin.cols - 2; col++) {
value = getEntryImage(admin, row, col);
while (value > 10) value /= 10;
if (value == blobNr) setEntryImage(admin, row, col, 0);
}
} // removeBLOB
// func: labeling of all blobs in a binary image with a area in [threhAreaMin,threhAreaMax]. Default
// threshold is [1,INT_MAX]. Alle gathered data during the labeling proces is returned,
// i.e. the positions of the firstpixel of each blob, the position of the blobs (i.e. the
// centres of gravity) and the area's of all blobs.
// pre : binaryImage has depth 16 bits signed int. Contains only values 0 and 1.
// post: labeledImage: binary 8-connected pixels with value 1 in binaryImage are
// labeled with the number of the object they belong to.
// areaVec: contains all area's of the blobs. The index corresponds to the number
// of the blobs. Index 0 has no meaning.
// return_value: the total number of objects.
int labelBLOBsInfo(Mat binaryImage, Mat & labeledImage,
vector<Point2d *> & firstpixelVec, vector<Point2d *> & posVec,
vector<int> & areaVec,
int threshAreaMin, int threshAreaMax) {
// admin contains the administration of the recursive process.
// meaning of the entry values:
// -1: a "1" which is not visited yet.
// 0: always a "0"
// 1, 2, 3,... : a "1" which is visited and is labeled with the object number.
Mat admin = makeAdmin(binaryImage);
int row = 1;
int col = 1;
// init volgnummer
int blobNr = 0;
int area;
int xGravity, yGravity;
// label alle BLOBs met een volgnummer
while ((row > 0) & (row < (admin.rows - 1)) &
(col > 0) & (col < (admin.cols - 1)))
if (findNextBlob(admin, row, col)) {
area = labelIterInfo(admin, row, col, ++blobNr, xGravity, yGravity);
if ((area >= threshAreaMin) & (area <= threshAreaMax)) {
firstpixelVec.push_back(new Point2d(row - 1, col - 1));
posVec.push_back(new Point2d(xGravity - 1, yGravity - 1));
areaVec.push_back(area);
}
else removeBLOB(admin, blobNr--);
}
retrieveLabeledImage(admin, labeledImage);
// laatste volgnummer is gelijk aan het aantal gevonden blobs
return blobNr;
} // labelBLOBsInfo
/*BEGIN********************************************** BACK PROPAGATION NEURAL NETWORK ****************************************************************/
// TRAININGSET: I0 because of bias V0
//
// setnr I0 I1 I2 I3 I4 O1 O2
// 1 1.0 0.4 -0.7 0.1 0.71 0.0 0.0
// 2 1.0 0.3 -0.5 0.05 0.34 0.0 0.0
// 3 1.0 0.6 0.1 0.3 0.12 0.0 1.0
// 4 1.0 0.2 0.4 0.25 0.34 0.0 1.0
// 5 1.0 -0.2 0.12 0.56 1.0 1.0 0.0
// 6 1.0 0.1 -0.34 0.12 0.56 1.0 0.0
// 7 1.0 -0.6 0.12 0.56 1.0 1.0 1.0
// 8 1.0 0.56 -0.2 0.12 0.56 1.0 1.0
void loadTrainingSet1(Mat & ITset, Mat & OTset) {
// input of trainingset
// remark: nummber of columns == number of inputneurons of the BPN
ITset = (Mat_<double>(8, 5) <<
1, 0.4, -0.7, 0.1, 0.71,
1, 0.3, -0.5, 0.05, 0.34,
1, 0.6, 0.1, 0.3, 0.12,
1, 0.2, 0.4, 0.25, 0.34,
1, -0.2, 0.12, 0.56, 1.0,
1, 0.1, -0.34, 0.12, 0.56,
1, 0.6, 0.12, 0.56, 1.0,
1, 0.56, -0.2, 0.12, 0.56);
// output of trainingset
// remark: nummber of columns == number of outputneurons of the BPN
OTset = (Mat_<double>(8, 2) <<
0, 0,
0, 0,
0, 1,
0, 1,
1, 0,
1, 0,
1, 1,
1, 1);
} // loadTestTrainingSet1
// TRAININGSET binary function O1 = (I1 OR I2) AND I3
// without bias
// setnr I1 I2 I3 O1
// 1 0 0 0 0
// 2 0 0 1 0
// 3 0 1 0 0
// 4 0 1 1 1
// 5 1 0 0 0
// 6 1 0 1 1
// 7 1 1 0 0
// 8 1 1 1 1
void loadBinaryTrainingSet1(Mat & ITset, Mat & OTset) {
// input of trainingset (without bias)
// remark: nummber of columns == number of inputneurons of the BPN
ITset = (Mat_<double>(8, 2) <<
0, 0,
0, 0,
0, 1,
0, 1,
1, 0,
1, 0,
1, 1,
1, 1);
// output of trainingset
// remark: nummber of columns == number of outputneurons of the BPN
OTset = (Mat_<double>(8, 1) <<
0,
0,
1,
1,
1,
1,
0,
0);
} // loadBinaryTrainingSet1
// func: Initialization of the (1) weigthmatrices V0 and W0 and (2) of the delta matrices dV0 and dW0.
// pre: inputNeurons, hiddenNeurons and outputNeurons define the Neural Network.
// From this numbers the dimensions of the weightmatrices can be determined.
// post: V0 and W0 have random values between 0.1 and 0.9
void initializeBPN(int inputNeurons, int hiddenNeurons, int outputNeurons,
Mat & V0, Mat & dV0, Mat & W0, Mat & dW0) {
// Instellen van alle weegfactoren met een random waarde
V0 = Mat_<double>(inputNeurons, hiddenNeurons);
W0 = Mat_<double>(hiddenNeurons, outputNeurons);
setRandomValue(V0, 0.1, 0.9);
setRandomValue(W0, 0.1, 0.9);
// Initiele aanpassing van de weegfactoren W
dV0 = Mat_<double>(inputNeurons, hiddenNeurons);
dW0 = Mat_<double>(hiddenNeurons, outputNeurons);
setValue(dV0, 0);
setValue(dW0, 0);
} // initializeBPN
// Test of a BPN with all values defined explicitly
void testBPN(Mat & IT, Mat & OT, Mat & V0, Mat & dV0, Mat & W0, Mat & dW0) {
// input of trainingset
// remark: number of columns == number of inputneurons of the BPN
IT = (Mat_<double>(5, 2) <<
0.4, -0.7,
0.3, -0.5,
0.6, 0.1,
0.2, 0.4,
0.1, -0.2);
// output of trainingset
// remark: nummber of columns == number of outputneurons of the BPN
OT = (Mat_<double>(5, 1) <<
0.1,
0.05,
0.3,
0.25,
0.12);
// STEP2: Initializing the weights
V0 = (Mat_<double>(2, 2) <<
0.1, 0.4,
-0.2, 0.2);
W0 = (Mat_<double>(2, 1) <<
0.2,
-0.5);
// Initiele aanpassing van de weegfactoren W
dW0 = (Mat_<double>(2, 1) <<
0.0,
0.0);
// Initiele aanpassing van de weegfactoren V
dV0 = (Mat_<double>(2, 2) <<
0.0, 0.0,
0.0, 0.0);
} // testBPN
// func: Given an inputvector of the inputlayer and a weightmatrix V calculates the outputvector of the hiddenlayer
// pre: II is input of the inputlayer. V = matrix with weightfactors between inputlayer and the hiddenlayer.
// post: OH is the outputvector of the hidden layer
void calculateOutputHiddenLayer(Mat II, Mat V, Mat & OH) {
// STEP1: Output inputlayer := Input inputlayer
Mat OI;
II.copyTo(OI);
// STEP2: Initializing the weights, already done, see input of this function
// STEP3: Calculate input of the hiddenlayer, i.e. IH = V0transposed * OI
Mat Vtr = transpose(V);
Mat IH = multiply(Vtr, OI);
// STEP4: Calculate output of the hiddenlayer, i.e. OH(i) = 1/(1+EXP(-IH(i)))
int hiddenNeurons = V.cols;
OH = Mat_<double>(hiddenNeurons, 1);
for (int row = 0; row < hiddenNeurons; row++)
setEntry(OH, row, 0, 1 / (1 + exp(-getEntry(IH, row, 0))));
} // calculateOutputHiddenLayer
// func: Given the outputvector of the hiddenlayer and a weigthmatrix W calculates the outputvector of the outputlayer
// pre: OH is the outputvector of the hiddenlayer. W = matrix with weightfactors between hiddenlayer and the outputlayer.
// post: OO is the outputvector of the output layer
void calculateOutputBPN(Mat OH, Mat W, Mat & OO) {
// STEP5: Calculate input of the outputlayer, i.e. IO = W0transposed * OH
Mat Wtr = transpose(W);
Mat IO = multiply(Wtr, OH);
// STEP6: Calculate output of the outputlayer, i.e. OO(i) = 1/(1+EXP(-IO(i)))
int outputNeurons = W.cols;
OO = Mat_<double>(outputNeurons, 1);
for (int row = 0; row < outputNeurons; row++)
setEntry(OO, row, 0, 1 / (1 + exp(-getEntry(IO, row, 0))));
} // calculateOutputBPN
// func: Calculates the total error Error = 1/2*Sigma(OTi-OOi)^2.
// OTi is the expected output according to the trainingvector i
// OOi is the calculated output from the current neural network of the traininngvector i
// pre: OO is the outputvector of the outputlayer. OT is the expected outputvector from the trainingset
// post: OO is the outputvector of the output layer
void calculateOutputBPNError(Mat OO, Mat OT, double & outputError) {
// STEP7: Calculate the error, i.e. Error = 1/2*Sigma(TOi-OOi)^2
double sumSqrErr = 0, diff = 0;
for (int row = 0; row < OT.rows; row++) {
diff = getEntry(OT, row, 0) - getEntry(OO, row, 0);
sumSqrErr += (diff * diff);
}
outputError = 0.5 * sumSqrErr;
} // calculateOutputBPNError
void adaptVW(Mat OT, Mat OO, Mat OH, Mat OI, Mat W0, Mat dW0, Mat V0, Mat dV0, Mat & W, Mat & V,
double ALPHA, double ETHA) {
/*BEGIN*** AANPASSING VAN DE WEEGFACTOREN W ****/
// STEP8:
// E = 1/2 Sigma(OOi - di)^2 ==> dE/dOO = Sigma(OOi - di)
// dE/dIO = dE/dOO * dOO/dIO = Sigma((OOi - Ti) * OOi * (1 - OOi))
// Here: d = dE/dIO = (T-OO) * OO * (1 - OO)
Mat OOerror = Mat_<double>(OT.rows, 1);
OOerror = OT - OO;
Mat d = Mat_<double>(OT.rows, 1);
double di;
for (int row = 0; row < OT.rows; row++) {
di = (getEntry(OT, row, 0) - getEntry(OO, row, 0)) * getEntry(OO, row, 0) * (1 - getEntry(OO, row, 0));
setEntry(d, row, 0, di);
}
// Y = OH * d
Mat dtr = transpose(d);
Mat Y = Mat_<double>(OH.rows, OT.rows);
Y = multiply(OH, dtr); // OH = mx1 ; d = nx1 ; dtr = 1xn
// STEP9: dW1 = alpha * dW0 + etha * Y // assume etha = 0.6
Mat dW = Mat_<double>(OH.rows, OT.rows);
dW = ALPHA * dW0 + ETHA * Y;
/*END*** AANPASSING VAN DE WEEGFACTOREN W ****/
/*BEGIN*** AANPASSING VAN DE WEEGFACTOREN V ****/
// STEP10: OHerror = W0 * d
Mat OHerror = Mat_<double>(OH.rows, 1);
OHerror = W0 * d;
// STEP11:
// d = dE/dIO = OOerror * OO * (1 - OO) // OOError = TO - OO
// d*= dE/dIH = OHerror * OH * (1 - OH) // OHerror = W0 * d
Mat dstar = Mat_<double>(OH.rows, 1);
double dstari;
for (int row = 0; row < OH.rows; row++) {
dstari = getEntry(OHerror, row, 0) * getEntry(OH, row, 0) * (1 - getEntry(OH, row, 0));
setEntry(dstar, row, 0, dstari);
}
// STEP12:
// X = OI * dstar
Mat dstartr = transpose(dstar);
Mat X = Mat_<double>(OI.rows, OH.rows);
X = OI * dstartr;
// STEP13: dV1 = ALPHA * dV0 + ETHA * X // assume etha = 0.6
Mat dV;
dV = ALPHA * dV0 + ETHA * X;
/*END*** AANPASSING VAN DE WEEGFACTOREN V ****/
/* Update van de matrices met gewichtsfactoren */
// STEP14:
V = Mat_<double>(V0.rows, V0.cols);
W = Mat_<double>(W0.rows, W0.cols);
V = V0 + dV;
W = W0 + dW;
}; // adaptVW
Mat BPN(Mat II, Mat V, Mat W) {
Mat OH, OO;
calculateOutputHiddenLayer(II, V, OH);
calculateOutputBPN(OH, W, OO);
return OO;
} // BPN
/*END********************************************** BACK PROPAGATION NEURAL NETWORK ****************************************************************/