Perimeter feature #113
-
After reading the documentation for the Perimeter feature, I see that it doesn't include the image border pixels in a label. I need the Perimeter feature to include the edges of the image. Is there a way to include the border pixels with the current API? Or would I have to create a custom feature? Also, why is pi added to the length of the chain code? Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 5 replies
-
You could certainly create a new feature that computes the perimeter including the edge pixels. But that is quite complicated. It would be a lot easier to directly fetch the image chain codes and do your computation on them. You could do it this way, for example (code not tested, sorry if there are typos): auto chain_codes = GetImageChainCodes(label_image);
for (auto& cc : chain_codes) {
// Remove the border flag from the chain code
for (auto& code : cc.codes) {
code = (unsigned)code;
}
// Compute the length
double length = cc.length() + dip::pi;
// do something with `length` here...
} It is necessary to add pi to the length for a closed contour because otherwise you'd underestimate the perimeter length. The chain code encodes the outer pixels of an object, and so describe a line that goes from pixel center to pixel center around the object. The true object boundary however does not follow that line, but is further out, somewhere in between the outer pixels of the object and the background pixels around them. For a circular object, the chain code represents a circle that has a radius smaller by 0.5 pixels, and thus a perimeter smaller by pi. For other shapes it is much harder to quantify exactly how much you'd underestimate the perimeter, but it should still be the same order of magnitude. |
Beta Was this translation helpful? Give feedback.
-
I've made some changes in 253418e and fa46259. In the code snippet I posted above, you can now skip modifying the chain code, and directly do dip::MeasurementTool measurementTool;
measurementTool.Configure("Perimeter", "include boundary pixels", 1);
measurementTool.Measure(...); Thanks for your feedback! I love these opportunities to improve DIPlib. |
Beta Was this translation helpful? Give feedback.
-
This algorithm works by counting even and odd chain codes, I don't think it properly generalizes to non-square pixels. Just scaling the weights for steps in different directions wouldn't lead to the same optimal, bias-free estimate. You could convert the chain code into a polygon, transform the polygon according to your pixel sizes, then get its length. But then you'd be overestimating the length because straight edges at angles other than multiples of 45 degrees will have a staircase shape auto polygon = cc.Polygon();
polygon.Scale(pixel_size_x, pixel_size_y);
double length = polygon.Length(); If you have an object without sharp corners, and not very small compared to the pixel size, then a bit of smoothing might avoid some of that overestimation: auto polygon = cc.Polygon();
polygon.Smooth(2); // smooth before scaling!
polygon.Scale(pixel_size_x, pixel_size_y);
double length = polygon.Length(); |
Beta Was this translation helpful? Give feedback.
I've made some changes in 253418e and fa46259. In the code snippet I posted above, you can now skip modifying the chain code, and directly do
length = cc.length("include") + dip::pi
. And you can also configure the "Perimeter" feature to measure perimeters including those pixels at the image boundary. In C++ you'd doThanks for your feedback! I love these opportunities to improve DIPlib.