2017/10/23

Reading barcode with zxing-cpp and OpenCV Volume 2

1년전쯤 작성한 글인데 ZXing-cpp 와 OpenCV를 이용한 바코드 인식 글(https://goo.gl/kRtaYn)을 올렸었는데 관련 댓글이 달리고 다른 활용처가 있어서 다시 시도를 해 보았다.
물론 한번에 되지는 않았다.

ZXing-cpp 빌드는 문제가 없었으나 이를 사용하는경우 아래와 같은 링크 에러가 발생하였다.
error LNK2005: "public: static unsigned int const zxing::DecodeHints::CHARACTER_SET" (?CHARACTER_SET@DecodeHints@zxing@@2IB) already defined in xxx.obj

인터넷 검색을 통해 해결책(https://goo.gl/WkEEcY)을 찾았다.
문제는 Visual C++ 컴파일러만 발생한 것으로 문제가 발생한 부분을 아래와 같이 수정을 하면 해결이 되었다.

Old code:

DecodeHints.h:
static const DecodeHintType CHARACTER_SET = 1 << 30;
DecodeHints.cpp:
const DecodeHintType DecodeHints::CHARACTER_SET;

has been replaced by:

DecodeHints.h:
static const DecodeHintType CHARACTER_SET;
DecodeHints.cpp:
const DecodeHintType DecodeHints::CHARACTER_SET = 1 << 30;

그리고 이전 코드에서는 컴파일에 문제가 있어서 최신 ZXing에 포함되어 있는 opencv cli 코드(zxing-cpp-master\opencv-cli\src\main.cpp)를 이용하여 테스트 해 보았다.

컴파일및 링크는 문제가 없었으나 인식률은 그리 좋아 보이지 않았다.
물론 ZXing을 얼마나 이해하고 잘 쓰는지가 문제가 될 수 있어 좀 더 분석하고 파고들어봐야 할 것 같다.

아래 코드는 테스트에 사용한 코드 이다.
 cv::String strFile( CStringA(dlgFile.GetPathName()) );
 cv::Mat matImage = cv::imread(strFile);

 cv::Mat matGray;
 cv::cvtColor( matImage, matGray, CV_RGB2GRAY );

 Ref<LuminanceSource> source = MatSource::create(matGray);
  
 bool bHybrid = true;
 string strResult;
 try
 {
  // Create luminance  source
  Ref<LuminanceSource> source = MatSource::create(matGray);

  // Search for QR code
  Ref<Reader> reader;

  if (true)
  {
   reader.reset(new MultiFormatReader);
  }
  else
  {
   reader.reset(new QRCodeReader);
  }

  Ref<Binarizer> binarizer(new GlobalHistogramBinarizer(source));
  Ref<BinaryBitmap> bitmap(new BinaryBitmap(binarizer));
  Ref<Result> result(reader->decode(bitmap, DecodeHints(DecodeHints::DEFAULT_HINT)));

  // Get result point count
  int resultPointCount = result->getResultPoints()->size();

  for (int j = 0; j < resultPointCount; j++)
  {
   // Draw circle
   circle(matImage
    , CvPoint(result->getResultPoints()[j]->getX(), result->getResultPoints()[j]->getY())
    , 10, cv::Scalar( 110, 220, 0 ), 2);
  }

  // Draw boundary on image
  if (resultPointCount > 1)
  {
   for (int j = 0; j < resultPointCount; j++)
   {
    // Get start result point
    Ref<ResultPoint> previousResultPoint = (j > 0) ? result->getResultPoints()[j - 1] : result->getResultPoints()[resultPointCount - 1];

    // Draw line
    //line(matImage, toCvPoint(previousResultPoint), toCvPoint(result->getResultPoints()[j]), cv::Scalar( 110, 220, 0 ),  2, 8 );
    line(matImage, CvPoint(previousResultPoint->getX(), previousResultPoint->getY())
     , CvPoint(result->getResultPoints()[j]->getX(), result->getResultPoints()[j]->getY())
     , cv::Scalar( 110, 220, 0 ), 2, 8 );

    // Update previous point
    previousResultPoint = result->getResultPoints()[j];
   }
  }

  if (resultPointCount > 0)
  {
   TRACE("Found Barcode : %s\n", result->getText()->getText().c_str());
   // Draw text
   putText(matImage, result->getText()->getText()
    , CvPoint(result->getResultPoints()[0]->getX(), result->getResultPoints()[0]->getY())
    , cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar( 110, 220, 0 ));
  }

  // Show captured image
  cv::namedWindow( "ZXing", cv::WINDOW_AUTOSIZE );
  cv::imshow("ZXing", matImage);
 }
 catch (const ReaderException& e) 
 {
  strResult = "zxing::ReaderException: " + string(e.what());
 }
 catch (const zxing::IllegalArgumentException& e) 
 {
  strResult = "zxing::IllegalArgumentException: " + string(e.what());
 }
 catch (const zxing::Exception& e) 
 {
  strResult = "zxing::Exception: " + string(e.what());
 }
 catch (const std::exception& e) 
 {
  strResult = "std::exception: " + string(e.what());
 }

 AfxMessageBox(CString(strResult.c_str()));