Posted on

對圖像做幾何變換

目標

學習對圖像應用不同的幾何變換,如平移、旋轉、仿射變換等。
你會看到這些功能:cv.getPerspectiveTransform

縮放

只是調整圖像的大小。interpolation參數有下面幾種

使用範例

import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
res = cv.resize(img,None,fx=2, fy=2, interpolation = cv.INTER_CUBIC)
#OR
height, width = img.shape[:2]
res = cv.resize(img,(2*width, 2*height), interpolation = cv.INTER_CUBIC)

平移

平移影像,下面程式會將圖,x 軸平移 100,y 軸平移 50

import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])# 2x3 矩陣,x 軸平移 100,y 軸平移 50
dst = cv.warpAffine(img,M,(cols,rows))
cv.imshow('img',dst)
cv.waitKey(0)
cv.destroyAllWindows()

旋轉影像

img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1) # 中心點 ((cols-1)/2.0,(rows-1)/2.0)),旋轉 90 度,尺寸 1
dst = cv.warpAffine(img,M,(cols,rows))

仿射變換

img = cv.imread('drawing.png')
rows,cols,ch = img.shape
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M = cv.getAffineTransform(pts1,pts2)
dst = cv.warpAffine(img,M,(cols,rows))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

透視變換

這個方法可以把3D的有角度的長方形,拉成2D的長方形,很常會用來使用在3為空間的像照片、名片等拉平的效果上

img = cv.imread('sudoku.png')
rows,cols,ch = img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv.getPerspectiveTransform(pts1,pts2)
dst = cv.warpPerspective(img,M,(300,300))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

Posted on

opencv圖像運算

理論

形態變換是一些基於圖像形狀的簡單操作。它通常在二進製圖像上執行。它需要兩個輸入,一個是我們的原始圖像,第二個稱為結構元素或內核,它決定了操作的性質。兩個基本的形態學算子是侵蝕和膨脹。然後它的變體形式如開、閉、梯度等也開始發揮作用。我們將在下圖的幫助下一一看到它們:

侵蝕cv2.erode

侵蝕的基本思想就像土壤侵蝕一樣,它侵蝕掉前景物體的邊界(總是盡量讓前景保持白色)。那它有什麼作用呢?內核在圖像中滑動(如在 2D 卷積中)。只有當內核下的所有像素都為 1 時,原始圖像中的像素(1 或 0)才會被認為是 1,否則它會被腐蝕(變為零)。

所以發生的事情是,根據內核的大小,邊界附近的所有像素都將被丟棄。因此,前景對象的厚度或大小會減少,或者圖像中的白色區域會減少。它對於去除小的白噪聲(正如我們在色彩空間章節中看到的)、分離兩個連接的對像等很有用。

在這裡,作為一個例子,我會使用一個 5×5 的內核。讓我們看看它是如何工作的:

import cv2 as cv
import numpy as np
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)

膨脹Dilation

它與侵蝕正好相反。這裡,如果內核下的至少一個像素為“1”,則像素元素為“1”。因此它增加了圖像中的白色區域或前景對象的大小增加。通常,在去除噪聲等情況下,腐蝕之後是膨脹。因為,腐蝕去除了白噪聲,但它也縮小了我們的對象。所以我們擴大它。由於噪音消失了,它們不會回來,但我們的對象區域增加了。它還可用於連接對象的損壞部分。

dilation = cv2.dilate(img,kernel,iterations = 1)

去噪cv2.MORPH_OPEN

在去除噪聲方面很有用。這裡我們使用函數cv2.morphologyEx()

opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

關閉線條

對關閉前景對象內的小孔或對像上的小黑點很有用。這個我也有使用來把canny所找到的邊緣關起來

closing = cv2.morphologyEx (img, cv2.MORPH_CLOSE, kernel)

形態梯度

這是圖像膨脹和腐蝕之間的區別。結果將看起來像對象的輪廓。

gradient = cv2.morphologyEx (img, cv2.MORPH_GRADIENT, kernel)

Top Hat

使用cv2.MORPH_TOPHAT

tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

Black Hat

使用cv2.MORPH_GRADIENT

gradient = cv2.morphologyEx (img, cv2.MORPH_GRADIENT, kernel)

更多資訊: https://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm

Posted on

使用opencv做圖片後製處理(如ps)

這個部落格有一個系列文:
12th 鐵人賽 – 【錢不夠買ps的我,只好用OpenCV來修圖了!】
分享了非常多好用的圖片後製方法
這邊分享幾個我覺得不錯的

黑強化

強化有顏色區域的深度

# do pre-process (black strengthen) in OCR
def image_filter(img, degree = 3):
    # degree is from 0 to Unlimited, bigger number => bigger strengthen
    decrease_img = (255.0/1)*(img/(255.0/1))**degree
    decrease_img = np.array(decrease_img, dtype=np.uint8)
    return decrease_img

白平衡

圖像光照校正處理

def mean_white_balance(img):
    b, g, r = cv2.split(img)
    r_avg = cv2.mean(r)[0]
    g_avg = cv2.mean(g)[0]
    b_avg = cv2.mean(b)[0]
    k = (r_avg + g_avg + b_avg) / 3
    kr = k / r_avg
    kg = k / g_avg
    kb = k / b_avg
    r = cv2.addWeighted(src1=r, alpha=kr, src2=0, beta=0, gamma=0)
    g = cv2.addWeighted(src1=g, alpha=kg, src2=0, beta=0, gamma=0)
    b = cv2.addWeighted(src1=b, alpha=kb, src2=0, beta=0, gamma=0)
    balance_img = cv2.merge([b, g, r])
    return balance_img

雙邊濾波

雙邊濾波(Bilateral filter)是一種非線性的濾波方法,是結合圖像的空間鄰近度和像素值相似度的一種折衷處理,同時考慮空域信息和灰度相似性,達到保邊去噪的目的。具有簡單、非迭代、局部的特點。

image = cv2.bilateralFilter(image, 5, 30, 30)
Posted on

使用opencv來找出紅色區塊

使用HSV色碼轉換器

一個好用的線上工具
色碼轉換器: https://www.peko-step.com/zhtw/tool/hsvrgb.html

若要將轉換過的顏色套用到python的色碼,記得將S,V的範圍改為0-255
然後從網站上看到的H的值(如這邊為26)要除以2,也就是13

以上圖來說,上面顯示的色碼為HSV:(26,90,223),然後填進python裡面要使用HSV:(13,90,223)

python裡面的HSV識別空間

一般對顏色空間的圖像進行有效處理都是在HSV空間進行的,然後對於基本色中對應的HSV分量的範圍為:
H: 0 — 180
S: 0 — 255
V: 0 — 255

基本HSV的顏色劃分


HSV的意義

HSB又稱HSV,表示一種顏色模式:在HSB模式中,H(hues)表示色相,S(saturation)表示飽和度,B(brightness)表示亮度HSB模式對應的媒介是人眼。
HSL 和HSV 二者都把顏色描述在圓柱體內的點,這個圓柱的中心軸取值為自底部的黑色到頂部的白色而在它們中間是的灰色,繞這個軸的角度對應於“色相”,到這個軸的距離對應於“飽和度”,而沿著這個軸的距離對應於“亮度”,“色調”或“明度”。這兩種表示在用目的上類似,但在方法上有區別。


二者在數學上都是圓柱,但HSV(色相,飽和度,明度)在概念上可以被認為是顏色的倒圓錐體(黑點在下頂點,白色在上底面圓心),HSL在概念上表示了一個雙圓錐體和圓球體(白色在上頂點,黑色在下頂點,最大橫切面的圓心是半程灰色)。注意儘管在HSL 和HSV 中“色相”指稱相同的性質,它們的“飽和度”的定義是明顯不同的。

抓取圖片中膚色大小大小

import cv2
def find_white_color_size(image, upper, lower, upper2 = None, lower2 = None):
    imageHSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(imageHSV, lower, upper)
    if lower2 is not None and upper2 is not None:
        mask2 = cv2.inRange(imageHSV, lower2, upper2)
        mask = cv2.bitwise_or(mask, mask2)
    area = 0;
    for i in range(len(mask)):
        filter_color = mask[i] > 0
        area += len(mask[i][filter_color])
    return area

lower_red1 = np.array([0, 70, 100], dtype=np.uint8)
upper_red1 = np.array([20, 120, 240], dtype=np.uint8)
lower_red2 = np.array([150, 70, 100], dtype=np.uint8)
upper_red2 = np.array([180, 120, 240], dtype=np.uint8)
print('color size:',find_white_color_size(card_image, upper_red1, lower_red1, upper_red2, lower_red2))
Posted on

python的socket-io-client 4.5.1不會自動重連問題

問題版本

python-socketio 4.5.1
相關討論串: https://github.com/miguelgrinberg/python-socketio/issues/485
can not reconnect after 503 error

解決方法

自己寫重連的程式碼

import socketio
from threading import Timer

timer = None
address = "http://127.0.0.1:2027"
sio = socketio.Client(reconnection=False, logger=False, engineio_logger=False)
isConnected = False

def connectSocket():
    global timer
    try:
        sio.connect(address, transports='polling')
    except:
        if timer is not None:
            timer.cancel()
        timer = Timer(1.0, connectSocket)
        timer.start()

def close():
    global sio
    global timer
    global isConnected
    sio.disconnect()
    isConnected = False
    if timer is not None:
        timer.cancel()

@sio.event
def test():
    print('(test)'


def send(data):
    sio.emit('send', data)


@sio.event
def connect():
    global isConnected
    print('(connect)')


@sio.event
def disconnect():
    global timer
    global isConnected
    print('(disconnected)')
    sio.disconnect()
    isConnected = False
    if timer is not None:
        timer.cancel()
    timer = Timer(1.0, connectSocket)
    timer.start()

Posted on

LBP 區域二值模式

其實會發這一篇文,主要是看到這個博客的文章真的感動到快哭了…

這幾天因為想增加OCR辨識正確率開始與LBP打交道
https://zh.wikipedia.org/wiki/%E5%B1%80%E9%83%A8%E4%BA%8C%E5%80%BC%E6%A8%A1%E5%BC%8F
我找到了一個看起來很強大很棒的函式庫
https://scikit-image.org/docs/stable/api/skimage.feature.html#skimage.feature.local_binary_pattern

網路上有很多教學文章,看起來是很知名的套件
a href=\”https://machine-learning-python.kspax.io/classification/ex1_recognizing_hand-written_digits\”>https://machine-learning-python.kspax.io/classification/ex1_recognizing_hand-written_digits
然後我遇到了和這位博主一樣的問題
https://www.cnblogs.com/ilk123/p/11797261.html
沒錯…….LBP明明出來的應該是1-256的值阿…我也是設定R=1, P=8\r\n這樣用default的LBP出來的應該要是1-256之間的值,但是卻是0和1的二值陣列….

我一直想..這麼偉大的一個scikit-image怎麼可能有錯,一定是我的使用方法有誤…
害我撞頭撞到快崩潰…..
沒想到在此遇到一個和我一樣的苦主
太感動了,特此記錄!

另外,後來我也找到了一個最原始的LBP算法
程式碼下載於 https://github.com/zhongqianli/local_binary_pattern/blob/master/local_binary_pattern.py

def original_lbp(image):
"""origianl local binary pattern"""
rows = image.shape[0]
cols = image.shape[1]

lbp_image = np.zeros((rows - 2, cols - 2), np.uint8)

for i in range(1, rows - 1):
for j in range(1, cols - 1):
code = 0
center_pix = image[i, j]
if image[i - 1, j - 1] > center_pix:
code = code | (1 << 7) if image[i - 1, j] > center_pix:
code = code | (1 << 6) if image[i - 1, j + 1] > center_pix:
code = code | (1 << 5) if image[i, j + 1] > center_pix:
code = code | (1 << 4) if image[i + 1, j + 1] > center_pix:
code = code | (1 << 3) if image[i + 1, j] > center_pix:
code = code | (1 << 2) if image[i + 1, j - 1] > center_pix:
code = code | (1 << 1) if image[i, j - 1] > center_pix:
code = code | (1 << 0) lbp_image[i - 1, j - 1] = code return lbp_image[/code]