iOS下使用OpenCV进行图像识别(不使用Haar和LBP,不需要大量机器学习)

本文翻译自iOS  Application  Development  with  OpenCV 3

在OpenCV中我们可以使用大量图片进行机器学习来识别图像,但这同样会花费大量的时间。接下来我们来看一下如何只进行少量的机器训练便可以识别图像。

我们分为以下5个步骤来进行:

1、将图像分割为前景和背景区域。前景区域为黑色,背景为白色。

2、使前景区域边缘更加平滑

3、检测前景的形状,并裁剪出前景的矩形图像。

4、对裁剪出来的前景图像进行直方图分析。

5、对图像进行特征匹配(或叫关键点匹配)

1、blob detection(将图像分割为前景区域和背景区域)

通常,一个blob detection需要解决以下几个问题:

1、Segmentation:区分前景区域和背景区域

2、Edge  detection:区分边缘部分和不是边缘部分

3、Contour analysis:简单的代表边缘,以便于我们形成一个几何图形

我们使用cv::inRang方法来区分前景区域和背景区域,在使用cv::Canny方法来画出边缘部分,最后使用cv::findContours来找到这些轮廓,并使用cv::boundingRect来画出矩形框住这些轮廓。

2、直方图分析

直方图是图像中每种颜色出现次数的计数。 通常,我们不会分别计算所有可能的颜色; 相反,我们将相似的颜色组合成一个bin。 对于较少数量的bin,直方图占用较少的内存并提供较粗略的比较基础。 我们选择每个通道32个bin(或总共32 ^ 3 = 32678(bin))。

直方图的比较可以告诉我们两个图像是否包含相似的颜色。 仅这种相似性并不一定意味着图像包含匹配的对象。 例如,银叉和银币可以具有类似的直方图。

OpenCV提供函数cv :: calcHist和cv :: compareHist来计算直方图并测量它们的相似性。

3、特征匹配

OpenCV提供了SURF detector/extractor配上FLANN matcher,和ORB detector/extractor配上brute-force matcher来实现特征匹配。

SURF有被申请专利,所以请不要用于商业用途,这个方法在opencv_contrib中通过cv::xfeature2d::SURF实现。如果我们没有导入opencv_contrib的话,我们可以用ORB,这个方法在cv::ORB类中实现。

以下是项目相关代码

创建Blob描述

BlobDescriptor BlobClassifier::createBlobDescriptor(const Blob &blob) const {

      const cv::Mat &mat = blob.getMat();

      int numChannels = mat.channels();

      // Calculate the histogram of the blob's image.

    cv::Mat histogram;

    int channels[] = { 0, 1, 2 };

    int numBins[] = { HISTOGRAM_NUM_BINS_PER_CHANNEL, HISTOGRAM_NUM_BINS_PER_CHANNEL, HISTOGRAM_NUM_BINS_PER_CHANNEL };

    float range[] = { 0.0f, 256.0f };

    const float *ranges[] = { range, range, range };

    cv::calcHist(&mat, 1, channels, cv::Mat(), histogram, 3, numBins, ranges);


    // Normalize the histogram.

    histogram *= (1.0f / (mat.rows * mat.cols));

    // Convert the blob's image to grayscale.

     cv::Mat grayMat; switch (numChannels) {

                case 4:

                          cv::cvtColor(mat, grayMat, cv::COLOR_BGRA2GRAY);

                           break;

                default:

                           cv::cvtColor(mat, grayMat, cv::COLOR_BGR2GRAY);

                            break;

          }


            // Adaptively equalize the grayscale image to enhance local contrast.  

            clahe->apply(grayMat, grayMat);

             // Detect features in the grayscale image. std::vector keypoints;     

             featureDetectorAndDescriptorExtractor->detect(grayMat, keypoints);

              // Extract descriptors of the features.

                cv::Mat keypointDescriptors;

                featureDetectorAndDescriptorExtractor->compute(grayMat, keypoints, keypointDescriptors);

                return BlobDescriptor(histogram, keypointDescriptors, blob.getLabel());

}

计算两个Blob之间的Distance

float BlobClassifier::findDistance(const BlobDescriptor &detectedBlobDescriptor, const BlobDescriptor &referenceBlobDescriptor) const {

         // Calculate the histogram distance.

         float histogramDistance = (float)cv::compareHist(detectedBlobDescriptor.getNormalizedHistogram(), referenceBlobDescriptor.getNormalizedHistogram(), HISTOGRAM_COMPARISON_METHOD);

         // Calculate the keypoint matching distance.

         float keypointMatchingDistance = 0.0f;

          std::vector keypointMatches;

    descriptorMatcher->match(detectedBlobDescriptor.getKeypointDescriptors(), referenceBlobDescriptor.getKeypointDescriptors(), keypointMatches);

         for (const cv::DMatch &keypointMatch : keypointMatches) {

        keypointMatchingDistance += keypointMatch.distance;

    }


    return histogramDistance * HISTOGRAM_DISTANCE_WEIGHT + keypointMatchingDistance * KEYPOINT_MATCHING_DISTANCE_WEIGHT;

}

检测Blob

void BlobDetector::detect(cv::Mat &image, std::vector &blobs, double resizeFactor, bool draw){         

           blobs.clear();

           if (resizeFactor == 1.0) {

                         createMask(image);

           } else {

                   cv::resize(image, resizedImage, cv::Size(), resizeFactor, resizeFactor, cv::INTER_AREA);

                   createMask(resizedImage);

            }

           // Find the edges in the mask.

           cv::Canny(mask, edges, 191, 255);

            // Find the contours of the edges.

             cv::findContours(edges, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

             std::vector   rects;

             int blobMinSize = (int)(MIN(image.rows, image.cols) * BLOB_RELATIVE_MIN_SIZE_IN_IMAGE);

            for (std::vector contour : contours) {


                        // Find the contour's bounding rectangle.

                        cv::Rect rect = cv::boundingRect(contour);


                       // Restore the bounding rectangle to the original scale.

                       rect.x /= resizeFactor;

                        rect.y /= resizeFactor;

                       rect.width /= resizeFactor;

                        rect.height /= resizeFactor;


                       if (rect.width < blobMinSize || rect.height < blobMinSize) {

                                 continue;

                      }


                      // Create the blob from the sub-image inside the bounding rectangle.

                       blobs.push_back(Blob(cv::Mat(image, rect)));


                       // Remember the bounding rectangle in order to draw it later.

                      rects.push_back(rect);

          }


           if (draw) {

                  // Draw the bounding rectangles.

                     for (const cv::Rect &rect : rects) {

                    cv::rectangle(image, rect.tl(), rect.br(), DRAW_RECT_COLOR);

              }

    }

}

创建mask

void BlobDetector::createMask(const cv::Mat &image) {


    // Find the image's mean color.

    // Presumably, this is the background color.

    // Also find the standard deviation.

    cv::Scalar meanColor;

    cv::Scalar stdDevColor;

    cv::meanStdDev(image, meanColor, stdDevColor);


    // Create a mask based on a range around the mean color.

    cv::Scalar halfRange = MASK_STD_DEVS_FROM_MEAN * stdDevColor;

    cv::Scalar lowerBound = meanColor - halfRange;

    cv::Scalar upperBound = meanColor + halfRange;

    cv::inRange(image, lowerBound, upperBound, mask);


    // Erode the mask to merge neighboring blobs.

    int kernelWidth = (int)(MIN(image.cols, image.rows) * MASK_EROSION_KERNEL_RELATIVE_SIZE_IN_IMAGE);

    if (kernelWidth > 0) {

        cv::Size kernelSize(kernelWidth, kernelWidth);

        cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, kernelSize);

        cv::erode(mask, mask, kernel, cv::Point(-1, -1), MASK_NUM_EROSION_ITERATIONS);

    }

}

你可能感兴趣的:(iOS下使用OpenCV进行图像识别(不使用Haar和LBP,不需要大量机器学习))