-
-
Notifications
You must be signed in to change notification settings - Fork 55.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
connectedcomponents submission #135
Conversation
…act that cv::Mat::at is expensive in a tight-loop -also added a "blobstats" version
…hings for a python export
ok, let's finally put it in. However, I would like you to modify interface significantly and partly modify implementation. Since the current trend in OpenCV is to introduce very stable API and do not change it for a long time, I want the API to be future-proof, and not only at the source code level, but also at binary level. Why we did not integrate the patch earlier in the first place? Because connected components is quite generic thing. They can be extracted from binary (bi-level) images, they can be extracted from grayscale or even multi-channel images if we define the predicate (close(p, q)) for the neighbor pixels. On the output side, representation of connected components can be very different too, they can be represented as contours, as sets of horizontal/vertical line segments etc. The statistics that is computed in your implementation may be sufficient for some users, but insufficient for others, e.g. J. Matas et al in "Real-time scene text localization and recognition" consider different, in my opinion very useful characteristics like the perimeter, number of holes, number of zero-crossings, number of inflections etc. So, we can not just "occupy" quite generic name "connected components" for something rather specific and not covering this area. But I admit that something is better than nothing, and designing an ideal connected component extraction function may take a very long time. So, let's put the current code in, but make the API more generic and more wrapper-friendly: vvvvvvvvvvvvvvvvvvvvvvvv CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int flags=0); enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_CX=4, CC_STAT_CY=5, CC_STAT_AREA=6, CC_STAT_INTEGRAL_X=7, CC_STAT_INTEGRAL_Y=8, ... }; CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, int connectivity = 8, int flags=0); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First of all, the use of uint64 should be eliminated since it's a weird type and its use here is not justified for any reasonable image size (2 billions of pixels is quite a big number). Then, input image should be made the first parameter, by our common convention and should be made InputArray, which was introduced in 2.4.0. "const Mat&" is implicitly converted to InputArray. The output label image should be made OutputArray. "Mat&" is implicitly converted to OutputArray. The output vector of cc statistics should be also wrapped into OutputArray. The structure ConnectedComponentStats should be removed as too specific and not wrapper-friendly. For Python you provided wrappers, but what about Java? Also, for example, quite a few people want Matlab & Octave interface for OpenCV, and each extra structure means that we will have to provide such a conversion function. Instead, I suggest to pack all the statistics into array of integers. The centroid can be represented in fixed-point format. Mat labels, stats; I would also add the label of the component in the "default" statistics, because if we remove (filter off) some of the connected components, e.g. too small ones, the ordering will be lost. flags parameter will regulate what exactly needs to be computed. 0 means your statistics, e.g. CC_STAT_MATAS_MODE could mean adding some more stuff etc. Now, on the implementation. It's a lot of code in OpenCV already, and we try to do our best to keep functionality/footprint ratio reasonable (when possible, we try to increase it). The proposed patch implements connected components extraction for binary image, which is 8-bit single-channel image (CV_8UC1) in 99% cases. If not, it can be converted to this format with a single line of code: connectedComponents((my_32f_image > eps), labels, stats); So, it's enough to support CV_8UC1 input. Similarly, it's enough to constrain the output image of labels to 32-bit format (CV_32S). We support only this format in our labeled distance transform and watershed transforms functions and did not hear a single complain. So, let's bring the code size down and make the function non-template 8u->32s code. I'm sure we will find a better use of the space (especially on mobile devices). One final point. For the code that can be tested (i.e. except for camera capturing, GUI stuff) we now demand some tests, at least smoke tests. The sample you prepared is great, but we can not run it nightly in batch mode. So we will need some tests for these connected component extraction functions. You can look at https://github.com/Itseez/opencv/blob/master/modules/imgproc/test/test_watershed.cpp for a relevant test. It just reads some image, runs the function and compares the output with the previously stored one. What do you think? If you are fine with the proposed changes and willing to do that and update the pull request, I will merge it in. |
This is indeed alot of changes... see my responses inline. On Thu, Nov 22, 2012 at 10:52 AM, Vadim Pisarevsky <notifications@github.com
I wasn't aware of this, it's fine, though at this point, annoying change. First of all, the use of uint64 should be eliminated since it's a weird
Mat labels, stats;
Not needed. Their position in the array tells you the label. If you flags parameter will regulate what exactly needs to be computed. 0 means
Further in-place threshholding a float without new image being constructed So I think it is unfortunate and true that there is some unneeded code wrt
I'll do that.
I'm extremely happy someone finally talked with me and your email was I should be able to get most of these done hopefully before the weekend is Thank you and best regards, —
|
Actually, I'll concede on the return type of uint64. I don't like using a After seeing the 360k gcc outputs for this binary after stripping I took On Thu, Nov 22, 2012 at 6:40 PM, Jason Newton nevion@gmail.com wrote:
|
…but this breaks python bindings; remove non-8bit input type support, not worth the binary size
Jason, class CComp : public Algorithm and everything else can be made properties, so it's super-extendable: Ptr ccomp = Algorithm::create("connected-component-extractor" /* or use some better name? */ ); I understand that:
so I suggest to do it together. If you make a branch in your repository and give me access to it, I can help to adjust the code. Or I can submit a pull request to your repository at github. Is it fine? On the various data type support. uint32 is actually needed very rarely, as we learnt from our experience and users' feedback. If int32 is not enough (we prefer to call it int, since on any known for us 32-bit or 64-bit OS sizeof(int)==4), we found double to be the best alternative. It's supported in hardware anywhere nowadays, from most low-power ARMs to the modern Intel chips with AVX instructions (that can process 4 double's at once). double can represent any integer <=2*53 exactly, it takes the same space as [u]int64, it's usually faster to process than 64-bit integers on 32-bit CPUs and about as fast on 64-bit CPUs (actually, it's faster there too if we take into account SSE2/AVX that have a very limited support for 64-bit integers). So, uint32, int64/uint64 are useless types in my opinion. (BTW, if "int" as return type is not enough for connectedComponent functions, I would suggest to use "double"). And if we have some spare resources to add another datatype to OpenCV's Mat, I would immediately choose float16 (half). |
oh, I forgot to add that another reason to make it an Algorithm is to add [later, if not now] some other useful properties, e.g. |
-Change input/output order from (out Labeled, in Image) -> (in Image, Out Labeled) and convert to Input/OutputArrays in the process. -Adopt OutputArray for statistics export so that the algorithm is "wrapper friendly" and not requiring a new struct in language bindings at the expense of using doubles for everything and slowing statistics computation down..
Hi Vadim, I've made alot of the changes we've discussed... I'll go through the On the stats thing, I made it an OutputArray of Ints (using the types as So 1) the fixed point way is:
I think the compute as needed way (2) is the best approach but would be I'm also a little worried about the integrals as well. Imagine a trivial Please let me know your thoughts on the best solution. I'm thinking if we I suppose I wouldn't mind seeing the following signature, as a user: CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray On the topic of the functor... I'm a little worried about bloat. I'm not Perhaps both should be provided... and maybe not just for this function, On Fri, Nov 23, 2012 at 12:44 AM, Vadim Pisarevsky <notifications@github.com
|
Hi Jason!
int connectedComponents(InputArray _img, OutputArray _labels, int connectivity=8, int ltype=CV_32S)
|
Vadim, Inlined comments. On Wed, Nov 28, 2012 at 11:36 AM, Vadim Pisarevsky <notifications@github.com
The integral is computed for centroid computation. It is the sum of —
|
ok, so if the ConnectedComp structure is used for intermediate representation (where integrals can be encoded as double's or 64-bit integers) then the output array of statistics can be without them, because they do not much sense for the users. On 8-bit output label array - let's throw it away and keep just CV_16U and CV_32S options. CX and CY representation - well, need to think. Personally I do not think that they should be stored with very high accuracy - any noise pixel added to or removed from the component may affect them. I would say, 1/256 should be enough, but I admit that I do not know all the possible use cases. Splitting the output array into 2 - I would say, this is the least convenient option. Hey, what about yet another option - encode each of CX and CY as a pair of integers? #define CV_CC_STAT_CENTROID_SCALE 4.656612873077393e-10 /* 1/(2**31) */ so that the actual centroid can be computed as stat[i][CC_STAT_CX]+stat[i][CC_STAT_FX]*CV_CC_STAT_CENTROID_SCALE. It will give significantly better accuracy than single-precision floating-point number. We can add inline function inline Point2f getCCompCentroid(const int* stat) { return Point2f(stat[CC_STAT_CX]+..., stat[CC_STAT_CY]+...); } which can be used as vector<Vec<int, 9> > ccomp; those who only need very rough position of the centroid can just use Point(stat[CC_STAT_CX], stat[CC_STAT_CY]). What do you think? |
On Sat, Dec 1, 2012 at 9:16 AM, Vadim Pisarevsky
I think fixed point is more ugly than a separate array and floating point Having said that, I think the stand alone function is just as ugly as -Jason
|
} | ||
Mat labelImage(img.size(), CV_32S); | ||
int nLabels = connectedComponents(bw, labelImage, 8); | ||
Vec3b colors[nLabels]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an error with MSVC9 (VS2008) since nLabels
is not a constant; use new and delete instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't const int fix it?
On Mon, Dec 3, 2012 at 10:49 AM, joshdoe notifications@github.com wrote:
In samples/cpp/connected_components.cpp:
- Mat dst = Mat::zeros(img.size(), CV_8UC3);
- if( !contours.empty() && !hierarchy.empty() )
- {
// iterate through all the top-level contours,
// draw each connected component with its own random color
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( (rand()&255), (rand()&255), (rand()&255) );
drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );
}
- Mat labelImage(img.size(), CV_32S);
- int nLabels = connectedComponents(bw, labelImage, 8);
- Vec3b colors[nLabels];
This is an error with MSVC9 (VS2008) since nLabels is not a constant; use
new and delete instead.—
Reply to this email directly or view it on GitHubhttps://github.com//pull/135/files#r2296401.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"const int" will not help. Using run-time, not a compile-time value to specify the array size is GCC extension to C/C++ standards. Use
vector colors(nLabels);
instead
ok, let's output the connected component centroid in a separate output array of 2-channel floating-point type (CV_32FC2 or CV_64FC2 - at your choice). |
…gram argument borkage
…ats with differening types over different outputarrays
Vadim, I believe I've got everything you mentioned taken care of, including the test case which I have also forked from your branch and updated with my input/expected output for regression testing. Let me know if there's anything missing still. -Jason |
looks good now. |
typedef unsigned short uint16_t; | ||
typedef int int32_t; | ||
typedef unsigned int uint32_t; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To fix the Windows errors, at least with VC10, add typedef unsigned __int64 uint64_t;
. However shouldn't this really be in core? Look at core\types_c.h
, as uint64
is already defined here. Consider using uint64
etc. in your code, moving the necessary bits of this set of typedefs there.
Vadim, any changes? |
I'll have the change(s) incorporate soon, I'm very busy with work at the On Wed, Dec 12, 2012 at 1:52 AM, Alexander Shishkov <
|
Hm, regarding these types I've added them to types_c.h... common programming lore states that the type of char is not guaranteed to be 8 bits though... and neither is int to be 32 (I've used one of these - a dsp platform with 16 bit ints and still using gcc 2.95 or some such). I'm not sure what to propose for older code but C++ 0x11/c99 guarantees stdint which provides integer types with guaranteed bit-depths. With that said I made the change reusing existing typedefs so int8 - uint32 are now also defined rather than just int64 and updated connectedcomponents afterwords. |
Hi Jason, Otherwise, the patch looks fine. With one little note - please, take a look at http://pullrequest.opencv.org, your code produces some warnings for android; the code can only be merged if it builds without warnings. You can fix it or I can submit pull request to your repository to fix that, as you wish. |
Vadim - yea I thought it was pretty controversial but somebody (joshdoe?) suggested to add such typedefs there. If I may ask where is the conflict you are concerned about and is this a reasonable judgment? I have received no compilation errors in regards to it and it just replicates the naming convention of u/int64 which is a sane and useful thing IMO. Or is this why they were originally missing? I can move them back but I do consider this a small step backwards.. I have been monitoring the builds since you originally provided me the URL, I figured there was such no merge unless ... conditions were in play. RE the android failing, wasn't able to decern why and I don't have an android environment setup... if it's not to much trouble could you submit a patch to fix the error there? |
Jason, |
Jason, here is the patch that fixes build problems with VS and probably Android; let's see if it works; For some reason, I could not submit a pull request to your repository, so you need to apply the patch manually: basically, you can merge https://github.com/vpisarev/opencv.git, branch cc, to your master. |
Vadim - I added your patch. Still build errors on android... something the compiler is having trouble with on Input/OutputArrays. On mac the regression test also fails with invalid test data... this only happens when the input matrix (loaded from a png file) is empty... ... perhaps the dataset repo isn't up to date on the mac test environment? This is just a warning however. Btw can you take references of Input/OutputArrays? I noted you changed the CCStatsOp fields to be pointers but thought it was funny you didn't use references instead... |
Hi Jason,
|
ok; I fixed the android build, please check the cc branch. Basically, you can just copy the implementation of the external cv::connectedComponentsWithStats functions to your .cpp file. The test data is still needed; with failing tests we can not accept the code |
Vadim - the test data is here (pushed a week ago): https://github.com/nevion/opencv_extra I pushed your patch too. |
ok, I added test data to our copy of opencv_extra; hopefully, now the tests will pass |
Vadim - not sure if it's been rebuilt since you added the test data. Can you force a rebuild or should I make a dummy commit? |
I can and I did. Looks like, this is the problem of our buildbot. Still, we have to wait a bit until it's solved. |
Hi Jason! 2 more things:
|
I'll get on 1 sometime today, re 2, we're tantalizingly close and the core code is stable and fairly well tested... I'd hope we could get it in in the next release which would allow such things in, whenever that is. I also hope we're not missing one though that seems like a bug fix release. Btw, come January I'm going to be on a long set of work related field tests so it works best for me to finish up before that... or after though I'd have to fight the distraction. |
Jason, Putting it to 2.4.3.x is too late; it's very long release process, going through multiple QA steps, it's already on the way. So the earliest release when we can put it in is 2.4.4, which is scheduled for 2013 Feb. Regards, |
Vadim, actually the earliest possible release is 2.5, since this is a new functionality and it is targeted for "master" branch. But I don't think that it will cause any issues, since after merge to master this will be a part of OpenCV! |
oh, that's right; the pull request should actually be retargeted for 2.4 branch in order to be put into 2.4.4 |
But I thought that 2.4 should accept only bug-fixes and optimizations =) Is it a "must have" for 2.4.4? |
there are "should" and "could". 2.4.4 can accept anything that does not break 2.4 API :) So, we can potentially add this functionality, no probs |
Ok I've pushed an update to the docs. Re 2.4, should I rebase off 2.4 and submit a pull request to that branch? |
Hi Jason, Let's put it to master to keep things going. We can still then to move it to 2.4 |
Looks like the android build bot is freaking out again - other than that I see no warnings relevant to connected components, just histogram equalization and a cascade classifier, not sure what to interpret from the docs, but the topic of interest isn't in it's logs. |
👍 |
Hi,
This is a git pull request for the following issue (that has went on way too long!). I've fixed up the Dustin's complaints... let's get this code out there!
http://code.opencv.org/issues/1236
There are COUNTLESS threads on how to do connectedcomponents almost monthly submissions to the ML. Stackoverflow alone must have 20+ threads on this. Most end up using cvblobslib which is often broken and too slow to boot. There are some other implementations too and it's possible to do it opencv with findContours but it's really a bad situation atm. People actually are rolling their own implementations still (naively).
Anyway this presents an easy to use single function call which gives the labeled image and optionally the statistics one often wants with connected components (bounding box, centroid, area) and does so with better worst case and average case performance than all the other implementations.
I hope I'll have better luck at submission than through the issue tracker.