B型超聲設備圖像處理:用二維數據生成扇形圖像

昨晚,有同學私信諮詢:如何將矩形數據轉爲扇掃圖像?面對這個問題,我也是一臉懵逼,什麼是扇掃?矩形數據又是啥?細問之下,才知道這是B型超聲設備的數據處理問題。B超輸出的數據保存在一個二維數組中,但顯示在屏幕上的卻需要轉換爲扇形。如下圖所示:
在這裏插入圖片描述
稍微思考一下,應該不難解決。比較直接的方法是,將二維數組的每一列旋轉合適的角度,就可以拼成一副圖像。我們用參數angle表示扇形夾角的一半,用參數k表示輸出圖像的高度與每一列數據數量的比值,很容易寫出如下代碼:

# -*- coding:utf-8 -*-

import numpy as np
from PIL import Image

def square2fan(fn_squ, fn_fan, angle=45, k=1):
    """將矩形圖像轉爲扇形
    
    fn_squ      - 輸入文件名
    fn_fan      - 輸出文件名
    angle       - 扇形夾角度數
    k           - 扇形因子,k大於1輸出環形
    """
    
    im = Image.open(fn_squ) # 打開輸入圖像爲PIL對象
    mode = im.mode # 輸入圖像模式
    w, h = im.size # 輸入圖像分辨率
    rows, cols = int(np.ceil(h*k)), int(np.ceil(2*h*k*np.sin(np.radians(angle)))) # 輸出圖像高度和寬度
    cols += cols%2 # 寬度爲單數則加1
    
    im_squ = np.array(im) # 輸入圖像轉爲numpy數組
    im_fan = np.zeros((rows, cols, im_squ.shape[2]), dtype=np.uint8) # 生成輸出圖像的numpy數組(全透明)
    
    alpha = np.radians(np.linspace(-angle, angle, w)) # 生成扇形角度序列,長度與輸入圖像寬度一致
    for i in range(w): # 遍歷輸入圖像的每一列
        # 當前列各像素在輸出圖像上的行號
        d = np.cos(alpha[i])*rows
        lats = np.int_(np.linspace(d*(k-1)/k, d, h)).astype(np.int)
        
        # 當前列各像素在輸出圖像上的列號
        d = np.sin(alpha[i])*rows
        lons = np.int_(np.linspace(cols/2+d*(k-1)/k, cols/2+d, h)).astype(np.int)
        
        # 輸出圖像上對應的點替換爲輸入圖像的點
        im_fan[(lats, lons)] = im_squ[:,i]
    
    # 保存爲文件
    im = Image.fromarray(im_fan, mode=im.mode)
    im.save(fn_fan)
    
if __name__ == '__main__':
    square2fan('demo.png', 'out.png', angle=45, k=1.0)

然而,輸出圖像的效果卻不夠完美:圖像下部出現了鏤空的白點。
在這裏插入圖片描述
沒關係,我們再加上一個臨近點插值,並用matplotlib將輸入數據和輸出數據畫在一起。完整代碼如下:

# -*- coding:utf-8 -*-

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def square2fan(fn_squ, fn_fan, angle=45, k=1):
    """將矩形圖像轉爲扇形
    
    fn_squ      - 輸入文件名
    fn_fan      - 輸出文件名
    angle       - 扇形夾角度數
    k           - 扇形因子,k大於1輸出環形
    """
    
    im = Image.open(fn_squ) # 打開輸入圖像爲PIL對象
    mode = im.mode # 輸入圖像模式
    w, h = im.size # 輸入圖像分辨率
    rows, cols = int(np.ceil(h*k)), int(np.ceil(2*h*k*np.sin(np.radians(angle)))) # 輸出圖像高度和寬度
    cols += cols%2 # 寬度爲單數則加1
    
    im_squ = np.array(im) # 輸入圖像轉爲numpy數組
    im_fan = np.zeros((rows, cols, im_squ.shape[2]), dtype=np.uint8) # 生成輸出圖像的numpy數組(全透明)
    
    alpha = np.radians(np.linspace(-angle, angle, w)) # 生成扇形角度序列,長度與輸入圖像寬度一致
    for i in range(w): # 遍歷輸入圖像的每一列
        # 當前列各像素在輸出圖像上的行號
        d = np.cos(alpha[i])*rows
        lats = np.int_(np.linspace(d*(k-1)/k, d, h)).astype(np.int)
        
        # 當前列各像素在輸出圖像上的列號
        d = np.sin(alpha[i])*rows
        lons = np.int_(np.linspace(cols/2+d*(k-1)/k, cols/2+d, h)).astype(np.int)
        
        # 輸出圖像上對應的點替換爲輸入圖像的點
        im_fan[(lats, lons)] = im_squ[:,i]
    
    # 空白區域臨近點插值
    for row in range(int(rows*(k-1)/k)+1, rows):
        ps, pe = 0, 0
        for col in range(cols):
            if im_fan[row, col, 3] > 0:
                if ps == 0:
                    ps, pe = col, col
                else:
                    pe = col
        for col in range(ps-1 ,pe):
            if im_fan[row, col, 3] == 0:
                im_fan[row, col] = im_fan[row, col-1]
    
    # 繪圖
    plt.figure('B超扇掃', facecolor='#f4f4f4', figsize=(15, 7))
    plt.subplot(121)
    plt.imshow(im_squ)
    plt.subplot(122)
    plt.imshow(im_fan)
    plt.savefig('out_plt.png')
    plt.show()
    
    # 保存爲文件
    im = Image.fromarray(im_fan, mode=im.mode)
    im.save(fn_fan)
    
if __name__ == '__main__':
    square2fan('demo.png', 'out.png', angle=35, k=1.2)

最終效果如下:
在這裏插入圖片描述

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