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)

最终效果如下:
在这里插入图片描述

发布了96 篇原创文章 · 获赞 1万+ · 访问量 146万+

猜你喜欢

转载自blog.csdn.net/xufive/article/details/104675998