(自己編寫函數)Python + Opencv 圖像幾何變換(縮小、放大、內凹,外凸,梯形變形、三角型變形、S型變形、圖片旋轉、圖片平移)

因爲之前寫這個實驗的時候,組織上有很多事情,所以當時,隨便網上找了個 C++ 的上交了,昨天就補寫了一下。這裏寫的縮小和方法都是等比例進行的,不是等比例的改也比較簡單,這裏就不改了,有興趣的朋友可以自己嘗試着改改,另外這裏用的是灰度圖,彩色圖稍微改改就行了

比例縮小

ratio 是縮小比例,大致思路是:

1、先根據你輸入的縮放比例生成一個與原圖 * 縮放比例相同大小的灰度級矩陣。

2、根據書中說的首先根據縮小的倍率去算出,在原圖中要隔多少個點採集一次生成新的圖像,這就是我裏面寫的步長。比如:4*4 的 圖像要縮小0.5倍,那麼步長就是2 除了第一個點之外,其他每個點都要每隔兩個點 取一次。

#按比例縮小
def DOWNRESIZE(gray , ratio=0.8) :
    height , width = gray.shape[0] , gray.shape[1]
    resize_width , resize_height = int(width * ratio) , int(height * ratio)
    resize_gray = np.zeros([resize_height,resize_width],np.uint8)   #創建一個和原圖像 *縮小比例相同像素的空圖
    stepsize = 1.0/ratio    #原圖像的採集步長
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            if i  == 0 and j == 0 :     #縮小圖(0,0)地方的像素用原圖(1,1)的地方代替
                resize_gray[i][j] = gray[1][1]
            else :      #縮小圖其他地方的像素用此時原圖對應的座標-1的地方代替
                resize_gray[i][j] = gray[int(i*stepsize)-1][int(j*stepsize)-1]
    cv2.imshow("old",gray)
    cv2.imshow("Downresize",resize_gray)
    cv2.waitKey(0)

比例放大

比例方法,這裏使用了兩種方法:①最鄰近插值法;②雙線性插值。完全是根據概念,寫的算法,概念就不重複了,可以百度,網上很多。

#按比例放大
def UPSIZE(gray , ratio=1.2) :
    height , width = gray.shape[0] , gray.shape[1]
    resize_width , resize_height = int(width * ratio) , int(height * ratio)
    resize_gray = np.zeros([resize_height,resize_width],np.uint8)
    """
    #最鄰近插值法
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            if i  == 0 and j == 0 :
                resize_gray[i][j] = gray[int(i/ratio)+1][int(j/ratio)+1]
            else :
                resize_gray[i][j] = gray[int(i/ratio)-1][int(j/ratio)-1]
    """
    #雙線性插值
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            x = i/ratio
            y = j/ratio
            p=(i+0.0)/ratio-x
            q=(j+0.0)/ratio-y
            x=int(x)-1
            y=int(y)-1
            if x+1<i and y+1<j:
                resize_gray[i,j]=int(gray[x,y]*(1-p)*(1-q)+gray[x,y+1]*q*(1-p)+gray[x+1,y]*(1-q)*p+gray[x+1,y+1]*p*q)
    cv2.imshow("old",gray)
    cv2.imshow("Upresize",resize_gray)
    cv2.waitKey(0)

水平內凹

內凹與外凸還有S型都差不多,大致思路,生成正弦波的波形,然後,從這個正弦波去確定那些地方是黑色的,最後根據每行中不是黑色的部分和原來的比重,根據之前的縮小的步長去進行每行的縮放即可。

#水平內凹
def CONCAVE(gray) :
    RANGE = 400
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width- RANGE) * math.sin(((2 * math.pi * i) / height) / 2))
        for j in range (int(temp+0.5),int(width-temp)) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Concave", resize_gray)
    cv2.waitKey(0)

水平外凸

#水平外凸
def CONVEX(gray) :
    RANGE = 400
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    temp1 = []
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width- RANGE) * math.sin(((2 * math.pi * i) / height) / 2))
        temp = 200 - temp
        for j in range (int(temp+0.5),int(width-temp)) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Convex", resize_gray)
    cv2.waitKey(0)

梯形形變

#梯形形變
def TRAPEZOID(gray,k=0.3) :
    height, width = gray.shape[0], gray.shape[1]
    value = k*height
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        temp = int( value - k * i ) 
        for j in range (temp,width-temp) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Trapezoid", resize_gray)
    cv2.waitKey(0)

三角形形變

#三角形形變
def TRIANGLE(gray,k=0.5) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        temp = int( k * i ) 
        for j in range (temp) :
            #每行非黑色區域的長度
            distance = temp
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Triangle", resize_gray)
    cv2.waitKey(0)

S形形變

#S形變
def SSHAPE(gray,RANGE=450) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width - RANGE) / 2 + (width- RANGE) * math.sin((2 * math.pi * i) / height + math.pi) / 2)
        for j in range (int(temp+0.5),int(RANGE+temp)) :
            #映射關係
            m = int(((j-temp) * width / RANGE))
            if m >= width :
                m=width-1
            if m < 0 :
                m=0
            resize_gray[i,j]=gray[i,m]

    cv2.imshow("old", gray)
    cv2.imshow("Sshape", resize_gray)
    cv2.waitKey(0)

圖片旋轉

#圖片旋轉
def ROTATE(gray, ANGLE = 45,center=None, scale=1.0) :
    #將角度轉化爲弧度
    """ #特別神奇的圖案
    ANGLE =  math.radians(ANGLE)
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([800,800], np.uint8)
    for i in range (height) :
        for j in range (width) :
            angle = math.asin( i / width )
            ANGLE = angle + ANGLE
            x,y = int(width * math.cos(ANGLE)) , int(width * math.sin(ANGLE))
            resize_gray[x][y] = gray[i][j]
    cv2.imshow("old", gray)
    cv2.imshow("Rotate", resize_gray)
    cv2.waitKey(0)
    """
    (height, width) = gray.shape[:2]
    (cX, cY) = (width // 2, height // 2)
 
    M = cv2.getRotationMatrix2D((cX, cY), -ANGLE, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])
 
    nW = int((height * sin) + (width * cos))
    nH = int((height * cos) + (width * sin))
 
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY
 
    shuchu=cv2.warpAffine(gray, M, (nW, nH))

    cv2.imshow("old", gray)
    cv2.imshow("Rotate", shuchu)
    cv2.waitKey(0)

圖像平移

平移很簡單,就不多說了

#圖片平移
def HORIZONTALMOVE(gray,move_x,move_y) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        for j in range (width) :
            if i + move_x < height and j + move_y < width :
                resize_gray[i][j] = gray[i+move_x][j+move_y]
            else :
                resize_gray [i][j] = 0
    cv2.imshow("old", gray)
    cv2.imshow("Horizeontalmove", resize_gray)
    cv2.waitKey(0)

主函數

def MAIN() :
    image = cv2.imread(r"D:\Code\Python\2.jpg")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    while True :
        print("0: 按比例縮小\n1: 按比例放大\n2: 水平外凹\n3: 水平外凸\n4: 梯形變形\n5: 三角形變形\n6: S形變形\n7: 圖片旋轉\n8: 圖片平移\n")
        keyinput = input ("請輸入數字0—8,選擇你需要的變換(按'Q'或'q'退出):\n")
        if keyinput == "0" :
            ratio = float(input("請輸入縮小倍數:\n"))
            DOWNRESIZE(gray , ratio)
            continue
        elif keyinput == "1" :
            ratio = float(input("請輸入放大倍數:\n"))
            UPSIZE(gray , ratio)
            continue
        elif keyinput == "2" :
            CONCAVE(gray)
            continue
        elif keyinput == "3" :
            CONVEX(gray)
            continue
        elif keyinput == "4" :
            k = float(input("請輸入梯形的斜度:\n"))
            TRAPEZOID(gray,k)
            continue
        elif keyinput == "5" :
            k = float(input("請輸入三角形的斜度:\n"))
            TRIANGLE(gray,k)
            continue
        elif keyinput == "6" :
            RANGE = float(input("請輸入變換強度:\n"))
            SSHAPE(gray,RANGE)
            continue
        elif keyinput == "7" :
            ANGLE = float(input("請輸入旋轉角度:\n"))
            ROTATE(gray,ANGLE)
            continue
        elif keyinput == "8" :
            move_x = int(input("請輸入x軸偏移值:\n"))
            move_y = int(input("請輸入y軸偏移值:\n"))
            HORIZONTALMOVE(gray,move_x,move_y)
            continue
        elif keyinput == "Q" or "q" :
            break
        else :
            keyinput = input("您輸入的字符有誤,請輸入 0-8 之間的數字:")

完整代碼以及展示

import matplotlib.pyplot as plt
from scipy import signal
import numpy as np
import copy as cp
import random
import math
import cv2
import collections

#按比例縮小
def DOWNRESIZE(gray , ratio=0.8) :
    height , width = gray.shape[0] , gray.shape[1]
    resize_width , resize_height = int(width * ratio) , int(height * ratio)
    resize_gray = np.zeros([resize_height,resize_width],np.uint8)   #創建一個和原圖像 *縮小比例相同像素的空圖
    stepsize = 1.0/ratio    #原圖像的採集步長
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            if i  == 0 and j == 0 :     #縮小圖(0,0)地方的像素用原圖(1,1)的地方代替
                resize_gray[i][j] = gray[1][1]
            else :      #縮小圖其他地方的像素用此時原圖對應的座標-1的地方代替
                resize_gray[i][j] = gray[int(i*stepsize)-1][int(j*stepsize)-1]
    cv2.imshow("old",gray)
    cv2.imshow("Downresize",resize_gray)
    cv2.waitKey(0)


#按比例放大
def UPSIZE(gray , ratio=1.2) :
    height , width = gray.shape[0] , gray.shape[1]
    resize_width , resize_height = int(width * ratio) , int(height * ratio)
    resize_gray = np.zeros([resize_height,resize_width],np.uint8)
    """
    #最鄰近插值法
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            if i  == 0 and j == 0 :
                resize_gray[i][j] = gray[int(i/ratio)+1][int(j/ratio)+1]
            else :
                resize_gray[i][j] = gray[int(i/ratio)-1][int(j/ratio)-1]
    """
    #雙線性插值
    for i in range (0,resize_height) :
        for j in range (0,resize_width) :
            x = i/ratio
            y = j/ratio
            p=(i+0.0)/ratio-x
            q=(j+0.0)/ratio-y
            x=int(x)-1
            y=int(y)-1
            if x+1<i and y+1<j:
                resize_gray[i,j]=int(gray[x,y]*(1-p)*(1-q)+gray[x,y+1]*q*(1-p)+gray[x+1,y]*(1-q)*p+gray[x+1,y+1]*p*q)
    cv2.imshow("old",gray)
    cv2.imshow("Upresize",resize_gray)
    cv2.waitKey(0)

#水平內凹
def CONCAVE(gray) :
    RANGE = 400
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width- RANGE) * math.sin(((2 * math.pi * i) / height) / 2))
        for j in range (int(temp+0.5),int(width-temp)) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Concave", resize_gray)
    cv2.waitKey(0)


#水平外凸
def CONVEX(gray) :
    RANGE = 400
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    temp1 = []
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width- RANGE) * math.sin(((2 * math.pi * i) / height) / 2))
        temp = 200 - temp
        for j in range (int(temp+0.5),int(width-temp)) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Convex", resize_gray)
    cv2.waitKey(0)

#梯形形變
def TRAPEZOID(gray,k=0.3) :
    height, width = gray.shape[0], gray.shape[1]
    value = k*height
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        temp = int( value - k * i ) 
        for j in range (temp,width-temp) :
            #每行非黑色區域的長度
            distance = int(width-temp) - int(temp+0.5)
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Trapezoid", resize_gray)
    cv2.waitKey(0)

#三角形形變
def TRIANGLE(gray,k=0.5) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        temp = int( k * i ) 
        for j in range (temp) :
            #每行非黑色區域的長度
            distance = temp
            #縮小的倍率
            ratio = distance / width
            #取點的步長
            stepsize = 1.0/ratio
            #將同意行縮小相同倍率
            resize_gray[i][j] = gray[i][int((j-temp)*stepsize)]
    cv2.imshow("old", gray)
    cv2.imshow("Triangle", resize_gray)
    cv2.waitKey(0)

#S形變
def SSHAPE(gray,RANGE=450) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range(height):
        # 得到正弦波的波形,即j對應的起點
        temp = float((width - RANGE) / 2 + (width- RANGE) * math.sin((2 * math.pi * i) / height + math.pi) / 2)
        for j in range (int(temp+0.5),int(RANGE+temp)) :
            #映射關係
            m = int(((j-temp) * width / RANGE))
            if m >= width :
                m=width-1
            if m < 0 :
                m=0
            resize_gray[i,j]=gray[i,m]

    cv2.imshow("old", gray)
    cv2.imshow("Sshape", resize_gray)
    cv2.waitKey(0)

#圖片旋轉
def ROTATE(gray, ANGLE = 45,center=None, scale=1.0) :
    #將角度轉化爲弧度
    """ #特別神奇的圖案
    ANGLE =  math.radians(ANGLE)
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([800,800], np.uint8)
    for i in range (height) :
        for j in range (width) :
            angle = math.asin( i / width )
            ANGLE = angle + ANGLE
            x,y = int(width * math.cos(ANGLE)) , int(width * math.sin(ANGLE))
            resize_gray[x][y] = gray[i][j]
    cv2.imshow("old", gray)
    cv2.imshow("Rotate", resize_gray)
    cv2.waitKey(0)
    """
    (height, width) = gray.shape[:2]
    (cX, cY) = (width // 2, height // 2)
 
    M = cv2.getRotationMatrix2D((cX, cY), -ANGLE, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])
 
    nW = int((height * sin) + (width * cos))
    nH = int((height * cos) + (width * sin))
 
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY
 
    shuchu=cv2.warpAffine(gray, M, (nW, nH))

    cv2.imshow("old", gray)
    cv2.imshow("Rotate", shuchu)
    cv2.waitKey(0)

#圖片平移
def HORIZONTALMOVE(gray,move_x,move_y) :
    height, width = gray.shape[0], gray.shape[1]
    resize_gray = np.zeros([height, width], np.uint8)
    for i in range (height) :
        for j in range (width) :
            if i + move_x < height and j + move_y < width :
                resize_gray[i][j] = gray[i+move_x][j+move_y]
            else :
                resize_gray [i][j] = 0
    cv2.imshow("old", gray)
    cv2.imshow("Horizeontalmove", resize_gray)
    cv2.waitKey(0)

def MAIN() :
    image = cv2.imread(r"D:\Code\Python\2.jpg")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    while True :
        print("0: 按比例縮小\n1: 按比例放大\n2: 水平外凹\n3: 水平外凸\n4: 梯形變形\n5: 三角形變形\n6: S形變形\n7: 圖片旋轉\n8: 圖片平移\n")
        keyinput = input ("請輸入數字0—8,選擇你需要的變換(按'Q'或'q'退出):\n")
        if keyinput == "0" :
            ratio = float(input("請輸入縮小倍數:\n"))
            DOWNRESIZE(gray , ratio)
            continue
        elif keyinput == "1" :
            ratio = float(input("請輸入放大倍數:\n"))
            UPSIZE(gray , ratio)
            continue
        elif keyinput == "2" :
            CONCAVE(gray)
            continue
        elif keyinput == "3" :
            CONVEX(gray)
            continue
        elif keyinput == "4" :
            k = float(input("請輸入梯形的斜度:\n"))
            TRAPEZOID(gray,k)
            continue
        elif keyinput == "5" :
            k = float(input("請輸入三角形的斜度:\n"))
            TRIANGLE(gray,k)
            continue
        elif keyinput == "6" :
            RANGE = float(input("請輸入變換強度:\n"))
            SSHAPE(gray,RANGE)
            continue
        elif keyinput == "7" :
            ANGLE = float(input("請輸入旋轉角度:\n"))
            ROTATE(gray,ANGLE)
            continue
        elif keyinput == "8" :
            move_x = int(input("請輸入x軸偏移值:\n"))
            move_y = int(input("請輸入y軸偏移值:\n"))
            HORIZONTALMOVE(gray,move_x,move_y)
            continue
        elif keyinput == "Q" or "q" :
            break
        else :
            keyinput = input("您輸入的字符有誤,請輸入 0-8 之間的數字:")

 
if __name__ == "__main__":
    MAIN()

變換結果

縮小

在這裏插入圖片描述
在這裏插入圖片描述

放大

在這裏插入圖片描述
在這裏插入圖片描述

水平內凹

在這裏插入圖片描述

水平外凸

在這裏插入圖片描述

梯形形變

在這裏插入圖片描述
在這裏插入圖片描述

三角形形變

在這裏插入圖片描述
在這裏插入圖片描述

S型形變

在這裏插入圖片描述
在這裏插入圖片描述

圖片旋轉

在這裏插入圖片描述
在這裏插入圖片描述

圖片平移

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章