iOS下使用OpenCV進行圖像識別(不使用Haar和LBP,不需要大量機器學習)

本文翻譯自iOS? Application? Development? with? OpenCV 3

在OpenCV中我們可以使用大量圖片進行機器學習來識別圖像厌均,但這同樣會花費大量的時間管行。接下來我們來看一下如何只進行少量的機器訓練便可以識別圖像。

我們分為以下5個步驟來進行:

1策添、將圖像分割為前景和背景區(qū)域。前景區(qū)域為黑色逸爵,背景為白色幅疼。

2、使前景區(qū)域邊緣更加平滑

3偷仿、檢測前景的形狀哩簿,并裁剪出前景的矩形圖像。

4酝静、對裁剪出來的前景圖像進行直方圖分析节榜。

5、對圖像進行特征匹配(或叫關鍵點匹配)

1别智、blob detection(將圖像分割為前景區(qū)域和背景區(qū)域)

通常宗苍,一個blob detection需要解決以下幾個問題:

1、Segmentation:區(qū)分前景區(qū)域和背景區(qū)域

2薄榛、Edge? detection:區(qū)分邊緣部分和不是邊緣部分

3讳窟、Contour analysis:簡單的代表邊緣,以便于我們形成一個幾何圖形

我們使用cv::inRang方法來區(qū)分前景區(qū)域和背景區(qū)域敞恋,在使用cv::Canny方法來畫出邊緣部分丽啡,最后使用cv::findContours來找到這些輪廓,并使用cv::boundingRect來畫出矩形框住這些輪廓硬猫。

2补箍、直方圖分析

直方圖是圖像中每種顏色出現(xiàn)次數(shù)的計數(shù)。 通常啸蜜,我們不會分別計算所有可能的顏色; 相反坑雅,我們將相似的顏色組合成一個bin。 對于較少數(shù)量的bin衬横,直方圖占用較少的內(nèi)存并提供較粗略的比較基礎裹粤。 我們選擇每個通道32個bin(或總共32 ^ 3 = 32678(bin))。

直方圖的比較可以告訴我們兩個圖像是否包含相似的顏色蜂林。 僅這種相似性并不一定意味著圖像包含匹配的對象遥诉。 例如,銀叉和銀幣可以具有類似的直方圖噪叙。

OpenCV提供函數(shù)cv :: calcHist和cv :: compareHist來計算直方圖并測量它們的相似性突那。

3、特征匹配

OpenCV提供了SURF detector/extractor配上FLANN matcher,和ORB detector/extractor配上brute-force matcher來實現(xiàn)特征匹配构眯。

SURF有被申請專利愕难,所以請不要用于商業(yè)用途,這個方法在opencv_contrib中通過cv::xfeature2d::SURF實現(xiàn)惫霸。如果我們沒有導入opencv_contrib的話猫缭,我們可以用ORB,這個方法在cv::ORB類中實現(xiàn)。

以下是項目相關代碼

創(chuàng)建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<cv::KeyPoint> 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<cv::DMatch> 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<Blob> &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 <cv::Rect>? rects;

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

??????????? for (std::vector<cv::Point> 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);

? ? ? ? ? ? ? }

? ? }

}

創(chuàng)建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);

? ? }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壹店,一起剝皮案震驚了整個濱河市猜丹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硅卢,老刑警劉巖射窒,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藏杖,死亡現(xiàn)場離奇詭異,居然都是意外死亡脉顿,警方通過查閱死者的電腦和手機蝌麸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艾疟,“玉大人来吩,你說我怎么就攤上這事”卫常” “怎么了弟疆?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盗冷。 經(jīng)常有香客問我怠苔,道長,這世上最難降的妖魔是什么仪糖? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任嘀略,我火速辦了婚禮,結(jié)果婚禮上乓诽,老公的妹妹穿的比我還像新娘帜羊。我一直安慰自己,他們只是感情好鸠天,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布讼育。 她就那樣靜靜地躺著识补,像睡著了一般风响。 火紅的嫁衣襯著肌膚如雪何址。 梳的紋絲不亂的頭發(fā)上掉房,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音锄蹂,去河邊找鬼蜓肆。 笑死贷屎,一個胖子當著我的面吹牛晦鞋,可吹牛的內(nèi)容都是我干的蹲缠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼悠垛,長吁一口氣:“原來是場噩夢啊……” “哼线定!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起确买,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤斤讥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后湾趾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芭商,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡派草,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了铛楣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近迁。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛉艾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衷敌,我是刑警寧澤勿侯,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站缴罗,受9級特大地震影響助琐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜面氓,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一兵钮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舌界,春花似錦掘譬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藐握,卻和暖如春靴拱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背猾普。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工袜炕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人初家。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓偎窘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溜在。 傳聞我的和親對象是個殘疾皇子评架,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容