c++ - extent - regionprops()
openCV 2.4.10 bwlabel-componentes conectados (1)
- He pensado que el etiquetado se hace para conectar 4-8 objetos. ¿Puedes explicar qué es realmente el etiquetado? Yo tomaría cualquier enlace.
La demostración más clara de lo que hace el etiquetado es en la documentación de Matlab para bwlabel
. Si compara la matriz original BW
con la matriz resultante L
, verá que toma una imagen binaria y asigna etiquetas únicas a cada grupo conectado de 1
:
L =
1 1 1 0 0 0 0 0
1 1 1 0 2 2 0 0
1 1 1 0 2 2 0 0
1 1 1 0 0 0 3 0
1 1 1 0 0 0 3 0
1 1 1 0 0 0 3 0
1 1 1 0 0 3 3 0
1 1 1 0 0 0 0 0
Aquí hay tres componentes etiquetados. Este ejemplo busca componentes con 4 conexiones; se considera que un píxel está conectado al píxel actual si está a la izquierda, derecha, arriba o debajo de él. Los objetos conectados 8 incluyen las diagonales, lo que daría como resultado que las etiquetas 2
y 3
se fusionen para la matriz anterior, ya que la esquina inferior derecha del objeto 2 y la parte superior del objeto 3 están conectadas diagonalmente. El algoritmo de etiquetado de componentes conectados se describe en la Wikipedia aquí .
2. componentes conectados en OpenCV en este artículo algunas personas están hablando de CVblob y algunos sobre cvContourArea de la opep, ¿puede explicar la diferencia? y ¿cuál será más adecuado para mi caso de uso?
OpenCV 3.0 está fuera de beta y tiene dos nuevos métodos: connectedComponents
y connectedComponentsWithStats
( documentación ). Si intentas replicar la bwlabel
la bwlabel
de Matlab, este es el camino a seguir.
Escribí un programa de prueba para probar connectedComponentsWithStats
(complete el código a continuación) usando esto como mi imagen de prueba:
(En realidad, esta imagen se reduce de 800x600 a 400x300, pero el código para generarlo se incluye a continuación).
Genere la imagen etiquetada usando:
int nLabels = connectedComponentsWithStats(src, labels, stats, centroids, 8, CV_32S);
El valor devuelto en nLabels
es 5
. Recuerde que este método considera que el fondo es la etiqueta 0
.
Para ver cuáles son las áreas etiquetadas, puede escalar los valores de escala de grises de [0..nLabels-1]
a [0..255]
, o puede asignar valores aleatorios RGB y crear una imagen en color. Para esta prueba, simplemente imprimí los valores en un par de ubicaciones que sabía que estaban en diferentes componentes.
cout << "Show label values:" << endl;
// Middle of square at top-left
int component1Pixel = labels.at<int>(150,150);
cout << "pixel at(150,150) = " << component1Pixel << endl;
// Middle of rectangle at far right
int component2Pixel = labels.at<int>(300,550);
cout << "pixel at(300,550) = " << component2Pixel << endl << endl;
Show label values:
pixel at(150,150) = 1
pixel at(300,550) = 2
Las stats
son 5 x nLabels
Tapetes que contienen left, top, width, height, and area
para cada componente (incluido el fondo). Para esta imagen:
stats:
(left,top,width,height,area)
[0, 0, 800, 600, 421697;
100, 100, 101, 101, 10201;
500, 150, 101, 301, 30401;
350, 246, 10, 10, 36;
225, 325, 151, 151, 17665]
Notarás que el componente 0
es el ancho / alto completo de la imagen. Al sumar todas las áreas, obtienes 480,000 = 800x600
. Los primeros 4 elementos se pueden usar para crear un rectángulo delimitador:
Rect(Point(left,top), Size(width,height))
centroids
es una estera de 2 x nLabels
contiene las coordenadas x, y
del centro de gravedad de cada componente:
centroids:
(x, y)
[398.8575636060963, 298.8746232484461;
150, 150;
550, 300;
354.5, 250.5;
300, 400]
Finalmente, en algún momento es probable que desee realizar un procesamiento adicional en uno de los componentes individualmente. Aquí uso compare
para generar un nuevo Mat only2
que solo contenga píxeles de labels
que marquen 2
.
compare(labels, 2, only2, CMP_EQ);
compare
útilmente establece estos píxeles a un valor de 255
en la nueva imagen para que pueda ver los resultados:
Aquí está el código completo:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, const char * argv[]) {
// Create an image
const int color_white = 255;
Mat src = Mat::zeros(600, 800, CV_8UC1);
rectangle(src, Point(100, 100), Point(200, 200), color_white, CV_FILLED);
rectangle(src, Point(500, 150), Point(600, 450), color_white, CV_FILLED);
rectangle(src, Point(350,250), Point(359,251), color_white, CV_FILLED);
rectangle(src, Point(354,246), Point(355,255), color_white, CV_FILLED);
circle(src, Point(300, 400), 75, color_white, CV_FILLED);
imshow("Original", src);
// Get connected components and stats
const int connectivity_8 = 8;
Mat labels, stats, centroids;
int nLabels = connectedComponentsWithStats(src, labels, stats, centroids, connectivity_8, CV_32S);
cout << "Number of connected components = " << nLabels << endl << endl;
cout << "Show label values:" << endl;
int component1Pixel = labels.at<int>(150,150);
cout << "pixel at(150,150) = " << component1Pixel << endl;
int component2Pixel = labels.at<int>(300,550);
cout << "pixel at(300,550) = " << component2Pixel << endl << endl;
// Statistics
cout << "Show statistics and centroids:" << endl;
cout << "stats:" << endl << "(left,top,width,height,area)" << endl << stats << endl << endl;
cout << "centroids:" << endl << "(x, y)" << endl << centroids << endl << endl;
// Print individual stats for component 1 (component 0 is background)
cout << "Component 1 stats:" << endl;
cout << "CC_STAT_LEFT = " << stats.at<int>(1,CC_STAT_LEFT) << endl;
cout << "CC_STAT_TOP = " << stats.at<int>(1,CC_STAT_TOP) << endl;
cout << "CC_STAT_WIDTH = " << stats.at<int>(1,CC_STAT_WIDTH) << endl;
cout << "CC_STAT_HEIGHT = " << stats.at<int>(1,CC_STAT_HEIGHT) << endl;
cout << "CC_STAT_AREA = " << stats.at<int>(1,CC_STAT_AREA) << endl;
// Create image with only component 2
Mat only2;
compare(labels, 2, only2, CMP_EQ);
imshow("Component 2", only2);
waitKey(0);
}
Aquí está el código original de matlab:
% Calculate each separated object area
cDist=regionprops(bwImg, ''Area'');
cDist=[cDist.Area];
% Label each object
[bwImgLabeled, ~]=bwlabel(bwImg);
% Calculate min and max object size based on assumptions on the color
% checker size
maxLabelSize = prod(size(imageData)./[4 6]);
minLabelSize = prod(size(imageData)./[4 6]./10);
% Find label indices for objects that are too large or too small
remInd = find(cDist > maxLabelSize);
remInd = [remInd find(cDist < minLabelSize)];
% Remove over/undersized objects
for n=1:length(remInd)
ri = bwImgLabeled == remInd(n);
bwImgLabeled(ri) = 0;
Aquí está mi código usando openCV
//regionprops(bwImg, ''Area'');
// cDist=[cDist.Area]
//cv::FileStorage file("C://Users//gdarmon//Desktop//gili.txt", cv::FileStorage::WRITE);
//
//file << dst;
dst.convertTo(dst,CV_8U);
cv::vector<cv::vector<cv::Point> > contours;
cv::vector<cv::Vec4i> hierarchy;
cv::findContours(dst,contours,hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
std::vector<cv::Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = cv::moments(contours[i],false);
}
vector<cv::Point2f> mc( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{
mc[i] = cv::Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );
}
Desde ahora tengo los contornos, me gustaría utilizar la función bwlabel
1. He pensado que el etiquetado se hace para conectar 4-8 objetos. ¿Puedes explicar qué es realmente el etiquetado? Yo tomaría cualquier enlace.
2. componentes conectados en OpenCV en este artículo. Algunas personas están hablando de CVblob y otras sobre cvContourArea de la opep, ¿puede explicar la diferencia? y ¿cuál será más adecuado para mi caso de uso?
Actualización: aquí es lo que he intentado usar cvBlobs
IplImage* img_bw = new IplImage(dst);
CBlobResult blobs;
CBlob *currentBlob;
blobs = CBlobResult(img_bw, NULL, 0);
// Exclude all white blobs smaller than the given value (80)
// The bigger the last parameter, the bigger the blobs need
// to be for inclusion
blobs.Filter( blobs,
B_EXCLUDE,
CBlobGetArea(),
B_LESS,
80 );
// Get the number of blobs discovered
int num_blobs = blobs.GetNumBlobs();
// Display the filtered blobs
IplImage* filtered = cvCreateImage( cvGetSize( img_bw ),
IPL_DEPTH_8U,
3 );
cvMerge( img_bw, img_bw, img_bw, NULL, filtered );
for ( int i = 0; i < num_blobs; i++ )
{
currentBlob = blobs.GetBlob( i );
currentBlob->FillBlob( filtered, CV_RGB(255,0,0));
}
// Display the input / output windows and images
cvNamedWindow( "input" );
cvNamedWindow( "output" );
cvShowImage("input", img_bw );
cvShowImage("output", filtered);
cv::waitKey(0);
/*% Calculate min and max object size based on assumptions on the color
% checker size
maxLabelSize = prod(size(imageData)./[4 6]);
minLabelSize = prod(size(imageData)./[4 6]./10);*/
double maxLabelSize = (dst.rows/4.0) * (dst.cols/6.0);
double minLabelSize = ((dst.rows/40.0) * (dst.cols/60.0));