python - 使用 Scipy 对分辨率不均匀的 3D 图像进行仿射变换的行为

标签 python image scipy transform resolution

我正在申请 affine transformation ,在不同分辨率的图像上以齐次坐标定义,但当一个轴与其他轴的分辨率不同时,我遇到一个问题。

通常,由于只有仿射的平移部分依赖于分辨率,因此我通过分辨率对平移部分进行归一化,并使用 scipy.ndimage.affine_transform 在图像上应用相应的仿射。

如果图像的分辨率对于所有轴都相同,则它可以正常工作,您可以看到下面的图像应用了相同的变换(缩放+平移或旋转+平移,请参见下面的代码) ,其下采样版本(意味着较低的分辨率)。图像(几乎)完美匹配,据我所知,体素值的差异主要是由插值错误引起的。

但是您可以看到形状在下采样变换图像和变换后(下采样用于比较)图像之间重叠

Scale affine transformation applied on the same image, at two different (uniform) resolutions

Rotation affine transformation applied on the same image, at two different (uniform) resolutions

不幸的是,如果图像轴之一的分辨率与另一个图像轴的分辨率不同(请参见下面的代码),则它可以很好地与具有空非对角项(如平移或缩放)的仿射变换配合使用,但变换的结果给出完全错误的结果。

Rotation affine transformation applied on the same image, at two different (non-uniform) resolutions

在这里您可以看到代码的最小工作示例:

<小时/>
import numpy as np
import nibabel as nib
from scipy.ndimage import zoom
from scipy.ndimage import affine_transform
import matplotlib.pyplot as plt

################################
#### LOAD ANY 3D IMAGE HERE ####
################################
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TO BE DEFINED BY USER
orig_img = np.zeros((100, 100, 100))
orig_img[25:75, 25:75, 25:75] = 1

ndim = orig_img.ndim

################################
##### DEFINE RESOLUTIONS #######

#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TO BE DEFINED BY USER
# Comment/uncomment to choose the resolutions (in mm) of the images
# ORIG_RESOLUTION = [1., 1., 1.]
# TARGET_RESOLUTION = [2., 2., 2.]

ORIG_RESOLUTION = [1., 0.5, 1.]
TARGET_RESOLUTION = [2., 2., 2.]

#####################################
##### DEFINE AFFINE TRANSFORM #######

affine_scale_translation = np.array([[1.5, 0.0, 0.0, -25.],
                                     [0.0, 0.8, 0.0, 0. ],
                                     [0.0, 0.0, 1.0, 0.  ],
                                     [0.0, 0.0, 0.0, 1.0]])
a = np.sqrt(2)/2.
affine_rotation_translation = np.array([[a  , a  , 0.0, -25.],
                                     [-a , a  , 0.0, 50. ],
                                     [0.0, 0.0, 1.0, 0.0 ],
                                     [0.0, 0.0, 0.0, 1.0]])

# #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TO BE DEFINED BY USER
# Comment/uncomment to choose the transformation to be applied 

# affine_tf, name_affine = affine_scale_translation, "Tf scale"
affine_tf, name_affine = affine_rotation_translation, "Tf rotation"

######################################################
######## DOWNSAMPLE IMAGE TO LOWER RESOLUTION ########
######################################################

downsample_img = zoom(orig_img,
                      zoom=np.array(ORIG_RESOLUTION)/np.array(TARGET_RESOLUTION),
                      prefilter=False, order=1)

##############################################################################
######## APPLY AFFINE TRANSFORMATION TO ORIGINAL AND DOWNSAMPLE IMAGE ########
##############################################################################

affine_st_full_res, affine_st_low_res = affine_tf.copy(), affine_tf.copy()

# Inverse transform as affine_transform apply the tf from the target space to the original space
affine_st_full_res, affine_st_low_res = np.linalg.inv(affine_st_full_res), np.linalg.inv(affine_st_low_res)

# Normalize translation part (normally expressed in millimeters) for the resolution
affine_st_full_res[:ndim, ndim] = affine_st_full_res[:ndim, ndim] / ORIG_RESOLUTION
affine_st_low_res[:ndim, ndim] = affine_st_low_res[:ndim, ndim] / TARGET_RESOLUTION


# Apply transforms on images of different resolutions

orig_tf_img = affine_transform(orig_img, affine_st_full_res, prefilter=False, order=1)
downsample_tf_img = affine_transform(downsample_img, affine_st_low_res, prefilter=False, order=1)


# Downsample result at full resolution to be compared to result on downsample image

downsample_orig_tf_img = zoom(orig_tf_img, zoom=np.array(
                              ORIG_RESOLUTION)/np.array(TARGET_RESOLUTION),
                              prefilter=False, order=1)

# print(orig_img.shape)
# print(downsample_img.shape)
# print(orig_tf_img.shape)
# print(downsample_orig_tf_img.shape)

###############################
######## VISUALISATION ########
###############################
# We'll visualize in 2D the slice at the middle of the z (third) axis of the image, in both resolution
mid_z_slice_full, mid_z_slice_low = orig_img.shape[2]//2, downsample_img.shape[2]//2

fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(nrows=2, ncols=3)

ax1.imshow(orig_img[:, :, mid_z_slice_full], cmap='gray')
ax1.axis('off')
ax1.set_title('1/ Origin image, at full res: {}'.format(ORIG_RESOLUTION))

ax2.imshow(downsample_img[:, :, mid_z_slice_low], cmap='gray')
ax2.axis('off')
ax2.set_title('2/ Downsampled image, at low res: {}'.format(TARGET_RESOLUTION))

ax3.imshow(downsample_tf_img[:, :, mid_z_slice_low], cmap='gray')
ax3.axis('off')
ax3.set_title('3/ Transformed downsampled image')

ax4.imshow(orig_tf_img[:, :, mid_z_slice_full], cmap='gray')
ax4.axis('off')
ax4.set_title('4/ Transformed original image')

ax5.imshow(downsample_orig_tf_img[:, :, mid_z_slice_low], cmap='gray')
ax5.axis('off')
ax5.set_title('5/ Downsampled transformed image')


error = ax6.imshow(np.abs(downsample_tf_img[:, :, mid_z_slice_low] -\
                          downsample_orig_tf_img[:, :, mid_z_slice_low]), cmap='hot')
ax6.axis('off')
ax6.set_title('Error map between 3/ and 5/')

fig.colorbar(error)

plt.suptitle('Result for {} applied on {} and {} resolution'.format(name_affine, ORIG_RESOLUTION, TARGET_RESOLUTION))
plt.tight_layout()

plt.show()

<小时/>

最佳答案

下采样基本上改变了图像的基向量。这使得下采样就像翻译一样。

但是旋转和平移不可交换(不可互换)。

https://gamedev.stackexchange.com/questions/16719/what-is-the-correct-order-to-multiply-scale-rotation-and-translation-matrices-f

因此,在下采样之后,您必须将图像内容转换回原始位置。这可以通过使用 ORIG_RESOLUTION 组成仿射变换矩阵并在实际变换之前应用它来完成。 或者使用矩阵乘法(例如使用np.dot)来修改实际的转换。

关于python - 使用 Scipy 对分辨率不均匀的 3D 图像进行仿射变换的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59846059/

相关文章:

Python 图像与 numpy 切片的卷积在某些图像上产生奇怪的结果

python - 有没有比 PIL.ImageGrab.grab() 更好的截屏方式?

python - 我如何告诉 'setup.py' 包含我的根目录中的文件以用于构建的发行版?

image - 提高图像的分辨率

python - 正弦信号与矩形脉冲的卷积

python - Cartopy 中正交投影的 Shapefile 绘图问题

html - 右对齐图像并给内容留下图像空间

image - 使用 mencoder 或 ffmpeg 合并/连接图像和视频文件

java - 如何配置Stanford QNMinimizer以获得与scipy.optimize.minimize L-BFGS-B类似的结果

python - 使用 SciPy 二维插值器时出错