Add rotation functionality and CLAHE preprocessing in Barcode module 56/257856/1 submit/tizen/20210506.010918
authorTae-Young Chung <ty83.chung@samsung.com>
Mon, 19 Apr 2021 08:21:56 +0000 (17:21 +0900)
committerTae-Young Chung <ty83.chung@samsung.com>
Tue, 4 May 2021 09:17:32 +0000 (09:17 +0000)
Change-Id: I806ad1e20a2b3a436b14b26d41218dbfa3c7fe32
Signed-off-by: Tae-Young Chung <ty83.chung@samsung.com>
(cherry picked from commit a9bcad59c6c72b617a836e0e4687f0a9ef52a7e6)

include/mv_barcode_detect.h
mv_barcode/barcode_detector/CMakeLists.txt
mv_barcode/barcode_detector/include/BarcodeUtils.h
mv_barcode/barcode_detector/src/BarcodeUtils.cpp
mv_barcode/barcode_detector/src/mv_barcode_detect_open.cpp

index bb9c893..61944ba 100644 (file)
@@ -44,6 +44,52 @@ extern "C" {
 #define MV_BARCODE_DETECT_ATTR_TARGET "MV_BARCODE_DETECT_ATTR_TARGET"  /**< Target: 0-all, 1-1D, 2-2D*/
 
 /**
+ * @brief Use MV_BARCODE_DETECT_ATTR_ROTATION_DEGREE
+ *        to set rotation degree attribute of the engine configuration.
+ *
+ * @since_tizen 6.5
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_COUNT
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION
+ */
+#define MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES "MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES"
+
+/**
+ * @brief Use MV_BARCODE_DETECT_ATTR_ROTATION_COUNT
+ *        to set rotation count attribute of the engine configuration.
+ * @details If #MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES, #MV_BARCODE_DETECT_ATTR_ROTATION_COUNT,
+ *          and #MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION are set to
+ *          20, 9, and MV_BARCODE_DETECT_ATTR_ROTATION_CLOCKWISE, repectively,\n
+ *          detection will be tried with 20, 40, 60, ... 180 degrees as well as 0 degrees.\n
+ *          Similarly, -20, -40, -60, ... will be tried for #MV_BARCODE_DETECT_ATTR_ROTATION_COUNTER_CLOCKWISE and
+ *          -20, +20, -40, +40, ... will be tried iteratively for #MV_BARCODE_DETECT_ATTR_ROTATION_ALL.
+ *
+ * @since_tizen 6.5
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION
+ * @see mv_barcode_detect_attr_rotation_direction_e
+ */
+#define MV_BARCODE_DETECT_ATTR_ROTATION_COUNT "MV_BARCODE_DETECT_ATTR_ROTATION_COUNT"
+
+/**
+ * @brief Use MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION
+ *        to set rotation direction attribute of the engine configuration.
+ *
+ * @since_tizen 6.5
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_COUNT
+ * @see mv_barcode_detect_attr_rotation_direction_e
+ */
+#define MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION "MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION"
+
+/**
+ * @brief Use MV_BARCODE_DETECT_ATTR_USE_ENHANCEMENT
+ *        to enable image quality enhancement attribute of the engine configuration.
+ *
+ * @since_tizen 6.5
+ */
+#define MV_BARCODE_DETECT_ATTR_USE_ENHANCEMENT "MV_BARCODE_DETECT_ATTR_USE_ENHANCEMENT"
+
+/**
  * @brief Enumeration to target attribute
  *
  * @since_tizen @if MOBILE 2.4 @else 3.0 @endif
@@ -54,6 +100,23 @@ typedef enum {
        MV_BARCODE_DETECT_ATTR_TARGET_2D_BARCODE,   /**< 2D barcode only */
 } mv_barcode_detect_attr_target_e;
 
+/**
+ * @brief Enumeration for rotation direction attribute.
+ * @details Set one of rotation direction, which are:\n
+ *          #MV_BARCODE_DETECT_ATTR_ROTATION_CLOCKWISE - clockwise rotation,\n
+ *          #MV_BARCODE_DETECT_ATTR_ROTATION_COUNTER_CLOCKWISE - counter clockwise rotation,\n
+ *          #MV_BARCODE_DETECT_ATTR_ROTATION_ALL - clockwise and counter clockwise rotation iteratively.\n
+ *
+ * @since_tizen 6.5
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION
+ * @see #MV_BARCODE_DETECT_ATTR_ROTATION_COUNT
+ */
+typedef enum {
+       MV_BARCODE_DETECT_ATTR_ROTATION_CLOCKWISE,           /**< Clockwise */
+       MV_BARCODE_DETECT_ATTR_ROTATION_COUNTER_CLOCKWISE,   /**< Counter clockwise */
+       MV_BARCODE_DETECT_ATTR_ROTATION_ALL,   /**< Clockwise and counter clockwise */
+} mv_barcode_detect_attr_rotation_direction_e;
+
 
 /**
  * @brief Called when barcode detection is completed.
index 8ff4612..123d3fb 100644 (file)
@@ -15,12 +15,20 @@ file(GLOB MV_BARCODE_DET_INC_LIST "${PROJECT_SOURCE_DIR}/include/*.h")
 file(GLOB MV_BARCODE_DET_SRC_LIST "${PROJECT_SOURCE_DIR}/src/*.cpp"
                                   "${PROJECT_SOURCE_DIR}/src/*.c")
 
+find_package(OpenCV REQUIRED core imgproc)
+if(NOT OpenCV_FOUND)
+    message(SEND_ERROR "Failed to find OpenCV")
+    return()
+else()
+    include_directories(${OpenCV_INCLUDE_DIRS})
+endif()
+
 if(FORCED_STATIC_BUILD)
     add_library(${PROJECT_NAME} STATIC ${MV_BARCODE_DET_INC_LIST} ${MV_BARCODE_DET_SRC_LIST})
 else()
     add_library(${PROJECT_NAME} SHARED ${MV_BARCODE_DET_INC_LIST} ${MV_BARCODE_DET_SRC_LIST})
 endif()
 
-target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} zbar dlog)
+target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} ${MV_COMMON_LIB_NAME} zbar dlog)
 
 INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR})
index 58f30c6..4b06ae5 100644 (file)
@@ -18,6 +18,8 @@
 #define __MEDIA_VISION_BARCODE_UTILS_H__
 
 #include "mv_common.h"
+#include <opencv2/core/core.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
 
 namespace zbar {
        class Image;
@@ -37,6 +39,17 @@ namespace Barcode {
  */
 int convertSourceMV2Zbar(mv_source_h mvSource, zbar::Image& zbarSource);
 
+/**
+ * @brief    This function converts media vision image handle to cv::Mat with gray color.
+ *
+ * @since_tizen 6.5
+ * @param [in]  mvSource      Media vision image handle
+ * @param [out] cv::Mat
+ * @return @c MEDIA_VISION_ERROR_NONE on success,
+           otherwise a negative error value
+ */
+int convertSourceMV2GrayCV(mv_source_h mvSource, cv::Mat& cvSource);
+
 } /* Barcode */
 } /* MediaVision */
 
index b8c70d9..6f4c7d2 100644 (file)
@@ -103,5 +103,105 @@ int convertSourceMV2Zbar(mv_source_h mvSource, zbar::Image& zbarSource)
        return err;
 }
 
+int convertSourceMV2GrayCV(mv_source_h mvSource, cv::Mat& cvSource)
+{
+       MEDIA_VISION_INSTANCE_CHECK(mvSource);
+
+       int depth = CV_8U; // Default depth. 1 byte for channel.
+       unsigned int channelsNumber = 0u;
+       unsigned int width = 0u, height = 0u;
+       unsigned int bufferSize = 0u;
+       unsigned char *buffer = NULL;
+
+       mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+
+       MEDIA_VISION_ASSERT(mv_source_get_width(mvSource, &width),
+                       "Failed to get the width.");
+       MEDIA_VISION_ASSERT(mv_source_get_height(mvSource, &height),
+                       "Failed to get the height.");
+       MEDIA_VISION_ASSERT(mv_source_get_colorspace(mvSource, &colorspace),
+                       "Failed to get the colorspace.");
+       MEDIA_VISION_ASSERT(mv_source_get_buffer(mvSource, &buffer, &bufferSize),
+                       "Failed to get the buffer size.");
+
+       int conversionType = -1; /* Type of conversion from given colorspace to gray */
+       switch(colorspace) {
+       case MEDIA_VISION_COLORSPACE_INVALID:
+               LOGE("Error: mv_source has invalid colorspace.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       case MEDIA_VISION_COLORSPACE_Y800:
+               channelsNumber = 1;
+               /* Without convertion */
+               break;
+       case MEDIA_VISION_COLORSPACE_I420:
+               channelsNumber = 1;
+               height *= 1.5;
+               conversionType = cv::COLOR_YUV2GRAY_I420;
+               break;
+       case MEDIA_VISION_COLORSPACE_NV12:
+               channelsNumber = 1;
+               height *= 1.5;
+               conversionType = cv::COLOR_YUV2GRAY_NV12;
+               break;
+       case MEDIA_VISION_COLORSPACE_YV12:
+               channelsNumber = 1;
+               height *= 1.5;
+               conversionType = cv::COLOR_YUV2GRAY_YV12;
+               break;
+       case MEDIA_VISION_COLORSPACE_NV21:
+               channelsNumber = 1;
+               height *= 1.5;
+               conversionType = cv::COLOR_YUV2GRAY_NV21;
+               break;
+       case MEDIA_VISION_COLORSPACE_YUYV:
+               channelsNumber = 2;
+               conversionType = cv::COLOR_YUV2GRAY_YUYV;
+               break;
+       case MEDIA_VISION_COLORSPACE_UYVY:
+               channelsNumber = 2;
+               conversionType = cv::COLOR_YUV2GRAY_UYVY;
+               break;
+       case MEDIA_VISION_COLORSPACE_422P:
+               channelsNumber = 2;
+               conversionType = cv::COLOR_YUV2GRAY_Y422;
+               break;
+       case MEDIA_VISION_COLORSPACE_RGB565:
+               channelsNumber = 2;
+               conversionType = cv::COLOR_BGR5652GRAY;
+               break;
+       case MEDIA_VISION_COLORSPACE_RGB888:
+               channelsNumber = 3;
+               conversionType = cv::COLOR_RGB2GRAY;
+               break;
+       case MEDIA_VISION_COLORSPACE_RGBA:
+               channelsNumber = 4;
+               conversionType = cv::COLOR_RGBA2GRAY;
+               break;
+       default:
+               LOGE("Error: mv_source has unsupported colorspace.");
+               return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
+       }
+
+       if (conversionType == -1) {/* Without conversion */
+               cvSource = cv::Mat(cv::Size(width, height),
+                                       CV_MAKETYPE(depth, channelsNumber), buffer).clone();
+       } else {/* With conversion */
+               /* Class for representation the given image as cv::Mat before conversion */
+               cv::Mat origin;
+
+               try {
+                       origin = cv::Mat(cv::Size(width, height),
+                                               CV_MAKETYPE(depth, channelsNumber), buffer);
+
+                       cv::cvtColor(origin, cvSource, conversionType);
+               } catch (const cv::Exception &e) {
+                       LOGE("Failed to cvtColor with %s", e.what());
+                       return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
+               }
+       }
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
 } /* Barcode */
 } /* MediaVision */
index 0d4be8e..54dee63 100644 (file)
@@ -20,7 +20,9 @@
 #include "BarcodeUtils.h"
 
 #include <mv_private.h>
-
+#include <vector>
+#include <opencv2/core/core.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
 #include <zbar.h>
 
 using namespace MediaVision::Barcode;
@@ -35,29 +37,16 @@ int mv_barcode_detect_open(
        if (!source || !detect_cb)
                return MEDIA_VISION_ERROR_INVALID_PARAMETER;
 
-       zbar::Image image;
-       int err = convertSourceMV2Zbar(source, image);
-       if (err != MEDIA_VISION_ERROR_NONE) {
-               LOGW("convertSourceMV2Zbar failed");
-               return err;
-       }
-
-       zbar::Image greyImage = image.convert("Y800");
-       if (!greyImage.get_data()) {
-               LOGE("fail to image convert by zbar");
-               return MEDIA_VISION_ERROR_INVALID_OPERATION;
-       }
-
-       greyImage.set_crop(roi.point.x, roi.point.y, roi.width, roi.height);
        zbar::ImageScanner scanner;
 
        int target_val;
-       err = mv_engine_config_get_int_attribute(
+       int err = mv_engine_config_get_int_attribute(
                        engine_cfg,
                        "MV_BARCODE_DETECT_ATTR_TARGET",
                &target_val);
        if (err != MEDIA_VISION_ERROR_NONE || engine_cfg == NULL) {
-               LOGW("mv_engine_config_get_int_attribute failed");
+               LOGW("mv_engine_config_get_int_attribute failed."
+                       "MV_BARCODE_DETECT_ATTR_TARGET_ALL is used as default");
                /* Default value */
                target_val = 0;
        }
@@ -110,65 +99,164 @@ int mv_barcode_detect_open(
                LOGW("Unavailable target value %d", target_val);
        }
 
-       int numberOfBarcodes = scanner.scan(greyImage);
-       LOGI("ZBar scanner has found %i barcodes on the mv_source_h",
-                       numberOfBarcodes);
-       mv_quadrangle_s *barcodeLocations = NULL;
-       mv_barcode_type_e *types = NULL;
-
-       if (numberOfBarcodes == 0) {
-               LOGI("Call the detect callback for 0 detected barcodes");
-               detect_cb(source, engine_cfg, barcodeLocations, NULL,
-                               types, numberOfBarcodes, user_data);
-               return MEDIA_VISION_ERROR_NONE;
-       } else if (numberOfBarcodes < 0) {
-               LOGW("Incorrect number of barcodes (%i), detection is terminated",
-                               numberOfBarcodes);
-               return MEDIA_VISION_ERROR_INTERNAL;
+       int rotateDegree = 0;
+       int rotateNumber = 0;
+       int rotateDirection = 0;
+       bool isEnhancementMode = false;
+       if (engine_cfg != NULL) {
+               err = mv_engine_config_get_int_attribute(
+                               engine_cfg,
+                               MV_BARCODE_DETECT_ATTR_ROTATION_DEGREES,
+                               &rotateDegree);
+               if (err != MEDIA_VISION_ERROR_NONE) {
+                       LOGE("mv_engine_config_get_int_attribute failed to get MV_BARCODE_DETECT_ATTR_ROTATE_DEGREES");
+                       return err;
+               }
+
+               err = mv_engine_config_get_int_attribute(
+                               engine_cfg,
+                               MV_BARCODE_DETECT_ATTR_ROTATION_COUNT,
+                               &rotateNumber);
+               if (err != MEDIA_VISION_ERROR_NONE) {
+                       LOGE("mv_engine_config_get_int_attribute failed to get MV_BARCODE_DETECT_ATTR_ROTATE_COUNT");
+                       return err;
+               }
+
+               err = mv_engine_config_get_int_attribute(
+                               engine_cfg,
+                               MV_BARCODE_DETECT_ATTR_ROTATION_DIRECTION,
+                               &rotateDirection);
+               if (err != MEDIA_VISION_ERROR_NONE) {
+                       LOGE("mv_engine_config_get_int_attribute failed to get MV_BARCODE_DETECT_ATTR_ROTATE_DIRECTION");
+                       return err;
+               }
+
+               err = mv_engine_config_get_bool_attribute(
+                               engine_cfg,
+                               MV_BARCODE_DETECT_ATTR_USE_ENHANCEMENT,
+                               &isEnhancementMode);
+               if (err != MEDIA_VISION_ERROR_NONE) {
+                       LOGE("mv_engine_config_get_bool_attribute failed to get MV_BARCODE_DETECT_ATTR_USE_ENHANCEMENT");
+                       return err;
+               }
        }
 
-       const char **messagesArray = new const char*[numberOfBarcodes];
-       barcodeLocations = new mv_quadrangle_s[numberOfBarcodes];
-       types = new mv_barcode_type_e[numberOfBarcodes];
+       cv::Mat graySource;
+       cv::Mat rotMat, rotBuffer;
+       err = convertSourceMV2GrayCV(source, graySource);
+       if (err != MEDIA_VISION_ERROR_NONE) {
+               LOGE("Failed to convertSourceMV2GrayCV[%d]", err);
+               return err;
+       }
 
-       int i = 0;
-       /* extract results and prepare them for callback passing */
-       for (zbar::SymbolIterator symbol = greyImage.symbol_begin();
-               symbol != greyImage.symbol_end();
-               ++symbol, ++i) {
-               Barcode curBarcode(*symbol);
+       cv::Mat rawBuffer = graySource(cv::Rect(roi.point.x, roi.point.y, roi.width, roi.height));
 
-               size_t messageLength = curBarcode.getMessage().size();
-               char *curMessage = new char[messageLength + 1];
-               curBarcode.getMessage().copy(curMessage, messageLength);
-               curMessage[messageLength] = '\0';
-               messagesArray[i] = curMessage;
+       if (isEnhancementMode) {
+               cv::Mat clacheBuffer;
+               cv::Ptr<cv::CLAHE> clache = cv::createCLAHE();
+               clache->setClipLimit(2.0);
+               clache->apply(rawBuffer, clacheBuffer);
 
-               types[i] = curBarcode.getType();
+               cv::threshold(clacheBuffer, rawBuffer, 240, 255, cv::THRESH_TRUNC | cv::THRESH_OTSU);
+       }
 
-               LOGI("barcode type: %d with message %s", types[i], curMessage);
-               int err = curBarcode.calculateLocation(barcodeLocations[i]);
-               if (err != MEDIA_VISION_ERROR_NONE) {
-                       LOGW("Can't determine location for barcode, detection is terminated");
-                       for (int j = 0; j <= i; ++j)
+       std::vector<std::string> barcodeMessages;
+       std::vector<mv_quadrangle_s> barcodeLocations;
+       std::vector<mv_barcode_type_e> barcodeTypes;
+       zbar::Image _image;
+
+       LOGI("%d numbers with %d degree to %d direction", rotateNumber, rotateDegree, rotateDirection);
+
+       if (rotateDirection == MV_BARCODE_DETECT_ATTR_ROTATION_ALL)
+               rotateNumber *= 2;
+
+       rotateNumber++;
+
+       int degree = 0;
+       for (int i = 0; i < rotateNumber; ++i) {
+               if (rotateDirection == MV_BARCODE_DETECT_ATTR_ROTATION_CLOCKWISE) {
+                       degree = -1 * rotateDegree * i;
+               } else if (rotateDirection == MV_BARCODE_DETECT_ATTR_ROTATION_COUNTER_CLOCKWISE) {
+                       degree = rotateDegree * i;
+               } else { // MV_BARCODE_DETECT_ATTR_ROTATE_ALL
+                       if (i%2) {
+                               degree = -1 * rotateDegree * ((i+1)/2);
+                       } else {
+                               degree =  rotateDegree * (i/2);
+                       }
+               }
+
+               rotMat = cv::getRotationMatrix2D(cv::Point((roi.width/2), (roi.height/2)), degree, 1.0);
+               warpAffine(rawBuffer, rotBuffer, rotMat, rawBuffer.size());
+
+               _image.set_format("Y800");
+               _image.set_size(rotBuffer.cols, rotBuffer.rows);
+               _image.set_data(rotBuffer.data, rotBuffer.total() * rotBuffer.elemSize());
+
+               LOGW("%p", _image.get_data());
+               if (!_image.get_data()) {
+                       LOGE("fail to get image data");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               int numberOfBarcodes = scanner.scan(_image);
+               if (numberOfBarcodes <= 0)
+                       continue;
+
+               LOGI("[rot:%d]: %d barcodes detected", i, numberOfBarcodes);
+               barcodeMessages.clear();
+               barcodeTypes.clear();
+               barcodeLocations.clear();
+               bool isDetected = true;
+               for (zbar::SymbolIterator symbol = _image.symbol_begin();
+                       symbol != _image.symbol_end(); ++symbol) {
+
+                       Barcode curBarcode(*symbol);
+                       mv_quadrangle_s location;
+                       int err = curBarcode.calculateLocation(location);
+                       if (err != MEDIA_VISION_ERROR_NONE) {
+                               isDetected = false;
+                               break;
+                       }
+
+                       barcodeMessages.push_back(curBarcode.getMessage());
+                       barcodeTypes.push_back(curBarcode.getType());
+                       barcodeLocations.push_back(location);
+               }
+
+               if (isDetected) {
+                       LOGI("Call the detect callback for %d detected barcodes", numberOfBarcodes);
+                       const char **messagesArray = new const char*[numberOfBarcodes];
+                       mv_barcode_type_e *types = new mv_barcode_type_e[numberOfBarcodes];
+                       mv_quadrangle_s *locations = new mv_quadrangle_s[numberOfBarcodes];
+
+                       for (int i = 0; i < numberOfBarcodes; ++i) {
+
+                               size_t messageLength = barcodeMessages[i].size();
+                               char *curMessage = new char[messageLength + 1];
+                               barcodeMessages[i].copy(curMessage, messageLength);
+                               curMessage[messageLength] = '\0';
+                               messagesArray[i] = curMessage;
+
+                               types[i] = barcodeTypes[i];
+                               locations[i] = barcodeLocations[i];
+                               LOGI("%d: barcode with %s with type %d", i, messagesArray[i], types[i]);
+                       }
+                       detect_cb(source, engine_cfg, locations, messagesArray, types,
+                                       numberOfBarcodes, user_data);
+                       LOGI("Clean the memory from barcodes messages and types");
+                       for (int j = 0; j < numberOfBarcodes; ++j)
                                delete[] messagesArray[j];
                        delete[] messagesArray;
-                       delete[] barcodeLocations;
+                       delete[] locations;
                        delete[] types;
-                       return err;
+
+                       return MEDIA_VISION_ERROR_NONE;
                }
        }
-
-       LOGI("Call the detect callback for %i detected barcodes", numberOfBarcodes);
-       detect_cb(source, engine_cfg, barcodeLocations, messagesArray, types,
-                       numberOfBarcodes, user_data);
-
-       LOGI("Clean the memory from barcodes messages, locations and types");
-       for (int j = 0; j < numberOfBarcodes; ++j)
-               delete[] messagesArray[j];
-       delete[] messagesArray;
-       delete[] barcodeLocations;
-       delete[] types;
+       LOGI("Call the detect callback for 0 detected barcodes");
+       detect_cb(source, engine_cfg, NULL, NULL,
+                       NULL, 0, user_data);
 
        return MEDIA_VISION_ERROR_NONE;
 }