OpenCV有相當多的函數可以進行幾何變換,但真的要用起來,實在不方便。故本篇將其常用的功能集合成一個類別,改寫成一個好用的sdk – class MahalCv
縮放
cv2.resize(src, (新寬, 新高), 內插值方式)
內插值有如下三種
cv2.INTER_AREA
cv2.INTER_CUBIC(三次插值)
cv2.INTER_LINEAR(線性插值)
cv2.INTER_NEAREST(最近鄰插值)
cv2.INTER_LANCZOS4(lANCZOS插值)
以上的插值法都是為了解決放大縮小時失真的問題,有著非常複雜的演算法。但常用的就二種,CUBIC(速度較慢)及LINEAR(速度較快)
from MahalSdk import MahalSdk as cv import pylab as plt img = cv.read("1.jpg") img = cv.resize(img, width=400) plt.imshow(img[:,:,::-1]) print(img.shape) plt.show()
sdk代碼如下
#sdk如下 import cv2 import numpy as np class MahalSdk(): @staticmethod def read(file): return cv2.imdecode( np.fromfile(file, dtype=np.uint8), cv2.IMREAD_COLOR ) @staticmethod def resize(img, width=800): h, w, _ = img.shape r = h / w if r<1:#橫圖 height = int(width * r) else: height = int(width / r) return cv2.resize(img, (width, height), interpolation=cv2.INTER_LINEAR)
影像裁切
想要取得影像中的某個區塊,可直接指定列及行。請注意,numpy的第一維為列,第二維為行,第三維為bgr值
import pylab as plt from MahalSdk import MahalSdk as cv img=cv.read("1.jpg") p1=cv.Size(1600,0) p2=cv.Size(3000,1500) img=cv.crop(img, p1, p2) plt.imshow(img[:,:,::-1]) plt.show()
sdk代碼如下
class MahalSdk(): class Size(): def __init__(self, x, y): self.x=x self.y=y @staticmethod def crop(img, p1, p2): return img[p1.y:p2.y, p1.x:p2.x]
平移
cv2.warpAffine(src, m, (新尺寸))
m 為平移參數。[1, 0. 500] 為水平平移,[0,1,500]為垂直平移
新尺寸為tuple型態, 是整個影像裁切後的尺寸,不是縮放後的尺寸,所以一般都是原始長寬。最後才使用resize縮放。
from MahalSdk import MahalSdk as cv import pylab as plt img = cv.read("1.jpg") img = cv.shift(img, 100, 500) plt.imshow(img[:,:,::-1]) print(img.shape) plt.show()
sdk代碼如下
@staticmethod def shift(img, x, y): h, w, _ = img.shape m = np.float32( [[1,0, x], [0,1,y]] ) return cv2.warpAffine(img, m, (w, h ))
旋轉
cv2.getRotationMatrix2D((中心x, 中心y), 角度, 縮放)
import pylab as plt from MahalSdk import MahalSdk as cv, MahalSdk img=cv.read("1.jpg") img=cv.resize(img, width=800) ax=plt.subplot(1,1,1) center=MahalSdk.Size(400,400) for i in range(0, 730,10): ax.clear() ax.imshow(cv.rotate(img, center=center,angle=i)[:,:,::-1]) plt.pause(0.1) plt.show()
sdk代碼如下
class MahalSdk(): class Size(): def __init__(self, x, y): self.x=x self.y=y @staticmethod def rotate(img, angle=0, center=None, scale=1): h, w, _ = img.shape if center is None: x = (w - 1) / 2 y = (h - 1) / 2 else: x = center.x y = center.y m = cv2.getRotationMatrix2D((x, y), angle, scale) return cv2.warpAffine(img, m, (w, h))
鏡射
from MahalSdk import MahalSdk as cv import pylab as plt img = cv.read("1.jpg") img1 = cv.flip(img, direction=cv.Both) plt.subplot(1,2,1).imshow(img[:,:,::-1]) plt.subplot(1,2,2).imshow(img1[:,:,::-1]) plt.show()
sdk代碼如下
class MahalSdk(): Horizontal=1#類別變數 Vertical=0 Both=-1 @staticmethod def flip(img, direction=Horizontal): return cv2.flip(img, direction)
透視圖(Prespective)
透視圖可以將非水平或非垂直的線拉直,如下圖所示
底下程式中,先選取原圖的四個點產生pts1陣列,其順序如上圖所示,依序為左上,右上,左下,右下。其座標為(x, y)。然後再選取要貼上的四個點pts2, 然後用getPerspectiveTransform()產生變換矩陣M, 再使用warpPerspective產生結果。
from MahalSdk import MahalSdk as cv import numpy as np import pylab as plt img=cv.read('1.jpg') pts1 = np.float32([[2000,0],[2800,0],[1500, 1500],[3500,1500]]) pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]]) img2 = cv.perspective(img,pts1, pts2, 300,300) plt.subplot(1,2,1).imshow(img[:,:,::-1]) plt.subplot(1,2,2).imshow(img2[:,:,::-1]) plt.show()
sdk 的代碼如下
@staticmethod def perspective(img, pts1, pts2, width, height): m = cv2.getPerspectiveTransform(pts1, pts2) return cv2.warpPerspective(img, m, (width, height))
多張影像相加
若有多張影像要合成一張,則要考慮是前面蓋掉後面,或是每張都有其權重。多張影像相疊在一起可以使用cv2.addWeighted()函數。另需注意,每張影像的長寬皆必需一樣,才可以相加。底下是採用權重相加的效果
import cv2 import pylab as plt from MahalSdk import MahalSdk as cv img1 = cv.read("1.jpg")[0:3000, 0:4000] img2 = cv.read("2.jpg")[0:3000, 0:4000] merge = cv2.addWeighted(img1, 0.8, img2, 0.2, 0) plt.imshow(merge[:,:,::-1]) plt.show()
上述addWeighted()的最後一個參數,為亮度,0為不變,愈大就愈接近白色(255)
np及cv2的相加,有所不一樣,請看如下代碼
x = np.uint8([250])
y = np.uint8([10])
print(cv2.add(x,y)) #會得到最高上限值 255
print(x+y) # 會得到除以256後的餘數
結果 :
[[255]]
[4]
從網路下載圖片
從網路上下載圖片, 需使用 urllib套件。此套件系統本身就有了,不需另行安裝。
經由 urllib 讀入的 byte,使用 np.asarray() 變成陣列,就是檔案的原始格式,然後再使用 cv2.imdecode() 解壓縮即可得到完整的 numpy 格式圖片。
from io import BytesIO from urllib.request import urlopen import pylab as plt import cv2 import numpy as np import requests from PIL import Image url="https://mahalbot.ddns.net/pictures/primitive/2019/20191102_%E5%85%AB%E5%8D%A6%E5%B1%B1/DSCN0166.jpg" #使用 urlopen r = urlopen(url) file = np.asarray(bytearray(r.read()), dtype=np.uint8) img = cv2.imdecode(file, cv2.IMREAD_COLOR) plt.subplot(1,2,1).imshow(img[:,:,::-1]) #使用 requests r = requests.get(url) #Pillow 格式 pil = Image.open(BytesIO(r.content)) plt.subplot(1,2,2).imshow(pil) plt.show()
cv2 to Pillow
使用 Image.fromarray() 可以將 cv2 格式轉成 Pillow 格式
from PIL import Image from MahalSdk import MahalSdk as cv import pylab as plt img=cv.read("1.jpg") pil = Image.fromarray(img[:,:,::-1]) plt.imshow(pil) plt.show()
Pillow to cv2
使用 np.asarray() 可以將 Pillow 格式轉成 cv2 格式
from PIL import Image import numpy as np import pylab as plt pil = Image.open("1.jpg") img = np.asarray(pil) plt.imshow(img) plt.show()