环境:ubuntu20.04 OPenCV3.3.0 编译环境 VScode
EDlines 线段检测的步骤:
(1)首先读入一个灰度图像,利用ED算法产生干净的,像素相邻的链,这样的链我们称之为边缘,边缘可以很好的反映图像的边界信息
(2)利用直线度准则,即最小二乘法拟合直线,从生成的像素链中进行像素的提取。
(3)最后,利用霍兹原理进行消除虚假线段的检测。
下面直接进行代码的演示:
CMakeLists.txt文件:
cmake_minimum_required(VERSION 3.3)
project(open)
set(CMAKE_CXX_FLAGS "-std=c++11 ")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(main main.cpp)
target_link_libraries(main ${OpenCV_LIBS} ${Line} )
主函数 main.cpp
#include
#include "opencv2/opencv.hpp"
#define WAIT_TIME 4000
using namespace cv;
using namespace std;
#define SS 0
#define SE 1
#define ES 2
#define EE 3
#define EDGE_VERTICAL 1
#define EDGE_HORIZONTAL 2
#define ANCHOR_PIXEL 254
#define EDGE_PIXEL 255
#define LEFT 1
#define RIGHT 2
#define UP 3
#define DOWN 4
enum GradientOperator { PREWITT_OPERATOR = 101, SOBEL_OPERATOR = 102, SCHARR_OPERATOR = 103 };
struct StackNode {
int r, c; // начальный пиксел
int parent; // родитель (-1 если нету)
int dir; // направление
};
// для соединения граней
struct Chain {
int dir; // направление цепочки
int len; // кол-во пикселей в цепи
int parent; // родитель (-1 если нету)
int children[2]; // дети (-1 если нету)
cv::Point *pixels; // указатель на начало массива пикселей
};
struct LS {
cv::Point2d start;
cv::Point2d end;
LS(cv::Point2d _start, cv::Point2d _end)
{
start = _start;
end = _end;
}
};
struct LineSegment {
double a, b;
int invert;
double sx, sy; // начало
double ex, ey; // конец
int segmentNo; // сегмент которому принадлежит отрезок
int firstPixelIndex; // индекс первого пикселя в сегменте
int len; // длина в пикселях
LineSegment(double _a, double _b, int _invert, double _sx, double _sy, double _ex, double _ey, int _segmentNo, int _firstPixelIndex, int _len) {
a = _a;
b = _b;
invert = _invert;
sx = _sx;
sy = _sy;
ex = _ex;
ey = _ey;
segmentNo = _segmentNo;
firstPixelIndex = _firstPixelIndex;
len = _len;
}
};
class EDLines {
public:
EDLines(cv::Mat _srcImage, GradientOperator _op = PREWITT_OPERATOR, int _gradThresh = 20, int _anchorThresh = 0, int _scanInterval = 1, int _minPathLen = 10, double _sigma = 1.5, bool _sumFlag = true, double _line_error = 1.0, int _min_line_len = -1, double _max_distance_between_two_lines = 6.0, double _max_error = 1.3);
cv::Mat getEdgeImage();
cv::Mat getAnchorImage();
cv::Mat getSmoothImage();
cv::Mat getGradImage();
cv::Mat getLineImage();
cv::Mat drawOnImage();
int getSegmentNo();
int getAnchorNo();
std::vector getAnchorPoints();
std::vector> getSegments();
std::vector> getSortedSegments();
cv::Mat drawParticularSegments(std::vector list);
std::vector getLines();
int getLinesNo();
protected:
int width; // ширина исходного изображения
int height; // высота исходного изображения
uchar *srcImg;
std::vector > segmentPoints;
double sigma; // сигма Гаусса
cv::Mat smoothImage;
uchar *edgeImg;
uchar *smoothImg;
int segmentNos;
int minPathLen;
cv::Mat srcImage;
private:
void ComputeGradient();
void ComputeAnchorPoints();
void JoinAnchorPointsUsingSortedAnchors();
int* sortAnchorsByGradValue1();
static int LongestChain(Chain *chains, int root);
static int RetrieveChainNos(Chain *chains, int root, int chainNos[]);
int anchorNos;
std::vector anchorPoints;
std::vector edgePoints;
cv::Mat edgeImage;
cv::Mat gradImage;
cv::Mat threshImage;
uchar *dirImg; // указатель на направление градиента пикселя
short *gradImg; // указатель на градиет пикселя
GradientOperator gradOperator; // оператор градиента
int gradThresh;
int anchorThresh;
int scanInterval;
bool sumFlag;
std::vector lines;
std::vector invalidLines;
std::vector linePoints;
int linesNo;
int min_line_len;
double line_error;
double max_distance_between_two_lines;
double max_error;
double prec;
int ComputeMinLineLength();
void SplitSegment2Lines(double *x, double *y, int noPixels, int segmentNo);
void JoinCollinearLines();
bool TryToJoinTwoLineSegments(LineSegment *ls1, LineSegment *ls2, int changeIndex);
static double ComputeMinDistance(double x1, double y1, double a, double b, int invert);
static void ComputeClosestPoint(double x1, double y1, double a, double b, int invert, double &xOut, double &yOut);
static void LineFit(double *x, double *y, int count, double &a, double &b, int invert);
static void LineFit(double *x, double *y, int count, double &a, double &b, double &e, int &invert);
static double ComputeMinDistanceBetweenTwoLines(LineSegment *ls1, LineSegment *ls2, int *pwhich);
static void UpdateLineParameters(LineSegment *ls);
static void EnumerateRectPoints(double sx, double sy, double ex, double ey,int ptsx[], int ptsy[], int *pNoPoints);
};
EDLines::EDLines(Mat _srcImage, GradientOperator _op, int _gradThresh, int _anchorThresh , int _scanInterval , int _minPathLen , double _sigma , bool _sumFlag , double _line_error , int _min_line_len , double _max_distance_between_two_lines , double _max_error)
{
// Проверка параметров на адекватность
if (_gradThresh < 1) _gradThresh = 1;
if (_anchorThresh < 0) _anchorThresh = 0;
if (_sigma < 1.0) _sigma = 1.0;
srcImage = _srcImage;
height = srcImage.rows;
width = srcImage.cols;
gradOperator = _op;
gradThresh = _gradThresh;
anchorThresh = _anchorThresh;
scanInterval = _scanInterval;
minPathLen = _minPathLen;
sigma = _sigma;
sumFlag = _sumFlag;
segmentNos = 0;
segmentPoints.push_back(vector()); // создаем пустой вектор точек для сегментов
edgeImage = Mat(height, width, CV_8UC1, Scalar(0)); // инициализируем изображение с гранями
smoothImage = Mat(height, width, CV_8UC1);
gradImage = Mat(height, width, CV_16SC1);
smoothImg = smoothImage.data;
gradImg = (short*)gradImage.data;
edgeImg = edgeImage.data;
srcImg = srcImage.data;
dirImg = new unsigned char[width*height];
/*------------ ПРИМЕНЯЕМ РАЗМЫТИЕ ПО ГАУССУ -------------------*/
if (sigma == 1.0)
GaussianBlur(srcImage, smoothImage, Size(5, 5), sigma);
else
GaussianBlur(srcImage, smoothImage, Size(), sigma); // calculate kernel from sigma
/*------------ ВЫСЧИТЫВАЕМ ГРАДИЕНТ И НАПРАВЛЕНИЕ ГРАНЕЙ -------------------*/
ComputeGradient();
/*------------ ВЫСЧИТЫВАЕМ ОПОРНЫЕ ТОЧКИ -------------------*/
ComputeAnchorPoints();
/*------------ СОЕДИНЯЕМ ОПОРНЫЕ ТОЧКИ -------------------*/
JoinAnchorPointsUsingSortedAnchors();
delete[] dirImg;
min_line_len = _min_line_len;
line_error = _line_error;
max_distance_between_two_lines = _max_distance_between_two_lines;
max_error = _max_error;
if(min_line_len == -1) // если не задано значение, посчитаем
min_line_len = ComputeMinLineLength();
if (min_line_len < 9) // устанавливаем мин длину отрезка
min_line_len = 9;
double *x = new double[(width+height) * 8];
double *y = new double[(width+height) * 8];
linesNo = 0;
// Обрабатывем каждый сегмент
for (int segmentNumber = 0; segmentNumber < segmentPoints.size(); segmentNumber++) {
std::vector segment = segmentPoints[segmentNumber];
for (int k = 0; k < segment.size(); k++) {
x[k] = segment[k].x;
y[k] = segment[k].y;
}
SplitSegment2Lines(x, y, segment.size(), segmentNumber);
}
/*----------- СОЕДИНЯЕМ КОЛЛИНЕАРНЫЕ ОТРЕЗКИ----------------*/
JoinCollinearLines();
for (int i = 0; i::iterator it;
for (it = anchorPoints.begin(); it != anchorPoints.end(); it++)
anchorImage.at(*it) = 255;
return anchorImage;
}
Mat EDLines::getSmoothImage()
{
return smoothImage;
}
Mat EDLines::getGradImage()
{
Mat result8UC1;
convertScaleAbs(gradImage, result8UC1);
return result8UC1;
}
int EDLines::getSegmentNo()
{
return segmentNos;
}
int EDLines::getAnchorNo()
{
return anchorNos;
}
std::vector EDLines::getAnchorPoints()
{
return anchorPoints;
}
std::vector> EDLines::getSegments()
{
return segmentPoints;
}
std::vector> EDLines::getSortedSegments()
{
// сортируем сегметы по убыванию длины
std::sort(segmentPoints.begin(), segmentPoints.end(), [](const std::vector & a, const std::vector & b) { return a.size() > b.size(); });
return segmentPoints;
}
Mat EDLines::drawParticularSegments(std::vector list)
{
Mat segmentsImage = Mat(edgeImage.size(), edgeImage.type(), Scalar(0));
std::vector::iterator it;
std::vector::iterator itInt;
for (itInt = list.begin(); itInt != list.end(); itInt++)
for (it = segmentPoints[*itInt].begin(); it != segmentPoints[*itInt].end(); it++)
segmentsImage.at(*it) = 255;
return segmentsImage;
}
void EDLines::ComputeGradient()
{
// инициализируем градиент
for (int j = 0; j= gradThresh) {
if (gx >= gy) dirImg[index] = EDGE_VERTICAL;
else dirImg[index] = EDGE_HORIZONTAL;
}
}
}
}
void EDLines::ComputeAnchorPoints()
{
for (int i = 2; i= anchorThresh && diff2 >= anchorThresh) {
edgeImg[i*width + j] = ANCHOR_PIXEL;
anchorPoints.push_back(Point(j, i));
}
}
else {
// horizontal edge
int diff1 = gradImg[i*width + j] - gradImg[(i - 1)*width + j];
int diff2 = gradImg[i*width + j] - gradImg[(i + 1)*width + j];
if (diff1 >= anchorThresh && diff2 >= anchorThresh) {
edgeImg[i*width + j] = ANCHOR_PIXEL;
anchorPoints.push_back(Point(j, i));
}
}
}
}
anchorNos = anchorPoints.size(); // суммарное число опорных точек
}
void EDLines::JoinAnchorPointsUsingSortedAnchors()
{
int *chainNos = new int[(width + height) * 8];
Point *pixels = new Point[width*height];
StackNode *stack = new StackNode[width*height];
Chain *chains = new Chain[width*height];
// сортируем опорные точки по убыванию градиента в них
int *A = sortAnchorsByGradValue1();
// соединяем опорные точки начиная с наибольших значений градиента
int totalPixels = 0;
for (int k = anchorNos - 1; k >= 0; k--) {
int pixelOffset = A[k];
int i = pixelOffset / width;
int j = pixelOffset % width;
if (edgeImg[i*width + j] != ANCHOR_PIXEL) continue;
chains[0].len = 0;
chains[0].parent = -1;
chains[0].dir = 0;
chains[0].children[0] = chains[0].children[1] = -1;
chains[0].pixels = NULL;
int noChains = 1;
int len = 0;
int duplicatePixelCount = 0;
int top = -1; // вершина стека
if (dirImg[i*width + j] == EDGE_VERTICAL) {
stack[++top].r = i;
stack[top].c = j;
stack[top].dir = DOWN;
stack[top].parent = 0;
stack[++top].r = i;
stack[top].c = j;
stack[top].dir = UP;
stack[top].parent = 0;
}
else {
stack[++top].r = i;
stack[top].c = j;
stack[top].dir = RIGHT;
stack[top].parent = 0;
stack[++top].r = i;
stack[top].c = j;
stack[top].dir = LEFT;
stack[top].parent = 0;
} //end-else
// пока стек не пуст
StartOfWhile:
while (top >= 0) {
int r = stack[top].r;
int c = stack[top].c;
int dir = stack[top].dir;
int parent = stack[top].parent;
top--;
if (edgeImg[r*width + c] != EDGE_PIXEL) duplicatePixelCount++;
chains[noChains].dir = dir; // traversal direction
chains[noChains].parent = parent;
chains[noChains].children[0] = chains[noChains].children[1] = -1;
int chainLen = 0;
chains[noChains].pixels = &pixels[len];
pixels[len].y = r;
pixels[len].x = c;
len++;
chainLen++;
if (dir == LEFT) {
while (dirImg[r*width + c] == EDGE_HORIZONTAL) {
edgeImg[r*width + c] = EDGE_PIXEL;
// Грань горизонтальная. Направлена влево
//
// A
// B x
// C
//
// очищаем верхний и нижний пиксели
if (edgeImg[(r - 1)*width + c] == ANCHOR_PIXEL) edgeImg[(r - 1)*width + c] = 0;
if (edgeImg[(r + 1)*width + c] == ANCHOR_PIXEL) edgeImg[(r + 1)*width + c] = 0;
// ищем пиксель на грани среди соседей
if (edgeImg[r*width + c - 1] >= ANCHOR_PIXEL) { c--; }
else if (edgeImg[(r - 1)*width + c - 1] >= ANCHOR_PIXEL) { r--; c--; }
else if (edgeImg[(r + 1)*width + c - 1] >= ANCHOR_PIXEL) { r++; c--; }
else {
// иначе -- идем в максимальный по градиенту пиксель СЛЕВА
int A = gradImg[(r - 1)*width + c - 1];
int B = gradImg[r*width + c - 1];
int C = gradImg[(r + 1)*width + c - 1];
if (A > B) {
if (A > C) r--;
else r++;
}
else if (C > B) r++;
c--;
}
if (edgeImg[r*width + c] == EDGE_PIXEL || gradImg[r*width + c] < gradThresh) {
if (chainLen > 0) {
chains[noChains].len = chainLen;
chains[parent].children[0] = noChains;
noChains++;
}
goto StartOfWhile;
}
pixels[len].y = r;
pixels[len].x = c;
len++;
chainLen++;
}
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = DOWN;
stack[top].parent = noChains;
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = UP;
stack[top].parent = noChains;
len--;
chainLen--;
chains[noChains].len = chainLen;
chains[parent].children[0] = noChains;
noChains++;
}
else if (dir == RIGHT) {
while (dirImg[r*width + c] == EDGE_HORIZONTAL) {
edgeImg[r*width + c] = EDGE_PIXEL;
// Грань горизонтальная. Направлена вправо
//
// A
// x B
// C
//
// очищаем верхний и нижний пиксели
if (edgeImg[(r + 1)*width + c] == ANCHOR_PIXEL) edgeImg[(r + 1)*width + c] = 0;
if (edgeImg[(r - 1)*width + c] == ANCHOR_PIXEL) edgeImg[(r - 1)*width + c] = 0;
// ищем пиксель на грани среди соседей
if (edgeImg[r*width + c + 1] >= ANCHOR_PIXEL) { c++; }
else if (edgeImg[(r + 1)*width + c + 1] >= ANCHOR_PIXEL) { r++; c++; }
else if (edgeImg[(r - 1)*width + c + 1] >= ANCHOR_PIXEL) { r--; c++; }
else {
// иначе -- идем в максимальный по градиенту пиксель СПРАВА
int A = gradImg[(r - 1)*width + c + 1];
int B = gradImg[r*width + c + 1];
int C = gradImg[(r + 1)*width + c + 1];
if (A > B) {
if (A > C) r--; // A
else r++; // C
}
else if (C > B) r++; // C
c++;
}
if (edgeImg[r*width + c] == EDGE_PIXEL || gradImg[r*width + c] < gradThresh) {
if (chainLen > 0) {
chains[noChains].len = chainLen;
chains[parent].children[1] = noChains;
noChains++;
}
goto StartOfWhile;
}
pixels[len].y = r;
pixels[len].x = c;
len++;
chainLen++;
}
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = DOWN;
stack[top].parent = noChains;
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = UP;
stack[top].parent = noChains;
len--;
chainLen--;
chains[noChains].len = chainLen;
chains[parent].children[1] = noChains;
noChains++;
}
else if (dir == UP) {
while (dirImg[r*width + c] == EDGE_VERTICAL) {
edgeImg[r*width + c] = EDGE_PIXEL;
// Грань вертикальная. Направлена вверх
//
// A B C
// x
//
// очищаем левый и правый пиксели
if (edgeImg[r*width + c - 1] == ANCHOR_PIXEL) edgeImg[r*width + c - 1] = 0;
if (edgeImg[r*width + c + 1] == ANCHOR_PIXEL) edgeImg[r*width + c + 1] = 0;
// ищем пиксель на грани среди соседей
if (edgeImg[(r - 1)*width + c] >= ANCHOR_PIXEL) { r--; }
else if (edgeImg[(r - 1)*width + c - 1] >= ANCHOR_PIXEL) { r--; c--; }
else if (edgeImg[(r - 1)*width + c + 1] >= ANCHOR_PIXEL) { r--; c++; }
else {
// иначе -- идем в максимальный по градиенту пиксель ВВЕРХ
int A = gradImg[(r - 1)*width + c - 1];
int B = gradImg[(r - 1)*width + c];
int C = gradImg[(r - 1)*width + c + 1];
if (A > B) {
if (A > C) c--;
else c++;
}
else if (C > B) c++;
r--;
}
if (edgeImg[r*width + c] == EDGE_PIXEL || gradImg[r*width + c] < gradThresh) {
if (chainLen > 0) {
chains[noChains].len = chainLen;
chains[parent].children[0] = noChains;
noChains++;
}
goto StartOfWhile;
}
pixels[len].y = r;
pixels[len].x = c;
len++;
chainLen++;
}
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = RIGHT;
stack[top].parent = noChains;
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = LEFT;
stack[top].parent = noChains;
len--;
chainLen--;
chains[noChains].len = chainLen;
chains[parent].children[0] = noChains;
noChains++;
}
else {
while (dirImg[r*width + c] == EDGE_VERTICAL) {
edgeImg[r*width + c] = EDGE_PIXEL;
// Грань вертикальная. Направлена вниз
//
// x
// A B C
//
// очищаем пиксле слева и справа
if (edgeImg[r*width + c + 1] == ANCHOR_PIXEL) edgeImg[r*width + c + 1] = 0;
if (edgeImg[r*width + c - 1] == ANCHOR_PIXEL) edgeImg[r*width + c - 1] = 0;
// ищем пиксель на грани среди соседей
if (edgeImg[(r + 1)*width + c] >= ANCHOR_PIXEL) { r++; }
else if (edgeImg[(r + 1)*width + c + 1] >= ANCHOR_PIXEL) { r++; c++; }
else if (edgeImg[(r + 1)*width + c - 1] >= ANCHOR_PIXEL) { r++; c--; }
else {
// иначе -- идем в максимальный по градиенту пиксель ВНИЗУ
int A = gradImg[(r + 1)*width + c - 1];
int B = gradImg[(r + 1)*width + c];
int C = gradImg[(r + 1)*width + c + 1];
if (A > B) {
if (A > C) c--; // A
else c++; // C
}
else if (C > B) c++; // C
r++;
}
if (edgeImg[r*width + c] == EDGE_PIXEL || gradImg[r*width + c] < gradThresh) {
if (chainLen > 0) {
chains[noChains].len = chainLen;
chains[parent].children[1] = noChains;
noChains++;
}
goto StartOfWhile;
}
pixels[len].y = r;
pixels[len].x = c;
len++;
chainLen++;
}
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = RIGHT;
stack[top].parent = noChains;
stack[++top].r = r;
stack[top].c = c;
stack[top].dir = LEFT;
stack[top].parent = noChains;
len--;
chainLen--;
chains[noChains].len = chainLen;
chains[parent].children[1] = noChains;
noChains++;
}
}
if (len - duplicatePixelCount < minPathLen) {
for (int k = 0; k 0) {
int count = RetrieveChainNos(chains, chains[0].children[1], chainNos);
// копируем пиксели в обратном порядке
for (int k = count - 1; k >= 0; k--) {
int chainNo = chainNos[k];
/* Пробуем удалить лишние пиксели */
int fr = chains[chainNo].pixels[chains[chainNo].len - 1].y;
int fc = chains[chainNo].pixels[chains[chainNo].len - 1].x;
int index = noSegmentPixels - 2;
while (index >= 0) {
int dr = abs(fr - segmentPoints[segmentNos][index].y);
int dc = abs(fc - segmentPoints[segmentNos][index].x);
if (dr <= 1 && dc <= 1) {
// neighbors. Erase last pixel
segmentPoints[segmentNos].pop_back();
noSegmentPixels--;
index--;
}
else break;
} //end-while
if (chains[chainNo].len > 1 && noSegmentPixels > 0) {
fr = chains[chainNo].pixels[chains[chainNo].len - 2].y;
fc = chains[chainNo].pixels[chains[chainNo].len - 2].x;
int dr = abs(fr - segmentPoints[segmentNos][noSegmentPixels - 1].y);
int dc = abs(fc - segmentPoints[segmentNos][noSegmentPixels - 1].x);
if (dr <= 1 && dc <= 1) chains[chainNo].len--;
}
for (int l = chains[chainNo].len - 1; l >= 0; l--) {
segmentPoints[segmentNos].push_back(chains[chainNo].pixels[l]);
noSegmentPixels++;
}
chains[chainNo].len = 0; // помечаем скопированной
}
}
totalLen = LongestChain(chains, chains[0].children[0]);
if (totalLen > 1) {
int count = RetrieveChainNos(chains, chains[0].children[0], chainNos);
// копируем цепочку в прямом порядке. пропускаем первый пиксель в цепи
int lastChainNo = chainNos[0];
chains[lastChainNo].pixels++;
chains[lastChainNo].len--;
for (int k = 0; k= 0) {
int dr = abs(fr - segmentPoints[segmentNos][index].y);
int dc = abs(fc - segmentPoints[segmentNos][index].x);
if (dr <= 1 && dc <= 1) {
segmentPoints[segmentNos].pop_back();
noSegmentPixels--;
index--;
}
else break;
}
int startIndex = 0;
int chainLen = chains[chainNo].len;
if (chainLen > 1 && noSegmentPixels > 0) {
int fr = chains[chainNo].pixels[1].y;
int fc = chains[chainNo].pixels[1].x;
int dr = abs(fr - segmentPoints[segmentNos][noSegmentPixels - 1].y);
int dc = abs(fc - segmentPoints[segmentNos][noSegmentPixels - 1].x);
if (dr <= 1 && dc <= 1) { startIndex = 1; }
}
for (int l = startIndex; l());
// копируем оставшиеся цепочки сюда
for (int k = 2; k= 10) {
int count = RetrieveChainNos(chains, k, chainNos);
// копируем пиксели
noSegmentPixels = 0;
for (int k = 0; k= 0) {
int dr = abs(fr - segmentPoints[segmentNos][index].y);
int dc = abs(fc - segmentPoints[segmentNos][index].x);
if (dr <= 1 && dc <= 1) {
// удаляем последний пиксель т к соседи
segmentPoints[segmentNos].pop_back();
noSegmentPixels--;
index--;
}
else break;
}
int startIndex = 0;
int chainLen = chains[chainNo].len;
if (chainLen > 1 && noSegmentPixels > 0) {
int fr = chains[chainNo].pixels[1].y;
int fc = chains[chainNo].pixels[1].x;
int dr = abs(fr - segmentPoints[segmentNos][noSegmentPixels - 1].y);
int dc = abs(fc - segmentPoints[segmentNos][noSegmentPixels - 1].x);
if (dr <= 1 && dc <= 1) { startIndex = 1; }
}
for (int l = startIndex; l());
segmentNos++;
}
}
}
}
// удаляем последний сегмент из массива, т.к. он пуст
segmentPoints.pop_back();
delete[] A;
delete[] chains;
delete[] stack;
delete[] chainNos;
delete[] pixels;
}
int * EDLines::sortAnchorsByGradValue1()
{
int SIZE = 128 * 256;
int *C = new int[SIZE];
memset(C, 0, sizeof(int)*SIZE);
// считаем кол-во значений градиента
for (int i = 1; i= len1) {
max = len0;
chains[root].children[1] = -1;
}
else {
max = len1;
chains[root].children[0] = -1;
}
return chains[root].len + max;
}
int EDLines::RetrieveChainNos(Chain * chains, int root, int chainNos[])
{
int count = 0;
while (root != -1) {
chainNos[count] = root;
count++;
if (chains[root].children[0] != -1) root = chains[root].children[0];
else root = chains[root].children[1];
}
return count;
}
vector EDLines::getLines()
{
return linePoints;
}
int EDLines::getLinesNo()
{
return linesNo;
}
Mat EDLines::getLineImage()
{
Mat lineImage = Mat(height, width, CV_8UC1, Scalar(0));
for (int i = 0; i < linesNo; i++) {
line(lineImage, linePoints[i].start, linePoints[i].end, Scalar(255), 1, LINE_AA, 0);
}
return lineImage;
}
Mat EDLines::drawOnImage()
{
Mat colorImage = Mat(height, width, CV_8UC1, srcImg);
cvtColor(colorImage, colorImage, COLOR_GRAY2BGR);
for (int i = 0; i < linesNo; i++) {
line(colorImage, linePoints[i].start, linePoints[i].end, Scalar(0, 255, 0), 1, LINE_AA, 0); // draw lines as green on image
}
return colorImage;
}
// Считает минимальную длину линии используя формулу NFA
int EDLines::ComputeMinLineLength() {
double logNT = 2.0*(log10((double)width) + log10((double)height));
return (int) round((-logNT / log10(0.125))*0.5);
}
//-----------------------------------------------------------------
// разбивает цепочку отрезков на линии
//
void EDLines::SplitSegment2Lines(double * x, double * y, int noPixels, int segmentNo)
{
// первый пиксель линии внутри сегмента
int firstPixelIndex = 0;
while (noPixels >= min_line_len) {
// пытаемся создать линию минимальной длины
bool valid = false;
double lastA, lastB, error;
int lastInvert;
while (noPixels >= min_line_len) {
LineFit(x, y, min_line_len, lastA, lastB, error, lastInvert);
if (error <= 0.5) { valid = true; break; }
noPixels -= 1;
x += 1; y += 1;
firstPixelIndex += 1;
}
if (valid == false) return;
// пытаемся удлинить линию
int index = min_line_len;
int len = min_line_len;
while (index < noPixels) {
int startIndex = index;
int lastGoodIndex = index - 1;
int goodPixelCount = 0;
int badPixelCount = 0;
while (index < noPixels) {
double d = ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert);
if (d <= line_error) {
lastGoodIndex = index;
goodPixelCount++;
badPixelCount = 0;
}
else {
badPixelCount++;
if (badPixelCount >= 5) break;
}
index++;
}
if (goodPixelCount >= 2) {
len += lastGoodIndex - startIndex + 1;
LineFit(x, y, len, lastA, lastB, lastInvert);
index = lastGoodIndex + 1;
}
if (goodPixelCount < 2 || index >= noPixels) {
// завершаем линию, подсчитывем конечную точку
double sx, sy, ex, ey;
int index = 0;
while (ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert) > line_error) index++;
ComputeClosestPoint(x[index], y[index], lastA, lastB, lastInvert, sx, sy);
int noSkippedPixels = index;
index = lastGoodIndex;
while (ComputeMinDistance(x[index], y[index], lastA, lastB, lastInvert) > line_error) index--;
ComputeClosestPoint(x[index], y[index], lastA, lastB, lastInvert, ex, ey);
// добавляем линию в список
lines.push_back(LineSegment(lastA, lastB, lastInvert, sx, sy, ex, ey, segmentNo, firstPixelIndex + noSkippedPixels, index - noSkippedPixels + 1));
linesNo++;
len = index + 1;
break;
}
}
noPixels -= len;
x += len;
y += len;
firstPixelIndex += len;
}
}
//------------------------------------------------------------------
// Соединяем коллинеарные линии
//
void EDLines::JoinCollinearLines()
{
int lastLineIndex = -1;
int i = 0;
while (i < linesNo) {
int segmentNo = lines[i].segmentNo;
lastLineIndex++;
if (lastLineIndex != i)
lines[lastLineIndex] = lines[i];
int firstLineIndex = lastLineIndex;
int count = 1;
for (int j = i + 1; j< linesNo; j++) {
if (lines[j].segmentNo != segmentNo) break;
// пытаемся соединить текущую линию с предыдущей в сегменте
if (TryToJoinTwoLineSegments(&lines[lastLineIndex], &lines[j],
lastLineIndex) == false) {
lastLineIndex++;
if (lastLineIndex != j)
lines[lastLineIndex] = lines[j];
}
count++;
}
// пытаемся замкнуть сегмент
if (firstLineIndex != lastLineIndex) {
if (TryToJoinTwoLineSegments(&lines[firstLineIndex], &lines[lastLineIndex],
firstLineIndex)) {
lastLineIndex--;
}
}
i += count;
}
linesNo = lastLineIndex + 1;
}
double EDLines::ComputeMinDistance(double x1, double y1, double a, double b, int invert)
{
double x2, y2;
if (invert == 0) {
if (b == 0) {
x2 = x1;
y2 = a;
}
else {
double d = -1.0 / (b);
double c = y1 - d*x1;
x2 = (a - c) / (d - b);
y2 = a + b*x2;
}
}
else {
if (b == 0) {
x2 = a;
y2 = y1;
}
else {
double d = -1.0 / (b);
double c = x1 - d*y1;
y2 = (a - c) / (d - b);
x2 = a + b*y2;
}
}
return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
//---------------------------------------------------------------------------------
// ищем близжайшие точки на линии
//
void EDLines::ComputeClosestPoint(double x1, double y1, double a, double b, int invert, double &xOut, double &yOut)
{
double x2, y2;
if (invert == 0) {
if (b == 0) {
x2 = x1;
y2 = a;
}
else {
double d = -1.0 / (b);
double c = y1 - d*x1;
x2 = (a - c) / (d - b);
y2 = a + b*x2;
}
}
else {
if (b == 0) {
x2 = a;
y2 = y1;
}
else {
double d = -1.0 / (b);
double c = x1 - d*y1;
y2 = (a - c) / (d - b);
x2 = a + b*y2;
}
}
xOut = x2;
yOut = y2;
}
//-----------------------------------------------------------------------------------
// Fits a line of the form y=a+bx (invert == 0) OR x=a+by (invert == 1)
// Assumes that the direction of the line is known by a previous computation
//
void EDLines::LineFit(double * x, double * y, int count, double &a, double &b, int invert)
{
if (count<2) return;
double S = count, Sx = 0.0, Sy = 0.0, Sxx = 0.0, Sxy = 0.0;
for (int i = 0; i max_distance_between_two_lines) return false;
// считаем длину линий. используем максимальную из них
double dx = ls1->sx - ls1->ex;
double dy = ls1->sy - ls1->ey;
double prevLen = sqrt(dx*dx + dy*dy);
dx = ls2->sx - ls2->ex;
dy = ls2->sy - ls2->ey;
double nextLen = sqrt(dx*dx + dy*dy);
// используем максимальную
LineSegment *shorter = ls1;
LineSegment *longer = ls2;
if (prevLen > nextLen) { shorter = ls2; longer = ls1; }
// используем три точки для проверки коллинеарности
dist = ComputeMinDistance(shorter->sx, shorter->sy, longer->a, longer->b, longer->invert);
dist += ComputeMinDistance((shorter->sx + shorter->ex) / 2.0, (shorter->sy + shorter->ey) / 2.0, longer->a, longer->b, longer->invert);
dist += ComputeMinDistance(shorter->ex, shorter->ey, longer->a, longer->b, longer->invert);
dist /= 3.0;
if (dist > max_error) return false;
/// 4 варианта: 1:(s1, s2), 2:(s1, e2), 3:(e1, s2), 4:(e1, e2)
/// 1: (s1, s2)
dx = fabs(ls1->sx - ls2->sx);
dy = fabs(ls1->sy - ls2->sy);
double d = dx + dy;
double max = d;
which = 1;
/// 2: (s1, e2)
dx = fabs(ls1->sx - ls2->ex);
dy = fabs(ls1->sy - ls2->ey);
d = dx + dy;
if (d > max) {
max = d;
which = 2;
}
/// 3: (e1, s2)
dx = fabs(ls1->ex - ls2->sx);
dy = fabs(ls1->ey - ls2->sy);
d = dx + dy;
if (d > max) {
max = d;
which = 3;
}
/// 4: (e1, e2)
dx = fabs(ls1->ex - ls2->ex);
dy = fabs(ls1->ey - ls2->ey);
d = dx + dy;
if (d > max) {
max = d;
which = 4;
}
if (which == 1) {
// (s1, s2)
ls1->ex = ls2->sx;
ls1->ey = ls2->sy;
}
else if (which == 2) {
// (s1, e2)
ls1->ex = ls2->ex;
ls1->ey = ls2->ey;
}
else if (which == 3) {
// (e1, s2)
ls1->sx = ls2->sx;
ls1->sy = ls2->sy;
}
else {
// (e1, e2)
ls1->sx = ls1->ex;
ls1->sy = ls1->ey;
ls1->ex = ls2->ex;
ls1->ey = ls2->ey;
}
// обновляем параметры первой линии
if (ls1->firstPixelIndex + ls1->len + 5 >= ls2->firstPixelIndex) ls1->len += ls2->len;
else if (ls2->len > ls1->len) {
ls1->firstPixelIndex = ls2->firstPixelIndex;
ls1->len = ls2->len;
}
UpdateLineParameters(ls1);
lines[changeIndex] = *ls1;
return true;
}
//-------------------------------------------------------------------------------
// посчитываем минимальное расстояние между концами двух отрезков
//
double EDLines::ComputeMinDistanceBetweenTwoLines(LineSegment * ls1, LineSegment * ls2, int * pwhich)
{
double dx = ls1->sx - ls2->sx;
double dy = ls1->sy - ls2->sy;
double d = sqrt(dx*dx + dy*dy);
double min = d;
int which = SS;
dx = ls1->sx - ls2->ex;
dy = ls1->sy - ls2->ey;
d = sqrt(dx*dx + dy*dy);
if (d < min) { min = d; which = SE; }
dx = ls1->ex - ls2->sx;
dy = ls1->ey - ls2->sy;
d = sqrt(dx*dx + dy*dy);
if (d < min) { min = d; which = ES; }
dx = ls1->ex - ls2->ex;
dy = ls1->ey - ls2->ey;
d = sqrt(dx*dx + dy*dy);
if (d < min) { min = d; which = EE; }
if (pwhich) *pwhich = which;
return min;
}
void EDLines::UpdateLineParameters(LineSegment * ls)
{
double dx = ls->ex - ls->sx;
double dy = ls->ey - ls->sy;
if (fabs(dx) >= fabs(dy)) {
ls->invert = 0;
if (fabs(dy) < 1e-3) { ls->b = 0; ls->a = (ls->sy + ls->ey) / 2; }
else {
ls->b = dy / dx;
ls->a = ls->sy - (ls->b)*ls->sx;
}
}
else {
ls->invert = 1;
if (fabs(dx) < 1e-3) { ls->b = 0; ls->a = (ls->sx + ls->ex) / 2; }
else {
ls->b = dx / dy;
ls->a = ls->sx - (ls->b)*ls->sy;
}
}
}
void EDLines::EnumerateRectPoints(double sx, double sy, double ex, double ey, int ptsx[], int ptsy[], int * pNoPoints)
{
double vxTmp[4], vyTmp[4];
double vx[4], vy[4];
int n, offset;
double x1 = sx;
double y1 = sy;
double x2 = ex;
double y2 = ey;
double width = 2;
double dx = x2 - x1;
double dy = y2 - y1;
double vLen = sqrt(dx*dx + dy*dy);
dx = dx / vLen;
dy = dy / vLen;
vxTmp[0] = x1 - dy * width / 2.0;
vyTmp[0] = y1 + dx * width / 2.0;
vxTmp[1] = x2 - dy * width / 2.0;
vyTmp[1] = y2 + dx * width / 2.0;
vxTmp[2] = x2 + dy * width / 2.0;
vyTmp[2] = y2 - dx * width / 2.0;
vxTmp[3] = x1 + dy * width / 2.0;
vyTmp[3] = y1 - dx * width / 2.0;
if (x1 < x2 && y1 <= y2) offset = 0;
else if (x1 >= x2 && y1 < y2) offset = 1;
else if (x1 > x2 && y1 >= y2) offset = 2;
else offset = 3;
for (n = 0; n<4; n++) {
vx[n] = vxTmp[(offset + n) % 4];
vy[n] = vyTmp[(offset + n) % 4];
}
int x = (int)ceil(vx[0]) - 1;
int y = (int)ceil(vy[0]);
double ys = -DBL_MAX, ye = -DBL_MAX;
int noPoints = 0;
while (1) {
y++;
while (y > ye && x <= vx[2]) {
x++;
if (x > vx[2]) break;
if ((double)x < vx[3]) {
if (fabs(vx[0] - vx[3]) <= 0.01) {
if (vy[0]vy[3]) ys = vy[3];
else ys = vy[0] + (x - vx[0]) * (vy[3] - vy[0]) / (vx[3] - vx[0]);
}
else
ys = vy[0] + (x - vx[0]) * (vy[3] - vy[0]) / (vx[3] - vx[0]);
}
else {
if (fabs(vx[3] - vx[2]) <= 0.01) {
if (vy[3]vy[2]) ys = vy[2];
else ys = vy[3] + (x - vx[3]) * (y2 - vy[3]) / (vx[2] - vx[3]);
}
else
ys = vy[3] + (x - vx[3]) * (vy[2] - vy[3]) / (vx[2] - vx[3]);
}
if ((double)x < vx[1]) {
/* интерполяция */
if (fabs(vx[0] - vx[1]) <= 0.01) {
if (vy[0]vy[1]) ye = vy[0];
else ye = vy[0] + (x - vx[0]) * (vy[1] - vy[0]) / (vx[1] - vx[0]);
}
else
ye = vy[0] + (x - vx[0]) * (vy[1] - vy[0]) / (vx[1] - vx[0]);
}
else {
/* интерполяция */
if (fabs(vx[1] - vx[2]) <= 0.01) {
if (vy[1]vy[2]) ye = vy[1];
else ye = vy[1] + (x - vx[1]) * (vy[2] - vy[1]) / (vx[2] - vx[1]);
}
else
ye = vy[1] + (x - vx[1]) * (vy[2] - vy[1]) / (vx[2] - vx[1]);
}
y = (int)ceil(ys);
}
// условие выхода
if (x > vx[2]) break;
ptsx[noPoints] = x;
ptsy[noPoints] = y;
noPoints++;
}
*pNoPoints = noPoints;
}
int main(int argc, const char * argv[])
{
StackNode n;
n.c=0;
cout << n.c << endl;
Mat imageRGB = imread("/home/siyu/下载/ope/open/1.png");
Mat image = imread("/home/siyu/下载/ope/open/1.png", 0);
EDLines lineHandler(image);
Mat outputImage;
imshow("INPUT IMAGE", imageRGB);
waitKey(WAIT_TIME);
outputImage = lineHandler.getSmoothImage();
imshow("SMOOTHING", outputImage);
waitKey(WAIT_TIME);
outputImage = lineHandler.getGradImage();
imshow("GRADIENT AND THRESHOLDING", outputImage);
waitKey(WAIT_TIME);
outputImage = lineHandler.getAnchorImage();
imshow("ANCHORING AND CONNECTING THEM", outputImage);
waitKey(WAIT_TIME);
outputImage = lineHandler.getEdgeImage();
imshow("EDGES", outputImage);
waitKey(WAIT_TIME);
outputImage = lineHandler.getLineImage();
imshow("ED LINES", outputImage);
waitKey(WAIT_TIME);
outputImage = lineHandler.drawOnImage();
imshow("ED LINES OVER SOURCE IMAGE", outputImage);
waitKey(0);
return 0;
}
运行结果: