python進階—OpenCV之圖像處理(三)


python opencv 圖像處理第三篇。

圖像直方圖(Histograms)

圖像的直方圖是用來表現圖像中亮度分佈的直方圖,給出的是圖像中某個亮度或者某個範圍亮度下共有幾個像素;X軸表示灰度的範圍,一般是0-255,Y軸表示相同灰度之下像素的個數。
直方圖的BIN:bins一般翻譯爲箱子,在圖像直方圖中,可以把一個灰度值設置爲一個bins,0~255強度的灰度值一共就需要256個bins,也可以把一個灰度範圍內的值設置爲一個bins。

直方圖查找,繪製,分析

cv.calcHist此函數由三種實現,下面僅介紹第一種

  • 函數原型:hist = cv.calcHist( images, channels, mask, histSize, ranges[, hist[, accumulate]] )
  • images: 輸入的圖像或數組,它們的深度必須爲CV_8U, CV_16U或CV_32F中的一類,尺寸必須相同。
  • nimages: 輸入數組個數,也就是第一個參數中存放了幾張圖像,有幾個原數組。
  • channels: 需要統計的通道dim,第一個數組通道從0到image[0].channels()-1,第二個數組從image[0].channels()到images[0].channels()+images[1].channels()-1,以後的數組以此類推
  • mask: 可選的操作掩碼。如果此掩碼不爲空,那麼它必須爲8位並且尺寸要和輸入圖像images一致。非零掩碼用於標記出統計直方圖的數組元素數據。
  • hist: 輸出的目標直方圖,一個二維數組
  • dims: 需要計算直方圖的維度,必須是正數且並不大於CV_MAX_DIMS(在opencv中等於32)
  • histSize: 每個維度的直方圖尺寸的數組
  • ranges: 每個維度中bin的取值範圍
  • uniform: 直方圖是否均勻的標識符,有默認值true
  • accumulate: 累積標識符,有默認值false,若爲true,直方圖再分配階段不會清零。此功能主要是允許從多個陣列中計算單個直方圖或者用於再特定的時間更新直方圖.
    此函數查找並計算直方圖,但是並沒有繪製直方圖。
    以下方式可以直接計算並繪製直方圖
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()

在這裏插入圖片描述

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

在這裏插入圖片描述

  • 繪製ROI內的直方圖
img = cv.imread('home.jpg',0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv.bitwise_and(img,img,mask = mask)
# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv.calcHist([img],[0],mask,[256],[0,256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()

在這裏插入圖片描述

直方圖均衡化

直方圖均衡化處理的“中心思想”是把原始圖像的灰度直方圖從比較集中的某個灰度區間變成在全部灰度範圍內的均勻分佈;這樣就增加了象素灰度值的動態範圍從而可達到增強圖像整體對比度的效果。
直方圖均衡化就是對圖像進行非線性拉伸,重新分配圖像像素值,使一定灰度範圍內的像素數量大致相同。
直方圖均衡化就是把給定圖像的直方圖分佈改變成“均勻”分佈直方圖分佈。
  1)變換後圖像的灰度級減少,某些細節消失;
  2)某些圖像,如直方圖有高峯,經處理後對比度不自然的過分增強。
當圖像有用數據的對比度相當接近的時候;通過這種方法,亮度可以更好地在直方圖上分佈;這樣就可以用於增強局部的對比度而不影響整體的對比度
  這種方法對於背景和前景都太亮或者太暗的圖像非常有用,這種方法尤其是可以帶來X光圖像中更好的骨骼結構顯示以及曝光過度或者曝光不足照片中更好的細節。這種方法的一個主要優勢是它是一個相當直觀的技術並且是可逆操作,如果已知均衡化函數,那麼就可以恢復原始的直方圖,並且計算量也不大。這種方法的一個缺點是它對處理的數據不加選擇,它可能會增加背景雜訊的對比度並且降低有用信號的對比度。

  • 函數原型:dst = cv.equalizeHist( src[, dst] )
  • src: Source 8-bit single channel image.
  • dst: Destination image of the same size and type as src .
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('wiki.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()) / cdf.max()
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()

在這裏插入圖片描述
局部區域自適應直方圖均衡

  • 函數原型:retval = cv.createCLAHE( [, clipLimit[, tileGridSize]] )
  • src: Source 8-bit single channel image.
  • dst: Destination image of the same size and type as src .
import numpy as np
import cv2 as cv
img = cv.imread('tsukuba_l.png',0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
cv.imwrite('clahe_2.jpg',cl1)

二維直方圖

二維直方圖中,考慮兩個特徵,通常來說它是用來獲取彩色直方圖,而它的兩個特徵是每個像素點的色調和飽和度。
二維直方圖計算,用的是同一個方法,cv.calcHist()。對於彩色直方圖,我們需要把圖像從 BGR 轉成 HSV。(記住對於一維直方圖,我們是把 BGR 轉成灰度圖像)。對於二維直方圖,它的參數被修改成如下:

  • channels = [0,1] 因爲我們需要同時處理H和S平面(譯者注:分別表示H色調和S飽和度)。
  • bins = [180,256] H平面最多180個抽屜,S平面最多256個抽屜。
  • range = [0,180,0,256] 色調值落在 0 到 180 & 飽和度落在 0 到 256。
import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

Numpy裏的2D直方圖
Numpy也爲此提供了一個特定的函數:np.histogram2d()。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])

繪製二維直方圖
方法 - 1:使用 cv.imshow()
我們拿到的結果是一個二維數組,大小 180x256。所以我們可以像往常一樣顯示它們,使用cv.imshow()函數。它將是一個灰度圖像,它不會直觀的顯示出那具體是什麼顏色,除非你知道不同顏色的色調值。
方法 - 2:使用 Matplotlib
我們可以使用 matplotlib.pyplot.imshow() 函數,通過不同的顏色映射關係來繪製2D直方圖。它讓我們對不同的像素密度有了更好的瞭解。但這也不能在第一時間告訴我們那顏色是什麼,除非你知道不同顏色的色調值。但我還是推薦這種方法。它很簡單,而且更好。
當使用這個函數時,記住,插補標記interpolation應該是最近的,以便獲得更好的結果。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist = cv.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )
plt.imshow(hist,interpolation = 'nearest')
plt.show()

在這裏插入圖片描述

直方圖反向投影

它用於圖像分割或尋找圖像中感興趣的對象。簡單的說,它創造了一個和我們輸入圖像相同大小(但只有一個單通道)的新圖像,其中每一個像素點對應了該像素屬於我們的(感興趣的)對象的概率。直方圖反映射常常與連續自適應均值漂移算法(CamShift算法的全稱是"Continuously Adaptive Mean-SHIFT",附鏈接)等一起使用。

我們怎麼做? 我們創建一個包含了我們感興趣對象的圖像的直方圖(在我們接下來的示例中,感興趣對象設爲地面,不去管球員和其他東西)。感興趣對象應該儘可能的填充這個圖像,這樣能得到更好的結果。並且推薦使用彩色直方圖而不是灰度直方圖,因爲物體的顏色比起物體的灰度強度更好的定義了這個物體。然後我們在需要找出感興趣對象的測試圖像上開始“反映射”這個直方圖,換句話說,我們計算每個像素點屬於地面(我們感興趣對象)的概率,並且顯示這些像素點。在給定合理閾值的情況下,結果輸出就會只給我們地面。

import numpy as np
import cv2 as cvfrom matplotlib import pyplot as plt
#roi is the object or region of object we need to find
roi = cv.imread('rose_red.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
#target is the image we search in
target = cv.imread('rose.png')
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)
# Find the histograms using calcHist. Can be done with np.histogram2d also
M = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )

OpenCV裏的反映射

OpenCV 提供了一個內置函數 cv.calcBackProject()。它的參數幾乎和 cv.calcHist() 函數一樣。其中一個參數是直方圖,也就是物體的直方圖我們必須找到它。並且,這個物體的直方圖參數應該在傳入這個反映射函數之前被標準化。它返回的是(那張表示了每個像素點屬於我們感興趣物體的)概率的圖像。然後我們用圓盤內核做卷積,應用閾值。以下是我的代碼和運行結果:

import numpy as np
import cv2 as cv
roi = cv.imread('rose_red.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
target = cv.imread('rose.png')
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)
# calculating object histogram
roihist = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
# normalize histogram and apply backprojection
cv.normalize(roihist,roihist,0,255,cv.NORM_MINMAX)
dst = cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
# Now convolute with circular disc
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(dst,-1,disc,dst)
# threshold and binary AND
ret,thresh = cv.threshold(dst,50,255,0)
thresh = cv.merge((thresh,thresh,thresh))
res = cv.bitwise_and(target,thresh)
res = np.vstack((target,thresh,res))
cv.imwrite('res.jpg',res)

在這裏插入圖片描述

圖像模板匹配(Template Matching)

模板匹配是在圖像中尋找目標的方法
模板匹配的工作方式跟直方圖的反向投影基本一樣,大致過程是這樣的:通過在輸入圖像上滑動圖像塊對實際的圖像塊和輸入圖像進行匹配。
假設我們有一張100x100的輸入圖像,有一張10x10的模板圖像,查找的過程如下:
(1)從輸入圖像的左上角(0,0)開始,切割一塊(0,0)至(10,10)的臨時圖像;
(2)用臨時圖像和模板圖像進行對比,對比結果記爲c;
(3)對比結果c,就是結果圖像(0,0)處的像素值;
(4)切割輸入圖像從(0,1)至(10,11)的臨時圖像,對比,並記錄到結果圖像;
(5)重複(1)~(4)步直到輸入圖像的右下角。

單目標模板匹配

模板匹配的函數: cv.matchTemplate,說明如下:

  • 函數原型:result = cv.matchTemplate( image, templ, method[, result[, mask]] )
  • image:待匹配的圖像,必須是 8-bit 或者 32-bit 浮點數
  • templ: 模板圖像,必須小於搜索圖像,數據類型與image一致
  • result: 模板匹配函數cvMatchTemplate依次計算模板與待測圖片的重疊區域的相似度,並將結果存入映射圖像result當中,也就是說result圖像中的每一個點的值代表了一次相似度比較結果。結果是單通道 32-bit 浮點數。如果image圖像大小是W×H,模板圖像大小是w×h,則result圖像大小是(W−w+1)×(H−h+1)
  • method:模板匹配方法。
  • CV_TM_SQDIFF 平方差匹配法:該方法採用平方差來進行匹配;最好的匹配值爲0;匹配越差,匹配值越大。
  • CV_TM_CCORR 相關匹配法:該方法採用乘法操作;數值越大表明匹配程度越好。
  • CV_TM_CCOEFF 相關係數匹配法:1表示完美的匹配;-1表示最差的匹配。
  • CV_TM_SQDIFF_NORMED 歸一化平方差匹配法
  • CV_TM_CCORR_NORMED 歸一化相關匹配法
  • CV_TM_CCOEFF_NORMED 歸一化相關係數匹配法
  • mask:模板掩模圖像,數據類型、尺寸大小必須與templ一致;當前只有TM_SQDIFF 與TM_CCORR_NORMED方法支持
    函數cv.minMaxLoc說明
  • 函數原型:minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc( src[, mask] )
  • src: 輸入單通道圖像
  • minVal:比對結果中的最小值
  • maxVal:比對結果中的最大值
  • minLoc:比對結果中的最小值位置座標
  • maxLoc:比對結果中的最大值位置座標
  • mask: 可選的mask,用於選擇sub-array.
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
img2 = img.copy()
template = cv.imread('template.jpg',0)
w, h = template.shape[::-1]
# All the 6 methods for comparison in a list
methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
            'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']
for meth in methods:
    img = img2.copy()
    method = eval(meth)
    # Apply template Matching
    res = cv.matchTemplate(img,template,method)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
    # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
    if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv.rectangle(img,top_left, bottom_right, 255, 2)
    plt.subplot(121),plt.imshow(res,cmap = 'gray')
    plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
    plt.subplot(122),plt.imshow(img,cmap = 'gray')
    plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)
    plt.show()

在這裏插入圖片描述

在這裏插入圖片描述

多目標模板匹配

可以使用閾值在圖像中搜索多目標圖像

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv.imread('mario.png')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('mario_coin.png',0)
w, h = template.shape[::-1]
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
    cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv.imwrite('res.png',img_rgb)

在這裏插入圖片描述

霍夫直線檢測(Hough Line Transform)

霍夫直線檢測數學原理性的東西,我講不來,網上有很多,待到我確實理解其數學原理後(慚愧,十幾年不用數學,全忘了)再來添加介紹;這裏只說明用法。

  • 函數原型: lines = cv.HoughLines( image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]] )
  • image:輸入圖像,要求是單通道的二值圖像,輸入圖像可能被此函數修改
  • lines:輸出直線向量,兩個元素的向量(ρ,θ)代表一條直線,ρ是從原點(圖像的左上角)的距離,θ是直線的角度(單位是弧度),0表示垂直線,π/2表示水平線
  • rho:像素相關單位的距離加器
  • theta:角度分辨率
  • threshold:閾值參數,如果相應的累計值大於 threshold, 則函數返回這條線段
  • srn:多尺度霍夫直線變換rto參數的除數,它導致累加精度爲:rho/srn 。
  • stn: 多尺度霍夫直線變換 theta參數的除數
  • 如果srn和stn同時爲0,就表示使用經典的霍夫變換;否則這兩個參數應該都爲正數。
  • min_theta:檢測直線的最小角度θ,取值範圍是:0 and max_theta.
  • max_theta:檢測直線的最大角度θ,取值範圍是: min_theta and CV_PI.
import cv2 as cv
import numpy as np
img = cv.imread('../data/sudoku.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLines(edges,1,np.pi/180,200)
for line in lines:
    rho,theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv.imwrite('houghlines3.jpg',img)

在這裏插入圖片描述

  • 函數原型: lines = cv.HoughLinesP( image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]] )
  • image: 必須是二值圖像,推薦使用canny邊緣檢測的結果圖像;
  • rho: 線段以像素爲單位的距離精度,double類型的,推薦用1.0
  • theta: 線段以弧度爲單位的角度精度,推薦用numpy.pi/180
  • threshod: 累加平面的閾值參數,int類型,超過設定閾值才被檢測出線段,值越大,基本上意味着檢出的線段越長,檢出的線段個數越少。根據情況推薦先用100試試
  • lines:這個參數的意義未知,發現不同的lines對結果沒影響,但是不要忽略了它的存在
  • minLineLength:線段以像素爲單位的最小長度,小於此值線段不會放入到lines集合
  • maxLineGap:同一方向上兩條線段判定爲一條線段的最大允許間隔,超過了設定值,則爲兩條線段,值越大,允許線段上的斷裂越大。
import cv2 as cv
import numpy as np
img = cv.imread('../data/sudoku.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLinesP(edges,1,np.pi/180,100,minLineLength=100,maxLineGap=10)
for line in lines:
    x1,y1,x2,y2 = line[0]
    cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv.imwrite('houghlines5.jpg',img)

在這裏插入圖片描述

霍夫圓檢測(Hough Circle Transform)

  • 函數原型:circles = cv.HoughCircles( image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]] )
  • image:8-bit, 單通道灰度圖像
  • circles:輸出圓向量,每個向量包括三個浮點型的元素——圓心橫座標,圓心縱座標和圓半徑
  • method:霍夫變換圓檢測的算法,見cv::HoughModes,當前僅支持 HOUGH_GRADIENT算法
  • dp:霍夫空間的分辨率,dp=1時表示霍夫空間與輸入圖像空間的大小一致,dp=2時霍夫空間是輸入圖像空間的一半,以此類推,dp的值不能比1小
  • minDist:圓心之間的最小距離,如果檢測到的兩個圓心之間距離小於該值,則認爲它們是同一個圓心,取值過小會造成鄰近圓檢測爲一個圓,取值過大回造成一些圓檢測丟失。
  • param1:Canny的邊緣閥值上限,下限被置爲上限的一半
  • param2:第二項參數method的明細參數, 算法爲CV_HOUGH_GRADIENT , 它爲圓心檢測累加器的閾值,取值越小,會造成越多的錯誤圓檢測,累加器值越大的圓,先返回
  • minRadius:圓最小直徑
  • maxRadius:圓最大直徑
import numpy as np
import cv2 as cv
img = cv.imread('opencv-logo-white.png',0)
img = cv.medianBlur(img,5)
cimg = cv.cvtColor(img,cv.COLOR_GRAY2BGR)
circles = cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    # draw the outer circle
    cv.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
    # draw the center of the circle
    cv.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('detected circles',cimg)
cv.waitKey(0)
cv.destroyAllWindows()

在這裏插入圖片描述

分水嶺算法的圖像分割(Image Segmentation with Watershed Algorithm)

分水嶺分割方法,它是一種基於拓撲理論的數學形態學的分割方法,其基本思想是把圖像看作是測地學上的拓撲地貌,圖像中每一點像素的灰度值表示該點的海拔高度,每一個局部極小值及其影響區域稱爲集水盆,而集水盆的邊界則形成分水嶺。分水嶺的概念和形成可以通過模擬浸入過程來說明。在每一個局部極小值表面,刺穿一個小孔,然後把整個模型慢慢浸入水中,隨着浸入的加深,每一個局部極小值的影響域慢慢向外擴展,在兩個集水盆匯合處構築大壩,即形成分水嶺。
一般的分水嶺算法會對微弱邊緣,圖像中的噪聲,物體表面細微的灰度變化造成過度的分割。opencv中的分水嶺算法對此進行了改進,它使用預定義的一組標記來引導對圖像的分割。
在執行分水嶺函數watershed之前,必須對第二個參數markers進行處理,它應該包含不同區域的輪廓,每個輪廓有一個自己唯一的編號,輪廓的定位可以通過Opencv中findContours方法實現,這個是執行分水嶺之前的要求。
接下來執行分水嶺會發生什麼呢?算法會根據markers傳入的輪廓作爲種子(也就是所謂的注水點),對圖像上其他的像素點根據分水嶺算法規則進行判斷,並對每個像素點的區域歸屬進行劃定,直到處理完圖像上所有像素點。而區域與區域之間的分界處的值被置爲“-1”,以做區分。
簡單概括一下就是說第二個入參markers必須包含了種子點信息。Opencv官方例程中使用鼠標劃線標記,其實就是在定義種子,只不過需要手動操作,而使用findContours可以自動標記種子點。而分水嶺方法完成之後並不會直接生成分割後的圖像,還需要進一步的顯示處理

  • 函數原型:markers = cv.watershed( image, markers )
  • image:Input 8-bit 3-channel image.
  • markers:Input/output 32-bit single-channel image (map) of markers. It should have the same size as image .
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('coins.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
# Marker labelling
ret, markers = cv.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

基於GrabCut算法的交互式前景提取(Interactive Foreground Extraction using GrabCut Algorithm)

GrabCut算法我解釋不來,我只能說明一下函數的用法。

  • 函數原型:mask, bgdModel, fgdModel = cv.grabCut( img, mask, rect, bgdModel, fgdModel, iterCount[, mode] )
  • img:待分割的源圖像,必須是8位3通道(CV_8UC3)圖像,在處理的過程中不會被修改
  • mask:輸入輸出8bit單通道掩碼圖像,如果使用mode=GC_INIT_WITH_RECT,則掩碼被函數進行初始化;在執行分割的時候,也可以將用戶交互所設定的前景與背景保存到mask中,然後再傳入grabCut函數;在處理結束之後,mask中會保存結果。mask只能取以下四種值:
    GCD_BGD(=0),背景;
    GCD_FGD(=1),前景;
    GCD_PR_BGD(=2),可能的背景;
    GCD_PR_FGD(=3),可能的前景。
    如果沒有手工標記GCD_BGD或者GCD_FGD,那麼結果只會有GCD_PR_BGD或GCD_PR_FGD
  • rect:用於限定需要進行分割的圖像範圍,只有該矩形窗口內的圖像部分才被處理,此參數僅在mode==GC_INIT_WITH_RECT有效
  • bgdModel:前景模型,如果爲null,函數內部會自動創建一個fgdModel;fgdModel必須是單通道浮點型(CV_32FC1)圖像,且行數只能爲1,列數只能爲13x5
  • fgdModel:前景模型,如果爲null,函數內部會自動創建一個fgdModel;fgdModel必須是單通道浮點型(CV_32FC1)圖像,且行數只能爲1,列數只能爲13x5
  • iterCount:迭代次數,必須大於0;返回結果前結果可能會被優化,如果 mode == GC_INIT_WITH_MASK or mode==GC_EVAL .
  • mode:用於指示grabCut函數進行什麼操作,可選的值有:
    GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
    GC_INIT_WITH_MASK(=1),用掩碼圖像初始化GrabCut;
    GC_EVAL(=2),執行分割
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)
cv.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

覺得寫的越來越差,很多原理性的東西我都不能解釋,只能模糊的學習了,內容也多半是看一看別人的總結,再加上自己的一點理解,拼湊而成,就想着以後自己查找的是方便。
如果有哪位博主覺得我總結的內容,冒犯了您,請通知我。
看來想要進入計算機視覺的門不容易啊,不過我會堅持的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章