1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Python 圖像處理 OpenCV (15):圖像輪廓

        共 6948字,需瀏覽 14分鐘

         ·

        2020-07-28 15:18


        引言

        其實(shí)蠻不好意思的,剛才翻了翻自己的博客,上次寫 OpenCV 的文章已經(jīng)接近半個(gè)月以前了,我用 3 秒鐘的時(shí)間回想了下最近兩星期時(shí)間都花在哪了。

        每次思考這種問題總會(huì)下意識甩鍋給工作,最近工作忙的一批,emmmmmmmmmmmm。。。。。。。。。

        這么騙自己是不對的!

        實(shí)際上是美劇真香,最近把「反擊」從第一季到第六季看了一遍,還不錯(cuò),喜歡看動(dòng)作類的同學(xué)可以嘗試下。

        本篇文章是關(guān)于圖像處理輪廓方面的,下面開始正文,希望能幫到各位。

        Q:什么是輪廓?

        A:輪廓是一系列相連的點(diǎn)組成的曲線,代表了物體的基本外形,相對于邊緣,輪廓是連續(xù)的,邊緣并不全部連續(xù)。

        尋找輪廓

        尋找輪廓 OpenCV 為我們提供了一個(gè)現(xiàn)成的函數(shù) findContours() 。

        在 OpenCV 中,輪廓提取函數(shù) findContours() 實(shí)現(xiàn)的是 1985 年由一名叫做 Satoshi Suzuki 的人發(fā)表的一篇論文中的算法,如下:

        Satoshi Suzuki and others. Topological structural analysis of digitized binary images by border following. Computer Vision, Graphics, and Image Processing, 30(1):32–46, 1985.

        對原理感興趣的同學(xué)可以去搜搜看,不是很難理解。

        先看一個(gè)示例代碼:

        import cv2 as cv

        img = cv.imread("black.png")
        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

        print(len(contours[0]))

        這段代碼先用 threshold() 對圖像進(jìn)行降噪處理,它的原型函數(shù)如下:

        retval, dst = cv.threshold(src, thresh, maxval, type[, dst] )
        • dst:結(jié)果圖像。
        • src:原圖像。
        • thresh:當(dāng)前閾值。
        • maxVal:最大閾值,一般為255。
        • type:閾值類型,可選值如下:
        enum ThresholdTypes {
        THRESH_BINARY = 0, # 大于閾值的部分被置為 255 ,小于部分被置為 0
        THRESH_BINARY_INV = 1, # 大于閾值部分被置為 0 ,小于部分被置為 255
        THRESH_TRUNC = 2, # 大于閾值部分被置為 threshold ,小于部分保持原樣
        THRESH_TOZERO = 3, # 小于閾值部分被置為 0 ,大于部分保持不變
        THRESH_TOZERO_INV = 4, # 大于閾值部分被置為 0 ,小于部分保持不變
        THRESH_OTSU = 8, # 自動(dòng)處理,圖像自適應(yīng)二值化,常用區(qū)間 [0,255]
        };

        查找輪廓使用的函數(shù)為 findContours() ,它的原型函數(shù)如下:

        cv2.findContours(image,?mode,?method[,?contours[,?hierarchy[,?offset?]]])??
        • image:源圖像。
        • mode:表示輪廓檢索模式。
        cv2.RETR_EXTERNAL 表示只檢測外輪廓。
        cv2.RETR_LIST 檢測的輪廓不建立等級關(guān)系。
        cv2.RETR_CCOMP 建立兩個(gè)等級的輪廓,上面的一層為外邊界,里面的一層為內(nèi)孔的邊界信息。如果內(nèi)孔內(nèi)還有一個(gè)連通物體,這個(gè)物體的邊界也在頂層。
        cv2.RETR_TREE 建立一個(gè)等級樹結(jié)構(gòu)的輪廓。
        • method:表示輪廓近似方法。
        cv2.CHAIN_APPROX_NONE 存儲(chǔ)所有的輪廓點(diǎn)。
        cv2.CHAIN_APPROX_SIMPLE 壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點(diǎn)坐標(biāo),例如一個(gè)矩形輪廓只需4個(gè)點(diǎn)來保存輪廓信息。

        這里可以使用 print(len(contours[0])) 函數(shù)將包含的點(diǎn)的數(shù)量打印出來,比如在上面的示例中,使用參數(shù) cv2.CHAIN_APPROX_NONE 輪廓點(diǎn)有 1382 個(gè),而使用參數(shù) cv2.CHAIN_APPROX_SIMPLE 則輪廓點(diǎn)只有 4 個(gè)。

        繪制輪廓

        繪制輪廓使用到的 OpenCV 為我們提供的 drawContours() 這個(gè)函數(shù),下面是它的三個(gè)簡單的例子:

        # To draw all the contours in an image:
        cv2.drawContours(img, contours, -1, (0,255,0), 3)
        # To draw an individual contour, say 4th contour:
        cv2.drawContours(img, contours, 3, (0,255,0), 3)
        # But most of the time, below method will be useful:
        cnt = contours[4]
        cv2.drawContours(img, [cnt], 0, (0,255,0), 3)

        drawContours() 函數(shù)中有五個(gè)參數(shù):

        • 第一個(gè)參數(shù)是源圖像。
        • 第二個(gè)參數(shù)是應(yīng)該包含輪廓的列表。
        • 第三個(gè)參數(shù)是列表索引,用來選擇要繪制的輪廓,為-1時(shí)表示繪制所有輪廓。
        • 第四個(gè)參數(shù)是輪廓顏色。
        • 第五個(gè)參數(shù)是輪廓線的寬度,為 -1 時(shí)表示填充。

        我們接著前面的示例把使用 findContours() 找出來的輪廓繪制出來:

        import cv2 as cv

        img = cv.imread("black.png")
        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        cv.imshow("img", img)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

        print(len(contours[0]))

        # 繪制綠色輪廓
        cv.drawContours(img, contours, -1, (0,255,0), 3)

        cv.imshow("draw", img)

        cv.waitKey(0)
        cv.destroyAllWindows()

        特征矩

        特征矩可以幫助我們計(jì)算一些圖像的特征,例如物體的質(zhì)心,物體的面積等,使用的函數(shù)為 moments() 。

        moments() 函數(shù)會(huì)將計(jì)算得到的矩以字典形式返回。

        import cv2 as cv

        img = cv.imread("number.png")

        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

        cnt = contours[0]
        # 獲取圖像矩
        M = cv.moments(cnt)
        print(M)

        # 質(zhì)心
        cx = int(M['m10'] / M['m00'])
        cy = int(M['m01'] / M['m00'])

        print(f'質(zhì)心為:[{cx}, {cy}]')

        這時(shí),我們?nèi)〉昧诉@個(gè)圖像的矩,矩 M 中包含了很多輪廓的特征信息,除了示例中展示的質(zhì)心的計(jì)算,還有如 M['m00'] 表示輪廓面積。

        輪廓面積

        area = cv.contourArea(cnt)
        print(f'輪廓面積為:{area}')

        這里取到的輪廓面積和上面 M['m00'] 保持一致。

        輪廓周長

        perimeter = cv.arcLength(cnt, True)
        print(f'輪廓周長為:{perimeter}')

        參數(shù) True 表示輪廓是否封閉,我們這里的輪廓是封閉的,所以這里寫 True

        輪廓外接矩形

        輪廓外接矩形分為正矩形和最小矩形。使用 cv2.boundingRect(cnt) 來獲取輪廓的外接正矩形,它不考慮物體的旋轉(zhuǎn),所以該矩形的面積一般不會(huì)最??;使用 cv.minAreaRect(cnt) 可以獲取輪廓的外接最小矩形。

        兩者的區(qū)別如上圖,綠線代表的是外接正矩形,紅線代表的是外接最小矩形,代碼如下:

        import cv2 as cv
        import numpy as np

        img = cv.imread("number.png")

        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

        cnt = contours[0]

        # 外接正矩形
        x, y, w, h = cv.boundingRect(cnt)
        cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

        # 外接最小矩形
        min_rect = cv.minAreaRect(cnt)
        print(min_rect)

        box = cv.boxPoints(min_rect)
        box = np.int0(box)
        cv.drawContours(img, [box], 0, (0, 0, 255), 2)

        cv.imshow("draw", img)

        cv.waitKey(0)
        cv.destroyAllWindows()

        boundingRect() 函數(shù)的返回值包含四個(gè)值,矩形框左上角的坐標(biāo) (x, y) 、寬度 w 和高度 h 。

        minAreaRect() 函數(shù)的返回值中還包含旋轉(zhuǎn)信息,返回值信息為包括中心點(diǎn)坐標(biāo) (x,y) ,寬高 (w, h) 和旋轉(zhuǎn)角度。

        輪廓近似

        根據(jù)我們指定的精度,它可以將輪廓形狀近似為頂點(diǎn)數(shù)量較少的其他形狀。它是由 Douglas-Peucker 算法實(shí)現(xiàn)的。

        OpenCV 提供的函數(shù)是 approxPolyDP(cnt, epsilon, True) ,第二個(gè)參數(shù) epsilon 用于輪廓近似的精度,表示原始輪廓與其近似輪廓的最大距離,值越小,近似輪廓越擬合原輪廓。第三個(gè)參數(shù)指定近似輪廓是否是閉合的。具體用法如下:

        import cv2 as cv

        img = cv.imread("number.png")

        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

        cnt = contours[0]

        # 計(jì)算 epsilon ,按照周長百分比進(jìn)行計(jì)算,分別取周長 1% 和 10%
        epsilon_1 = 0.1 * cv.arcLength(cnt, True)
        epsilon_2 = 0.01 * cv.arcLength(cnt, True)

        # 進(jìn)行多邊形逼近
        approx_1 = cv.approxPolyDP(cnt, epsilon_1, True)
        approx_2 = cv.approxPolyDP(cnt, epsilon_2, True)

        # 畫出多邊形
        image_1 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
        image_2 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)

        cv.polylines(image_1, [approx_1], True, (0, 0, 255), 2)
        cv.polylines(image_2, [approx_2], True, (0, 0, 255), 2)

        cv.imshow("image_1", image_1)
        cv.imshow("image_2", image_2)
        cv.waitKey(0)
        cv.destroyAllWindows()

        第一張圖是 epsilon 為原始輪廓周長的 10% 時(shí)的近似輪廓,第二張圖中綠線就是 epsilon 為原始輪廓周長的 1% 時(shí)的近似輪廓。

        輪廓凸包

        凸包外觀看起來與輪廓逼近相似,只不過它是物體最外層的「凸」多邊形。

        如下圖,紅色的部分為手掌的凸包,雙箭頭部分表示凸缺陷(Convexity Defects),凸缺陷常用來進(jìn)行手勢識別等。

        import cv2 as cv

        img = cv.imread("number.png")
        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
        cnt = contours[0]
        # 繪制輪廓
        image = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
        cv.drawContours(image, contours, -1, (0, 0 , 255), 2)

        # 尋找凸包,得到凸包的角點(diǎn)
        hull = cv.convexHull(cnt)

        # 繪制凸包
        cv.polylines(image, [hull], True, (0, 255, 0), 2)

        cv.imshow("image", image)
        cv.waitKey(0)
        cv.destroyAllWindows()

        還有一個(gè)函數(shù),是可以用來判斷圖形是否凸形的:

        print(cv.isContourConvex(hull)) # True

        它的返回值是 True 或者 False 。

        最小閉合圈

        接下來,使用函數(shù) cv.minEnclosingCircle() 查找對象的圓周。它是一個(gè)以最小面積完全覆蓋物體的圓。

        import cv2 as cv

        img = cv.imread("number.png")
        gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 降噪
        ret, thresh = cv.threshold(gray_img, 127, 255, 0)
        # 尋找輪廓
        contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
        cnt = contours[0]

        # 繪制最小外接圓
        (x, y), radius = cv.minEnclosingCircle(cnt)
        center = (int(x), int(y))
        radius = int(radius)
        cv.circle(img, center, radius, (0, 255, 0), 2)

        cv.imshow("img", img)
        cv.waitKey(0)
        cv.destroyAllWindows()

        下一個(gè)是把一個(gè)橢圓擬合到一個(gè)物體上。它返回內(nèi)接橢圓的旋轉(zhuǎn)矩形。

        ellipse = cv.fitEllipse(cnt)
        cv.ellipse(img, ellipse, (0, 255, 0), 2)

        參考

        https://zhuanlan.zhihu.com/p/61328775

        https://zhuanlan.zhihu.com/p/77783347

        感謝閱讀



        瀏覽 50
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            女生脱了裤子让男生捅 | 日韩字幕 | 欧美日韩人妻精品一区二区 | 翔田千里无码流出 | 亚洲欧美一区二区三区在线 | 亚洲一级内射 | 久热精品视频在线观看 | 久久国产精品m码 | 国产一级a毛一级a毛视频在线网站) | 91精品国产91久久久久久国产三级 |