OpenCV Python 系列教程3 - Core 組件

基本知識

  • 灰度圖像的存儲方式:


    image
  • 多通道圖像存儲方式


    image

OpenCV 中的通道存儲為 BGR

像素值的存儲方式

  • RGB 模式缴饭,顯示設(shè)備采用這種模式
  • HSV、HLS 將顏色分解成色調(diào)蛤铜、飽和度和亮度/明度
  • YCrCb 在 JPEG 圖像格式中廣泛使用
  • CIE Lab* 是一種感知上均勻的顏色空間,它適用來度量兩個顏色之間的距離

圖片的基本操作

學(xué)習(xí)目標(biāo):

  • 訪問像素值并修改
  • 訪問圖片屬性
  • 設(shè)置圖像區(qū)域(ROI)
  • 拆分哀蘑、合并圖像

這一節(jié)主要大部分涉及 Numpy 庫的使用

訪問并且修改像素值

import cv2
import numpy as np
# 加載一個彩色圖
img = cv2.imread("img.jpg")
img.shape
(150, 220, 3)
px = img[100, 100]
print(px)
blue = img[100, 100, 0]
print(blue)
[ 85 180 173]
85
# 修改特定的像素
img[100, 100] = [255, 255, 255]
print(img[100, 100])

# 這種修改每個像素的做法效率很低
[255 255 255]

最好使用下面這種方法

# 獲取 RED 通道值
img.item(10, 10, 1)

# 修改
img.itemset((10, 10, 2), 100)
img.item(10, 10, 2)
100

顏色空間縮減

若是單通道的像素撩满,像素有 256(0-255)個值,若是三通道税弃,則顏色數(shù)就更多(一千六百多萬種)纪岁,如此多的顏色進(jìn)行處理,會對算法的性能造成影響则果。這些顏色中幔翰,有代表性的顏色只是小部分。

顏色空間縮減(color space reduction)可以大大降低運(yùn)算復(fù)雜度西壮,具體做法是:

  • 0-9 范圍的像素值為 0遗增;
  • 10-19 范圍像素值為 10;
  • 以此類推

算法實現(xiàn)步驟:

  1. 遍歷圖像矩陣的每個像素
  2. 根據(jù)公式:I_{new} = (I_{old} / 10) * 10
img = cv2.imread("img.jpg", 0) # img.shape = 150*220
img_new = np.array([i for i in map(lambda x:(x//10)*10, img)])

cv2.imshow("img", img)
cv2.imshow("img_new", img_new)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 統(tǒng)計不同元素的個數(shù)
np.unique(img),np.unique(img_new)
(array([ 21,  26,  28,  33,  34,  35,  37,  38,  39,  40,  41,  42,  43,
         44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,
         57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
         70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,
         83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
         96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
        109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
        122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
        135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
        148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
        161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
        174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
        187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
        200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
        213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
        226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
        239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
        252, 253, 254, 255], dtype=uint8),
 array([ 20,  30,  40,  50,  60,  70,  80,  90, 100, 110, 120, 130, 140,
        150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250], dtype=uint8))

運(yùn)行結(jié)果茸时,從肉眼來看贡定,差別不大


image

獲取圖像的屬性

img;
img.shape
# 若是彩色圖像,則返回元組 1*3
# 若是灰色圖像可都,則返回元組 1*2 只有行和列
# 此方法可以用來判斷是否為彩色圖像
(150, 220)
# 所有像素的總和即上面的元組值相乘
img.size
33000
# 圖像的數(shù)據(jù)類型
img.dtype
dtype('uint8')

img.dtype 在調(diào)試時非常重要缓待,因為大部分錯誤是因為無效的數(shù)據(jù)類型引起的

感興趣區(qū)域(ROI)

該部分的功能是對圖像的一小部分區(qū)域進(jìn)行處理(我們感興趣的那部分)蚓耽,可以減少處理時間,增加精度旋炒,給圖像處理帶來便利步悠。

img = cv2.imread("messi5.jpg")
cv2.imshow("image_init", img)
ball = img[280:340, 330:390]
img[273:333, 100:160] = ball
cv2.imshow("image_roi", img)
k = cv2.waitKey(0) & 0xFF
if k == 27:
    cv2.destroyAllWindows()

本例是對找到圖像中球的位置,并復(fù)制一個放在另外一個地方瘫镇,注意看的話鼎兽,你會發(fā)現(xiàn)復(fù)制的球是一個矩形,看起來并不協(xié)調(diào)铣除?


image

拆分谚咬、合并圖像通道

# 拆分每個通道
b,g,r = cv2.split(img) # 或者 b = img[:, :, 0]
# 把各個通道合并起來
img = cv2.merge((b,g,r))

# 讓紅色通道置零,可以不用拆分紅色通道尚粘,直接置零
img[:, :, 2] = 0

cv2.split() 對系統(tǒng)來說開銷很大择卦,所以只在需要使用的時候再使用,使用 Numpy 索引的方法更有效

為圖像創(chuàng)建邊框(填充)

cv2.copyMakeBorder(): 可以為圖像創(chuàng)建邊框它在 卷積運(yùn)算郎嫁,零填充等方面有更多的應(yīng)用

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

BLUE = [255, 0, 0]

img1 = cv2.imread('opencv-logo.png')

replicate = cv2.copyMakeBorder(
    img1, 10, 10, 10, 10, cv2.BORDER_REPLICATE)  # 最后一個元素的復(fù)制
reflect = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REFLECT)  # 邊框的鏡面
reflect101 = cv2.copyMakeBorder(
    img1, 10, 10, 10, 10, cv2.BORDER_REFLECT_101)  # 同上秉继,但有細(xì)微變化
wrap = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_WRAP)
# 添加一個固定顏色的邊框,因為 matpltlib 和 opencv 顏色顯示不一樣,所有圖標(biāo)的紅色和藍(lán)色互換了
constant = cv2.copyMakeBorder(
    img1, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=BLUE)


plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')

plt.show()
image

函數(shù)說明

copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]]) -> dst

  • src: 輸入圖片
  • top, bottom, left, right: 上下左右的寬度
  • borderType: 邊框類型泽铛,多個可以選擇 參見

更多資料

本節(jié)原文

圖像對比度尚辑、亮度值調(diào)整

學(xué)習(xí)目標(biāo):

  • 用 OpenCV 進(jìn)行圖像對比度、亮度的動態(tài)調(diào)整

理論依據(jù)

一般的圖像處理算子都是一個函數(shù)盔腔,它接受一個或多個輸入圖像杠茬,并產(chǎn)生輸出圖像。算子的一般形式:g(x) = h(f(x)) 或者 g(x) = h(f_0(x)···f_n(x))

圖像亮度和對比度的調(diào)整操作铲觉,屬于圖像操作中的點操作澈蝙。點操作的特點:僅僅根據(jù)輸入像素值(有時可加上某些全局信息或參數(shù)),來計算相應(yīng)的輸出像素值撵幽。這類算子包括:亮度(brightness)灯荧、對比度(contrast)調(diào)整、顏色校正(colorcorrection)盐杂、變換(transformations)逗载。

常見的點操作(或說點算子)是乘上一個常數(shù)(對應(yīng)對比度發(fā)調(diào)節(jié))和加上一個常數(shù)(對應(yīng)亮度的調(diào)節(jié))。公式為:g(x)=a*f(x)+b
各個參數(shù)的含義:

  • 參數(shù) f(x) 表示源圖像像素
  • 參數(shù) g(x) 表示輸出圖像像素
  • 參數(shù) a (需要滿足 a>0 )被稱為增益(gain)链烈,控制圖像的對比度
  • 參數(shù) b 稱為偏置(bias)厉斟,控制圖像的亮度

公式改寫:g(i, j)=a*f(i, j)+b
即對圖像的 i 行 j 列的像素進(jìn)行操作。(對所有的像素進(jìn)行操作)

案例(利用軌跡條調(diào)節(jié)圖像的亮度和對比度)

import numpy as np
import cv2
from skimage import img_as_int


def nothing(x):
    pass

# 對比度和亮度調(diào)節(jié)函數(shù)
def adjust_bright_contrast(img, a=1, b=0):
    new_img = np.zeros(img.shape, dtype=img.dtype)
    for b in range(img.shape[0]):
        for g in range(img.shape[1]):
            for r in range(img.shape[2]):
                new_img[b, g, r] = np.clip(a*img[b, g, r]+b, 0, 255)
    return new_img

# 使用軌跡條應(yīng)該先創(chuàng)建一個窗口强衡,因為
cv2.namedWindow("image") 
cv2.createTrackbar("bright", "image", 0, 300, nothing)
cv2.createTrackbar("contrast", "image", 0, 255, nothing)


img = cv2.imread("img.jpg")
new_img = np.zeros(img.shape, dtype=img.dtype)

while(1):
    
    cv2.imshow("image", new_img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
    brightness = cv2.getTrackbarPos("bright", "image")
    contrast = cv2.getTrackbarPos("contrast", "image")
    new_img[:] = adjust_bright_contrast(img, brightness*0.01, contrast)
    
cv2.destroyAllWindows()

運(yùn)行結(jié)果:
image

這個方式運(yùn)行起來很慢擦秽,因為里面有三層循環(huán)

離散傅立葉變換

待學(xué)習(xí)

圖像的算術(shù)運(yùn)算

學(xué)習(xí)目標(biāo):

  • 圖像的加法、減法、位運(yùn)算
  • cv2.add(), cv2.addWeighted()

圖像加法

cv2.add() 使用該函數(shù)操作是 Numpy 操作感挥,兩個圖片應(yīng)該要有一樣的數(shù)據(jù)類型和深度缩搅,否則第二個圖像只能是標(biāo)量值。

x = np.uint8([250])
y = np.uint8([10])
z = np.uint8([255])
print("opencv add operation:", cv2.add(x, y)) # 250+10 = 260 => 255 
print("opencv sub operation:", cv2.subtract(x, z)) # 250-255 = -5 => 0

print("numpy add operation:", x+y) # 250+10 = 260 % 256 = 4
print("numpy sub operation:", x-z) # 250-255 = -5 % 256 = 251
opencv add operation: [[255]]
opencv sub operation: [[0]]
numpy add operation: [4]
numpy sub operation: [251]

OpenCV 運(yùn)算和 Numpy 運(yùn)算有區(qū)別:OpenCV 是飽和運(yùn)算触幼,即相加最大只能是 255 ,相減最小只能是 0硼瓣。Numpy 是模運(yùn)算。見上面注釋置谦。

最好使用 OpenCV 中的 add 進(jìn)行運(yùn)算

圖像融合

cv2.addWeighted() 兩張圖片以權(quán)重進(jìn)行融合堂鲤,使其給人一種混合或透明的感覺。圖片按以下公式運(yùn)算
g(x) = (1 - \alpha)f_0(x) + \alpha f_1(x)

img1 = cv2.imread("ml.png")
img2 = cv2.imread("opencv-logo.png")

dst = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

addWeighted 運(yùn)算公式為: dst = \alpha*img1 + \beta*img2 + \gamma

image

軌跡條版本

img1 = cv2.imread("ml.png")
img2 = cv2.imread("opencv-logo.png")

def nothing(x):
    pass

cv2.namedWindow("image")
cv2.createTrackbar("alpha", "image", 0, 100, nothing)
dst = np.zeros(img1.shape, dtype=img1.dtype)

while(1):
    cv2.imshow('image', dst)
    alpha  = cv2.getTrackbarPos("alpha", "image")
    dst = cv2.addWeighted(img1, alpha*0.01, img2, 1 -alpha*0.01, 0)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
    
cv2.destroyAllWindows()

位運(yùn)算

包括 AND媒峡、OR瘟栖、NOT 和 XOR 操作。它們在提取圖像的任何部分丝蹭、定義和處理非矩形 ROI 時非常有用慢宗。

AND 運(yùn)算
# 畫矩形
Rectangle = np.zeros((300, 300), dtype="uint8")
cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
cv2.imshow("Rectangle", Rectangle)
cv2.waitKey(0)

# 畫圓形
Circle = np.zeros((300, 300), dtype="uint8")
cv2.circle(Circle, (150, 150), 150, 255, -1)
cv2.imshow("Circle", Circle)
cv2.waitKey(0)

bit_and = cv2.bitwise_and(Rectangle, Circle)
cv2.imshow("bit_and", bit_and)
cv2.waitKey(0)

"""
sub = cv2.subtract(Rectangle, Circle)
cv2.imshow("sub", sub)
cv2.waitKey()
"""

cv2.destroyAllWindows()

x = np.uint8([10])
y = np.uint8([20])
z = np.uint8([10])

cv2.bitwise_and(x, y), cv2.bitwise_and(x, z)
(array([[0]], dtype=uint8), array([[10]], dtype=uint8))

運(yùn)行結(jié)果


image
OR 運(yùn)算
# 畫矩形
Rectangle = np.zeros((300, 300), dtype="uint8")
cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
cv2.imshow("Rectangle", Rectangle)
cv2.waitKey(0)

# 畫圓形
Circle = np.zeros((300, 300), dtype="uint8")
cv2.circle(Circle, (150, 150), 150, 255, -1)
cv2.imshow("Circle", Circle)
cv2.waitKey(0)

bit_or = cv2.bitwise_or(Rectangle, Circle)
cv2.imshow("bit_or", bit_or)
cv2.waitKey(0)


add = cv2.add(Rectangle, Circle)
cv2.imshow("add", add) 
cv2.waitKey(0)

cv2.destroyAllWindows()
x = np.uint8([10]) # 0000 1010
y = np.uint8([20]) # 0001 0100 
z = np.uint8([40]) # 0010 1000

print(cv2.bitwise_or(x, y)) # 0001 1110 => 30
print(cv2.bitwise_or(x, z)) # 0010 1010 => 42
[[30]]
[[42]]

OR 運(yùn)算和 cv2.add() 結(jié)果一致?

運(yùn)行結(jié)果


image
XOR(異或)運(yùn)算
# 畫矩形
Rectangle = np.zeros((300, 300), dtype="uint8")
cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
cv2.imshow("Rectangle", Rectangle)
cv2.waitKey(0)

# 畫圓形
Circle = np.zeros((300, 300), dtype="uint8")
cv2.circle(Circle, (150, 150), 150, 255, -1)
cv2.imshow("Circle", Circle)
cv2.waitKey(0)

bit_xor = cv2.bitwise_xor(Rectangle, Circle)
cv2.imshow("bit_xor", bit_xor)
cv2.waitKey(0)
cv2.destroyAllWindows()

運(yùn)行結(jié)果


image
NOT 運(yùn)算
# 畫矩形
Rectangle = np.zeros((300, 300), dtype="uint8")
cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
cv2.imshow("Rectangle", Rectangle)
cv2.waitKey(0)

# 畫圓形
Circle = np.zeros((300, 300), dtype="uint8")
cv2.circle(Circle, (150, 150), 150, 255, -1)
cv2.imshow("Circle", Circle)
cv2.waitKey(0)

bit_not = cv2.bitwise_not(Rectangle, Circle)
cv2.imshow("bit_not", bit_not)
cv2.waitKey(0)
cv2.destroyAllWindows()

運(yùn)行結(jié)果


image
綜合例程

mask:圖像掩模

# 加載圖片
img1 = cv2.imread("messi5.jpg")
img2 = cv2.imread("opencv-logo.png")

print(img1.shape, img2.shape)

# 把 OpenCV logo 放在圖片的左上角坪蚁,創(chuàng)建一個 ROI 大小和 logo 圖像一致
rows, cols, channels = img2.shape
roi = img1[0:rows, 0:cols]
print("roi.shape:", roi.shape)
cv2.imshow("roi", roi)
cv2.waitKey(0)

# 灰度圖
img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
cv2.imshow("img2gray", img2gray)
cv2.waitKey(0)

ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)
# 解釋:若圖片 im2gray 的像素值小于 25奔穿,則像素置 0(黑),否則置 255(白)
# ret :得到的閾值敏晤,mask:閾值化后的圖像
print("ret", ret)
cv2.imshow("mask", mask)
cv2.waitKey(0)

mask_inv = cv2.bitwise_not(mask)
# 把 mask 圖像的白色像素變?yōu)楹谏铮谏袼刈優(yōu)榘咨?cv2.imshow("mask_inv", mask_inv)
cv2.waitKey(0)


# 將 img1 中的 ROI 的 logo 區(qū)域涂黑
img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)
cv2.imshow("img1_bg", img1_bg)
cv2.waitKey(0)
# plt.imshow(img1_bg)

# 只提取 logo 圖像中的 logo
img2_fg = cv2.bitwise_and(img2, img2, mask=mask)
cv2.imshow("img2_fg", img2_fg)
cv2.waitKey(0)


# 將 ROI 區(qū)域與 logo 圖標(biāo)運(yùn)算
dst = cv2.add(img1_bg, img2_fg)
cv2.imshow("dst", dst)
cv2.waitKey(0)


img1[0:rows, 0:cols] = dst

cv2.imshow("res", img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
(342, 548, 3) (186, 152, 3)
roi.shape: (186, 152, 3)
ret 25.0

運(yùn)行結(jié)果


image

要將兩張圖放在一起,且都是原來的圖像嘴脾,如上圖所示男摧,將 OpenCV logo (img2)放在 img1 上面,巧妙的利用黑色像素(0)译打。
如何將兩個圖一原來的圖像進(jìn)行重疊:

  1. 創(chuàng)建一個 ROI 區(qū)域(ROI 區(qū)域來自底圖耗拓,如上面的例子的 img1)
  2. 將 logo 之外的像素置 0 (就是背景是純黑色),如上圖的 img2_fg
  3. 將 ROI 區(qū)域上要放置 logo 的位置置 0奏司,如上圖的 img1_bg
  4. 將 2乔询、3 步得到的圖像相加( cv2.add() )
  5. 將第 4 步得到的圖像將 img1 的區(qū)域替換掉

練習(xí)(用函數(shù)實現(xiàn)圖像的無縫拼接)

def pinjie(img1, img2, x=0, y=0):
    """
    img1: 底圖
    img2: 上方的圖
    x, y:選擇放置的位置
    """
    # 創(chuàng)建一個 ROI 
    rows, cols, channel = img2.shape
    roi = img1[x:x+rows, y:y+cols]
    img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask)    
    roi = cv2.bitwise_and(roi, roi, mask=mask_inv) 
    img2_bg = cv2.bitwise_and(img2, img2, mask=mask)
    img1[x:x+rows, y:y+cols] = cv2.add(roi, img2_bg)
    return img1


img1 = cv2.imread("messi5.jpg")
img2 = cv2.imread("opencv-logo.png")
dst = pinjie(img1, img2, 100, 100)
cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

更多資料

本節(jié)原文

Python 下 opencv 使用筆記(四)(圖像的閾值處理)

性能評估和改進(jìn)技術(shù)

圖像處理中,不僅需要準(zhǔn)確韵洋,更需要快速的方法進(jìn)行處理竿刁。
學(xué)習(xí)目標(biāo):

  • 評估代碼的性能
  • 提升代碼性能的小技巧
  • cv2.getTickCount, cv2.getTickFrequency

time 模塊可以測量執(zhí)行時間;
profile 模塊有助于獲得關(guān)于代碼的詳細(xì)報告搪缨,比如代碼中每個函數(shù)花費了多少時間食拜、調(diào)用了多少次等等。

使用 OpenCV 衡量性能

  • cv2.getTickCount 在代碼前后使用可以得到代碼的運(yùn)行時間
  • cv2.getTickFrequency 返回時鐘周期的頻率副编,即每秒的時鐘周期數(shù)
img1 = cv2.imread('messi5.jpg')

e1 = cv2.getTickCount()
for i in range(5,49,2):
    img1 = cv2.medianBlur(img1,i)
e2 = cv2.getTickCount()
t = (e2 - e1)/cv2.getTickFrequency()
e2 - e1, t
(8315303, 0.8315303)

python 中的 time 模塊也可以實現(xiàn)該功能

OpenCV 中的默認(rèn)優(yōu)化

OpenCV 的許多函數(shù)都是使用SSE2负甸、AVX等優(yōu)化的。它還包含未優(yōu)化的代碼。因此呻待,如果我們的系統(tǒng)支持這些特性煮盼,我們應(yīng)該利用它們(幾乎所有現(xiàn)代處理器都支持它們)。它在編譯時默認(rèn)啟用带污。OpenCV 在啟用時運(yùn)行優(yōu)化后的代碼僵控,否則運(yùn)行未優(yōu)化的代碼。使用 cv2.useoptimization()來檢查它是否啟用/禁用鱼冀,使用
cv2.setuseoptimization()來啟用/禁用它报破。

# 檢查優(yōu)化器是否開啟
cv2.setUseOptimized(True)
cv2.useOptimized()
True

img = cv2.imread("messi5.jpg")
%timeit res = cv2.medianBlur(img,49)
31.8 ms ± 1.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# 設(shè)置優(yōu)化器的開關(guān)
cv2.setUseOptimized(False)
cv2.useOptimized()
False

%timeit res = cv2.medianBlur(img,49)
30.9 ms ± 448 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

可以看出使用優(yōu)化器前后的運(yùn)行性能

IPython 性能測試

測試一下下面幾個計算平方的方法哪個更好。
例子:
x = 5; y = x**2千绪、x = 5; y = x*x充易、x = np.uint8([5]); y = x*xy = np.square(x) 可以使用 %timeit 進(jìn)行測試

x = 5
%timeit y = x**2
505 ns ± 18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit y = x*x
77.5 ns ± 1.98 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

z = np.uint8([5])
%timeit y=z*z
709 ns ± 57.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit y=np.square(z)
674 ns ± 66.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

y = x*x 比 Numpy 運(yùn)算快不少

Python 標(biāo)量操作比 Numpy 標(biāo)量操作快荸型。因此對于包含一個或兩個元素的操作盹靴,Python 標(biāo)量優(yōu)于 Numpy 數(shù)組。當(dāng)數(shù)組的大小稍微大一點時瑞妇,Numpy 就更有優(yōu)勢稿静。

# 比較 cv2.countNonZero() 和 np.count_nonzero() 的性能
%timeit z = cv2.countNonZero(img[:,:, 0]) # 該函數(shù)只能計算單通道的圖片
269 μs ± 30.7 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit z = np.count_nonzero(img[:,:, 0])
677 μs ± 54.5 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

性能優(yōu)化技術(shù)

首先嘗試以一種簡單的方式實現(xiàn)該算法。一旦開始工作辕狰,對其進(jìn)行分析改备,找到瓶頸并對其進(jìn)行優(yōu)化。

  • 盡量避免循環(huán)蔓倍,特別是 2/3 重的循環(huán)
  • 最大限度地向量化算法/代碼悬钳,因為 Numpy 和 OpenCV 是針對向量操作進(jìn)行優(yōu)化的。
  • 利用緩存一致性
  • 除非必要偶翅,不然盡量不要對數(shù)組進(jìn)行復(fù)制默勾,數(shù)組復(fù)制的開銷很大

即使在執(zhí)行了所有這些操作之后,如果代碼仍然很慢聚谁,或者不可避免地要使用大型循環(huán)母剥,那么可以使用其他庫,比如Cython來加快速度垦巴。

更多資料

Python Optimization Techniques媳搪、Scipy Lecture Notes - Advanced Numpy
time profile 模塊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骤宣,隨后出現(xiàn)的幾起案子秦爆,更是在濱河造成了極大的恐慌,老刑警劉巖憔披,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件等限,死亡現(xiàn)場離奇詭異爸吮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)望门,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門形娇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人筹误,你說我怎么就攤上這事桐早。” “怎么了厨剪?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵哄酝,是天一觀的道長。 經(jīng)常有香客問我祷膳,道長陶衅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任直晨,我火速辦了婚禮搀军,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勇皇。我一直安慰自己罩句,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布儒士。 她就那樣靜靜地躺著的止,像睡著了一般。 火紅的嫁衣襯著肌膚如雪着撩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天匾委,我揣著相機(jī)與錄音拖叙,去河邊找鬼。 笑死赂乐,一個胖子當(dāng)著我的面吹牛薯鳍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挨措,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼挖滤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浅役?” 一聲冷哼從身側(cè)響起斩松,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎觉既,沒想到半個月后惧盹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乳幸,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年钧椰,在試婚紗的時候發(fā)現(xiàn)自己被綠了粹断。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫡霞,死狀恐怖瓶埋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诊沪,我是刑警寧澤悬赏,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站娄徊,受9級特大地震影響闽颇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寄锐,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一兵多、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧橄仆,春花似錦剩膘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至您宪,卻和暖如春奈懒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宪巨。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工磷杏, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捏卓。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓极祸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怠晴。 傳聞我的和親對象是個殘疾皇子遥金,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容