Posted on 1 Comment

如何檢測畫面中可能的正方形

最簡單-使用findContours

OpenCV 中有一個名為 findContours 的函數,可以用來查找圖像中的輪廓。一旦你找到了圖像中的輪廓,你可以使用 approxPolyDP 函數來近似地計算輪廓的形狀。如果你要查找正方形,你可以在這些形狀中尋找具有 4 個頂點的多邊形,這些多邊形應該有相近的邊長和角度。如果你要檢測的正方形不是水平的或垂直的,你可能需要使用角度信息來確定它的方向。

import cv2
# 读入图像
img = cv2.imread('square.jpg')

# 将图像转为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)

# 寻找轮廓
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 遍历轮廓
for contour in contours:
    # 近似计算轮廓
    approx = cv2.approxPolyDP(contour, 0.01 * cv2.arcLength(contour, True), True)
    # 如果是 4 个顶点的多边形,且边长和角度相近,则认为是正方形
    if len(approx) == 4 and cv2.isContourConvex(app

當我們可以取得相黏物件邊緣時 – 分水嶺演算法

分水嶺演算法-偵測相連區域形狀

當形狀邊緣不清楚時-使用霍夫找線

使用霍夫變換檢測直線。因為正方形的四條邊是直線,因此可以通過檢測這四條直線來確定正方形。

具體來說,可以使用 OpenCV 中的 HoughLinesP 函數來檢測直線。該函數會返回一組檢測到的線段,這些線段可以是任意方向和長度的線段,因此我們需要對這些線段進行篩選,只保留長度、方向和相對位置都符合要求的線段。接著,我們可以將這些線段按照一定的規則組合成四條邊,從而確定正方形。

以下是一個示例代碼,演示如何使用霍夫變換檢測正方形:

import cv2
import numpy as np

# 读入图像
img = cv2.imread('square.jpg')

# 将图像转为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)

# 使用霍夫变换检测直线
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=100, maxLineGap=10)

# 筛选直线
filtered_lines = []
for line in lines:
    x1, y1, x2, y2 = line[0]
    length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
    angle = np.arctan2(y2-y1, x2-x1)
    # 保留长度在一定范围内的直线
    if 100 < length < 200:
        filtered_lines.append((length, angle, line))

# 组合直线
sides = []
for i, (length1, angle1, line1) in enumerate(filtered_lines):
    for j, (length2, angle2, line2) in enumerate(filtered_lines[i+1:], i+1):
        angle_diff = np.abs(angle1 - angle2)
        # 保留角度相近的直线
        if angle_diff < np.pi/4:
            # 计算两条直线的中心点
            x1, y1, x2, y2 = line1
            cx1, cy1 = (x1+x2)/2, (y1+y2)/2
            x1, y1, x2, y2 = line2
            cx2, cy2 = (x1+x2)/2, (y1+y2)/2
            center_diff = np.sqrt((cx2-cx1)**2 + (cy2-cy1)**2)
            # 保留中心点距离相近的直线
            if center_diff < 20:
                # 将两条直线组合成一条边
                sides.append((length1+length2, angle1, angle2, line1, line2))

# 按照长度排序,取前四条直线作为正方形的四条边
sides = sorted(sides
取前四条直线作为正方形的四条边
sides = sorted(sides, reverse=True)[:4]

计算正方形的四个顶点
corners = []
for i, (length1, angle1, angle2, line1, line2) in enumerate(sides):
for j, (length3, angle3, angle4, line3, line4) in enumerate(sides[i+1:], i+1):
# 计算两条直线的交点
x1, y1, x2, y2 = line1
cx1, cy1 = (x1+x2)/2, (y1+y2)/2
x1, y1, x2, y2 = line3
cx2, cy2 = (x1+x2)/2, (y1+y2)/2
k1 = np.tan(angle1)
k2 = np.tan(angle3)
x = (cy1 - cy2 + k2cx2 - k1cx1) / (k2 - k1)
y = k1*(x - cx1) + cy1
corners.append((x, y))

绘制正方形
for i in range(4):
x1, y1 = corners[i]
x2, y2 = corners[(i+1)%4]
cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 3)

显示结果
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

基於角點的角點檢測

哈里斯角點檢測器簡介

Harris Corner Detector 是一種角點檢測算子,常用於計算機視覺算法中以提取角點並推斷圖像的特徵。它由 Chris Harris 和 Mike Stephens 於 1988 年在 Moravec 的角檢測器改進後首次提出。Harris 角點檢測器相比之前的角點檢測器,直接考慮了角點分數的差異,直接參考方向,而不是每 45 度角使用 shifting patches,並被證明在區分邊緣和邊緣方面更準確。角落。從那以後,它被改進並被許多算法採用,為後續應用預處理圖像。

Posted on

使用OpenCV判別圖像清晰度

3種清晰度評價方法

  • Tenengrad梯度方法: Tenengrad梯度方法利用Sobel算子分別計算水平和垂直方向的梯度,同一場景下梯度值越高,圖像越清晰。以下是具體實現,這裡衡量的指標是經過Sobel算子處理後的圖像的平均灰度值,值越大,代表圖像越清晰。
  • Laplacian梯度方法: Laplacian()變換不需要區分圖像的x和y方向計算梯度,從上圖的2種kernel也可以看到其x和y方向是對稱的。
  • 方差方法: 方差是概率論中用來考察一組離散數據和其期望(即數據的均值)之間的離散(偏離)成都的度量方法。方差較大,表示這一組數據之間的偏差就較大,組內的數據有的較大,有的較小,分佈不均衡;方差較小,表示這一組數據之間的偏差較小,組內的數據之間分佈平均,大小相近。

圖像清晰度識別之Laplacian算子

Laplacce算子是一種各向同性算子,二階微分算子,在只關心邊緣的位置而不考慮其周圍的像素灰度差值時比較合適。Laplace算子對孤立像素的響應要比對邊緣或線的響應要更強烈,因此只適用於無噪聲圖像。存在噪聲情況下,使用Laplacian算子檢測邊緣之前需要先進行低通濾波。所以,通常的分割算法都是把Laplacian算子和平滑算子結合起來生成一個新的模板。

從模板形式容易看出,如果在圖像中一個較暗的區域中出現了一個亮點,那麼用拉普拉斯運算就會使這個亮點變得更亮。因為圖像中的邊緣就是那些灰度發生跳變的區域,所以拉普拉斯銳化模板在邊緣檢測中很有用。一般增強技術對於陡峭的邊緣和緩慢變化的邊緣很難確定其邊緣線的位置。但此算子卻可用二次微分正峰和負峰之間的過零點來確定,對孤立點或端點更為敏感,因此特別適用於以突出圖像中的孤立點、孤立線或線端點為目的的場合。

在圖像處理中,圖像的清晰程度可以被表示為圖像的邊緣和顏色變化的強度。圖像的清晰度越強,邊緣和顏色變化的強度就越高。因此,通過評估圖像的清晰度,可以檢測圖像是否模糊。

使用範例和結果

import numpy as np
import cv2
from os import listdir
import re
files = [f for f in listdir('./wrong2/') if re.match(r'.*\.jpg', f)]
for i in range(len(files)):
    image = cv2.imread("./wrong2/"+files[i])
    image = cv2.resize(image, (100, 120))
    image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # Calculate the Laplacian of the image
    laplacian = cv2.Laplacian(image, cv2.CV_64F)
    score = np.var(laplacian)
    print(files[i], score)
cv2.waitKey(0)

下面這張圖片的分數為1099.5216466388888

而這張為2966.9266674375

所以可以知道,由於拉普拉斯是在求邊緣,而模糊偵測就會是一種比較級的狀況,也就是說,如果一個動態的影片,前一偵的邊緣多,後一偵突然變少,有可能就是因為正在移動而造成的模糊導致邊緣變少

Posted on

讓OpenCV支持GPU

OpenCV CUDA

https://opencv.org/platforms/cuda/

現代 GPU 加速器已經變得強大且功能強大,足以執行通用計算 (GPGPU)。這是一個發展非常迅速的領域,引起了開發計算密集型應用程序的科學家、研究人員和工程師的極大興趣。儘管在 GPU 上重新實現算法存在困難,但許多人這樣做是為了檢查它們的速度。為了支持這些努力,許多高級語言和工具已經可用,例如 CUDA、OpenCL、C++ AMP、調試器、分析器等。

計算機視覺的重要組成部分是圖像處理,這是圖形加速器最初設計的領域。其他部分也假定大規模並行計算並且通常自然映射到 GPU 架構。因此,實現所有這些優勢並在圖形處理器上加速 OpenCV 具有挑戰性,但非常有益。

目標

  • 在 GPU 上為開發者提供方便的計算機視覺框架,與當前 CPU 功能保持概念上的一致性。
  • 使用 GPU 實現最佳性能(針對現代架構調整的高效內核、優化的數據流,如異步執行、複製重疊、零複製)
  • 完整性(盡可能多地實施,即使加速不是很好;這樣可以完全在 GPU 上運行算法並節省應對開銷)

表現

使用範例

這兩個都是載入圖片的方法,上面的是載至CPU,而下方則是載至GPU
img_Mat = cv2.imread(‘test.jpg’) # 16-bit float, OpenCV GPU版本安裝教學

請見此篇: https://cloud.tencent.com/developer/article/1722771

所需套件如下:
1. nVidia 驅動程式和cuDNN
2. Cmake:cmake-3.20.0-rc3-windows-x86_64.msi(用來重新編譯支援GPU的OpenCV)
3. OpenCV 4.51:opencv-4.5.1.tar.gz
4. OpenCV_contrib 4.5.1:opencv_contrib-4.5.1.tar.gz

Posted on

使用GrabCut抓取前景

理論

GrabCut 算法由英國劍橋微軟研究院的 Carsten Rother、Vladimir Kolmogorov 和 Andrew Blake 設計。在他們的論文“GrabCut”:使用迭代圖切割的交互式前景提取中。需要一種算法來以最少的用戶交互進行前景提取,結果就是 GrabCut。

從用戶的角度來看它是如何工作的?最初用戶在前景區域周圍繪製一個矩形(前景區域應該完全在矩形內)。然後算法迭代地對其進行分段以獲得最佳結果。完畢。但在某些情況下,分割效果不佳,例如,它可能將一些前景區域標記為背景,反之亦然。在這種情況下,用戶需要進行精細的修飾。只需在有錯誤結果的圖像上畫一些筆劃即可。Strokes 基本上說 *“嘿,這個區域應該是前景,你將它標記為背景,在下一次迭代中更正它”* 或者它的反面是背景。然後在下一次迭代中,你會得到更好的結果。

請參見下圖。第一個球員和足球被包圍在一個藍色矩形中。然後進行一些帶有白色筆觸(表示前景)和黑色筆觸(表示背景)的最終潤色。我們得到了一個不錯的結果。

函數介紹

grabCut()

下面介紹幾個常用的參數:

  • img – 輸入圖像
  • mask – 這是一個蒙版圖像,我們在其中指定哪些區域是背景、前景或可能的背景/前景等。它由以下標誌完成,cv.GC_BGD、cv.GC_FGD、cv.GC_PR_BGD、cv.GC_PR_FGD,或者簡單地通過0,1,2,3 到圖像。
  • rect – 它是包含格式為 (x,y,w,h) 的前景對象的矩形的坐標
  • bdgModel、fgdModel – 這些是算法內部使用的數組。您只需創建兩個大小為 (1,65) 的 np.float64 類型零數組。
  • iterCount – 算法應該運行的迭代次數。
  • model- 它應該是cv.GC_INIT_WITH_RECT或cv.GC_INIT_WITH_MASK或組合決定我們是繪製矩形還是最終的修飾筆劃。

簡單範例

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('image.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()
# newmask是我手動標註的mask圖片
newmask = cv.imread('newmask.png',0)
# 標記為白色的地方(當然是前景),更改 mask=1
# 標記為黑色的地方(確定背景),更改 mask=0
mask[newmask == 0] = 0
mask[newmask == 255] = 1
mask, bgdModel, fgdModel = cv.grabCut(img,mask,None,bgdModel,fgdModel,5,cv.GC_INIT_WITH_MASK)
mask = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask[:,:,np.newaxis]
cv.imshow("img",img)
plt.imshow(img),plt.colorbar(),plt.show()

結果

Posted on

用opencv作圖片旋轉

可以使用 OpenCV 的 cv2.getRotationMatrix2D() 函數來生成旋轉矩陣,然後使用 cv2.warpAffine() 函數將圖像旋轉。

import cv2
import numpy as np

# 讀取第一幅圖像
image = cv2.imread('image1.jpg')

import cv2
import numpy as np

def rotate_image(image, angle):
    # 取得圖像的高度和寬度
    (h, w) = image.shape[:2]
    # 計算圖像的中心點
    center = (w // 2, h // 2)
    # 取得旋轉矩陣
    M = cv2.getRotationMatrix2D(center, angle, 1.0)
    # 旋轉圖像
    rotated_image = cv2.warpAffine(image, M, (w, h))
    return rotated_image

# 示例: 將圖像旋轉 90 度
rotated_image = rotate_image(image, 90)

# 顯示結果
cv2.imshow('image', image)
cv2.imshow('rotated_image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

請注意,旋轉角度是以逆時針方向為正的。例如,如果要將圖像逆時針旋轉 90 度,則角度值應設為 90。

此外,如果你想要在旋轉圖像時保留圖像的邊界,你可以使用 cv2.warpAffine() 函數的 borderValue參數來設置(255,255,255)。這樣會將旋轉後圖像的邊界填充為白色。

import cv2
import numpy as np

# 讀取第一幅圖像
image = cv2.imread('image1.jpg')


def rotate_image(image, angle):
    # 取得圖像的高度和寬度
    (h, w) = image.shape[:2]
    # 計算圖像的中心點
    center = (w // 2, h // 2)
    # 取得旋轉矩陣,並在旋轉時保留圖像的邊界
    M = cv2.getRotationMatrix2D(center, angle, 1.0)
    # 旋轉圖像
    rotated_image = cv2.warpAffine(image, M, (w, h), borderValue=(255,255,255))
    return rotated_image

# 示例: 將圖像旋轉 90 度
rotated_image = rotate_image(image, 90)


# 顯示結果
cv2.imshow('image', image)
cv2.imshow('rotated_image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Posted on

調整圖片的亮度

可以使用 OpenCV 的 cv2.convertScaleAbs() 函數來增亮圖像。這個函數會將圖像的每個像素值乘上一個指定的比例因子並加上一個常數,以此來調整圖像的亮度。

請注意,這個函數可能會導致圖像中的一些像素值超出 0 到 255 的範圍,因此你可能需要在函數末尾使用new_image = np.where(new_image > 255, 255, new_image)來將這些像素值限制在合法範圍內。

import cv2
import numpy as np

# 讀取第一幅圖像
image = cv2.imread('image1.jpg')

def increase_brightness(image, value):
    # 將圖像的每個像素值乘上指定的比例因子並加上常數
    new_image = cv2.convertScaleAbs(image, alpha=1, beta=value)
    # 將像素值限制在 0 到 255 之間
    new_image = np.where(new_image > 255, 255, new_image)
    return new_image

# 示例: 將圖像的亮度提高 50
bright_image = increase_brightness(image, 50)
# 示例: 將圖像的亮度降低50
dark_image = increase_brightness(image, -50)


# 顯示結果
cv2.imshow('image', image)
cv2.imshow('bright_image', bright_image)
cv2.imshow('dark_image', dark_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Posted on

用opencv做圖像融合

OpenCV 的 cv2.addWeighted() 函數可用來實現圖像增量。

這個函數有三個輸入參數:

src1: 第一個輸入圖像,應該是第一幅圖像的數組。
alpha: 一個浮點數,用於決定第一幅圖像在輸出圖像中的權重。
src2: 第二個輸入圖像,應該是第二幅圖像的數組。
此函數返回值為加權的結果,其中第一幅圖像的權重為 alpha,第二幅圖像的權重為 1-alpha。

例如,假設你想要將第一幅圖像的 50% 与第二幅圖像的 50%相加,你可以使用以下代碼:

import cv2

# 讀取第一幅圖像
img1 = cv2.imread('image1.jpg')

# 讀取第二幅圖像
img2 = cv2.imread('image2.jpg')

# 將第一幅圖像的 50% 和第二幅圖像的 50% 相加
result = cv2.addWeighted(img1, 0.5, img2, 0.5, 0)

# 顯示結果
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

PS: 在使用 cv2.addWeighted() 函數時,兩幅圖像的大小必須相同,否則會出現錯誤。

Posted on

圖像處理之圖像金字塔

理論

通常,我們使用尺寸固定的圖像。但在某些情況下,我們需要使用不同分辨率的(相同)圖像。例如,當我們在圖像中搜索物體時,比如人臉,我們並不確定物體出現在圖像中的大小。在這種情況下,我們需要創建一組具有不同分辨率的圖像,並在所有圖像中搜索對象。這些不同分辨率的圖像被稱為圖像金字塔(如果將它們放在一起,最高分辨率的圖像在底部,最低分辨率的圖像在頂部,它看起來像一個金字塔)。

圖像金字塔有兩種:高斯金字塔和拉普拉斯金字塔

高斯金字塔

高斯金字塔的高層(低層)圖像是通過去除低層(高層)圖像中的連續行和列來實現的。再由下一層的5個像素的高斯權值貢獻構成上一層的每個像素。這樣,一個M×N圖像就變成了M/2×N/2圖像。所以面積減少到原來的四分之一。它被稱為Octave。同樣的模式持續到金字塔的上方(即分辨率下降)。同樣地,在擴展過程中,每一層的面積會增加4倍。我們可以使用cv.pyrDown()和cv.pyrUp()函數找到高斯金字塔。

img = cv.imread('messi5.jpg')
lower_reso = cv.pyrDown(higher_reso)

可以使用cv.pyrUp()函數放大圖像

higher_reso2 = cv.pyrUp(lower_reso)

拉普拉斯金字塔

拉普拉斯金字塔是由高斯金字塔形成的。拉普拉斯金字塔圖像就像邊緣圖像。它的大部分元素都是0。它們用於圖像壓縮。拉普拉斯金字塔的層次是由高斯金字塔的這一層次與高斯金字塔上一層次的擴展版之間相減形成的。Laplacian層次的三個層次如下(調整對比度以增強內容):

使用圖像金字塔實現圖像混合

金字塔的一個應用是圖像混合。
例如,在圖像拼接中,你將需要將兩個圖像疊加在一起,但由於圖像之間的不連續性,可能看起來不太好。
在這種情況下,使用金字塔圖像混合可以實現無縫混合,而不會在圖像中留下太多數據。
其中一個經典的例子就是混合兩種水果,橙子和蘋果。
現在看看結果本身來理解我在說什麼:

簡單地說就是這樣做的:
1. 加載蘋果和橘子的兩個圖像
2. 尋找蘋果和橘子的高斯金字塔(在這個例子中,層數是6)
3. 從高斯金字塔,找到他們的拉普拉斯金字塔
4. 現在把蘋果的左半部分和橘子的右半部分加入拉普拉斯金字塔的每一層
5. 最後,從這個聯合圖像金字塔,重建原始圖像。

import cv2 as cv
import numpy as np,sys
A = cv.imread('apple.jpg')
B = cv.imread('orange.jpg')
# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
for i in range(6):
    G = cv.pyrDown(G)
    gpA.append(G)
# generate Gaussian pyramid for B
G = B.copy()
gpB = [G]
for i in range(6):
    G = cv.pyrDown(G)
    gpB.append(G)
# generate Laplacian Pyramid for A
lpA = [gpA[5]]
for i in range(5,0,-1):
    GE = cv.pyrUp(gpA[i])
    L = cv.subtract(gpA[i-1],GE)
    lpA.append(L)
# generate Laplacian Pyramid for B
lpB = [gpB[5]]
for i in range(5,0,-1):
    GE = cv.pyrUp(gpB[i])
    L = cv.subtract(gpB[i-1],GE)
    lpB.append(L)
# Now add left and right halves of images in each level
LS = []
for la,lb in zip(lpA,lpB):
    rows,cols,dpt = la.shape
    ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:]))
    LS.append(ls)
# now reconstruct
ls_ = LS[0]
for i in range(1,6):
    ls_ = cv.pyrUp(ls_)
    ls_ = cv.add(ls_, LS[i])
# image with direct connecting each half
real = np.hstack((A[:,:cols/2],B[:,cols/2:]))
cv.imwrite('Pyramid_blending2.jpg',ls_)
cv.imwrite('Direct_blending.jpg',real)
Posted on

使用模板匹配查找圖像中的對象

模板匹配

參考此篇教學: https://docs.opencv.org/4.x/d4/dc6/tutorial_py_template_matching.html
使用範例如下:

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()

使用cv.matchTemplate(), cv.minMaxLoc()與方式,當模板在圖片中被縮放或旋轉後,匹配成效不佳。
但實際應用中,物件在3D範圍內很常會被縮放或旋轉,就無法使用上述模板匹配方式

改良方法

嘗試Features2DFramework 中的 openCV 函數。例如SIFT或SURF描述符,以及FLANN匹配器。另外,您將需要findHomography方法。
這是在場景中查找旋轉對象的一個很好的例子。

簡而言之,算法是這樣的:

  • 尋找目標圖像的關鍵點(Keypoints)
  • 從這些關鍵點(Keypoints)中提取描述符(des)
  • 尋找場景圖像的關鍵點
  • 從關鍵點提取描述符
  • 通過匹配器匹配描述符
  • 分析圖片內容尋找目標圖像

有不同類別的 FeatureDetectors、DescriptorExtractors 和 DescriptorMatches,選擇適合的任務的那些。
以下為提取關鍵點的一個範例

from __future__ import print_function
import cv2
import numpy as np
import argparse
print(cv2.__version__)
img = cv2.imread('./D10.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.SIFT_create()

kp = sift.detect(gray, None)


ret = cv2.drawKeypoints(gray, kp, img)
cv2.imshow('ret', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()

kp, des = sift.compute(gray, kp)

print(np.shape(kp))
print(np.shape(des))

print(des[0])

更多資訊

Posted on

使用opencv尋找邊緣

使用Canny算子

Canny 邊緣檢測是一種從不同的視覺對像中提取有用的結構信息並顯著減少要處理的數據量的技術。它已廣泛應用於各種計算機視覺系統。Canny發現,邊緣檢測在不同視覺系統上的應用需求是比較相似的。因此,可以在各種情況下實施滿足這些要求的邊緣檢測解決方案。邊緣檢測的一般標準包括:

錯誤率低的邊緣檢測,這意味著檢測應該盡可能準確地捕獲圖像中顯示的邊緣
算子檢測到的邊緣點應該準確地定位在邊緣的中心。
圖像中的給定邊緣應僅標記一次,並且在可能的情況下,圖像噪聲不應產生錯誤邊緣。

以下為一個簡單使用範例

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

使用cartToPolar

使用 cv2.Sobel 函數計算圖像的梯度值和方向,並使用 cv2.cartToPolar 函數將梯度值和方向轉換為極坐標形式。

import numpy as np
import cv2
gray = cv2.imread('./unknow/img_2022-12-15_18-47-31_1.jpg')
gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
gray = gray/255.0
# 計算圖像的梯度值和方向
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=1)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=1)
magnitude, angle = cv2.cartToPolar(sobelx, sobely, angleInDegrees=True)

cv2.imshow("magnitude",magnitude)
cv2.imshow("angle",angle)
cv2.imshow("gray",gray)

在上面的方法中,要注意的是使用cv.CV_64F能有較好的結果,若想要有CV_8U的結果,可以先採用CV_64F再用下面方式轉為CV_8U

# 輸出數據類型 = cv.CV_64F。然後取其絕對值並轉換為 cv.CV_8U
sobelx64f = cv.Sobel (img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)

分水嶺演算法

OpenCV 實現了一種基於標記的分水嶺算法,您可以在其中指定哪些是要合併的所有谷點,哪些不是。它是一種交互式圖像分割。我們所做的是為我們所知道的對象賦予不同的標籤。用一種顏色(或強度)標記我們確定是前景或物體的區域,用另一種顏色標記我們確定是背景或非物體的區域,最後是我們不確定的區域,用 0 標記它。那是我們的標記。然後應用分水嶺算法。然後我們的標記將使用我們提供的標籤進行更新,並且對象的邊界值為 -1。

import cv2
import numpy

img = cv2.imread("image/water_coins.jpg")
cv2.imshow("img", img)

# 1.圖像二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)

kernel = numpy.ones((3, 3), dtype=numpy.uint8)
# 2.噪聲去除
open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 3.確定背景區域
sure_bg = cv2.dilate(open, kernel, iterations=3)
# 4.尋找前景區域
dist_transform = cv2.distanceTransform(open, 1, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.5 * dist_transform.max(), 255, cv2.THRESH_BINARY)
# 5.找到未知區域
sure_fg = numpy.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg)

# 6.類別標記
ret, markers = cv2.connectedComponents(sure_fg)
# 為所有的標記加1,保證背景是0而不是1
markers = markers + 1
# 現在讓所有的未知區域為0
markers[unknow == 255] = 0

# 7.分水嶺算法
markers = cv2.watershed(img, markers)
img[markers == -1] = (0, 0, 255)

cv2.imshow("gray", gray)
cv2.imshow("thresh", thresh)
cv2.imshow("open", open)
cv2.imshow("sure_bg", sure_bg)
cv2.imshow("sure_fg", sure_fg)
cv2.imshow("unknow", unknow)
cv2.imshow("img_watershed", img)
cv2.waitKey(0)
cv2.destroyWindow()


下面是我自己的嘗試

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('./img_2022-12-15_18-47-31_1.jpg')
imageHSV = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# 白色的部分
lower_white = np.array([0, 0, 220], dtype=np.uint8)
upper_white = np.array([180, 130, 255], dtype=np.uint8)
thresh = cv.inRange(imageHSV, lower_white, upper_white)

# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
cv.imshow("opening",opening)

# sure background area - Green
sure_bg = cv.dilate(opening, kernel, iterations=3)
cv.imshow("sure_bg",sure_bg)

# Finding sure foreground area
# 紅色的部分
red_lower = np.array([0, 30, 100], dtype=np.uint8)
red_upper = np.array([30, 255, 240], dtype=np.uint8)
red_lower2 = np.array([135, 30, 100], dtype=np.uint8)
red_upper2 = np.array([180, 255, 240], dtype=np.uint8)
red_mask = cv.bitwise_or(cv.inRange(imageHSV, red_lower, red_upper),cv.inRange(imageHSV, red_lower2, red_upper2))
red_mask = cv.dilate(red_mask, kernel, iterations=3)
sure_fg = cv.bitwise_or(thresh, red_mask)

# 黑色的部分
black_lower = np.array([85, 0, 0], dtype=np.uint8)
black_upper = np.array([180, 40, 100], dtype=np.uint8)
black_lower2 = np.array([0, 0, 0], dtype=np.uint8)
black_upper2 = np.array([35, 40, 100], dtype=np.uint8)
black_mask = cv.bitwise_or(cv.inRange(imageHSV, black_lower, black_upper),cv.inRange(imageHSV, black_lower2, black_upper2))
black_mask = cv.dilate(black_mask, kernel, iterations=3)
cv.imshow("black_mask",black_mask)
sure_fg = cv.bitwise_or(sure_fg, black_mask)
sure_fg = cv.erode(sure_fg, kernel, iterations=3)
cv.imshow("sure_fg",sure_fg)

# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
cv.imshow("unknown",unknown)
# 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

markers = cv.watershed(img,markers)
img[markers == -1] = (0, 0, 255)
cv.imshow("watershed",img)
cv.waitKey(0)