Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ std::pair<int, std::vector<Chain *>> Chain::getAllChains(bool only_longest_path)

void Chain::appendAllChains(std::vector<Chain *> &allChains, int &total_length, bool only_longest_path)
{
if (only_longest_path && this->is_extracted)
return;

allChains.push_back(this);
total_length += pixels.size();
if (first_childChain && (!only_longest_path || is_first_childChain_longest_path))
Expand Down
1 change: 0 additions & 1 deletion Chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ struct Chain
Chain *const parent_chain; // Pointer to parent chain (never changes after init)
Chain *first_childChain; // Pointer to left/up child chain
bool is_first_childChain_longest_path; // Flag to indicate if this is the longest path use first child
bool is_extracted = false; // Flag to indicate if this chain has been extracted into a segment
Chain *second_childChain; // Pointer to right/down child chain
const Direction direction; // Direction of this chain (never changes after init)

Expand Down
206 changes: 79 additions & 127 deletions ED-perso.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ bool ED::areNeighbors(int offset1, int offset2)
return (row_distance <= 1 && col_distance <= 1);
}

// We take the last or first pixel of the current processed chain and clean its neighbors in the segment
// We take the last (or first for the first child chain) pixel of the current processed chain and clean its neighbors in the segment
void ED::cleanUpPenultimateSegmentPixel(Chain *chain, std::vector<cv::Point> &anchorSegment, bool is_first_child)
{
if (!chain || chain->pixels.empty())
Expand All @@ -443,134 +443,87 @@ void ED::cleanUpPenultimateSegmentPixel(Chain *chain, std::vector<cv::Point> &an
}
}

// Backward extraction, we start from the end of the latest sub chain and move towards the anchor root
void ED::extractSecondChildChains(Chain *anchor_chain_root, std::vector<Point> &anchorSegment)
// Explore the binary graph by depth first from the children of the anchor root.
// Prune the considered node to its longest path. Add the childnode that is not from longest path to the processed stack
// Call cleanUpPenultimateSegmentPixel with a forward pass, add the chains pixels to the segment before processing to the next stack node
// To reconstruct the main segment, when we arrive to the segment from the second main child, we revert the segment and add it to the first segment.
void ED::extractChainsRecur(Chain *anchor_chain_root, std::vector<std::vector<Point>> &anchorSegments)
{
if (!anchor_chain_root || !anchor_chain_root->second_childChain)
return;
// We assume that main children chains have already been extracted
// Store the pointer to the main children in order to reconstruct the 'main segment'
Chain *main_first_child = anchor_chain_root->first_childChain;
Chain *main_second_child = anchor_chain_root->second_childChain;
bool is_first_pass = true;

std::pair<int, std::vector<Chain *>> resp = anchor_chain_root->second_childChain->getAllChains(true);
std::vector<Chain *> all_second_child_chains_in_longest_path = resp.second;
// Explore the whole chain depth first
std::stack<Chain *> chain_stack;
chain_stack.push(anchor_chain_root);

// iterate through all sub chains in the longest path, clean and add pixels to the anchor segment
for (size_t chain_index = all_second_child_chains_in_longest_path.size() - 1; chain_index > 0; --chain_index)
while (!chain_stack.empty())
{
Chain *chain = all_second_child_chains_in_longest_path[chain_index];
if (!chain || chain->is_extracted)
break;

cleanUpPenultimateSegmentPixel(chain, anchorSegment, false);
Chain *current_chain = chain_stack.top();
chain_stack.pop();

// add the chain pixels to the anchor segment
for (int pixel_index = (int)chain->pixels.size() - 1; pixel_index >= 0; --pixel_index)
{
int pixel_offset = chain->pixels[pixel_index];
anchorSegment.push_back(Point(pixel_offset % image_width, pixel_offset / image_width));
}
chain->is_extracted = true;
}
}
if (current_chain == nullptr)
continue;

// Forward extraction, we start from the anchor root, and go deeper
void ED::extractFirstChildChains(Chain *anchor_chain_root, std::vector<Point> &anchorSegment)
{
if (!anchor_chain_root || !anchor_chain_root->first_childChain)
return;
// Compute the longest path from the current chain
int nb_pixels_longest_path = current_chain->pruneToLongestChain();

std::pair<int, std::vector<Chain *>> resp = anchor_chain_root->first_childChain->getAllChains(true);
std::vector<Chain *> all_first_child_chains_in_longest_path = resp.second;
// add the child that is not part of the longest path in the stack
if (current_chain->is_first_childChain_longest_path)
chain_stack.push(current_chain->second_childChain);
else
chain_stack.push(current_chain->first_childChain);

for (size_t chain_index = 0; chain_index < all_first_child_chains_in_longest_path.size(); ++chain_index)
{
Chain *chain = all_first_child_chains_in_longest_path[chain_index];
if (!chain || chain->is_extracted)
break;
// Skip the extraction of the main children chains (already extracted) or if the longest path is too short
if (nb_pixels_longest_path < minPathLen)
continue;

cleanUpPenultimateSegmentPixel(chain, anchorSegment, true);
// Extraction of the longest path of the current chain into a new segment
std::pair<int, std::vector<Chain *>> all_chains_resp = current_chain->getAllChains(true);
std::vector<Chain *> chainChilds_in_longest_path = all_chains_resp.second;

for (size_t pixel_index = 0; pixel_index < chain->pixels.size(); ++pixel_index)
// Initialize a segment that will hold the points of the current chain longest path
std::vector<Point> currentSegment;
// Extract the pixels and add them to the current segment
for (size_t chain_index = 0; chain_index < chainChilds_in_longest_path.size(); ++chain_index)
{
int pixel_offset = chain->pixels[pixel_index];
anchorSegment.push_back(Point(pixel_offset % image_width, pixel_offset / image_width));
}
chain->is_extracted = true;
}
}
Chain *child_chain = chainChilds_in_longest_path[chain_index];
if (!child_chain)
break;

// Extract the remaining significant chains that are not part of the main anchor segment
void ED::extractOtherChains(Chain *anchor_chain_root, std::vector<std::vector<Point>> &anchorSegments)
{
if (!anchor_chain_root)
return;
cleanUpPenultimateSegmentPixel(child_chain, currentSegment, true);

std::pair<int, std::vector<Chain *>> resp_all = anchor_chain_root->getAllChains(false);
// This is all chains in the anchor root, traversed depth-first adding the first child first.
std::vector<Chain *> all_anchor_root_chains = resp_all.second;
for (size_t pixel_index = 0; pixel_index < child_chain->pixels.size(); ++pixel_index)
currentSegment.push_back(Point(child_chain->pixels[pixel_index] % image_width, child_chain->pixels[pixel_index] / image_width));
}

// Start the iteration from the anchor root and go deeper
for (size_t k = 0; k < all_anchor_root_chains.size(); ++k)
{
Chain *other_chain = all_anchor_root_chains[k];
if (!other_chain)
if (currentSegment.empty())
{
is_first_pass = false;
continue;
}

std::vector<Point> otherAnchorSegment;
other_chain->pruneToLongestChain();

std::pair<int, std::vector<Chain *>> other_resp = other_chain->getAllChains(true);
int other_chain_total_length = other_resp.first;
std::vector<Chain *> other_chain_chainChilds_in_longest_path = other_resp.second;

if (other_chain_total_length < minPathLen)
continue;
// If the current segment is not empty, add it to the anchor segments or to the main segment for the main childs
// Clean possible boucle at the beginning of the segment
if (currentSegment.size() > 1 && areNeighbors(currentSegment[1].y * image_width + currentSegment[1].x,
currentSegment.back().y * image_width + currentSegment.back().x))
currentSegment.erase(currentSegment.begin());

for (size_t chain_index = 0; chain_index < other_chain_chainChilds_in_longest_path.size(); ++chain_index)
if ((current_chain == main_first_child || current_chain == main_second_child) && !is_first_pass)
{
Chain *other_chain_childChain = other_chain_chainChilds_in_longest_path[chain_index];
if (!other_chain_childChain || other_chain_childChain->is_extracted)
break;

cleanUpPenultimateSegmentPixel(other_chain_childChain, otherAnchorSegment, true);

for (size_t pixel_index = 0; pixel_index < other_chain_childChain->pixels.size(); ++pixel_index)
{
int pixel_offset = other_chain_childChain->pixels[pixel_index];
otherAnchorSegment.push_back(Point(pixel_offset % image_width, pixel_offset / image_width));
}
other_chain_childChain->is_extracted = true;
// Revert the order of the main child segment and add it to the first segment
std::reverse(currentSegment.begin(), currentSegment.end());
anchorSegments[0].insert(anchorSegments[0].end(), currentSegment.begin(), currentSegment.end());
}
else
anchorSegments.push_back(currentSegment);

if (!otherAnchorSegment.empty())
anchorSegments.push_back(otherAnchorSegment);
is_first_pass = false;
}
}

void ED::extractSegmentsFromChain(Chain *anchor_chain_root, std::vector<std::vector<Point>> &anchorSegments)
{
if (!anchor_chain_root)
return;

std::vector<Point> anchorSegment;

// second child (backward)
extractSecondChildChains(anchor_chain_root, anchorSegment);

// first child (forward)
extractFirstChildChains(anchor_chain_root, anchorSegment);

// Clean possible boucle at the beginning of the segment
if (anchorSegment.size() > 1 && areNeighbors(anchorSegment[1].y * image_width + anchorSegment[1].x,
anchorSegment.back().y * image_width + anchorSegment.back().x))
anchorSegment.erase(anchorSegment.begin());

// Add the main anchor segment to the anchor segments (only if non-empty)
if (!anchorSegment.empty())
anchorSegments.push_back(anchorSegment);

// other long segments attached to anchor root
extractOtherChains(anchor_chain_root, anchorSegments);
}

void ED::JoinAnchorPointsUsingSortedAnchors()
{
int *SortedAnchors = sortAnchorsByGradValue();
Expand All @@ -582,31 +535,27 @@ void ED::JoinAnchorPointsUsingSortedAnchors()
if (edgeImgPointer[anchorPixelOffset] != ANCHOR_PIXEL)
continue;

int total_pixels_in_anchor_chain = 0; // Count total pixels in the anchor chain and its children

Chain *anchor_chain_root = new Chain();
// We initialize two distinct nodes to start anchor chain exploration in order to avoid duplicate pixels from the start.
// This is not done in the original implementation where we set the anchor point two times
if (gradOrientationImgPointer[anchorPixelOffset] == EDGE_VERTICAL)
{
process_stack.push(StackNode(anchorPixelOffset, DOWN, anchor_chain_root));
process_stack.push(StackNode(anchorPixelOffset - image_width, UP, anchor_chain_root));
process_stack.push(StackNode(anchorPixelOffset, UP, anchor_chain_root));
}
else
{
process_stack.push(StackNode(anchorPixelOffset, RIGHT, anchor_chain_root));
process_stack.push(StackNode(anchorPixelOffset - 1, LEFT, anchor_chain_root));
process_stack.push(StackNode(anchorPixelOffset, LEFT, anchor_chain_root));
}

int total_pixels_in_anchor_chain = -1; // Count total pixels in the anchor chain and its children. Start at one for the duplicate anchor.

while (!process_stack.empty())
{
StackNode currentNode = process_stack.top();
process_stack.pop();

// processed stack pixel are in two chains in opposite directions, we track duplicates
if (edgeImgPointer[currentNode.offset] != EDGE_PIXEL)
total_pixels_in_anchor_chain--;

Chain *new_process_stack_chain = new Chain(currentNode.node_direction, currentNode.parent_chain);
setChildToChain(new_process_stack_chain->parent_chain, new_process_stack_chain);
// Explore from the stack node to add more pixels to the new created chain
Expand All @@ -622,7 +571,7 @@ void ED::JoinAnchorPointsUsingSortedAnchors()
anchor_chain_root->second_childChain->pruneToLongestChain();
// Create a segment corresponding to this anchor chain
std::vector<std::vector<Point>> anchorSegments;
extractSegmentsFromChain(anchor_chain_root, anchorSegments);
extractChainsRecur(anchor_chain_root, anchorSegments);
segmentPoints.insert(segmentPoints.end(), anchorSegments.begin(), anchorSegments.end());
}

Expand All @@ -632,10 +581,10 @@ void ED::JoinAnchorPointsUsingSortedAnchors()
}

// Clean pixel perpendicular to edge direction
void ED::cleanUpSurroundingAnchorPixels(StackNode &current_node)
void ED::cleanUpSurroundingAnchorPixels(const StackNode &current_node)
{
int offset = current_node.offset;
int offset_diff = (current_node.node_direction == LEFT || current_node.node_direction == RIGHT) ? image_width : 1;
int offset_diff = gradOrientationImgPointer[offset] == EDGE_HORIZONTAL ? image_width : 1;

// Left/up neighbor
if (edgeImgPointer[offset - offset_diff] == ANCHOR_PIXEL)
Expand All @@ -646,7 +595,7 @@ void ED::cleanUpSurroundingAnchorPixels(StackNode &current_node)
}

// Get next pixel in the chain based on current node direction and gradient values
StackNode ED::getNextChainPixel(StackNode &current_node)
StackNode ED::getNextChainPixel(const StackNode &current_node)
{
const int offset = current_node.offset;
const Direction dir = current_node.node_direction;
Expand Down Expand Up @@ -685,7 +634,6 @@ StackNode ED::getNextChainPixel(StackNode &current_node)

void ED::exploreChain(StackNode &current_node, Chain *current_chain, int &total_pixels_in_anchor_chain)
{

GradOrientation chain_orientation = current_chain->direction == LEFT || current_chain->direction == RIGHT ? EDGE_HORIZONTAL : EDGE_VERTICAL;
// Explore until we find change direction or we hit an edge pixel or the gradient is below threshold
while (gradOrientationImgPointer[current_node.offset] == chain_orientation)
Expand Down Expand Up @@ -715,18 +663,22 @@ void ED::exploreChain(StackNode &current_node, Chain *current_chain, int &total_
{
// Add UP and DOWN for horizontal chains if the pixels are valid
// The border pixels were set to a low gradient threshold, so we do not need to check for out of bounds access
if (edgeImgPointer[current_node.offset + image_width] == EDGE_PIXEL || gradImgPointer[current_node.offset + image_width] < gradThresh)
process_stack.push(StackNode(current_node.offset, DOWN, current_chain));
if (edgeImgPointer[current_node.offset - image_width] == EDGE_PIXEL || gradImgPointer[current_node.offset - image_width] < gradThresh)
process_stack.push(StackNode(current_node.offset, UP, current_chain));
StackNode up_next_node = getNextChainPixel(StackNode(current_node.offset, UP, current_chain));
StackNode down_next_node = getNextChainPixel(StackNode(current_node.offset, DOWN, current_chain));
if (edgeImgPointer[down_next_node.offset] != EDGE_PIXEL && gradImgPointer[down_next_node.offset] >= gradThresh)
process_stack.push(down_next_node);
if (edgeImgPointer[up_next_node.offset] != EDGE_PIXEL && gradImgPointer[up_next_node.offset] >= gradThresh)
process_stack.push(up_next_node);
}
else
{
StackNode left_next_node = getNextChainPixel(StackNode(current_node.offset, LEFT, current_chain));
StackNode right_next_node = getNextChainPixel(StackNode(current_node.offset, RIGHT, current_chain));
// Add LEFT and RIGHT for vertical chains
if (edgeImgPointer[current_node.offset + 1] == EDGE_PIXEL || gradImgPointer[current_node.offset + 1] < gradThresh)
process_stack.push(StackNode(current_node.offset, RIGHT, current_chain));
if (edgeImgPointer[current_node.offset - 1] == EDGE_PIXEL || gradImgPointer[current_node.offset - 1] < gradThresh)
process_stack.push(StackNode(current_node.offset, LEFT, current_chain));
if (edgeImgPointer[left_next_node.offset] != EDGE_PIXEL && gradImgPointer[left_next_node.offset] >= gradThresh)
process_stack.push(left_next_node);
if (edgeImgPointer[right_next_node.offset] != EDGE_PIXEL && gradImgPointer[right_next_node.offset] >= gradThresh)
process_stack.push(right_next_node);
}
}

Expand Down
9 changes: 3 additions & 6 deletions ED-perso.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,11 @@ class ED
void ComputeAnchorPoints();
void JoinAnchorPointsUsingSortedAnchors();
void exploreChain(StackNode &current_node, Chain *current_chain, int &total_pixels_in_anchor_chain);
void extractSegmentsFromChain(Chain *chain, std::vector<std::vector<cv::Point>> &anchorSegments);
void extractSecondChildChains(Chain *anchor_chain_root, std::vector<cv::Point> &anchorSegment);
void extractFirstChildChains(Chain *anchor_chain_root, std::vector<cv::Point> &anchorSegment);
void extractOtherChains(Chain *anchor_chain_root, std::vector<std::vector<cv::Point>> &anchorSegments);
void extractChainsRecur(Chain *anchor_chain_root, std::vector<std::vector<cv::Point>> &anchorSegments);
int *sortAnchorsByGradValue();

void cleanUpSurroundingAnchorPixels(StackNode &current_node);
StackNode getNextChainPixel(StackNode &current_node);
void cleanUpSurroundingAnchorPixels(const StackNode &current_node);
StackNode getNextChainPixel(const StackNode &current_node);
bool areNeighbors(int offset1, int offset2);
void cleanUpPenultimateSegmentPixel(Chain *chain, std::vector<cv::Point> &anchorSegment, bool is_first_child);
void revertChainEdgePixel(Chain *&chain);
Expand Down
2 changes: 1 addition & 1 deletion test_ED.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ int main(int argc, char **argv)
// -------------------------------
tm.reset();
tm.start();
ED testED = ED(testImg, LSD_OPERATOR, 6, 2, 10, 1.0, true);
ED testED = ED(testImg, LSD_OPERATOR, 6, 0, 10, 1.0, true);
tm.stop();
double newEdTime = tm.getTimeMilli();
cout << "New ED Implementation : " << newEdTime << " ms" << endl;
Expand Down
Loading