opencv學習指南入門篇(二):查找俄羅斯方塊的個數

(注:本文摘錄自Adrian Rosebrock的教程文章經翻譯整理而來)

本篇將介紹如下要點:html

  • 如何使用OpenCV將彩色圖像轉換爲灰度圖像
  • 邊緣檢測
  • 灰度圖像求閾值
  • 檢測、計數和繪製輪廓
  • 腐蝕和膨脹
  • 遮罩圖像

首先須要導入必要的庫、解析參數python

#導入必要的軟件包,包括Python附帶的命令行參數解析包argparse
import argparse
import imutils
import cv2

#構造參數解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
    help="path to input image")
args = vars(ap.parse_args())

1.將彩色圖像轉換爲灰度圖像

# 加載圖像 (路徑包含在命令行參數中)而且顯示
image = cv2.imread(args["image"])
cv2.imshow("Image", image)
cv2.waitKey(0)

# 轉換圖像爲灰度圖像,須要image和cv2.COLOR_BGR2GRAY標誌
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray", gray)
cv2.waitKey(0)

opencv_tutorial_grayscale.jpg

2.邊緣檢測

邊緣檢測對於查找圖像中對象的邊界頗有用,多用於圖像分割。算法

# 應用邊緣檢測找到圖像中目標物體的輪廓
edged = cv2.Canny(gray, 30, 150)
cv2.imshow("Edged", edged)
cv2.waitKey(0)

使用流行的Canny算法(由John F. Canny在1986年開發),咱們能夠找到圖像中的邊緣。
cv2.Canny函數須要三個參數:app

  • img :灰度圖像
  • 最小值 :本例中的最低閾值30
  • 最大值 :本例中最大閾值是150
  • aperture_size :Sobel內核大小,默認狀況下,此值爲3,所以未在代碼中顯示

不一樣的最小閾值和最大閾值將返回不一樣的邊緣圖
opencv_tutorial_edge_detection.jpgless

3.灰度圖像求閾值

圖像閾值化是圖像處理的重要中間步驟,閾值處理能夠幫助咱們去除較亮或較暗的圖像區域和輪廓。
經過反覆試驗(以及經驗)對如下代碼進行了調整,使其適用於本示例:函數

# 全部灰度值<225的像素點設置爲255(白色)-俄羅斯方塊
# 灰度值>=225且<=255的像素點設置爲0(黑色)——背景
thresh = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imshow("Thresh", thresh)
cv2.waitKey(0)

有關cv2.threshold函數的更多信息請參考opencv官方文檔
使用二值化圖像從背景中分割前景對於找到輪廓相當重要。
opencv_tutorial_thresholding.pngoop

4.檢測、計數和繪製輪廓

# 在圖像中找到前景物體的輪廓
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
output = image.copy()

# 循環繪製輪廓
for c in cnts:
    # 以紫色線條繪製輪廓
    # 一次顯示一個物體的輪廓
    cv2.drawContours(output, [c], -1, (240, 0, 159), 3)
    cv2.imshow("Contours", output)
    cv2.waitKey(0)

使用cv2.findContours以檢測圖像中的輪廓,圖像使用的是二值化圖像,注意函數的參數可是問題簡單化就是找到前景(白色)像素點。
使用以前文章中的知識,在圖像上覆蓋一些文本:ui

# 註明紫色輪廓的個數
text = "I found {} objects!".format(len(cnts))
cv2.putText(output, text, (10, 25),  cv2.FONT_HERSHEY_SIMPLEX, 0.7,
    (240, 0, 159), 2)
cv2.imshow("Contours", output)
cv2.waitKey(0)

變量text是包含形狀輪廓數量的字符串,計算此圖像中的對象總數就是檢查輪廓列表的長度len(cnts)
a.pngspa

5.腐蝕和膨脹

侵蝕和膨脹一般用於減小二進制圖像中的噪聲(閾值的反作用)。
(1)爲了減小前景對象的尺寸,咱們能夠經過屢次迭代來腐蝕掉像素:命令行

#經過腐蝕減少前景物體的尺寸,利用cv2.erode將輪廓尺寸減少5
mask = thresh.copy()
mask = cv2.erode(mask, None, iterations=5)
cv2.imshow("Eroded", mask)
cv2.waitKey(0)

opencv_tutorial_erosion.jpg
使用OpenCV腐蝕輪廓,有效地縮小輪廓或使它們在通過足夠的迭代後徹底消失,這對於去除二值化圖像中的噪聲點一般頗有用。
(2)要擴大前景對象的尺寸,只需使用cv2.dilate:

# 膨脹能夠擴大前景對象的尺寸
mask = thresh.copy()
mask = cv2.dilate(mask, None, iterations=5)
cv2.imshow("Dilated", mask)
cv2.waitKey(0)

opencv_tutorial_dilation.jpg
在圖像處理中,若是須要鏈接附近的輪廓,則能夠對圖像進行放大。圖中顯示的是經過五次迭代對輪廓進行擴張的結果,但並未使兩個輪廓變爲一個。

6.遮罩圖像

稱爲遮罩是由於它們將隱藏咱們不關心的圖像區域,好比使用二值化圖像將原始圖像覆蓋,將獲得如下結果:
opencv_tutorial_bitwise_masking-768x483.jpg
背景如今是黑色,前景由彩色像素組成——一些像素點被二值化,突出俄羅斯方塊區域

#咱們可能要應用的典型操做是遮蓋圖像某部分
#對輸入圖像按位與
# regions
mask = thresh.copy()
output = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("Output", output)
cv2.waitKey(0)

7.運行腳本

終端輸入:

python opencv_tutorial_02.py --image tetris_blocks.png

參數標誌是--image,而且image參數是tetris_blocks.png——目錄中相關文件的路徑。
最後貼上源代碼:

# USAGE
# python opencv_tutorial_02.py --image tetris_blocks.png

# import the necessary packages
import argparse
import imutils
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
    help="path to input image")
args = vars(ap.parse_args())

# load the input image (whose path was supplied via command line
# argument) and display the image to our screen
image = cv2.imread(args["image"])
cv2.imshow("Image", image)
cv2.waitKey(0)

# convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray", gray)
cv2.waitKey(0)

# applying edge detection we can find the outlines of objects in
# images
edged = cv2.Canny(gray, 30, 150)
cv2.imshow("Edged", edged)
cv2.waitKey(0)

# threshold the image by setting all pixel values less than 225
# to 255 (white; foreground) and all pixel values >= 225 to 255
# (black; background), thereby segmenting the image
thresh = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imshow("Thresh", thresh)
cv2.waitKey(0)

# find contours (i.e., outlines) of the foreground objects in the
# thresholded image
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
output = image.copy()

# loop over the contours
for c in cnts:
    # draw each contour on the output image with a 3px thick purple
    # outline, then display the output contours one at a time
    cv2.drawContours(output, [c], -1, (240, 0, 159), 3)
    cv2.imshow("Contours", output)
    cv2.waitKey(0)

# draw the total number of contours found in purple
text = "I found {} objects!".format(len(cnts))
cv2.putText(output, text, (10, 25),  cv2.FONT_HERSHEY_SIMPLEX, 0.7,
    (240, 0, 159), 2)
cv2.imshow("Contours", output)
cv2.waitKey(0)

# we apply erosions to reduce the size of foreground objects
mask = thresh.copy()
mask = cv2.erode(mask, None, iterations=5)
cv2.imshow("Eroded", mask)
cv2.waitKey(0)

# similarly, dilations can increase the size of the ground objects
mask = thresh.copy()
mask = cv2.dilate(mask, None, iterations=5)
cv2.imshow("Dilated", mask)
cv2.waitKey(0)

# a typical operation we may want to apply is to take our mask and
# apply a bitwise AND to our input image, keeping only the masked
# regions
mask = thresh.copy()
output = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("Output", output)
cv2.waitKey(0)
相關文章
相關標籤/搜索