發佈日期:

如何判讀訓練結果

訓練過程

下面為一個epochs為10的訓練過程的log

機器學習結果判讀

如上圖,當我們下了model.fit開始跑機器學習後,會可以看到跑出下面這些資訊,這時候要如何解讀這些資訊呢?
loss: 0.0337, accuracy: 0.9910, val_loss: 0.1509, val_accuracy: 0.9762​

  • loss:指的是模型在訓練數據集上的損失。損失越低,代表模型的表現越好。​
  • accuracy:指的是模型在訓練數據集上的準確率。準確率越高,代表模型的表現越好。​
  • val_loss:指的是模型在驗證數據集上的損失。損失越低,代表模型的表現越好。​
  • val_accuracy:指的是模型在驗證數據集上的準確率。準確率越高,代表模型的表現越好。​

0.9762 的準確率可以說是相當不錯的,但是在某些應用中可能還不夠。例如,在自動駕駛應用中,準確率可能需要更高,因為一個錯誤的預測可能會導致嚴重的後果。
在這個結果的數據集中,準確率為 0.9762 ,其大約會有 2.38% 的錯誤率。
如果有 100 張圖片,則大約有 2.38 張圖片會被錯誤地預測。

學習效果不好的原因

如果模型在驗證數據集上的準確率低於實際準確率,那可能是因為以下原因:

  • 驗證數據集不具有代表性: 如果驗證數據集中的數據與實際應用中的數據有很大的差異,那麼模型的驗證數據集上的準確率可能會低於實際準確率。
  • 模型過度擬合: 如果模型在訓練數據集上的表現很好,但是在驗證數據集上的表現較差,那麼這可能是因為模型過度擬合了訓練數據集。過度擬合的模型往往在訓練數據集上表現較好,但是在沒有看過的數據集上表現較差。因此,可嘗試增加數據集的大小使用正則化來減少模型的過度擬合。
  • 模型訓練不足: 如果模型沒有被訓練足夠長的時間,那麼它的表現可能會不夠好。你可以增加訓練的 epoch 數,或者增加每個 epoch 中的訓練次數來幫助模型學習。
  • 還有一種情況就是模型的訓練準確率和驗證準確率之間的差距較大,這可能是因為模型過擬合或者過欠擬合。如果模型過擬合,則訓練準確率會很高,但是驗證準確率會很低;如果模型過欠擬合,則訓練準確率和驗證準確率都會很低。可以通過調整模型的超參數來平衡訓練準確率

歸納學習成效不好的原因如下:

  • 欠擬合。如果模型過於簡單,則可能會欠擬合。欠擬合的模型在訓練數據集上的表現可能不夠好,導致訓練準確率越來越差。
  • 過擬合。如果模型過於複雜,則可能會過擬合。過擬合的模型在訓練數據集上的表現可能很好,但是在沒有看過的數據集上的表現可能較差。
  • 學習率過大。如果模型的學習率過大,則模型可能會跳過最佳解,導致訓練準確率越來越差。

如何解決

  • 調整模型的複雜度。你可以增加或減少模型的層數或每層的神經元數量,以平衡模型的複雜度。
  • 使用正則化。正則化是一種常見的技巧,可以減少模型的過擬合。
  • 調整學習率。你可以使用不同的學習率來平衡收敛速度和準確率。
發佈日期:

Keras介紹

甚麼是Keras

Keras是一個用Python編寫的開源神經網路庫,旨在快速實現深度神經網路,專注於使用者友好、模組化和可延伸性,除標準神經網路外,Keras還支援卷積神經網路和迴圈神經網路。包含許多常用神經網路構建塊的實現,例如層、目標、啟用功能、最佳化器和一系列工具,可以更輕鬆地處理圖像和文字資料。

Keras由純Python編寫而成並基於Tensorflow、Theano以及CNTK後端。Keras 為支持快速實驗而生,能夠把你的idea迅速轉換為結果,如果你有如下需求,請選擇Keras:

  • 簡易和快速的原型設計(keras具有高度模塊化,極簡,和可擴充特性)
  • 支持CNN和RNN,或二者的結合
  • 無縫CPU和GPU切換

Keras建立模型的流程

  • 載入數據集: 首先,您需要載入您要用來訓練模型的圖像數據集。您可以使用 Python 的數據讀取庫(如 pandas 或 numpy) 來讀取本地數據集,或者使用 Keras 自帶的數據集(如 MNIST 手寫數字數據集)。
  • 將數據拆分為訓練集和測試集: 接下來,您需要將數據集拆分為訓練集和測試集。這樣可以幫助您評估模型的表現。通常,您會將大約 80% 的數據集用於訓練,剩餘的 20% 用於測試。
  • 預處理數據: 在進行模型訓練之前,您需要對數據進行預處理。這可能包括將圖像大小轉換為相同的大小,標準化像素值等。
  • 建立模型: 接下來,您可以使用 Keras 來建立模型。Keras 提供了許多不同的模型建構器(如 Sequential 或 functional API),您可以使用它們來建立不同類型的模型。在建立模型時,您需要考慮使用哪種模型類型(如卷積神經網絡或長短期記憶網絡)、使用哪些層類型(如卷積層、池化層、全連接層等),以及如何連接這些層。在決定模型結構時,您可以參考相關研究或嘗試不同的組合以找出最佳的模型結構。
  • 編譯模型: 一旦您已經建立了模型,您需要使用 Keras 的 compile 方法將其編譯。在編譯時,您需要指定損失函數和優化器。損失函數用於評估模型的表現,優化器用於更新模型的權重以使損失最小化。
  • 訓練模型: 一旦您已經編譯了模型,您就可以使用 Keras 的 fit 方法對模型進行訓練。在訓練過程中,您需要指定訓練集和模型訓練的參數,如批次大小和訓練輪數。
  • 評估模型: 一旦模型訓練完成,您就可以使用 Keras 的 evaluate 方法對模型進行評估。評估時,您需要指定測試集,Keras 會自動計算損失和指標(如準確率)。
  • 使用模型: 一旦您已經訓練好了模型,就可使用model.predict來預測資料

與Tensorflow的關係

TensorFlow是一個用於構建和訓練模型的平台。它提供了很多功能,可以讓你輕鬆地構建、訓練和部署各種不同類型的模型。

Keras是一個用於構建和訓練深度學習模型的高階框架。它是在TensorFlow之上構建的,可以讓你輕鬆地使用TensorFlow的功能。

总之,TensorFlow是一個底層的框架,Keras是建立在TensorFlow之上的高階框架,可以讓你更輕鬆地構建和訓練模型。

基礎範例

這是一個建立模型的範例,主要用以做為圖片分類的神經學習,使用了下面幾種模型結構:
1、卷積層: 可以使用一或多個卷積層來提取圖像中的特徵。
2、池化層: 可以使用池化層來縮小圖像的尺寸,並且保留有用的特徵。
3、全連接層: 可以使用一或多個全連接層來將特徵轉換為預測結果。

from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from keras.models import Sequential
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
# X_train 和 y_train 都是 NumPy 陣列
images = np.array(images)
labels= np.array(labels)
# 如果 y_train 是字符串類型的標籤,則可以使用 sklearn 的 LabelEncoder 來將它們轉換為數值。例如:
le = LabelEncoder()
y_train = le.fit_transform(y_train)
# 分割為訓練集及驗證集
X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2)
# 建立模型
model = Sequential()

# 加入卷積層
model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu', input_shape=(200,200,3)))

# 加入池化層
model.add(MaxPooling2D(pool_size=(2,2)))

# 加入卷積層
model.add(Conv2D(filters=64, kernel_size=(3,3), activation='relu'))

# 加入池化層
model.add(MaxPooling2D(pool_size=(2,2)))

# 將特徵圖拉平
model.add(Flatten())

# 加入全連接層
model.add(Dense(units=64, activation='relu'))

# 加入輸出層
model.add(Dense(units=52, activation='softmax'))

# 編譯模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 開始訓練
model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))

這是一個使用 Keras 載入模型並預測答案的範例

import numpy as np
import tensorflow as tf
from tensorflow import keras

# 載入模型
model = keras.models.load_model('model.h5')

# 假設我們有一個輸入矩陣 X,其中包含一些資料點
X = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])

# 使用模型預測答案
predictions = model.predict(X)

# 輸出預測結果
print(predictions)
發佈日期:

用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()
發佈日期:

調整圖片的亮度

可以使用 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()

發佈日期:

用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() 函數時,兩幅圖像的大小必須相同,否則會出現錯誤。

發佈日期:

為每個python專案建立獨立的虛擬環境

甚麼是虛擬環境

虛擬環境 (virtual environment) 是一個隔離的 Python 環境,可以讓您在同一台電腦上安裝不同版本的 Python 套件,或是不同專案之間使用不同的套件版本。

使用虛擬環境可以幫助您避免不同專案之間因為套件版本不同而造成的衝突。例如,如果您有兩個專案,一個專案需要使用舊版本的套件,而另一個專案需要使用新版本的套件,您可以在兩個專案中分別建立兩個虛擬環境,並在每個虛擬環境中安裝所需的套件版本。這樣就可以避免在不同專案中使用不同版本的套件造成的衝突。

虛擬環境還可以讓您在不影響全域環境的情況下,安裝、卸載或更新套件。這對於在測試某些套件時特別有用,因為您可以在虛擬環境中安裝或卸載套件,而不用擔心會對全域環境造成影響。

總的來說,虛擬環境可以讓您更輕鬆地在同一台電腦上進行多個 Python 專案的開發,並且可以有效地隔離不同專案之間的套件版本。

於Windows建立虛擬環境

要建立新的虛擬環境,請選擇 Python 解譯器,並建立用來存放的 .\venv 目錄:

python -m venv --system-site-packages .\venv

啟動虛擬環境:

.\venv\Scripts\activate

在不影響主機系統設定的情況下,在虛擬環境中安裝套件。首先,請升級 pip:

pip install --upgrade pip

pip list  # show packages installed within the virtual environment

之後再離開虛擬環境:

deactivate 

於macOS建立虛擬環境

要建立新的虛擬環境,請選擇 Python 解譯器,並建立用來存放的 ./venv 目錄:

python3 -m venv --system-site-packages ./venv

使用殼層特定的指令啟動虛擬環境:

source ./venv/bin/activate  # sh, bash, or zsh
. ./venv/bin/activate.fish  # fish
source ./venv/bin/activate.csh  # csh or tcsh

虛擬環境啟用時,你的殼層提示會包含 (venv) 前置字元。

在不影響主機系統設定的情況下,在虛擬環境中安裝套件。首先,請升級 pip:

pip install --upgrade pip

pip list  # show packages installed within the virtual environment

之後再離開虛擬環境:

deactivate

啟動虛擬環境

可使使用下列指令啟動已建立好的虛擬環境

source ./env/bin/activate

為你的專案設定使用函式庫的列表

匯出現在專案所使用的函式庫

python -m pip freeze > requirements.txt

安裝函式庫列表

python -m pip install -r requirements.txt
發佈日期:

圖像處理之圖像金字塔

理論

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

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

高斯金字塔

高斯金字塔的高層(低層)圖像是通過去除低層(高層)圖像中的連續行和列來實現的。再由下一層的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)
發佈日期:

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

模板匹配

參考此篇教學: 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])

更多資訊

發佈日期:

使用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)

發佈日期:

圖片降維處理(從彩色變灰階再變黑白)

從彩色變灰階

使用 cv.cvtColor()函數可作色彩的空間轉換,例如要偵測顏色時,要轉成HSV

imageHSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

而降為灰階則為

imageHSV = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

從灰階到黑白

要把圖片從灰階變成黑白很簡單。對於每個像素,應用相同的閾值。如果像素值小於閾值,則設置為0,否則設置為最大值。函數cv.threshold用於應用閾值。第一個參數是源圖像,應該是灰度圖像。第二個參數是用於對像素值進行分類的閾值。第三個參數是分配給超過閾值的像素值的最大值。
可使用參數cv.thresholdcv.adaptiveThreshold

cv.threshold

使用範例

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('gradient.png',0)
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray',vmin=0,vmax=255)
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

cv.adaptiveThreshold

在cv.threshold使用一個全局值作為閾值。但這可能並不適用於所有情況,例如,如果圖像在不同區域具有不同的光照條件。在這種情況下,自適應閾值可以提供幫助。在這裡,算法根據像素周圍的小區域確定像素的閾值。因此,我們為同一圖像的不同區域獲得不同的閾值,這為具有不同光照的圖像提供了更好的結果。

除了上述參數外,方法cv.adaptiveThreshold 還需要三個輸入參數:

adaptiveMethod決定如何計算閾值:

  • cv.ADAPTIVE_THRESH_MEAN_C:閾值是鄰域面積的平均值減去常量C。
  • cv.ADAPTIVE_THRESH_GAUSSIAN_C :閾值是鄰域值減去常量C的高斯加權和。
  • blockSize確定鄰域區域的大小,C是從鄰域像素的平均值或加權總和中減去的常數。

下面比較了具有不同光照的圖像的全局閾值和自適應閾值:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('sudoku.png',0)
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\
            cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
            'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

cv.threshold使用THRESH_OTSU

在全局閾值中,使用任意選擇的值作為閾值。相反,Otsu 的方法避免了必須選擇一個值並自動確定它。

考慮只有兩個不同圖像值的圖像(雙峰圖像),其中直方圖僅包含兩個峰值。一個好的閾值應該在這兩個值的中間。類似地,Otsu 的方法從圖像直方圖中確定最佳全局閾值。
為此,使用了cv.threshold()函數,其中cv.THRESH_OTSU作為額外標誌傳遞。閾值可以任意選擇。然後算法找到最佳閾值,該閾值作為第一個輸出返回。
查看下面的示例。輸入圖像是有噪聲的圖像。在第一種情況下,應用值為 127 的全局閾值。在第二種情況下,直接應用 Otsu 的閾值。在第三種情況下,首先使用 5×5 高斯核對圖像進行濾波以去除噪聲,然後應用 Otsu 閾值處理。查看噪聲過濾如何改善結果。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('noisy2.png',0)
# global thresholding
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu's thresholding
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
blur = cv.GaussianBlur(img,(5,5),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# plot all the images and their histograms
images = [img, 0, th1,
          img, 0, th2,
          blur, 0, th3]
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
          'Original Noisy Image','Histogram',"Otsu's Thresholding",
          'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
for i in range(3):
    plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
    plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
    plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)
    plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])
    plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')
    plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
plt.show()