1、Mat生成圖片php
面的簡單代碼就能夠生成兩種表示方式下,圖6-1中矩陣的對應的圖像,生成圖像後,放大看就能體會到區別:python
import numpy as np import cv2 import matplotlib.pyplot as plt # 圖6-1中的矩陣 img = np.array([ [[255, 0, 0], [0, 255, 0], [0, 0, 255]], [[255, 255, 0], [255, 0, 255], [0, 255, 255]], [[255, 255, 255], [128, 128, 128], [0, 0, 0]], ], dtype=np.uint8) # 用matplotlib存儲 plt.imsave('img_pyplot.jpg', img) # 用OpenCV存儲 cv2.imwrite('img_cv2.jpg', img)
無論是RGB仍是BGR,都是高度×寬度×通道數,H×W×C的表達方式,而在深度學習中,由於要對不一樣通道應用卷積,因此用的是另外一種方式:C×H×W,就是把每一個通道都單獨表達成一個二維矩陣,如圖所示。編程
一、存取圖像canvas
讀圖像用cv2.imread(),能夠按照不一樣模式讀取,通常最經常使用到的是讀取單通道灰度圖,或者直接默認讀取多通道。存圖像用cv2.imwrite(),注意存的時候是沒有單通道這一說的,根據保存文件名的後綴和當前的array維度,OpenCV自動判斷存的通道,另外壓縮格式還能夠指定存儲質量,來看代碼例子:網絡
import cv2 # 讀取一張400x600分辨率的圖像 color_img = cv2.imread('test_400x600.jpg') print(color_img.shape) # 直接讀取單通道 gray_img = cv2.imread('test_400x600.jpg', cv2.IMREAD_GRAYSCALE) print(gray_img.shape) # 把單通道圖片保存後,再讀取,仍然是3通道,至關於把單通道值複製到3個通道保存 cv2.imwrite('test_grayscale.jpg', gray_img) reload_grayscale = cv2.imread('test_grayscale.jpg') print(reload_grayscale.shape) # cv2.IMWRITE_JPEG_QUALITY指定jpg質量,範圍0到100,默認95,越高畫質越好,文件越大 cv2.imwrite('test_imwrite.jpg', color_img, (cv2.IMWRITE_JPEG_QUALITY, 80)) # cv2.IMWRITE_PNG_COMPRESSION指定png質量,範圍0到9,默認3,越高文件越小,畫質越差 cv2.imwrite('test_imwrite.png', color_img, (cv2.IMWRITE_PNG_COMPRESSION, 5))
二、縮放,裁剪和補邊數據結構
縮放經過cv2.resize()實現,裁剪則是利用array自身的下標截取實現,此外OpenCV還能夠給圖像補邊,這樣能對一幅圖像的形狀和感興趣區域實現各類操做。下面的例子中讀取一幅400×600分辨率的圖片,並執行一些基礎的操做:app
import cv2 # 讀取一張四川大錄古藏寨的照片 img = cv2.imread('tiger_tibet_village.jpg') # 縮放成200x200的方形圖像 img_200x200 = cv2.resize(img, (200, 200)) # 不直接指定縮放後大小,經過fx和fy指定縮放比例,0.5則長寬都爲原來一半 # 等效於img_200x300 = cv2.resize(img, (300, 200)),注意指定大小的格式是(寬度,高度) # 插值方法默認是cv2.INTER_LINEAR,這裏指定爲最近鄰插值 img_200x300 = cv2.resize(img, (0, 0), fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST) # 在上張圖片的基礎上,上下各貼50像素的黑邊,生成300x300的圖像 img_300x300 = cv2.copyMakeBorder(img, 50, 50, 0, 0, cv2.BORDER_CONSTANT, value=(0, 0, 0)) # 對照片中樹的部分進行剪裁 patch_tree = img[20:150, -180:-50] cv2.imwrite('cropped_tree.jpg', patch_tree) cv2.imwrite('resized_200x200.jpg', img_200x200) cv2.imwrite('resized_200x300.jpg', img_200x300) cv2.imwrite('bordered_300x300.jpg', img_300x300)
注意:less
//裁剪的時候,x,y是反的,正確的是rect=image[y0:yo+h0,x0:x0+w0]
這些處理的效果見圖6-2。dom
三、色調,明暗,直方圖和Gamma曲線ide
除了區域,圖像自己的屬性操做也很是多,好比能夠經過HSV空間對色調和明暗進行調節。HSV空間是由美國的圖形學專家A. R. Smith提出的一種顏色空間,HSV分別是色調(Hue),飽和度(Saturation)和明度(Value)。在HSV空間中進行調節就避免了直接在RGB空間中調節是還須要考慮三個通道的相關性。OpenCV中H的取值是[0, 180),其餘兩個通道的取值都是[0, 256),下面例子接着上面例子代碼,經過HSV空間對圖像進行調整:
# 經過cv2.cvtColor把圖像從BGR轉換到HSV img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # H空間中,綠色比黃色的值高一點,因此給每一個像素+15,黃色的樹葉就會變綠 turn_green_hsv = img_hsv.copy() turn_green_hsv[:, :, 0] = (turn_green_hsv[:, :, 0]+15) % 180 turn_green_img = cv2.cvtColor(turn_green_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('turn_green.jpg', turn_green_img) # 減少飽和度會讓圖像損失鮮豔,變得更灰 colorless_hsv = img_hsv.copy() colorless_hsv[:, :, 1] = 0.5 * colorless_hsv[:, :, 1] colorless_img = cv2.cvtColor(colorless_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('colorless.jpg', colorless_img) # 減少明度爲原來一半 darker_hsv = img_hsv.copy() darker_hsv[:, :, 2] = 0.5 * darker_hsv[:, :, 2] darker_img = cv2.cvtColor(darker_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('darker.jpg', darker_img)
不管是HSV仍是RGB,咱們都較難一眼就對像素中值的分佈有細緻的瞭解,這時候就須要直方圖。若是直方圖中的成分過於靠近0或者255,可能就出現了暗部細節不足或者亮部細節丟失的狀況。好比圖6-2中,背景裏的暗部細節是很是弱的。這個時候,一個經常使用方法是考慮用Gamma變換來提高暗部細節。Gamma變換是矯正相機直接成像和人眼感覺圖像差異的一種經常使用手段,簡單來講就是經過非線性變換讓圖像從對曝光強度的線性響應變得更接近人眼感覺到的響應。具體的定義和實現,仍是接着上面代碼中讀取的圖片,執行計算直方圖和Gamma變換的代碼以下:
import numpy as np # 分通道計算每一個通道的直方圖 hist_b = cv2.calcHist([img], [0], None, [256], [0, 256]) hist_g = cv2.calcHist([img], [1], None, [256], [0, 256]) hist_r = cv2.calcHist([img], [2], None, [256], [0, 256]) # 定義Gamma矯正的函數 def gamma_trans(img, gamma): # 具體作法是先歸一化到1,而後gamma做爲指數值求出新的像素值再還原 gamma_table = [np.power(x/255.0, gamma)*255.0 for x in range(256)] gamma_table = np.round(np.array(gamma_table)).astype(np.uint8) # 實現這個映射用的是OpenCV的查表函數 return cv2.LUT(img, gamma_table) # 執行Gamma矯正,小於1的值讓暗部細節大量提高,同時亮部細節少許提高 img_corrected = gamma_trans(img, 0.5) cv2.imwrite('gamma_corrected.jpg', img_corrected) # 分通道計算Gamma矯正後的直方圖 hist_b_corrected = cv2.calcHist([img_corrected], [0], None, [256], [0, 256]) hist_g_corrected = cv2.calcHist([img_corrected], [1], None, [256], [0, 256]) hist_r_corrected = cv2.calcHist([img_corrected], [2], None, [256], [0, 256]) # 將直方圖進行可視化 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() pix_hists = [ [hist_b, hist_g, hist_r], [hist_b_corrected, hist_g_corrected, hist_r_corrected] ] pix_vals = range(256) for sub_plt, pix_hist in zip([121, 122], pix_hists): ax = fig.add_subplot(sub_plt, projection='3d') for c, z, channel_hist in zip(['b', 'g', 'r'], [20, 10, 0], pix_hist): cs = [c] * 256 ax.bar(pix_vals, channel_hist, zs=z, zdir='y', color=cs, alpha=0.618, edgecolor='none', lw=0) ax.set_xlabel('Pixel Values') ax.set_xlim([0, 256]) ax.set_ylabel('Channels') ax.set_zlabel('Counts') plt.show()
上面三段代碼的結果統一放在下圖中:
能夠看到,Gamma變換後的暗部細節比起原圖清楚了不少,而且從直方圖來看,像素值也從集中在0附近變得散開了一些。
圖像的仿射變換涉及到圖像的形狀位置角度的變化,是深度學習預處理中常到的功能,在此簡單回顧一下。仿射變換具體到圖像中的應用,主要是對圖像的縮放,旋轉,剪切,翻轉和平移的組合。在OpenCV中,仿射變換的矩陣是一個2×3的矩陣,其中左邊的2×2子矩陣是線性變換矩陣,右邊的2×1的兩項是平移項:
對於圖像上的任一位置(x,y),仿射變換執行的是以下的操做:
須要注意的是,對於圖像而言,寬度方向是x,高度方向是y,座標的順序和圖像像素對應下標一致。因此原點的位置不是左下角而是右上角,y的方向也不是向上,而是向下。在OpenCV中實現仿射變換是經過仿射變換矩陣和cv2.warpAffine()這個函數,仍是經過代碼來理解一下,例子中圖片的分辨率爲600×400:
代碼實現的操做示意在下圖中:
import cv2 import numpy as np # 讀取一張斯里蘭卡拍攝的大象照片 img = cv2.imread('lanka_safari.jpg') # 沿着橫縱軸放大1.6倍,而後平移(-150,-240),最後沿原圖大小截取,等效於裁剪並放大 M_crop_elephant = np.array([ [1.6, 0, -150], [0, 1.6, -240] ], dtype=np.float32) img_elephant = cv2.warpAffine(img, M_crop_elephant, (400, 600)) cv2.imwrite('lanka_elephant.jpg', img_elephant) # x軸的剪切變換,角度15° theta = 15 * np.pi / 180 M_shear = np.array([ [1, np.tan(theta), 0], [0, 1, 0] ], dtype=np.float32) img_sheared = cv2.warpAffine(img, M_shear, (400, 600)) cv2.imwrite('lanka_safari_sheared.jpg', img_sheared) # 順時針旋轉,角度15° M_rotate = np.array([ [np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0] ], dtype=np.float32) img_rotated = cv2.warpAffine(img, M_rotate, (400, 600)) cv2.imwrite('lanka_safari_rotated.jpg', img_rotated) # 某種變換,具體旋轉+縮放+旋轉組合能夠經過SVD分解理解 M = np.array([ [1, 1.5, -400], [0.5, 2, -100] ], dtype=np.float32) img_transformed = cv2.warpAffine(img, M, (400, 600)) cv2.imwrite('lanka_safari_transformed.jpg', img_transformed)
OpenCV提供了各類繪圖的函數,能夠在畫面上繪製線段,圓,矩形和多邊形等,還能夠在圖像上指定位置打印文字,好比下面例子:
import numpy as np import cv2 # 定義一塊寬600,高400的畫布,初始化爲白色 canvas = np.zeros((400, 600, 3), dtype=np.uint8) + 255 # 畫一條縱向的正中央的黑色分界線 cv2.line(canvas, (300, 0), (300, 399), (0, 0, 0), 2) # 畫一條右半部份畫面以150爲界的橫向分界線 cv2.line(canvas, (300, 149), (599, 149), (0, 0, 0), 2) # 左半部分的右下角畫個紅色的圓 cv2.circle(canvas, (200, 300), 75, (0, 0, 255), 5) # 左半部分的左下角畫個藍色的矩形 cv2.rectangle(canvas, (20, 240), (100, 360), (255, 0, 0), thickness=3) # 定義兩個三角形,並執行內部綠色填充 triangles = np.array([ [(200, 240), (145, 333), (255, 333)], [(60, 180), (20, 237), (100, 237)]]) cv2.fillPoly(canvas, triangles, (0, 255, 0)) # 畫一個黃色五角星 # 第一步經過旋轉角度的辦法求出五個頂點 phi = 4 * np.pi / 5 rotations = [[[np.cos(i * phi), -np.sin(i * phi)], [i * np.sin(phi), np.cos(i * phi)]] for i in range(1, 5)] pentagram = np.array([[[[0, -1]] + [np.dot(m, (0, -1)) for m in rotations]]], dtype=np.float) # 定義縮放倍數和平移向量把五角星畫在左半部分畫面的上方 pentagram = np.round(pentagram * 80 + np.array([160, 120])).astype(np.int) # 將5個頂點做爲多邊形頂點連線,獲得五角星 cv2.polylines(canvas, pentagram, True, (0, 255, 255), 9) # 按像素爲間隔從左至右在畫面右半部份的上方畫出HSV空間的色調連續變化 for x in range(302, 600): color_pixel = np.array([[[round(180*float(x-302)/298), 255, 255]]], dtype=np.uint8) line_color = [int(c) for c in cv2.cvtColor(color_pixel, cv2.COLOR_HSV2BGR)[0][0]] cv2.line(canvas, (x, 0), (x, 147), line_color) # 若是定義圓的線寬大於半斤,則等效於畫圓點,隨機在畫面右下角的框內生成座標 np.random.seed(42) n_pts = 30 pts_x = np.random.randint(310, 590, n_pts) pts_y = np.random.randint(160, 390, n_pts) pts = zip(pts_x, pts_y) # 畫出每一個點,顏色隨機 for pt in pts: pt_color = [int(c) for c in np.random.randint(0, 255, 3)] cv2.circle(canvas, pt, 3, pt_color, 5) # 在左半部分最上方打印文字 cv2.putText(canvas, 'Python-OpenCV Drawing Example', (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) cv2.imshow('Example of basic drawing functions', canvas) cv2.waitKey()