I am looking for an OpenCV function that can find connected components and perform a few tasks on them ( like getting the number of pixels, contour, list of pixels in the object etc.. )
Is there a function of OpenCV (C++) that is similar to MatLab's regionprops ?
Starting from version 3.0, OpenCV has connectedComponents function.
Have a look at the cvFindContours function. It's very versatile -- it can find both interior and exterior contours, and return the results in a variety of formats (e.g. flat list vs. tree structure). Once you have the contours, functions like cvContourArea allow you to determine basic properties of the connected component corresponding to a particular contour.
If you prefer to use the newer C++ interface (as opposed to the older C-style interface I described above), then the function names are similar.
set -std=c++0x option when compiling
.h file
//connected_components.h
#ifndef CONNECTED_COMPONENTS_H_
#define CONNECTED_COMPONENTS_H_
#include <opencv2/core/core.hpp>
#include <memory>
class DisjointSet {
private:
std::vector<int> m_disjoint_array;
int m_subset_num;
public:
DisjointSet();
DisjointSet(int size);
~DisjointSet();
int add(); //add a new element, which is a subset by itself;
int find(int x); //return the root of x
void unite(int x, int y);
int getSubsetNum(void);
};
class ConnectedComponent {
private:
cv::Rect m_bb;
int m_pixel_count;
std::shared_ptr< std::vector<cv::Point2i> > m_pixels;
public:
ConnectedComponent();
ConnectedComponent(int x, int y);
~ConnectedComponent();
void addPixel(int x, int y);
int getBoundingBoxArea(void) const;
cv::Rect getBoundingBox(void) const;
int getPixelCount(void) const;
std::shared_ptr< const std::vector<cv::Point2i> > getPixels(void) const;
};
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc);
#endif //CONNECTED_COMPONENTS_H_
.cc file
//connected_components.cpp
#include "connected_components.h"
using namespace std;
/** DisjointSet **/
DisjointSet::DisjointSet() :
m_disjoint_array(),
m_subset_num(0)
{ }
DisjointSet::DisjointSet(int size) :
m_disjoint_array(),
m_subset_num(0)
{
m_disjoint_array.reserve(size);
}
DisjointSet::~DisjointSet()
{ }
//add a new element, which is a subset by itself;
int DisjointSet::add()
{
int cur_size = m_disjoint_array.size();
m_disjoint_array.push_back(cur_size);
m_subset_num ++;
return cur_size;
}
//return the root of x
int DisjointSet::find(int x)
{
if (m_disjoint_array[x] < 0 || m_disjoint_array[x] == x)
return x;
else {
m_disjoint_array[x] = this->find(m_disjoint_array[x]);
return m_disjoint_array[x];
}
}
// point the x and y to smaller root of the two
void DisjointSet::unite(int x, int y)
{
if (x==y) {
return;
}
int xRoot = find(x);
int yRoot = find(y);
if (xRoot == yRoot)
return;
else if (xRoot < yRoot) {
m_disjoint_array[yRoot] = xRoot;
}
else {
m_disjoint_array[xRoot] = yRoot;
}
m_subset_num--;
}
int DisjointSet::getSubsetNum()
{
return m_subset_num;
}
/** ConnectedComponent **/
ConnectedComponent::ConnectedComponent() :
m_bb(0,0,0,0),
m_pixel_count(0),
m_pixels()
{
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
}
ConnectedComponent::ConnectedComponent(int x, int y) :
m_bb(x,y,1,1),
m_pixel_count(1),
m_pixels()
{
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
}
ConnectedComponent::~ConnectedComponent(void)
{ }
void ConnectedComponent::addPixel(int x, int y) {
m_pixel_count++;
// new bounding box;
if (m_pixel_count == 0) {
m_bb = cv::Rect(x,y,1,1);
}
// extend bounding box if necessary
else {
if (x < m_bb.x ) {
m_bb.width+=(m_bb.x-x);
m_bb.x = x;
}
else if ( x > (m_bb.x+m_bb.width) ) {
m_bb.width=(x-m_bb.x);
}
if (y < m_bb.y ) {
m_bb.height+=(m_bb.y-y);
m_bb.y = y;
}
else if ( y > (m_bb.y+m_bb.height) ) {
m_bb.height=(y-m_bb.y);
}
}
m_pixels->push_back(cv::Point(x,y));
}
int ConnectedComponent::getBoundingBoxArea(void) const {
return (m_bb.width*m_bb.height);
}
cv::Rect ConnectedComponent::getBoundingBox(void) const {
return m_bb;
}
std::shared_ptr< const std::vector<cv::Point2i> > ConnectedComponent::getPixels(void) const {
return m_pixels;
}
int ConnectedComponent::getPixelCount(void) const {
return m_pixel_count;
}
/** find connected components **/
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc) {
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = src.total();
int frame_label[total_pix];
DisjointSet labels(total_pix);
int root_map[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val;
int cur_idx, left_idx, up_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++ ) {
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++) {
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
if ( x == 0)
left_val = 0;
else
left_val = cur_p[x-1];
if (y == 0)
up_val = 0;
else
up_val = prev_p[x];
if (cur_p[x] > 0) {
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0) {
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
}
//current pixel is foreground and has left neighbor connected
else if (left_val != 0 && up_val == 0) {
frame_label[cur_idx] = frame_label[left_idx];
}
//current pixel is foreground and has up neighbor connect
else if (up_val != 0 && left_val == 0) {
frame_label[cur_idx] = frame_label[up_idx];
}
//current pixel is foreground and is connected to left and up neighbors
else {
frame_label[cur_idx] = (frame_label[left_idx] > frame_label[up_idx]) ? frame_label[up_idx] : frame_label[left_idx];
labels.unite(frame_label[left_idx], frame_label[up_idx]);
}
}//endif
else {
frame_label[cur_idx] = -1;
}
} //end for x
prev_p = cur_p;
}//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++ ) {
for (x = 0; x < src.size().width; x++, cur_idx++) {
curLabel = frame_label[cur_idx];
if (curLabel != -1) {
curLabel = labels.find(curLabel);
if( root_map[curLabel] != -1 ) {
cc[root_map[curLabel]].addPixel(x, y);
}
else {
cc.push_back(ConnectedComponent(x,y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
}
}
}//end for x
}//end for y
}
If you don't mind using an external library that uses OpenCV, you can do that using cvBlobsLib.
A library to perform binary images connected component labelling
(similar to regionprops Matlab function). It also provides functions
to manipulate, filter and extract results from the extracted blobs,
see features section for more information.
You can use cv::connectedComponentsWithStats() function.
Here is an example.
// ...
cv::Mat labels, stats, centroids;
int connectivity = 8; // or 4
int label_count = cv::connectedComponentsWithStats(src, labels, stats, centroids, connectivity);
for (int i = 0; i < label_count; i++)
{
int x = stats.at<int>(i, cv::CC_STAT_LEFT);
int y = stats.at<int>(i, cv::CC_STAT_TOP);
int w = stats.at<int>(i, cv::CC_STAT_WIDTH);
int h = stats.at<int>(i, cv::CC_STAT_HEIGHT);
int area = stats.at<int>(i, cv::CC_STAT_AREA);
double cx = centroids.at<double>(i, 0);
double cy = centroids.at<double>(i, 1);
// ...
}
Following DXM's code above which assumes 4-connected components, here is a version for 'findCC' that detects 8-connected components.
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc) {
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = int(src.total());
int *frame_label = new int[total_pix];
DisjointSet labels(total_pix);
int *root_map = new int[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val, up_left_val, up_right_val;
int cur_idx, left_idx, up_idx, up_left_idx, up_right_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++) {
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++) {
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
up_left_idx = up_idx - 1;
up_right_idx = up_idx + 1;
if (x == 0)
{
left_val = 0;
}
else
{
left_val = cur_p[x - 1];
}
if (y == 0)
{
up_val = 0;
}
else
{
up_val = prev_p[x];
}
if (x == 0 || y == 0)
{
up_left_val = 0;
}
else
{
up_left_val = prev_p[x-1];
}
if (x == src.cols - 1 || y == 0)
{
up_right_val = 0;
}
else
{
up_right_val = prev_p[x+1];
}
if (cur_p[x] > 0) {
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0 && up_left_val == 0 && up_right_val == 0) {
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
}
//Current pixel is foreground and has at least one neighbor
else
{
vector<int> frame_lbl;
frame_lbl.reserve(4);
//Find minimal label
int min_frame_lbl = INT_MAX;
int valid_entries_num = 0;
if (left_val != 0)
{
frame_lbl.push_back(frame_label[left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[left_idx]);
valid_entries_num++;
}
if (up_val != 0)
{
frame_lbl.push_back(frame_label[up_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_idx]);
valid_entries_num++;
}
if (up_left_val != 0)
{
frame_lbl.push_back(frame_label[up_left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_left_idx]);
valid_entries_num++;
}
if (up_right_val != 0)
{
frame_lbl.push_back(frame_label[up_right_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_right_idx]);
valid_entries_num++;
}
CV_Assert(valid_entries_num > 0);
frame_label[cur_idx] = min_frame_lbl;
//Unite if necessary
if (valid_entries_num > 1)
{
for (size_t i = 0; i < frame_lbl.size(); i++)
{
labels.unite(frame_lbl[i], min_frame_lbl);
}
}
}
}//endif
else {
frame_label[cur_idx] = -1;
}
} //end for x
prev_p = cur_p;
}//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++) {
for (x = 0; x < src.size().width; x++, cur_idx++) {
curLabel = frame_label[cur_idx];
if (curLabel != -1) {
curLabel = labels.find(curLabel);
if (root_map[curLabel] != -1) {
cc[root_map[curLabel]].addPixel(x, y);
}
else {
cc.push_back(ConnectedComponent(x, y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
}
}
}//end for x
}//end for y
//Free up allocated memory
delete[] frame_label;
delete[] root_map;
}
Related
I am encountering an issue with my code. I aim to generate three seeds at different locations in the image and observe the pixels growing. I am using the onMouse function to select the areas, however, only the last point is being recognized. What changes do I need to make for this code to function correctly?
Pont seed,seed2,seed3;
bool clicked = false;
void onmouse(int event, int x, int y, int flags, void* userdata)
{
if (event == EVENT_LBUTTONDOWN) {
seed = Point2i(y, x);
seed2 = Point2i(y, x);
seed3 = Point2i(y, x);
clicked = true;
}
}
int main() {
string path = "C:/img_4.jpg";
Mat im_gray = imread(path, IMREAD_GRAYSCALE);
Mat im_new;
int threshold = 50;
imshow("Image", im_gray);
setMouseCallback("Image", onmouse, NULL);
while (!clicked) {
waitKey(0);
}
regionGrowing_teste1(im_gray, im_new, seed, seed2, seed3, threshold);
imshow("Segmented", im_new);
waitKey(0);
return 0;
}
Mat regionGrowing_teste1(Mat& src, Mat& dst, Point2i seed1, Point2i seed2, Point2i seed3, int threshold) {
dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
vector<Point2i> queue1, queue2, queue3;
queue1.push_back(seed1);
queue2.push_back(seed2);
queue3.push_back(seed3);
int intensity1 = (int)src.at<uchar>(seed1);
int intensity2 = (int)src.at<uchar>(seed2);
int intensity3 = (int)src.at<uchar>(seed3);
int x, y;
while (!queue1.empty() || !queue2.empty() || !queue3.empty()) {
if (!queue1.empty()) {
Point2i p = queue1.back();
queue1.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity1 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue1.push_back(Point2i(x + 1, y));
queue1.push_back(Point2i(x - 1, y));
queue1.push_back(Point2i(x, y + 1));
queue1.push_back(Point2i(x, y - 1));
}
}
}
}
if (!queue2.empty()) {
Point2i p = queue2.back();
queue2.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity2 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue2.push_back(Point2i(x + 1, y));
queue2.push_back(Point2i(x - 1, y));
queue2.push_back(Point2i(x, y + 1));
queue2.push_back(Point2i(x, y - 1));
}
}
}
}
// process points in queue3
if (!queue3.empty()) {
Point2i p = queue3.back();
queue3.pop_back();
x = p.x; y = p.y;
if (x > 0 && x < src.rows - 1 && y > 0 && y < src.cols - 1) {
if ((int)dst.at<uchar>(x, y) == 0) {
int intensityNeighbor = (int)src.at<uchar>(x, y);
if (abs(intensity3 - intensityNeighbor) < threshold) {
dst.at<uchar>(x, y) = 255;
queue3.push_back(Point2i(x + 1, y));
queue3.push_back(Point2i(x - 1, y));
queue3.push_back(Point2i(x, y + 1));
queue3.push_back(Point2i(x, y - 1));
}
}
}
}
}
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
if (dst.at<uchar>(i, j) == 0) {
dst.at<uchar>(i, j) = src.at<uchar>(i, j);
}
}
}
return dst;
}
I hope to see the placement of three points and watch them grow.
Hi i'm somewhat new with c++ and openframeworks and I'm trying to get my program running more smooth by erasing the element from the vector when it goes out of the screen. At the moment as the time goes on it still gets slower and slower. I'm breaking my head over this. If you have any idea how I could improve this.
bool isDead(Particle &p) {
if (p.position.x > ofGetWindowWidth() || p.position.x == -1 || p.position.y > ofGetWindowHeight() || p.position.y == -1) {
return true;
}
else {
return false;
}
}
//--------------------------------------------------------------
void ofApp::setup() {
ofSetFrameRate(60);
ofEnableAlphaBlending();
ofBackground(ofColor::black);
for (int i = 0; i < 50; i++) {
Particle p;
p.setup(ofVec2f(ofRandom(ofGetWindowWidth() * 0.45, ofGetWindowWidth() * 0.55), ofRandom(ofGetWindowHeight() * 0.45, ofGetWindowHeight() * 0.55)));
particles.push_back(p);
}
beat1.load("beat1.wav");
beat2.load("beat2.wav");
beat3.load("beat3.wav");
fftSmooth = new float[8192];
for (int i = 0; i < 8192; i++) {
fftSmooth[i] = 0;
}
bands = 128;
beat1.setVolume(0.2);
beat2.setVolume(0.2);
beat3.setVolume(0.2);
}
//--------------------------------------------------------------
void ofApp::update() {
ofSoundUpdate();
float * value = ofSoundGetSpectrum(bands);
for (int i = 0; i < bands; i++) {
fftSmooth[i] *= 0.2f;
if (fftSmooth[i] < value[i]) {
fftSmooth[i] = value[i];
}
}
ofRemove(particles, isDead);
for (Particle& p : particles) {
if (!p.isActive) {
p.position = ofVec2f(ofRandom(ofGetWindowWidth() * 0.45, ofGetWindowWidth() * 0.55), ofRandom(ofGetWindowHeight() * 0.45, ofGetWindowHeight() * 0.55));
p.isActive = true;
return;
}
p.update();
}
if (isDead) {
Particle p;
p.setup(ofVec2f(0, 0));
particles.push_back(p);
}
}
//--------------------------------------------------------------
void ofApp::draw() {
for (Particle& p1 : particles) {
if (!p1.isActive) continue;
bool foundConnection = false;
//search for connections.
for (Particle& p2 : particles) {
if (!p2.isActive || p2.drawPosition == p1.drawPosition) continue;
float distance = p1.drawPosition.distance(p2.drawPosition);
for (int i = 0; i < bands; i++) {
if (distance > 10 && distance < 50 * fftSmooth[i]) {
ofDrawLine(p1.drawPosition, p2.drawPosition);
foundConnection = true;
}
}
}
for (int i = 0; i < 50; i++) {
p1.draw(-(fftSmooth[i] * 10));
}
}
}
Run-Time Check Failure #2 Stack around the variable 'maze' were corrupted.
Whenever I compile and run my program, I receive this error whenever the program finishes running. I believe the problem is happening in my addPaths function in my implementation. I posted all my code just in case. This program is creating a maze and the addPaths function is "digging" the paths for the user to move through. The path direction is chosen at random and the paths are only drawn at even number spaces on the maze.
HEADER:
const int HEIGHT = 3;
const int WIDTH = 5;
class Coordinate
{
public:
int row, column;
};
class Maze
{
public:
Maze();
~Maze();
void buildMaze();
void displayMaze();
void addPaths();
void startGame();
void movePlayer(int);
bool solved();
int getKey();
void addDestinationToGrid();
private:
char grid[WIDTH][HEIGHT];
Coordinate player;
const static char PLAYER = 'P';
const static char DESTINATION = 'X';
const static char START = 'S';
const static char PATH = ' ';
const static char WALL = (char)219;
};
IMPLEMENTATION:
#include <iostream>
#include "maze.h"
#include <windows.h>
#include <stack>
using std::cout;
using std::endl;
Maze::Maze()
{
buildMaze();
}
Maze::~Maze()
{
cout << "yay";
}
void Maze::buildMaze()
{
for (int x = 0; x <= HEIGHT-1; x++)
{
for (int y = 0; y <= WIDTH-1; y++)
{
grid[x][y] = WALL;
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
}
}
}
void Maze::displayMaze()
{
for (int x = 0; x <= HEIGHT-1; x++)
{
for (int y = 0; y <= WIDTH-1; y++)
{
cout << grid[x][y];
}
cout << endl;
}
}
void Maze::startGame()
{
int input;
do
{
input = getKey();
movePlayer(input);
} while (!solved());
}
bool Maze::solved()
{
return true;
}
void Maze::movePlayer(int direction)
{
if (direction == VK_UP || direction == VK_DOWN || direction == VK_LEFT || direction == VK_RIGHT)
{
COORD newCoord = { player.column + 1, player.row + 1 };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), newCoord);
if (grid[player.row][player.column] == START)
{
cout << START;
}
else
{
cout << PATH;
}
}
}
int Maze::getKey()
{
int result = 0;
while (!solved() && result == 0)
{
short MAX_SHORT = 0x7FFF; //111111111111111
if (GetAsyncKeyState(VK_LEFT) & MAX_SHORT)
{
result = VK_LEFT;
}
else if (GetAsyncKeyState(VK_UP) & MAX_SHORT)
{
result = VK_UP;
}
else if (GetAsyncKeyState(VK_RIGHT) & MAX_SHORT)
{
result = VK_RIGHT;
}
else if (GetAsyncKeyState(VK_DOWN) & MAX_SHORT)
{
result = VK_DOWN;
}
}
return result;
}
void Maze::addPaths()
{
Coordinate currentLocation;
Coordinate startLocation;
//Coordinate endLocation; //not used yet
std::stack<Coordinate> myStack;
currentLocation.row = (((rand() % HEIGHT) / 2) * 2);
currentLocation.column = (((rand() % WIDTH) / 2) * 2);
startLocation = currentLocation;
grid[currentLocation.row][currentLocation.column] = START;
player = currentLocation;
do
{
bool canMoveUp = !(currentLocation.row == 0 || grid[currentLocation.row - 2][currentLocation.column] != WALL);
bool canMoveDown = !(currentLocation.row == HEIGHT - 1 || grid[currentLocation.row + 2][currentLocation.column] != WALL);
bool canMoveLeft = !(currentLocation.column == 0 || grid[currentLocation.row][currentLocation.column - 2] != WALL);
bool canMoveRight = !(currentLocation.column == WIDTH - 1 || grid[currentLocation.row][currentLocation.column + 2] != WALL);
if (canMoveUp || canMoveDown || canMoveLeft || canMoveRight)
{
myStack.push(currentLocation);
//choose random location to dig
bool moveFound = false;
while (moveFound != true)
{
int direction = rand() % 4;
if (direction == 0 && canMoveUp)
{
moveFound = true;
grid[currentLocation.row - 2][currentLocation.column] = PATH;
grid[currentLocation.row - 1][currentLocation.column] = PATH;
currentLocation.row -= 2;
}
else if (direction == 1 && canMoveDown)
{
moveFound = true;
grid[currentLocation.row + 2][currentLocation.column] = PATH;
grid[currentLocation.row + 1][currentLocation.column] = PATH;
currentLocation.row += 2;
}
else if (direction == 2 && canMoveLeft)
{
moveFound = true;
grid[currentLocation.row][currentLocation.column - 2] = PATH;
grid[currentLocation.row][currentLocation.column - 1] = PATH;
currentLocation.column -= 2;
}
else if (direction == 3 && canMoveRight)
{
moveFound = true;
grid[currentLocation.row][currentLocation.column + 2] = PATH;
grid[currentLocation.row][currentLocation.column - 2] = PATH;
currentLocation.column += 2;
}
}
}
else if (!myStack.empty())
{
currentLocation = myStack.top();
myStack.pop();
}
}
while (!myStack.empty());
addDestinationToGrid();
}
void Maze::addDestinationToGrid()
{
int randomRow = rand() % HEIGHT;
int randomColumn = rand() % WIDTH;
while (grid[randomRow][randomColumn] != PATH)
{
randomRow = rand() % HEIGHT;
randomColumn = rand() % WIDTH;
}
grid[randomRow][randomColumn] = DESTINATION;
}
MAIN:
#include <iomanip>
#include <iostream>
#include "maze.h"
using namespace std;
int main()
{
Maze maze;
maze.addPaths();
maze.displayMaze();
maze.startGame();
/*if (maze.solved())
{
cout << " You Win!";
}*/
}
There are two problems. One is that you are not consistent in the order you access elements of grid. It is declared grid[WIDTH][HEIGHT] but most (but not all) accesses use a HEIGHT based index first. This isn't causing your problems since WIDTH is greater than HEIGHT and you stay within the object's memory when doing normal accesses to it.
The problem is this line:
grid[currentLocation.row][currentLocation.column - 2] = PATH;
in the moveRight handler. The column offset should be + 1, not - 2. The way it is can cause you to write to memory before the first element of grid.
bool cancelFlag = true;
bool useUserSelectionForSubplugs = false;
bool allowMultipleDevicesInOneDieFlag = false;
int diesToSearchX, diesToSearchY;
diesToSearchX = diesToSearchY = 2;
PBEdgeInterpolationWindow *edgeInterpolationWindow = new PBEdgeInterpolationWindow(&diesToSearchX, &diesToSearchY, &useUserSelectionForSubplugs, &allowMultipleDevicesInOneDieFlag, &cancelFlag);
edgeInterpolationWindow->exec();
if(cancelFlag) return;
if(fSelectedDeviceList.empty())
{
QMessageBox::warning(this, tr("Edge Crown Creation"), tr("Nothing to create crown with: only the devices in Selected Devices List will be considered for this operation\nPlease add devices to the \"Selected Devices\" list"));
return;
}
PBWaferMapRevision *pendingRevision = fData->fPending_Revision;
if(!pendingRevision)
{
QMessageBox::warning(this, tr("Edge Crown Creation"), tr("Revision to be edited needs to be a PENDING revision"));
return;
}
struct WMDeviceForCrown
{
double x, y;
double wrtShotX, wrtShotY;
int crownShotRow, crownShotCol;
WaferDevice *WaferDevicePointer;
};
std::vector<WMDeviceForCrown> DeviceForCrownList;
PBProductData *productData = static_cast<PBProductData*>(fData->Getparent()->Getparent());
double offsetX = productData->fOffsetX;
double offsetY = productData->fOffsetY;
double diesizeX = productData->fDieSizeX;
double diesizeY = productData->fDieSizeY;
/* Collect all devices within the edges, assign them coordinates of their first probe */
if(useUserSelectionForSubplugs )
{
qDebug()<<"inside if crown";
bool noSubplugSelected = true;
for(int i=0; i<pendingRevision->fShotList.size(); i++)
{
for(int j=0; j<pendingRevision->fShotList[i].fSubPlugList.size(); j++)
{
if(pendingRevision->fShotList[i].fSubPlugList[j].fIsSelected)
{
noSubplugSelected = false;
PBSubPlug *subPlug = fCurrentRevision->fShotList[i].fSubPlugList[j].fSubPlug;
for(int k=0; k<pendingRevision->fShotList[i].fDeviceList.size(); k++)
{
WaferDevice *dev = &(pendingRevision->fShotList[i].fDeviceList[k]);
if(!dev->fIsOutsideEE)
{
if(std::find(fSelectedDeviceList.begin(), fSelectedDeviceList.end(), dev->fDevice) != fSelectedDeviceList.end())
{
double probeX = dev->fDevice->fDevice->fMPFProbeList->fProbeList.front()->fProbe_pos_x;
double probeY = dev->fDevice->fDevice->fMPFProbeList->fProbeList.front()->fProbe_pos_y;
double x = subPlug->fSubplgx;
double y = subPlug->fSubplgy;
double w = subPlug->fSubplgwidth;
double h = subPlug->fSubplgheight;
if((probeX > x-kDoubleTolerence) && (probeX < x+w+kDoubleTolerence) && (probeY > y-kDoubleTolerence) && (probeY < y+h+kDoubleTolerence))
{
WMDeviceForCrown devForCrown;
devForCrown.WaferDevicePointer = dev;
double shotX = pendingRevision->fShotList[i].fShot->fShotX;
double shotY = pendingRevision->fShotList[i].fShot->fShotY;
devForCrown.wrtShotX = probeX;
devForCrown.wrtShotY = probeY;
if(dev->fDevice->fDevice->fMPFProbeList->fProbeList.empty()) return;
double probe1X = shotX + devForCrown.wrtShotX;
double probe1Y = shotY + devForCrown.wrtShotY;
devForCrown.x = probe1X;
devForCrown.y = probe1Y;
devForCrown.crownShotRow= pendingRevision->fShotList[i].fShot->fShotRow;
devForCrown.crownShotCol= pendingRevision->fShotList[i].fShot->fShotCol;
DeviceForCrownList.push_back(devForCrown);
//break;
}
}
}
}
}
}
}
if(noSubplugSelected)
{
QMessageBox::warning(this, tr("Edge Crown Creation"), tr("Nothing to create crown with: only the selected devices in selected subplugs will be considered for this operation\nPlease select one or more subplugs in the wafer map\nor uncheck option \"consider devices only in selected subplugs\""));
return;
}
}
else
{
qDebug()<<"inside else crown";
for(int i=0; i<pendingRevision->fShotList.size(); i++)
{
if(pendingRevision->fShotList[i].fShot->fShotName == "Std shots")
{
for(int j=0; j<pendingRevision->fShotList[i].fDeviceList.size(); j++)
{
WaferDevice *dev = &(pendingRevision->fShotList[i].fDeviceList[j]);
if(std::find(fSelectedDeviceList.begin(), fSelectedDeviceList.end(), dev->fDevice)==fSelectedDeviceList.end()) continue;
if(!dev->fIsOutsideEE)
{
WMDeviceForCrown devForCrown;
devForCrown.WaferDevicePointer = dev;
double shotX = pendingRevision->fShotList[i].fShot->fShotX;
double shotY = pendingRevision->fShotList[i].fShot->fShotY;
devForCrown.wrtShotX = dev->fDevice->fDevice->fMPFProbeList->fProbeList.front()->fProbe_pos_x;
devForCrown.wrtShotY = dev->fDevice->fDevice->fMPFProbeList->fProbeList.front()->fProbe_pos_y;
if(dev->fDevice->fDevice->fMPFProbeList->fProbeList.empty()) return;
double probe1X = shotX + devForCrown.wrtShotX;
double probe1Y = shotY + devForCrown.wrtShotY;
devForCrown.x = probe1X;
devForCrown.y = probe1Y;
devForCrown.crownShotRow= pendingRevision->fShotList[i].fShot->fShotRow;
devForCrown.crownShotCol= pendingRevision->fShotList[i].fShot->fShotCol;
DeviceForCrownList.push_back(devForCrown);
// break;
}
}
}
}
}
if(DeviceForCrownList.empty())
{
QMessageBox::warning(this, tr("Edge Crown Creation"), tr("No valid devices for the crown operation. \nNo device from selected devices list could be found in the selected subplugs."));
return;
}
std::vector<WMDeviceForCrown> DeviceOnEdgeList;
for(int i=0; i<DeviceForCrownList.size(); i++)
{
WMDeviceForCrown dev = DeviceForCrownList[i];
bool quadrantEmpty;
/* Axes as thick as diesize */
int xOffset = int(dev.wrtShotX)%int(diesizeX);
int xReminder = diesizeX - xOffset;
double xmin = dev.x - xOffset;
double xmax = dev.x + xReminder;
int yOffset = int(dev.wrtShotY)%int(diesizeY);
int yReminder = diesizeY - yOffset;
double ymin = dev.y - yOffset;
double ymax = dev.y + yReminder;
/* First Quadrant */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if((DeviceForCrownList[j].x>xmax && DeviceForCrownList[j].y>ymax) )
{
/*if((DeviceForCrownList[j].y>ymax && DeviceForCrownList[j].x>=xmin && DeviceForCrownList[j].x<=xmax))
{
DeviceOnEdgeList.push_back(dev);
continue;
}*/
/*qDebug()<<"xmin::"<<xmin;
qDebug()<<"xmax::"<<xmax;
qDebug()<<"ymin::"<<ymin;
qDebug()<<"ymax::"<<ymax;
*/
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
//* Second Quadrant */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if(DeviceForCrownList[j].x<xmin && DeviceForCrownList[j].y>ymax /*|| (DeviceForCrownList[j].x<xmin && DeviceForCrownList[j].y>=ymin && DeviceForCrownList[j].y<=ymax*/)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
//* Third Quadrant */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if(DeviceForCrownList[j].x<xmin && DeviceForCrownList[j].y<ymin)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
//
/////* Fourth Quadrant */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if((DeviceForCrownList[j].x>xmax && DeviceForCrownList[j].y<ymin) /*|| (DeviceForCrownList[j].x<xmax && DeviceForCrownList[j].y>ymin)*/)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
// /* Along X-Positive Axis */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if(DeviceForCrownList[j].x>xmax && DeviceForCrownList[j].y>=ymin && DeviceForCrownList[j].y<=ymax)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
//// ///* Along Y-Positive Axis */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if((DeviceForCrownList[j].y>ymax && DeviceForCrownList[j].x>=xmin && DeviceForCrownList[j].x<=xmax) /*&& (DeviceForCrownList[j].x>xmax && DeviceForCrownList[j].y>=ymin && DeviceForCrownList[j].y<=ymax)*/ )
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
////
// ///* Along X-Negative Axis */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if(DeviceForCrownList[j].x<xmin && DeviceForCrownList[j].y>=ymin && DeviceForCrownList[j].y<=ymax)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
///////* Along Y-Negative Axis */
quadrantEmpty = true;
for(int j=0; j<DeviceForCrownList.size(); j++)
{
if(i!=j)
{
if(DeviceForCrownList[j].y<ymin && DeviceForCrownList[j].x>=xmin && DeviceForCrownList[j].x<=xmax)
{
quadrantEmpty = false;
break;
}
}
}
if(quadrantEmpty)
{
DeviceOnEdgeList.push_back(dev);
continue;
}
}
if(DeviceForCrownList.empty())
{
QMessageBox::warning(this, tr("Edge Crown Creation"), tr("No valid devices found along the edge"));
return;
}
/* Temporary code for visualization */
//for(int i=0; i<DeviceOnEdgeList.size(); i++)
//{
// QGraphicsEllipseItem *itm = new QGraphicsEllipseItem(DeviceOnEdgeList[i].x-150, -DeviceOnEdgeList[i].y-150, 300, 300);
// itm->setBrush(Qt::blue);
// fWaferMapView.scene()->addItem(itm);
//}
I have a circle in 2d plane with centre as 0,0 .
Now i have drawn rectangles of fixed sizes all over the circle , rectangle 0,0 is the one which contains centre of circle and then rectangle 0,1 ,02 so on are along x, axis and similarly in all Quadrants.
Now each rectangle has some devices and i knw the x,y of those devices for each rectangle. each rectangle has same devices in it.
Now i am selecting the devices in grey as per this picture:
https://docs.google.com/file/d/0B_oYS4i7-wQMWldWdzRjcG1qekU/edit?usp=drive_web&pli=1
now when i run the above code , I am trying to select the devices at the edges of the selection in grey like this :
https://drive.google.com/file/d/0B_oYS4i7-wQMc0x2cFhSMWE0SmM/view?usp=sharing
but still, some devices are not selected as per this link (see red circle).
so need some help/logic to achieve the same.
Thanks in advance!
I am looking for an OpenCV function that can find connected components and perform a few tasks on them ( like getting the number of pixels, contour, list of pixels in the object etc.. )
Is there a function of OpenCV (C++) that is similar to MatLab's regionprops ?
Starting from version 3.0, OpenCV has connectedComponents function.
Have a look at the cvFindContours function. It's very versatile -- it can find both interior and exterior contours, and return the results in a variety of formats (e.g. flat list vs. tree structure). Once you have the contours, functions like cvContourArea allow you to determine basic properties of the connected component corresponding to a particular contour.
If you prefer to use the newer C++ interface (as opposed to the older C-style interface I described above), then the function names are similar.
set -std=c++0x option when compiling
.h file
//connected_components.h
#ifndef CONNECTED_COMPONENTS_H_
#define CONNECTED_COMPONENTS_H_
#include <opencv2/core/core.hpp>
#include <memory>
class DisjointSet {
private:
std::vector<int> m_disjoint_array;
int m_subset_num;
public:
DisjointSet();
DisjointSet(int size);
~DisjointSet();
int add(); //add a new element, which is a subset by itself;
int find(int x); //return the root of x
void unite(int x, int y);
int getSubsetNum(void);
};
class ConnectedComponent {
private:
cv::Rect m_bb;
int m_pixel_count;
std::shared_ptr< std::vector<cv::Point2i> > m_pixels;
public:
ConnectedComponent();
ConnectedComponent(int x, int y);
~ConnectedComponent();
void addPixel(int x, int y);
int getBoundingBoxArea(void) const;
cv::Rect getBoundingBox(void) const;
int getPixelCount(void) const;
std::shared_ptr< const std::vector<cv::Point2i> > getPixels(void) const;
};
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc);
#endif //CONNECTED_COMPONENTS_H_
.cc file
//connected_components.cpp
#include "connected_components.h"
using namespace std;
/** DisjointSet **/
DisjointSet::DisjointSet() :
m_disjoint_array(),
m_subset_num(0)
{ }
DisjointSet::DisjointSet(int size) :
m_disjoint_array(),
m_subset_num(0)
{
m_disjoint_array.reserve(size);
}
DisjointSet::~DisjointSet()
{ }
//add a new element, which is a subset by itself;
int DisjointSet::add()
{
int cur_size = m_disjoint_array.size();
m_disjoint_array.push_back(cur_size);
m_subset_num ++;
return cur_size;
}
//return the root of x
int DisjointSet::find(int x)
{
if (m_disjoint_array[x] < 0 || m_disjoint_array[x] == x)
return x;
else {
m_disjoint_array[x] = this->find(m_disjoint_array[x]);
return m_disjoint_array[x];
}
}
// point the x and y to smaller root of the two
void DisjointSet::unite(int x, int y)
{
if (x==y) {
return;
}
int xRoot = find(x);
int yRoot = find(y);
if (xRoot == yRoot)
return;
else if (xRoot < yRoot) {
m_disjoint_array[yRoot] = xRoot;
}
else {
m_disjoint_array[xRoot] = yRoot;
}
m_subset_num--;
}
int DisjointSet::getSubsetNum()
{
return m_subset_num;
}
/** ConnectedComponent **/
ConnectedComponent::ConnectedComponent() :
m_bb(0,0,0,0),
m_pixel_count(0),
m_pixels()
{
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
}
ConnectedComponent::ConnectedComponent(int x, int y) :
m_bb(x,y,1,1),
m_pixel_count(1),
m_pixels()
{
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
}
ConnectedComponent::~ConnectedComponent(void)
{ }
void ConnectedComponent::addPixel(int x, int y) {
m_pixel_count++;
// new bounding box;
if (m_pixel_count == 0) {
m_bb = cv::Rect(x,y,1,1);
}
// extend bounding box if necessary
else {
if (x < m_bb.x ) {
m_bb.width+=(m_bb.x-x);
m_bb.x = x;
}
else if ( x > (m_bb.x+m_bb.width) ) {
m_bb.width=(x-m_bb.x);
}
if (y < m_bb.y ) {
m_bb.height+=(m_bb.y-y);
m_bb.y = y;
}
else if ( y > (m_bb.y+m_bb.height) ) {
m_bb.height=(y-m_bb.y);
}
}
m_pixels->push_back(cv::Point(x,y));
}
int ConnectedComponent::getBoundingBoxArea(void) const {
return (m_bb.width*m_bb.height);
}
cv::Rect ConnectedComponent::getBoundingBox(void) const {
return m_bb;
}
std::shared_ptr< const std::vector<cv::Point2i> > ConnectedComponent::getPixels(void) const {
return m_pixels;
}
int ConnectedComponent::getPixelCount(void) const {
return m_pixel_count;
}
/** find connected components **/
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc) {
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = src.total();
int frame_label[total_pix];
DisjointSet labels(total_pix);
int root_map[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val;
int cur_idx, left_idx, up_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++ ) {
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++) {
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
if ( x == 0)
left_val = 0;
else
left_val = cur_p[x-1];
if (y == 0)
up_val = 0;
else
up_val = prev_p[x];
if (cur_p[x] > 0) {
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0) {
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
}
//current pixel is foreground and has left neighbor connected
else if (left_val != 0 && up_val == 0) {
frame_label[cur_idx] = frame_label[left_idx];
}
//current pixel is foreground and has up neighbor connect
else if (up_val != 0 && left_val == 0) {
frame_label[cur_idx] = frame_label[up_idx];
}
//current pixel is foreground and is connected to left and up neighbors
else {
frame_label[cur_idx] = (frame_label[left_idx] > frame_label[up_idx]) ? frame_label[up_idx] : frame_label[left_idx];
labels.unite(frame_label[left_idx], frame_label[up_idx]);
}
}//endif
else {
frame_label[cur_idx] = -1;
}
} //end for x
prev_p = cur_p;
}//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++ ) {
for (x = 0; x < src.size().width; x++, cur_idx++) {
curLabel = frame_label[cur_idx];
if (curLabel != -1) {
curLabel = labels.find(curLabel);
if( root_map[curLabel] != -1 ) {
cc[root_map[curLabel]].addPixel(x, y);
}
else {
cc.push_back(ConnectedComponent(x,y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
}
}
}//end for x
}//end for y
}
If you don't mind using an external library that uses OpenCV, you can do that using cvBlobsLib.
A library to perform binary images connected component labelling
(similar to regionprops Matlab function). It also provides functions
to manipulate, filter and extract results from the extracted blobs,
see features section for more information.
You can use cv::connectedComponentsWithStats() function.
Here is an example.
// ...
cv::Mat labels, stats, centroids;
int connectivity = 8; // or 4
int label_count = cv::connectedComponentsWithStats(src, labels, stats, centroids, connectivity);
for (int i = 0; i < label_count; i++)
{
int x = stats.at<int>(i, cv::CC_STAT_LEFT);
int y = stats.at<int>(i, cv::CC_STAT_TOP);
int w = stats.at<int>(i, cv::CC_STAT_WIDTH);
int h = stats.at<int>(i, cv::CC_STAT_HEIGHT);
int area = stats.at<int>(i, cv::CC_STAT_AREA);
double cx = centroids.at<double>(i, 0);
double cy = centroids.at<double>(i, 1);
// ...
}
Following DXM's code above which assumes 4-connected components, here is a version for 'findCC' that detects 8-connected components.
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc) {
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = int(src.total());
int *frame_label = new int[total_pix];
DisjointSet labels(total_pix);
int *root_map = new int[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val, up_left_val, up_right_val;
int cur_idx, left_idx, up_idx, up_left_idx, up_right_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++) {
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++) {
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
up_left_idx = up_idx - 1;
up_right_idx = up_idx + 1;
if (x == 0)
{
left_val = 0;
}
else
{
left_val = cur_p[x - 1];
}
if (y == 0)
{
up_val = 0;
}
else
{
up_val = prev_p[x];
}
if (x == 0 || y == 0)
{
up_left_val = 0;
}
else
{
up_left_val = prev_p[x-1];
}
if (x == src.cols - 1 || y == 0)
{
up_right_val = 0;
}
else
{
up_right_val = prev_p[x+1];
}
if (cur_p[x] > 0) {
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0 && up_left_val == 0 && up_right_val == 0) {
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
}
//Current pixel is foreground and has at least one neighbor
else
{
vector<int> frame_lbl;
frame_lbl.reserve(4);
//Find minimal label
int min_frame_lbl = INT_MAX;
int valid_entries_num = 0;
if (left_val != 0)
{
frame_lbl.push_back(frame_label[left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[left_idx]);
valid_entries_num++;
}
if (up_val != 0)
{
frame_lbl.push_back(frame_label[up_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_idx]);
valid_entries_num++;
}
if (up_left_val != 0)
{
frame_lbl.push_back(frame_label[up_left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_left_idx]);
valid_entries_num++;
}
if (up_right_val != 0)
{
frame_lbl.push_back(frame_label[up_right_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_right_idx]);
valid_entries_num++;
}
CV_Assert(valid_entries_num > 0);
frame_label[cur_idx] = min_frame_lbl;
//Unite if necessary
if (valid_entries_num > 1)
{
for (size_t i = 0; i < frame_lbl.size(); i++)
{
labels.unite(frame_lbl[i], min_frame_lbl);
}
}
}
}//endif
else {
frame_label[cur_idx] = -1;
}
} //end for x
prev_p = cur_p;
}//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++) {
for (x = 0; x < src.size().width; x++, cur_idx++) {
curLabel = frame_label[cur_idx];
if (curLabel != -1) {
curLabel = labels.find(curLabel);
if (root_map[curLabel] != -1) {
cc[root_map[curLabel]].addPixel(x, y);
}
else {
cc.push_back(ConnectedComponent(x, y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
}
}
}//end for x
}//end for y
//Free up allocated memory
delete[] frame_label;
delete[] root_map;
}