Python使用嵌套循環實現圖像處理算法

圖像的數據結構基礎


一:在“底層”圖像是由像素點組成的二維數,每個像素點的位置表示爲兩個整數的元組

二:像素的值根據圖像模式由對應的元組組成(例如,RGB模式表示爲三個整數值組成的元組,分別表示構成顏色的紅、綠、藍的值,範圍從0到255)

PIL.Image模塊中的Image類的方法:

  • getpixel(loc) 返回位於位置loc的像素的顏色
  • putpixel(loc, pix) 把位於位置loc的顏色替換爲pix

拷貝圖像


拷貝圖像的算法可以通過打開原始圖像,創建一個新的大小相同的空白圖像,然後將舊圖像中的像素顏色複製到新圖像相應的像素中。

即使用嵌套循環,把舊圖像位置 (i, j) 的像素複製到新圖像的位置 (i, j)

我的電腦,D:\zgh\picture這個路徑下有一個圖片zgh.png

import PIL.Image

def copy(im):
    im_new = PIL.Image.new(im.mode, im.size)
    width, height = im.size
    for i in range(0, width):
        for j in range(0, height):
            pix = im.getpixel((i, j))
            im_new.putpixel((i, j), pix)
    return im_new

if __name__ == '__main__':
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    copy(im).show()

運行之後,會出現一個圖像文件,顯示的圖像與上圖一樣

  • im.size返回包含圖像寬度和高度的元組,單位爲像素
  • im.mode返回包含圖像模式的字符串(RGB、CYMK、Grayscale…)

剪裁圖像


剪裁圖像的算法可以通過打開原始圖像,指定一個四元組的剪裁框,創建一個與剪裁框大小相同的空白圖像,然後將舊圖像中剪裁框內的像素顏色複製到新圖像中。同樣可以實用嵌套循環實現像素複製

原圖像:

其分辨率爲959 × 959

import PIL.Image

def crop(im, box):
    x1, y1, x2, y2 = box
    width, height = x2-x1, y2-y1
    im_new = PIL.Image.new(im.mode, (width, height))
    for i in range(0, width):
        for j in range(0, height):
            pix = im.getpixel((x1+i, y1+j))
            im_new.putpixel((i, j), pix)
    return im_new

if __name__ == '__main__':
    box = (400, 400, 800, 800)
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    crop(im, box).show()

我圖像對圖像的剪裁框,左上角座標爲(400, 400),右下角爲(800, 800)

box = (400, 400, 800, 800)

運行後,顯示剪裁圖像:

水平或垂直翻轉圖像


水平或垂直翻轉的算法可以通過打開原始圖像,創建一個新的大小相同的空白圖像,然後講舊圖像複製到新圖像對應的像素中

  • 水平翻轉時:原始圖像的的像素(i, j)映射到目標圖像的位置(width-i-1, j)
  • 垂直翻轉時:原始圖像的的像素(i, j)映射到目標圖像的位置(i, height-j-1)

原圖像:

import PIL.Image

def flip(im, orient='H'):
    width, height = im.size
    im_new = PIL.Image.new(im.mode, im.size)
    for i in range(0, width):
        for j in range(0, height):
            pix = im.getpixel((i, j))
            if orient == 'H':
                im_new.putpixel((width-1-i, j), pix)
            else:
                im_new.putpixel((i, height-1-j), pix)
    return im_new

if __name__ == '__main__':
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    flip(im, 'H').show()
    flip(im, 'V').show()

語義化的取名:

  • H 即horizon,水平線
  • V 即vertical,垂直線

水平翻轉:

垂直翻轉:

逆時針或順時針旋轉圖像90度


逆時針或順時針旋轉圖像90度的算法可以通過打開原始圖像,創建一個新的大小相同的空白圖像,然後講舊圖像複製到新圖像對應的像素中

  • 逆時針旋轉圖像90度:原始圖像的的像素(i, j)映射到目標圖像的位置(j, width-i-1)
  • 順時針旋轉圖像90度:原始圖像的的像素(i, j)映射到目標圖像的位置(height-j-1, i)

原圖像:

import PIL.Image

def rotate(im, orient='CCW'):
    width, height = im.size
    im_new = PIL.Image.new(im.mode, im.size)
    for i in range(0, width):
        for j in range(0, height):
            pix = im.getpixel((i, j))
            if orient == 'CCW':
                im_new.putpixel((j, width-1-i), pix)
            else:
                im_new.putpixel((height-1-j, i), pix)
    return im_new

if __name__ == '__main__':
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    rotate(im, 'CCW').show()
    rotate(im, 'CW').show()

語義化的取名:

  • CCW,即Counter clockwise rotation逆時針旋轉
  • CW,即Clockwise rotation順時針旋轉

逆時針旋轉圖像90度:

順時針旋轉圖像90度:

平滑圖像過濾器


圖像過濾器是原始圖像中靠近位置(i, j)的多個像素顏色以某種方式組合運算形成新的圖像對象

例如,簡單的平滑過濾器算法可以通過打開原始圖像,創建一個新的大小相同的空白圖像,然後將新圖像中的每個像素(i, j)的顏色設置爲原始像素(i, j)及其相鄰像素的顏色的平均值。

  1. 不位於圖像邊界上像素(i, j)有8個相鄰像素,其相鄰像素位於從列 i-1 到 列 i+1,和行 j-1 到 行 j+1 範圍
  2. 位於圖像邊界上像素(i, j)
    需要特殊處理,我偷個懶,因爲邊界上的點相對於圖像佔比特別小,我就讓邊界點上的像素不變了

原圖像:

代碼一(跟個傻憨憨一樣,寫的繁瑣,且容易出錯):

import PIL.Image

def smooth(im):
    width, height = im.size
    im_new = PIL.Image.new(im.mode, im.size)
    for i in range(0, width):
        for j in range(0, height):
            if i > 0 and i < width-1 and j > 0 and j < height-1:
                pix_R = int((im.getpixel((i, j))[0] + im.getpixel((i-1, j))[0] + im.getpixel((i+1, j))[0] + im.getpixel((i, j-1))[0] + im.getpixel((i, j+1))[0] + im.getpixel((i-1, j-1))[0] + im.getpixel((i-1, j+1))[0] + im.getpixel((i+1, j-1))[0] + im.getpixel((i+1, j+1))[0]) / 9)
                pix_G = int((im.getpixel((i, j))[1] + im.getpixel((i-1, j))[1] + im.getpixel((i+1, j))[1] + im.getpixel((i, j-1))[1] + im.getpixel((i, j+1))[1] + im.getpixel((i-1, j-1))[1] + im.getpixel((i-1, j+1))[1] + im.getpixel((i+1, j-1))[1] + im.getpixel((i+1, j+1))[1]) / 9)
                pix_B = int((im.getpixel((i, j))[2] + im.getpixel((i-1, j))[2] + im.getpixel((i+1, j))[2] + im.getpixel((i, j-1))[2] + im.getpixel((i, j+1))[2] + im.getpixel((i-1, j-1))[2] + im.getpixel((i-1, j+1))[2] + im.getpixel((i+1, j-1))[2] + im.getpixel((i+1, j+1))[2]) / 9)
                pix = (pix_R, pix_G, pix_B)
            else:
                pix = im.getpixel((i, j))
            im_new.putpixel((i, j), pix)
    return im_new

if __name__ == '__main__':
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    smooth(im).show()

代碼二(代碼簡潔明瞭):

import PIL.Image
import numpy

def smooth(im):
    width, height = im.size
    im_new = PIL.Image.new(im.mode, im.size)
    for i in range(1, width-1):
        for j in range(1, height-1):
            pix_R = 0; pix_G = 0; pix_B = 0
            for x in range(-1, 2):
                for y in range(-1, 2):
                    pix_R += im.getpixel((i+x, j+y))[0] / 9
                    pix_G += im.getpixel((i+x, j+y))[1] / 9
                    pix_B += im.getpixel((i+x, j+y))[2] / 9
            im_new.putpixel((i, j), (int(pix_R), int(pix_G), int(pix_B)))
    return im_new

if __name__ == '__main__':
    im = PIL.Image.open("D:\zgh\picture\zgh.png")
    smooth(im).show()

可惜水平有限,我寫的平滑圖像處理器的代碼性能不高,運行需要一定時間,我的電腦至少需要15秒

平滑圖像過濾後:

個人感覺沒啥子變化…

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