1. 捕獲圖像
sensor模塊,用於設置感光元件的參數。
常用函數列表
sensor.snapshot()
sensor.skip_frames([n, time]) # 跳過n張照片,等待感光元件變穩定
sensor.set_pixformat() # 設置像素模式。
+ sensor.GRAYSCALE: 8-bits per pixel.
+ sensor.RGB565: 16-bits per pixel.
+ sensor.BAYER: 8-bits per pixel bayer pattern.
sensor.set_framesize() # 設置圖像的大小
- sensor.QQVGA: 160x120
- sensor.HQVGA: 240x160
- sensor.QVGA: 320x240
- sensor.VGA: 640x480
sensor.set_windowing(roi)
+ roi: (x, y, w, h)
+ 或者自動居中: (w,h)
sensor.set_auto_gain() # 自動增益
sensor.set_auto_whitebal() # 自動白平衡
sensor.set_auto_exposure(enable[\, exposure_us]) # 自動曝光
sensor.set_gainceiling(gainceiling) # 設置相機圖像增益上限。2, 4, 8, 16, 32, 64, 128
sensor.set_contrast(constrast) # 設置相機圖像對比度。-3至+3
sensor.set_brightness(brightness) # 設置相機圖像亮度。-3至+3
sensor.set_saturation(saturation) # 設置相機圖像飽和度。-3至+3
sensor.set_quality(quality) # 設置相機圖像JPEG壓縮質量。0-100
sensor.set_hmirror(enable) # 打開(True)或關閉(False)水平鏡像模式。默認關閉
sensor.set_vflip(enable) # 打開(True)或關閉(False)垂直翻轉模式。默認關閉
2. 圖像Image對象的操作
2.1. 保存
image.save(path[, roi[, quality=50]])
將圖像的副本保存到 path 中的文件系統。
支持bmp/pgm/ppm/jpg/jpeg格式的圖像文件。注意:您無法將jpeg格式的壓縮圖像保存成未壓縮的格式。
-
roi 是一個用以復制的矩形的感興趣區域(x, y, w, h)。如果未指定,ROI即復制整個圖像的圖像矩形。但這不適用於JPEG圖像。
-
quality 指在圖像尚未被壓縮時將圖像保存為JPEG格式的JPEG壓縮質量。
2.2. 屬性操作
image.width() # 返回以像素計的圖像的寬度。
image.height() # 返回以像素計的圖像的高度。
image.format() # 返回 sensor.GRAYSCALE(2) 或 sensor.RGB565(3)
image.size() # 返回以字節計的圖像大小。
2.3. 其他操作
image.clear([mask])
將圖像中的所有像素設置為零(非常快)。
image.copy([roi[, x_scale[, y_scale[, copy_to_fb=False]]]])
創建一個圖像對象的副本。
2.4. 裁剪
image.crop([roi[, x_scale[, y_scale[, copy_to_fb=False]]]])
類似 image.copy(),但它對image對象進行操作,而不是進行深度復制。
- roi 是要從中復制的感興趣區域矩形(x, y, w, h)。 如果沒有指定,它等於復制整個圖像的圖像矩形。此參數不適用於JPEG圖像。
- x_scale 是一個浮點值,通過它可以在x方向上縮放圖像。
- y_scale 是一個浮點值,通過它可以在y方向上縮放圖像。
2.5. 格式轉換
image.to_bitmap([copy=False[, rgb_channel=-1]])
# 位圖圖像就像只有兩個像素值(0和1)的灰度圖像一樣。
image.to_grayscale([copy=False[, rgb_channel=-1]])
image.to_rgb565([copy=False[, rgb_channel=-1]])
image.to_rainbow([copy=False[, rgb_channel=-1[, color_palette=sensor.PALETTE_RAINBOW]]])
# 彩虹圖像是彩色圖像,對於圖像中的每個8位掩模灰度照明值具有唯一的顏色值。例如,它為熱圖像提供熱圖顏色。
此方法會修改基礎圖像像素,以字節為單位更改圖像大小,因此只能在灰度圖像或RGB565圖像上進行。否則 copy 必須為True才能在堆上創建新的修改圖像。
rgb_channel 如果設置為0/1/2,則分別從R/G/B通道創建一個灰度圖像,而如果在RGB565圖像上調用此方法,則從RGB565像素計算灰度值。
3. 統計信息
image.get_statistics([thresholds[, invert=False[, roi[, bins[, l_bins[, a_bins[, b_bins]]]]]]])
如果傳遞 thresholds 列表,則直方圖信息將僅從閾值列表中的像素計算得出。
針對灰度圖:
statistics.mean() # 返回灰度的平均數(0-255)
statistics.median() # 返回灰度的中位數(0-255)
statistics.mode() # 返回灰度的眾數(0-255)
statistics.stdev() # 返回灰度的標准差(0-255)
statistics.min() # 返回灰度的最小值(0-255)
statistics.max() # 返回灰度的最大值(0-255)
statistics.lq() # 返回灰度的第一四分數(0-255)
statistics.uq() # 返回灰度的第三四分數(0-255)
LAB三個通道的平均數,中位數,眾數,標准差,最小值,最大值,第一四分數,第三四分數:
l_mean(), l_median(), l_mode(), l_stdev(), l_min(), l_max(), l_lq(), l_uq()
a_mean(), a_median(), a_mode(), a_stdev(), a_min(), a_max(), a_lq(), a_uq()
b_mean(), b_median(), b_mode(), b_stdev(), b_min(), b_max(), b_lq(), b_uq()
示例
import sensor, image, time
sensor.reset() # 初始化攝像頭
sensor.set_pixformat(sensor.RGB565) # 格式為 RGB565.
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(10) # 跳過10幀,使新設置生效
sensor.set_auto_whitebal(False) # Create a clock object to track the FPS.
ROI=(80,30,15,15)
while(True):
img = sensor.snapshot()
statistics=img.get_statistics(roi=ROI)
# 色塊內的色彩均值
color_l=statistics.l_mode()
color_a=statistics.a_mode()
color_b=statistics.b_mode()
print(color_l,color_a,color_b)
img.draw_rectangle(ROI)
終端輸出:
56 66 51
56 66 55
56 66 51
...
image.get_regression(thresholds[, invert=False[, roi[, x_stride=2[, y_stride=1[, area_threshold=10[, pixels_threshold=10[, robust=False]]]]]]])
4. 繪圖
image.draw_line(x0, y0, x1, y1[, color[, thickness=1]])
image.draw_rectangle(x, y, w, h[, color[, thickness=1[, fill=False]]])
image.draw_circle(x, y, radius, color=White)
image.draw_cross(x, y, size=5, color=White)
image.draw_string(x, y, text, color=White)
在圖像上繪制一個矩形。 您可以單獨傳遞x,y,w,h或作為元組(x,y,w,h)傳遞。
color 是用於灰度或RGB565圖像的RGB888元組。默認為白色。但是,您也可以傳遞灰度圖像的基礎像素值(0-255)或RGB565圖像的字節反轉RGB565值。
將 fill=True
時,可以填充矩形。
5. 圖像預處理
5.1. 畸變校正
image.lens_corr([strength=1.8[, zoom=1.0]])
進行鏡頭畸變校正,以去除鏡頭造成的圖像魚眼效果。
- strength 是一個浮點數,該值確定了對圖像進行去魚眼效果的程度。在默認情況下,首先試用取值1.8,然后調整這一數值使圖像顯示最佳效果。
- zoom 是在對圖像進行縮放的數值。默認值為 1.0 。
返回圖像對象,以便您可以使用 .
表示法調用另一個方法。
不支持壓縮圖像和bayer圖像。
img = sensor.snapshot().lens_corr(strength = 1.8, zoom = 1.0)
5.2. 閾值、二值化
image.binary(thresholds[, invert=False[, zero=False[, mask=None[, to_bitmap=False[, copy=False]]]]])
thresholds 必須是元組列表。例如: [(lo, hi), (lo, hi), ..., (lo, hi)]
,其中,(lo, hi)
定義你想追蹤的顏色范圍,而外層的list,則是可以對多個范圍進行組合。
- 對於灰度圖像,每個元組需要包含兩個值 - 最小灰度值和最大灰度值。
- 對於RGB565圖像,每個元組需要有六個值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分別是LAB L,A和B通道的最小值和最大值。
5.3. 直方圖
while(True):
clock.tick()
img = sensor.snapshot()
# Gets the grayscale histogram for the image into 8 bins.
# Bins defaults to 256 and may be between 2 and 256.
print(img.get_histogram(bins=8))
print(clock.fps())
5.4. 圖像濾波
5.4.1. Adaptive_Histogram_Equalization, 自適應直方圖均衡
此示例展示了如何使用自適應直方圖均衡來改善圖像中的對比度。自適應直方圖均衡將圖像分割成區域,然后均衡這些區域中的直方圖,以改善圖像對比度與全局直方圖均衡化。
# clip_limit < 0: 為您提供正常的自適應直方圖均衡,這可能會導致大量的對比噪音...
# clip_limit = 1: 什么都不做。為獲得最佳效果,請略高於1,如下所示。
# 越高,越接近標准自適應直方圖均衡,並產生巨大的對比度波動。
img = sensor.snapshot().histeq(adaptive=True, clip_limit=3)
print(clock.fps())
5.4.2. Histogram_Equalization 直方圖均衡
直方圖均衡化使圖像中的對比度和亮度標准化。
img = sensor.snapshot().histeq()
5.4.3. median 中值濾波
在圖像上運行中值濾波。在保留邊緣的條件下,中值濾波是用來平滑表面的最佳濾波,但是運行速度極慢。
-
Size 是內核的大小。取1 (3x3 內核)、2 (5x5 內核)或更高值。
-
percentile 控制內核中所使用值的百分位數。默認情況下,每個像素都使用相鄰的第五十個百分位數(中心)替換。使用最小濾波時,您可將此值設置為0,使用下四分位數濾波時設置為0.25,使用上四分位數濾波時設置為0.75,使用最大濾波時設置為1。
-
如果你想在濾波器的輸出上自適應地設置閾值,你可以傳遞 threshold=True 參數來啟動圖像的自適應閾值處理,他根據環境像素的亮度(核函數周圍的像素的亮度有關),將像素設置為1或者0。 負數 offset 值將更多像素設置為1,而正值僅將最強對比度設置為1。 設置 invert 以反轉二進制圖像的結果輸出。
-
mask 是另一個用作繪圖操作的像素級掩碼的圖像。掩碼應該是一個只有黑色或白色像素的圖像,並且應該與你正在繪制的 image 大小相同。 僅掩碼中設置的像素被修改。
5.4.4. midpoint 中點濾波
image.midpoint(size[, bias=0.5, threshold=False, offset=0, invert=False, mask])
bias: 控制圖像混合的最小/最大程度。0只適用於最小濾波,1僅用於最大濾波。您可以通過 bias 對圖像進行最小/最大化過濾。
5.4.5. gaussian 高斯濾波
通過平滑高斯核對圖像進行卷積。
image.gaussian(size[, unsharp=False[, mul[, add=0[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]]])
-
unsharp: 如果設置為True,那么這種方法不會僅進行高斯濾波操作,而是執行非銳化掩模操作,從而提高邊緣的圖像清晰度。
-
mul: 是用以與卷積像素結果相乘的數字。若不設置,則默認一個值,該值將防止卷積輸出中的縮放。
-
size: 是內核的大小。取1 (3x3 內核)、2 (5x5 內核)或更高值。
-
mask: 是另一個用作繪圖操作的像素級掩碼的圖像。掩碼應該是一個只有黑色或白色像素的圖像,並且應該與你正在繪制的 image 大小相同。 僅掩碼中設置的像素被修改。
如果你想在濾波器的輸出上自適應地設置閾值,你可以傳遞 threshold=True
參數來啟動圖像的自適應閾值處理,將會根據環境像素的亮度(核函數周圍的像素的亮度有關),將像素設置為1或者0。負數 offset 值將更多像素設置為1,而正值僅將最強對比度設置為1。設置 invert 以反轉二進制圖像的結果輸出。
5.4.6. laplacian
image.laplacian(size[, sharpen=False[, mul[, add=0[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]]])
5.4.7. 通用卷積函數
image.morph(size, kernel[, mul[, add=0[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]])
- kernel: 用來卷積圖像的內核,可為一個元組或一個取值[-128:127]的列表。
size 將內核的大小控制為((size*2)+1)x((size*2)+1)
像素。
kernel_size = 1 # 3x3==1, 5x5==2, 7x7==3, etc.
kernel = [-2, -1, 0, \
-1, 1, 1, \
0, 1, 2]
img = sensor.snapshot()
# Run the kernel on every pixel of the image.
img.morph(kernel_size, kernel)
5.4.8. cartoon_filter 卡通化濾波
漫游圖像並使用flood-fills算法填充圖像中的所有像素區域。這通過使圖像的所有區域中的顏色變平來有效地從圖像中去除紋理。為了獲得最佳效果,圖像應具有大對比度,以使區域不會太容易相互滲透。
image.cartoon(size[, seed_threshold=0.05[, floating_threshold=0.05[, mask=None]]])
- seed_threshold: 控制填充區域中的像素與原始起始像素的差異
- floating_threshold: 控制填充區域中的像素與任何相鄰像素的差異
5.4.9. 雙邊濾波
image.bilateral(size[, color_sigma=0.1[, space_sigma=1[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]])
- color_sigma: 控制彩色明智像素之間必須有多近的距離才能模糊。增加此值可增加顏色模糊
- space_sigma: 控制空間像素彼此之間必須有多近才能模糊。增加此值可增加像素模糊
如果你想在濾波器的輸出上自適應地設置閾值,你可以傳遞 threshold=True
參數來啟動圖像的自適應閾值處理,他根據環境像素的亮度(核函數周圍的像素的亮度有關),將像素設置為1或者0。負數 offset 值將更多像素設置為1,而正值僅將最強對比度設置為1。設置 invert 以反轉二進制圖像的結果輸出。
請注意,如果將color_sigma/space_sigma設置為聚合,雙邊過濾器可能會引入圖像缺陷。如果你看到缺陷,增加sigma值直到缺陷消失
5.4.10. 位運算
image.b_and(image[, mask=None]) -> image
image.b_nand(image[, mask=None]) -> image
image.b_or(image[, mask=None]) -> image
image.b_nor(image[, mask=None]) -> image
image.b_xor(image[, mask=None]) -> image
image.b_xnor(image[, mask=None]) -> image
5.4.11. difference
Replace the image with the "abs(NEW-OLD)" frame difference.
img.difference("temp/bg.bmp")
5.5. 特效功能
5.5.1. logpolar 笛卡爾轉極坐標系
image.logpolar([reverse=False])
圖像從笛卡爾坐標到對數極坐標重新投影。
- 設置 reverse = True 可以在相反的方向重新投影。
對數極坐標重新投影將圖像的旋轉轉換為x平移和縮放到y平移。
5.5.2. 畸變矯正
image.lens_corr([strength=1.8[, zoom=1.0]])
進行鏡頭畸變校正,以去除鏡頭造成的圖像魚眼效果。
strength 是一個浮點數,該值確定了對圖像進行去魚眼效果的程度。在默認情況下,首先試用取值1.8,然后調整這一數值使圖像顯示最佳效果。
zoom 是在對圖像進行縮放的數值。默認值為 1.0 。
5.5.3. 透視坐標系矯正
img.rotation_corr([x_rotation=0.0[, y_rotation=0.0[, z_rotation=0.0[, x_translation=0.0[, y_translation=0.0[, zoom=1.0]]]]]])
通過執行幀緩沖區的3D旋轉來糾正圖像中的透視問題。
- x_rotation 是圍繞x軸在幀緩沖器中旋轉圖像的度數(這使圖像上下旋轉)。
- y_rotation 是幀緩沖區中圍繞y軸旋轉圖像的度數(即左右旋轉圖像)。
- z_rotation 是圍繞z軸在幀緩沖器中旋轉圖像的度數(即,使圖像旋轉到適當位置)。
- x_translation 是旋轉后將圖像移動到左側或右側的單位數。因為這個變換是應用在三維空間的,單位不是像素…
- y_translation 是旋轉后將圖像上移或下移的單位數。因為這個變換是應用在三維空間的,單位不是像素…
- zoom 是通過圖像縮放的量。默認情況下1.0。
5.5.4. 取反(僅適用於二值圖像)
image.invert()
將二進制圖像0(黑色)變為1(白色),1(白色)變為0(黑色),非常快速地翻轉二進制圖像中的所有像素值。
5.5.5. 移除陰影
image.remove_shadows([image]) -> image
從該圖像中移除陰影。
如果當前圖像沒有“無陰影”版本出現,則此方法將嘗試從圖像中去除陰影,但沒有真實無陰影的圖像依據。 這種算法適用於去除平坦均勻背景中的陰影。 請注意,此方法需要多秒才能運行,並且僅適用於實時移除陰影,動態生成無陰影版本的圖像。 該算法的未來版本將適用於更多的環境,但同樣緩慢。
如果當前圖像有“無陰影”版本出現,則此方法將使用“真實源”背景無陰影圖像去除圖像中的所有陰影以濾除陰影。 非陰影像素不會被過濾掉,因此您可以向場景中添加以前不存在的新對象,並且這些對象中的任何非陰影像素都將顯示出來。
5.5.6. 刪除照明效果
image.chrominvar() -> image
從圖像中刪除照明效果,僅留下顏色漸變。比 image.illuminvar() 更快但受陰影影響。
image.illuminvar()
從圖像中刪除照明效果,僅留下顏色漸變。比 image.chrominvar() 慢但不受陰影影響。
5.5.7. 比對差異
image.get_similarity(image) -> Similarity
返回一個“相似度”對象,描述兩幅圖像使用SSIM算法來比較兩幅圖像之間的8x8像素色塊的相似度。
image 可以是圖像對象,未壓縮圖像文件的路徑(bmp/pgm/ppm),也可以是標量值。
6. 特征匹配
6.1. AprilTag: 3D定位
Apriltag定位算法的主要步驟如下:
- 自適應閾值分割
- 查找輪廓,使用Union-find查找連通域
- 對輪廓進行直線擬合,查找候選的凸四邊形
- 對四邊形進行解碼,識別Tag
- 坐標變換,轉換到世界坐標系
6.2. 尋找色塊
image.find_blobs(thresholds, roi=Auto, x_stride=2, y_stride=1, invert=False, area_threshold=10, pixels_threshold=10, merge=False, margin=0, threshold_cb=None, merge_cb=None)
-
thresholds:
list of tuples
- 灰度值:[(lo, hi), (lo, hi), ..., (lo, hi)]
- 顏色值:[(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi), ...]
-
invert: 反轉閾值
-
x_stride: 是查找某色塊時需要跳過的x像素的數量。找到色塊后,直線填充算法將精確像素。若已知色塊較大,可增加 x_stride 來提高查找色塊的速度。
-
area_threshold: 色塊的邊界框區域
-
pixel_threshold: 色塊像素數量
-
merge:
- True,則合並所有沒有被過濾掉的色塊,這些色塊的邊界矩形互相交錯重疊。
margin 可在相交測試中用來增大或減小色塊邊界矩形的大小。例如:邊緣為1、相互間邊界矩形為1的色塊將被合並。
合並色塊使顏色代碼追蹤得以實現。每個色塊對象有一個代碼值 code ,該值為一個位向量。 例如:若您在 image.find_blobs 中輸入兩個顏色閾值,則第一個閾值代碼為1,第二個代碼為2(第三個代碼為4,第四個代碼為8,以此類推)。 合並色塊對所有的code使用邏輯或運算,以便您知道產生它們的顏色。這使得您可以追蹤兩個顏色,若您用兩種顏色得到一個色塊對象,則可能是一種顏色代碼。
若用戶使用嚴格的顏色范圍,無法完全追蹤目標對象的所有像素,您可能需要合並色塊。
最后,若您想要合並色塊,但不想兩種不同閾值顏色的色塊被合並,只需分別兩次調用 image.find_blobs ,不同閾值色塊就不會被合並。
-
threshold_cb: callback(blob) -> 返回True以保留色塊或返回False以過濾色塊。
-
merge_cb: callback(blob_1, blob_2) -> 返回True以合並色塊,或返回False以防止色塊合並。
示例1: 自適應灰度色塊
print("Learning thresholds...")
threshold = [128, 128] # Middle grayscale values.
for i in range(60):
img = sensor.snapshot()
hist = img.get_histogram(roi=r)
lo = hist.get_percentile(0.01) # Get the CDF of the histogram at the 1% range (ADJUST AS NECESSARY)!
hi = hist.get_percentile(0.99) # Get the CDF of the histogram at the 99% range (ADJUST AS NECESSARY)!
# Average in percentile values.
threshold[0] = (threshold[0] + lo.value()) // 2
threshold[1] = (threshold[1] + hi.value()) // 2
for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
img.draw_rectangle(r)
print("Thresholds learned...")
print("Tracking colors...")
while(True):
clock.tick()
img = sensor.snapshot()
for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
print(clock.fps())
示例2: 查找彩色塊
thresholds = [(30, 100, 15, 127, 15, 127), # generic_red_thresholds
(30, 100, -64, -8, -32, 32), # generic_green_thresholds
(0, 30, 0, 64, -128, 0)] # generic_blue_thresholds
while(True):
clock.tick()
img = sensor.snapshot()
for blob in img.find_blobs([thresholds[threshold_index]], pixels_threshold=200, area_threshold=200, merge=True):
# These values depend on the blob not being circular - otherwise they will be shaky.
if blob.elongation() > 0.5:
img.draw_edges(blob.min_corners(), color=(255,0,0))
img.draw_line(blob.major_axis_line(), color=(0,255,0))
img.draw_line(blob.minor_axis_line(), color=(0,0,255))
# These values are stable all the time.
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
# Note - the blob rotation is unique to 0-180 only.
img.draw_keypoints([(blob.cx(), blob.cy(), int(math.degrees(blob.rotation())))], size=20)
print(clock.fps())
6.2.1. blob色塊對象
blob.rect() 返回這個色塊的外框——矩形元組(x, y, w, h),可以直接在image.draw_rectangle中使用。
blob.x()
blob.y()
blob.w()
blob.h()
blob.pixels()
blob.cx()
blob.cy()
blob.rotation()
blob.code() # 返回一個16bit數字,每一個bit會對應每一個閾值。舉個例子:
"""
blobs = img.find_blobs([red, blue, yellow], merge=True)
如果這個色塊是紅色,那么它的code就是0001,如果是藍色,那么它的code就是0010。注意:一個blob可能是合並的,如果是紅色和藍色的blob,那么這個blob就是0011。這個功能可以用於查找顏色代碼。也可以通過blob[8]來獲取。
"""
blob.count()
"""
如果merge=True,那么就會有多個blob被合並到一個blob,這個函數返回的就是這個的數量。
如果merge=False,那么返回值總是1。
"""
blob.area() # 返回色塊的外框的面積。應該等於(w * h)
blob.density()
"""
返回色塊的密度。這等於色塊的像素數除以外框的區域。如果密度較低,那么說明目標鎖定的不是很好。
比如,識別一個紅色的圓,返回的blob.pixels()是目標圓的像素點數,blob.area()是圓的外接正方形的面積。
"""
6.3. 形狀特征
image.find_lines([roi[, x_stride=2[, y_stride=1[, threshold=1000[, theta_margin=25[, rho_margin=25]]]]]])
image.find_line_segments([roi[, merge_distance=0[, max_theta_difference=15]]])
image.find_rects([roi=Auto, threshold=10000])
image.find_circles([roi[, x_stride=2[, y_stride=1[, threshold=2000[, x_margin=10[, y_margin=10[, r_margin=10[, r_min=2[, r_max[, r_step=2]]]]]]]]]])
image.find_edges(edge_type[, threshold])
image.find_hog([roi[, size=8]])
image.find_lbp(roi)
image.find_features(cascade[, threshold=0.5[, scale=1.5[, roi]]])
image.find_keypoints([roi[, threshold=20[, normalized=False[, scale_factor=1.5[, max_keypoints=100[, corner_detector=image.CORNER_AGAST]]]]]])
6.3.1. Line
x1()
y1()
x2()
y2()
lenght()
magnitude() # return: 霍夫變換后的直線的模
theta() # 返回霍夫變換后的直線的角度(0-179度)
rho() # 返回霍夫變換后的直線p值
6.3.2. 矩形對象
rect.corners() # return: 由矩形對象的四個角組成的四個元組(x,y)的列表
rect.rect() # return: 矩形元組(x, y, w, h)
x()
y()
w()
h()
rect.magnitude() # return: 矩形的模(magnitude)
6.3.3. 圓形對象
x()
y()
r()
magnitude()
示例:
for c in img.find_circles(threshold=3500,
x_margin=10,
y_margin=10,
r_margin=10,
r_min=2,
r_max=100,
r_step=2):
img.draw_circle(c.x(), c.y(), c.r(), color=(255, 0, 0))
print(c)
6.4. 讀碼
image.find_barcodes([roi])
image.find_qrcodes([roi])
image.find_apriltags([roi[, families=image.TAG36H11[, fx[, fy[, cx[, cy]]]]]])
image.find_datamatrices([roi[, effort=200]])
6.5. 模版匹配
嘗試使用歸一化互相關(NCC)算法在圖像中找到第一個模板匹配的位置。返回匹配位置的邊界框元組(x, y, w, h),否則返回None。
注意:僅支持灰度圖像。
image.find_template(template, threshold[, roi[, step=2[, search=image.SEARCH_EX]]])
-
threshold 是浮點數(0.0-1.0),其中較小的值在提高檢測速率同時增加誤報率。相反,較高的值會降低檢測速率,同時降低誤報率。
-
step 是查找模板時需要跳過的像素數量。跳過像素可大大提高算法運行的速度。該方法只適用於SERACH_EX模式下的算法。
-
search 可為
image.SEARCH_DS
orimage.SEARCH_EX
. image.SEARCH_DS 搜索模板所用算法較 image.SEARCH_EX 更快,但若模板位於圖像邊緣周圍,可能無法成功搜索。 image.SEARCH_EX 可對圖像進行較為詳盡的搜索,但其運行速度遠低於 image.SEARCH_DS 。
for t in templates_files:
template = image.Image(t)
#對每個模板遍歷進行模板匹配
r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
#find_template(template, threshold, [roi, step, search]),threshold中
#的0.7是相似度閾值,roi是進行匹配的區域(左上頂點為(10,0),長80寬60的矩形),
#注意roi的大小要比模板圖片大,比frambuffer小。
#把匹配到的圖像標記出來
if r:
img.draw_rectangle(r)
print(t) #打印模板名字
NCC算法,只能匹配與模板圖片大小和角度基本一致的圖案。局限性相對來說比較大,視野中的目標圖案稍微比模板圖片大一些或者小一些就可能匹配不成功。
6.6. 特征點檢測
多角度多大小匹配可以嘗試保存多個模板,采用多模板匹配。
特征點檢測(find_keypoint): 如果是剛開始運行程序,例程提取最開始的圖像作為目標物體特征,kpts1保存目標物體的特征。默認會匹配目標特征的多種比例大小和角度,而不僅僅是保存目標特征時的大小角度,比模版匹配靈活,也不需要像多模板匹配一樣保存多個模板圖像。
特征點檢測,也可以提前保存目標特征,之前是不推薦這么做的,因為環境光線等原因的干擾,可能導致每次運行程序光線不同特征不同,匹配度會降低。但是最新版本的固件中,增加了對曝光度、白平衡、自動增益值的調節,可以人為的定義曝光值和白平衡值,相對來說會減弱光線的干擾。也可以嘗試提前保存目標特征。
image.find_keypoints([roi[, threshold=20[, normalized=False[, scale_factor=1.5[, max_keypoints=100[, corner_detector=image.CORNER_AGAST]]]]]])
-
corner_detector 是從圖像中提取鍵點所使用的角點檢測器算法。 可為
image.CORNER_FAST
或image.CORNER_AGAST
。FAST角點檢測器運行速度更快,但其准確度較低。 -
threshold 是控制提取的數量的數字(取值0-255)。對於默認的AGAST角點檢測器,該值應在20左右。 對於FAST角點檢測器,該值約為60-80。閾值越低,您提取的角點越多。
-
normalized 是布爾值。若為True,在多分辨率下關閉提取鍵點。 若您不關心處理擴展問題,且希望算法運行更快,就將之設置為True。
-
scale_factor 是一個必須大於1.0的浮點數。較高的比例因子運行更快,但其圖像匹配相應較差。理想值介於1.35-1.5之間。
kpts2 = img.find_keypoints(max_keypoints=150, threshold=10, normalized=True)
#如果檢測到特征物體
cdif (kpts2):
match = image.match_descriptor(kpts1, kpts2, threshold=85)
#match.count()是kpt1和kpt2的匹配的近似特征點數目。
if (match.count()>10): # 如果大於10,證明兩個特征相似,匹配成功
img.draw_rectangle(match.rect())
img.draw_cross(match.cx(), match.cy(), size=10)
#match.theta()是匹配到的特征物體相對目標物體的旋轉角度。
print(kpts2, "matched:%d dt:%d"%(match.count(), match.theta()))
7. 外設: pyb
作為一個單片機,控制IO口,IIC,SPI,CAN,PWM,定時器當然都是可以的。
而且,使用python語言,可以非常簡單的調用它們,而不用考慮寄存器。
因為MicroPython可以在很多平台上運行。最開始在pyb模塊,pyboard,是基於STM32的,但是后來又加入了esp8266和esp32,以及nrf系列,他們的架構和STM32不同。所以官方統一制定了machine模塊,所以通用性更高一些。最終pyb會被淘汰,但是目前pyb比machine功能要多。
常用外設
7.1. LED
from pyb import LED
led = LED(1) # 紅led
led.toggle()
led.on()#亮
led.off()#滅
7.2. IO引腳
from pyb import Pin
p_out = Pin('P7', Pin.OUT_PP)#設置p_out為輸出引腳
p_out.high()#設置p_out引腳為高
p_out.low()#設置p_out引腳為低
p_in = Pin('P7', Pin.IN, Pin.PULL_UP)#設置p_in為輸入引腳,並開啟上拉電阻
value = p_in.value() # get value, 0 or 1#讀入p_in引腳的值
7.3. Servo(舵機)
from pyb import Servo
s1 = Servo(1) # servo on position 1 (P7)
s1.angle(45) # move to 45 degrees
s1.angle(-60, 1500) # move to -60 degrees in 1500ms
s1.speed(50) # for continuous rotation servos
7.4. 定時器
from pyb import Timer
tim = Timer(4, freq=1000)
tim.counter() # get counter value
tim.freq(0.5) # 0.5 Hz
tim.callback(lambda t: pyb.LED(1).toggle())
7.5. PWM
from pyb import Pin, Timer
p = Pin('P7') # P7 has TIM4, CH1
tim = Timer(4, freq=1000)
ch = tim.channel(1, Timer.PWM, pin=p)
ch.pulse_width_percent(50)
7.6. ADC
from pyb import Pin, ADC
adc = ADC('P6')
adc.read() # read value, 0-4095
7.7. UART
UART(Universal Asynchronous Receive Transmitter):也就是我們經常所說的串口,基本都用於調試。
主機和從機至少要接三根線,RX、TX和GND。TX用於發送數據,RX用於接受數據。注意A和B通信A.TX要接B.RX,A.RX要接B.TX。
from pyb import UART
uart = UART(3, 9600)
uart.write('hello')
uart.read(5) # read up to 5 bytes
7.8. SPI
SPI(Serial Peripheral Interface, 同步外設接口)是由摩托羅拉公司開發的全雙工同步串行總線,該總線大量用在與EEPROM、ADC、FRAM和顯示驅動器之類的慢速外設器件通信。
SPI是一種串行同步通訊協議,由一個主設備和一個或多個從設備組成,主設備啟動一個與從設備的同步通訊,從而完成數據的交換。SPI 接口由SDI(串行數據輸入),SDO(串行數據輸出),SCK(串行移位時鍾),CS(從使能信號)四種信號構成,CS 決定了唯一的與主設備通信的從設備,片選信號低電平有效。如沒有CS 信號,則只能存在一個從設備,主設備通過產生移位時鍾來發起通訊。通訊時,數據由SDO 輸出,SDI 輸入,數據在時鍾的上升或下降沿由SDO 輸出,在緊接着的下降或上升沿由SDI 讀入,這樣經過8/16 次時鍾的改變,完成8/16 位數據的傳輸。
I2C總線傳輸速度在100kbps-4Mbps。spi總線傳輸速度更快,可以達到30MHZ以上。
from pyb import SPI
spi = SPI(2, SPI.MASTER, baudrate=200000, polarity=1, phase=0)
spi.send('hello')
spi.recv(5) # receive 5 bytes on the bus
spi.send_recv('hello') # send a receive 5 bytes
7.9. IIC
也長寫成“I2C”(INTER IC BUS)。
SPI和UART可以實現全雙工,但I2C不行。
I2C線更少,我覺得比UART、SPI更為強大,但是技術上也更加麻煩些,因為I2C需要有雙向IO的支持,而且使用上拉電阻,我覺得抗干擾能力較弱,一般用於同一板卡上芯片之間的通信,較少用於遠距離通信。SPI實現要簡單一些,UART需要固定的波特率,就是說兩位數據的間隔要相等,而SPI則無所謂,因為它是有時鍾的協議。
from machine import I2C, Pin
i2c = I2C(sda=Pin('P5'),scl=Pin('P4'))
i2c.scan()
i2c.writeto(0x42, b'123') # write 3 bytes to slave with 7-bit address 42
i2c.readfrom(0x42, 4) # read 4 bytes from slave with 7-bit address 42
i2c.readfrom_mem(0x42, 8, 3) # read 3 bytes from memory of slave 42, starting at memory-address 8 in the slave
i2c.writeto_mem(0x42, 2, b'\x10') # write 1 byte to memory of slave 42, starting at address 2 in the slave
7.10. CAN
CAN總線使用的雙絞線(屏蔽/非屏蔽雙絞線):
from pyb import CAN
can = CAN(2, CAN.NORMAL)
# 設置不同的波特率(默認為125Kbps)
# 注意:以下參數僅適用於H7。
#
# can.init(CAN.NORMAL, prescaler=32, sjw=1, bs1=8, bs2=3) # 125Kbps
# can.init(CAN.NORMAL, prescaler=16, sjw=1, bs1=8, bs2=3) # 250Kbps
# can.init(CAN.NORMAL, prescaler=8, sjw=1, bs1=8, bs2=3) # 500Kbps
# can.init(CAN.NORMAL, prescaler=4, sjw=1, bs1=8, bs2=3) # 1000Kbps
can.restart()
while (True):
# Send message with id 1
can.send('Hello', 1)