opencv / opencv

Open Source Computer Vision Library
https://opencv.org
Apache License 2.0
79.04k stars 55.82k forks source link

Unable to Compile GAPI Code returning contours from cv::gapi::findContours #21524

Closed webbbt456 closed 2 years ago

webbbt456 commented 2 years ago
System information (version)
Detailed description

I have compiled version 4.5.5 on windows with Visual Studio. I am unable to compile code dealing with GAPI that calls findContours and returns the contours. In my example I have a conditional compilation that returns a GMat instead of contours, the code compiles.

Compiler Error from VS (Error List Window) Error C2338 Type not found testGAPI opencv\include\opencv2\gapi\util\variant.hpp 37

'''

Compiler Warning from VS (Output Window) 1>opencv\include\opencv2\gapi\util\variant.hpp(37,56): error C2338: Type not found 1>opencv\include\opencv2\gapi\util\variant.hpp(31): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<5,Target,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(31): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<4,Target,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(31): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<3,Target,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(31): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<2,Target,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(31): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<1,Target,cv::GMatP,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(45): message : see reference to class template instantiation 'cv::util::detail::type_list_index_helper<0,Target,cv::GMat,cv::GMatP,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1> with 1> [ 1> Target=int 1> ] 1>opencv\include\opencv2\gapi\util\variant.hpp(351): message : see reference to class template instantiation 'cv::util::type_list_index<int,cv::GMat,cv::GMatP,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>' being compiled 1>opencv\include\opencv2\gapi\gproto.hpp(50): message : see reference to function template instantiation 'cv::util::variant<cv::GMat,cv::GMatP,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>::variant<_Ty,void>(T &&)' being compiled 1> with 1> [ 1> _Ty=int, 1> T=int 1> ] 1>opencv\include\opencv2\gapi\gproto.hpp(51): message : see reference to function template instantiation 'cv::util::variant<cv::GMat,cv::GMatP,cv::GFrame,cv::GScalar,cv::detail::GArrayU,cv::detail::GOpaqueU>::variant<_Ty,void>(T &&)' being compiled 1> with 1> [ 1> _Ty=int, 1> T=int 1> ] 1>opencv\include\opencv2\gapi\gproto.hpp(97): message : see reference to function template instantiation 'cv::GProtoArgs cv::detail::packArgs<cv::GMat,int,int>(cv::GMat,int,int)' being compiled 1>testGAPI\testGAPI.cpp(44): message : see reference to function template instantiation 'cv::GProtoInputArgs cv::GIn<cv::GMat&,int&,int&>(cv::GMat &,int &,int &)' being compiled 1>Done building project "testGAPI.vcxproj" -- FAILED.

'''

Steps to reproduce

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>

#define DOES_NOT_COMPILE 1  // COMMENT THIS LINE TO COMPILE SUCCESSFULLY

cv::GComputation mGComputation([]() {
  cv::GMat in;
#ifdef DOES_NOT_COMPILE
  int numDilation;
  int numErosions;
#else
  int numDilation(3);
  int numErosions(3);
#endif

  cv::GMat mask(cv::gapi::gaussianBlur(in, cv::Size(11, 11), 0.0));
  mask = cv::gapi::erode3x3(mask, numErosions);
  mask = cv::gapi::dilate3x3(mask, numDilation);
#ifdef DOES_NOT_COMPILE
  cv::GArray<cv::GArray<cv::Point>> contours = cv::gapi::findContours(mask, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
  return cv::GComputation(cv::GIn(in,numDilation,numErosions), cv::GOut(contours));
#else
  return cv::GComputation(in, mask);
#endif
});

const cv::Scalar white(cv::Scalar(255));

int main(int argc, char** argv)
{
  int nDilation(3), nErosions(3), rw(60), rh(40), ofs(50), lw(1);
  int x(ofs), y(ofs),i;
  cv::Mat input(cv::Mat::zeros(640, 480, CV_8UC1));

  for (i = 0; i < 3; i++) {
    cv::rectangle(input, cv::Rect(x, y, rw, rh), white, lw, cv::FILLED);
    x += rw + ofs;
    y += rh + ofs;
  }
#ifdef DOES_NOT_COMPILE
  std::vector<std::vector<cv::Point>> contours;
  mGComputation.apply(cv::gin(input,nDilation,nErosions),cv::gout(contours));
#else
  cv::Mat output;
  mGComputation.apply(input,output);           
#endif

#ifdef DOES_NOT_COMPILE
  if (contours.empty()) {
    std::cout << "No Contours found." << std::endl;
  } else {
    std::cout << "Found " << contours.size() << " contours." << std::endl;
  }
#endif
  return 0;
}
alalek commented 2 years ago

Problem is here:

cv::GIn(in,numDilation,numErosions) numDilation,numErosions

instead of findContours.

webbbt456 commented 2 years ago

Changed code using cv::GScalar type for numDilations and numErosions. The code compiles.

I am trying to allow numDilations and numErosions. to runtime configurable parameters.

How do I convert numDilations and numErosions to int for use in erode3x3 and dilate3x3?

Or should they be sent in as parameters to the lambda (i.e. [](const int numDilations, const int numErosions) {})?

New Code

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>

cv::GComputation mGComputation([]() {
  cv::GMat in;
  cv::GScalar numDilations;  // runtime configurable value
  cv::GScalar numErosions;  // runtime configurable value

  int nd(3), ne(3);

  // need to convert numDilation to int and store in nd
  // need to convert numErosions to int and store in ne

  cv::GMat mask(cv::gapi::gaussianBlur(in, cv::Size(11, 11), 0.0));
  mask = cv::gapi::erode3x3(mask, ne);
  mask = cv::gapi::dilate3x3(mask, nd);
  cv::GArray<cv::GArray<cv::Point>> contours = cv::gapi::findContours(mask, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
  return cv::GComputation(cv::GIn(in,numDilations,numErosions), cv::GOut(contours));
});

const cv::Scalar white(cv::Scalar(255));

int main(int argc, char** argv)
{
  cv::Scalar nDilations(3), nErosions(3);
  int rw(60), rh(40), ofs(50), lw(1);
  int x(ofs), y(ofs),i;
  cv::Mat input(cv::Mat::zeros(640, 480, CV_8UC1));

  for (i = 0; i < 3; i++) {
    cv::rectangle(input, cv::Rect(x, y, rw, rh), white, lw, cv::FILLED);
    x += rw + ofs;
    y += rh + ofs;
  }
  std::vector<std::vector<cv::Point>> contours;
  mGComputation.apply(cv::gin(input,nDilations,nErosions),cv::gout(contours));

  if (contours.empty()) {
    std::cout << "No Contours found." << std::endl;
  } else {
    std::cout << "Found " << contours.size() << " contours." << std::endl;
  }
  return 0;
}
alalek commented 2 years ago

How do I convert numDilations and numErosions to int for use in erode3x3 and dilate3x3?

They are usually constants of CV algorithms. Not a computed parameters.

Instead of creation of global pipeline you could create a function with numDilations and numErosions parameters and return the related pipeline with specified values. Check sample code.

webbbt456 commented 2 years ago

Thanks for your help. I have working code as an example now.

Is it possible to convert the contours to rectangles in the lambda? cv::gapi::boundingRect only takes one contour, not a list of contours.

Latest Code

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>
#include <opencv2/highgui.hpp>
#ifdef _DEBUG
#pragma comment(lib, "opencv_highgui455d.lib")
#else
#pragma comment(lib, "opencv_highgui455.lib")
#endif

cv::GComputation CreateGComputation(int nErrosions, int nDilations) {
  return cv::GComputation([nErrosions, nDilations]() {
    cv::GMat in;
    cv::GMat mask(cv::gapi::gaussianBlur(in, cv::Size(11, 11), 0.0));
    mask = cv::gapi::erode3x3(mask, nErrosions);
    mask = cv::gapi::dilate3x3(mask, nDilations);
    cv::GArray<cv::GArray<cv::Point>> contours = cv::gapi::findContours(mask, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);

    // Is it possible to convert the contours to bounding rectangles here?
    // cv:GArray<cv::Rect> bboxes = cv::gapi::????(contours);
   // return cv::GComputation(cv::GIn(in), cv::GOut(bboxes));

    return cv::GComputation(cv::GIn(in), cv::GOut(contours));
  });
}

const cv::Scalar white(cv::Scalar(255));
const cv::Scalar black(cv::Scalar(0));
const cv::Scalar gray(cv::Scalar(175));

int main(int argc, char** argv)
{
  int nDilations{ 3 }, nErosions{ 3 };
  int rw(60), rh(40), ofs(50), lw(1),iw(640),ih(480);
  int x(ofs), y(ofs), i;
  cv::Rect bounding;
  cv::Mat img(ih, iw, CV_8UC1, black);

  cv::GComputation mGComputation = CreateGComputation(nErosions, nDilations);
  for (i = 0; i < 3; i++) {
    cv::Rect rr(x, y, rw, rh);
    cv::rectangle(img, rr, white, cv::FILLED);
    x += rw + ofs;
    y += rh + ofs;
  }
  std::vector<std::vector<cv::Point>> contours;
  mGComputation.apply(cv::gin(img), cv::gout(contours));

  if (contours.empty()) {
    std::cout << "No Contours found." << std::endl;
  } else {
    std::cout << "Found " << contours.size() << " contours." << std::endl;
    for (auto c : contours) {
      bounding = cv::boundingRect(c);
      cv::rectangle(img, bounding, gray, 3);
    }
    char win1[] = "Contour Image";
    cv::imshow(win1, img);
    cv::moveWindow(win1, iw, 200);
    cv::waitKey(0);

  }
  mGComputation.apply(cv::gin(img), cv::gout(contours));

  if (contours.empty()) {
    std::cout << "No Contours found." << std::endl;
  } else {
    std::cout << "Found " << contours.size() << " contours." << std::endl;
    for (auto c : contours) {
      bounding = cv::boundingRect(c);
      cv::rectangle(img, bounding, gray, 3);
    }
    char win2[] = "Contour Image 2";
    cv::imshow(win2, img);
    cv::moveWindow(win2, iw*2, 200);
    cv::waitKey(0);
  }
  return 0;
}
webbbt456 commented 2 years ago

Thanks again for your help. I have figured out the last piece of the puzzle.

Latest Code

#include <iostream>
#include <functional>
#include <unordered_map>
#include <utility>
#include <vector>

#include <opencv2/highgui.hpp>

#include "opencv2/gapi/cpu/gcpukernel.hpp"

#ifdef _DEBUG
#pragma comment(lib, "opencv_highgui455d.lib")
#else
#pragma comment(lib, "opencv_highgui455.lib")
#endif

namespace custom {

  G_API_OP(ToBoundingRects, <cv::GArray<cv::Rect>(cv::GArray<cv::GArray<cv::Point>>)>, "custom.fd_ToBoundingRects") {
    static cv::GArrayDesc outMeta(const cv::GArrayDesc&) {
      // This function is required for G-API engine to figure out
      // what the output format is, given the input parameters.
      // Since the output is an array (with a specific type),
      // there's nothing to describe.
      return cv::empty_array_desc();
    }
  };

  GAPI_OCV_KERNEL(OCVToBoundingRects, ToBoundingRects) {
    static void run(const std::vector<std::vector<cv::Point>> &in_contours,
                    std::vector<cv::Rect> &out_boundingRects) {
      out_boundingRects.clear();
      cv::Rect br;
      for (auto c : in_contours) {
        br = cv::boundingRect(c);
        out_boundingRects.push_back(br);
      }
    }
  };

} // namespace custom

cv::GComputation CreateGComputation(int nErrosions, int nDilations) {
  return cv::GComputation([nErrosions, nDilations]() {
    cv::GMat in;
    cv::GMat mask(cv::gapi::gaussianBlur(in, cv::Size(11, 11), 0.0));
    mask = cv::gapi::erode3x3(mask, nErrosions);
    mask = cv::gapi::dilate3x3(mask, nDilations);
    cv::GArray<cv::GArray<cv::Point>> contours = cv::gapi::findContours(mask, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
    cv::GArray<cv::Rect> boundingRects = custom::ToBoundingRects::on(contours);
    return cv::GComputation(cv::GIn(in), cv::GOut(boundingRects));
  });
}

const cv::Scalar white(cv::Scalar(255));
const cv::Scalar black(cv::Scalar(0));
const cv::Scalar gray(cv::Scalar(175));

int main(int argc, char** argv)
{
  int nDilations{ 3 }, nErosions{ 3 };
  int rw(60), rh(40), ofs(50), lw(1),iw(640),ih(480);
  int x(ofs), y(ofs), i;
  cv::Mat img(ih, iw, CV_8UC1, black);

  cv::gapi::GKernelPackage kernels = cv::gapi::kernels<custom::OCVToBoundingRects>();

  cv::GComputation mGComputation = CreateGComputation(nErosions, nDilations);
  for (i = 0; i < 3; i++) {
    cv::Rect rr(x, y, rw, rh);
    cv::rectangle(img, rr, white, cv::FILLED);
    x += rw + ofs;
    y += rh + ofs;
  }
  std::vector<cv::Rect> boundingRects;

  mGComputation.apply(cv::gin(img), cv::gout(boundingRects),cv::compile_args(kernels));

  if (boundingRects.empty()) {
    std::cout << "No Bounding Rectangles found." << std::endl;
  } else {
    std::cout << "Found " << boundingRects.size() << " Bounding Rectangles." << std::endl;
    for (auto bounding : boundingRects) {
      cv::rectangle(img, bounding, gray, 3);
    }
    char win1[] = "Bounding Rectangle Image 1";
    cv::imshow(win1, img);
    cv::moveWindow(win1, iw, 200);
    cv::waitKey(0);
  }

  x -= (ofs + rw);
  y = ofs;
  for (i = 0; i < 3; i++) {
    cv::Rect rr(x, y, rw, rh);
    cv::rectangle(img, rr, white, cv::FILLED);
    x += rw + ofs;
    y += rh + ofs;
  }

  mGComputation.apply(cv::gin(img), cv::gout(boundingRects),cv::compile_args(kernels));
  if (boundingRects.empty()) {
    std::cout << "No Bounding Rectangles found." << std::endl;
  } else {
    std::cout << "Found " << boundingRects.size() << " Bounding Rectangles." << std::endl;
    for (auto bounding : boundingRects) {
      cv::rectangle(img, bounding, gray, 3);
    }
    char win2[] = "Bounding Rectangle Image 2";
    cv::imshow(win2, img);
    cv::moveWindow(win2, iw*2, 200);
    cv::waitKey(0);
  }

  return 0;
}