* apps/camera-process/process.cc (process): Here. (min_rgb_comp): New functor. * apps/camera-process/camprocess.cc: Set the size of the captured image to 640x480 pixels. --- milena/ChangeLog | 9 +++ milena/apps/camera-process/camprocess.cc | 4 + milena/apps/camera-process/process.cc | 105 ++++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 28 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog index 77113b3..14b8ce1 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,3 +1,12 @@ +2012-09-27 Roland Levillain roland@lrde.epita.fr + + Make apps/camera-process/process detect large, white objects. + + * apps/camera-process/process.cc (process): Here. + (min_rgb_comp): New functor. + * apps/camera-process/camprocess.cc: Set the size of the captured + image to 640x480 pixels. + 2012-09-25 Roland Levillain roland@lrde.epita.fr
New app: camera stream processing (using OpenCV for capture). diff --git a/milena/apps/camera-process/camprocess.cc b/milena/apps/camera-process/camprocess.cc index 2a10fa6..f815d72 100644 --- a/milena/apps/camera-process/camprocess.cc +++ b/milena/apps/camera-process/camprocess.cc @@ -51,6 +51,10 @@ int main() exit(1); }
+ // Set the size of the captured image. + cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 640); + cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 480); + // Create a GUI window. cvNamedWindow("Window", CV_WINDOW_AUTOSIZE);
diff --git a/milena/apps/camera-process/process.cc b/milena/apps/camera-process/process.cc index 8cf2446..d76405f 100644 --- a/milena/apps/camera-process/process.cc +++ b/milena/apps/camera-process/process.cc @@ -26,13 +26,43 @@ #include <mln/core/image/image2d.hh> #include <mln/opt/at.hh>
+#include <mln/core/image/vmorph/fun_image.hh> +#include <mln/core/image/dmorph/image_if.hh> + #include <mln/value/int_u8.hh> #include <mln/value/rgb8.hh> -#include <mln/fun/v2v/rgb_to_int_u.hh> +#include <mln/literal/colors.hh> +#include <mln/literal/black.hh> +#include <mln/literal/white.hh> + +#include <mln/math/min.hh> + +#include <mln/pw/all.hh> + +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/alias/window2d.hh> + +#include <mln/morpho/dilation.hh> +#include <mln/morpho/gradient.hh> +#include <mln/morpho/opening/area.hh>
#include "process.hh"
+// FIXME: Move this into the library. +/// Functor returning the minimal component of an mln::value::rgb<n> value. +template <unsigned n> +struct min_rgb_comp : mln::Function_v2v< min_rgb_comp<n> > +{ + typedef mln::value::int_u<n> result; + + result operator()(const mln::value::rgb<n>& c) const + { + return mln::math::min(mln::math::min(c.red(), c.green()), c.blue()); + } +}; + + /* FIXME: Hide OpenCV-Milena conversion operations; or better, wrap the `Iplimage' data structure in a Milena image so that it can be processed directly. */ @@ -40,32 +70,51 @@ void process(IplImage* cv_ima) { using namespace mln; + using mln::value::int_u8; + using mln::value::rgb8; + + char* data = cv_ima->imageData; + int height = cv_ima->height; + int width = cv_ima->width; + int widthstep = cv_ima->widthStep; + int nchannels = cv_ima->nChannels; + + // Convert `cv_ima' into a Milena image. + image2d<rgb8> mln_ima(height, width); + for (int row = 0; row < height; ++row) + for (int col = 0; col < width; ++col) + for (int chan = 0; chan < nchannels; ++chan) + opt::at(mln_ima, row, col).comp(chan) = + (unsigned char) data[row * widthstep + col * nchannels + chan]; + + // Detect white objects having a minimal area. + image2d<bool> bin; + initialize(bin, mln_ima); + data::fill(bin, false); + int_u8 threshold = 160; + data::fill((bin | (pw::value(min_rgb_comp<8>() << mln_ima) + >= pw::cst(threshold))).rw(), + true); + // For a lack of a better characterization. + unsigned area = 15000; + image2d<bool> objects = morpho::opening::area(bin, c4(), area); + // Find contours. + image2d<bool> outline = morpho::gradient_external(objects, win_c4p()); + // Enlarge them. + image2d<bool> contours = morpho::dilation(outline, win_c4p()); + + // Alter the original OpenCV image `cv_ima'. + for (int row = 0; row < height; ++row) + for (int col = 0; col < width; ++col) + if (char(opt::at(contours, row, col))) + { + /* Paint the pixel in red.
- // Convert `cv_ima' into a Milena image... - image2dvalue::rgb8 mln_ima(cv_ima->height, cv_ima->width); - for (int row = 0; row < cv_ima->height; ++row) - for (int col = 0; col < cv_ima->width; ++col) - for (int chan = 0; chan < cv_ima->nChannels; ++chan) - { - opt::at(mln_ima, row, col).comp(chan) = - (unsigned char) cv_ima->imageData[row * cv_ima->widthStep - + col * cv_ima->nChannels - + chan]; - } - - // ...do something with it... - mln_piter_(image2dvalue::rgb8) p(mln_ima.domain()); - for_all(p) - convert::from_to(fun::v2v::rgb_to_int_u<8>()(mln_ima(p)), mln_ima(p)); - - // ...and paste `mln_ima' back into the OpenCV image `cv_ima'. - for (int row = 0; row < cv_ima->height; ++row) - for (int col = 0; col < cv_ima->width; ++col) - for (int chan = 0; chan < cv_ima->nChannels; ++chan) - { - cv_ima->imageData[row * cv_ima->widthStep - + col * cv_ima->nChannels - + chan] = - (char) opt::at(mln_ima, row, col).comp(chan); - } + In the initial test environment, the frame was encoded + as a BGR image. */ + int pos = row * widthstep + col * nchannels; + data[pos + 0] = mln_min(int_u8); // Channel 0: Blue. + data[pos + 1] = mln_min(int_u8); // Channel 1: Green. + data[pos + 2] = mln_max(int_u8); // Channel 2: Red. + } }