在两个图像之间插值

时间:2018-02-16 00:19:51

标签: python image numpy scipy

我试图在Python中插入两个图像。

图像具有形状(188,188)

Image_1 Image_2

我希望在中间插入图像'这两个图像。假设Image_1位于z = 0位置,Image_2位于z = 2位置。我希望插值图像位于z = 1。

我相信这个答案(MATLAB)包含一个类似的问题和解决方案。

Creating intermediate slices in a 3D MRI volume with MATLAB

我尝试将此代码转换为Python,如下所示:

from scipy.interpolate import interpn
from scipy.interpolate import griddata

# Construct 3D volume from images
#    arr.shape = (2, 182, 182)
arr = np.r_['0,3', image_1, image_2]

slices,rows,cols = arr.shape

# Construct meshgrids
[X,Y,Z] = np.meshgrid(np.arange(cols), np.arange(rows), np.arange(slices));
[X2,Y2,Z2] = np.meshgrid(np.arange(cols), np.arange(rows), np.arange(slices*2));

# Run n-dim interpolation
Vi = interpn([X,Y,Z], arr, np.array([X1,Y1,Z1]).T)

然而,这会产生错误:

ValueError: The points in dimension 0 must be strictly ascending

我怀疑我没有正确构建我的网格,但是这种方法是否正确却有点迷失。

有什么想法吗?

----------编辑-----------

找到一些似乎可以解决这个问题的MATLAB代码:

Interpolating Between Two Planes in 3d space

我试图将其转换为Python:

from scipy.ndimage.morphology import distance_transform_edt
from scipy.interpolate import interpn

def ndgrid(*args,**kwargs):
    """
    Same as calling ``meshgrid`` with *indexing* = ``'ij'`` (see
    ``meshgrid`` for documentation).
    """
    kwargs['indexing'] = 'ij'
    return np.meshgrid(*args,**kwargs)

def bwperim(bw, n=4):
    """
    perim = bwperim(bw, n=4)
    Find the perimeter of objects in binary images.
    A pixel is part of an object perimeter if its value is one and there
    is at least one zero-valued pixel in its neighborhood.
    By default the neighborhood of a pixel is 4 nearest pixels, but
    if `n` is set to 8 the 8 nearest pixels will be considered.
    Parameters
    ----------
      bw : A black-and-white image
      n : Connectivity. Must be 4 or 8 (default: 8)
    Returns
    -------
      perim : A boolean image

    From Mahotas: http://nullege.com/codes/search/mahotas.bwperim
    """

    if n not in (4,8):
        raise ValueError('mahotas.bwperim: n must be 4 or 8')
    rows,cols = bw.shape

    # Translate image by one pixel in all directions
    north = np.zeros((rows,cols))
    south = np.zeros((rows,cols))
    west = np.zeros((rows,cols))
    east = np.zeros((rows,cols))

    north[:-1,:] = bw[1:,:]
    south[1:,:]  = bw[:-1,:]
    west[:,:-1]  = bw[:,1:]
    east[:,1:]   = bw[:,:-1]
    idx = (north == bw) & \
          (south == bw) & \
          (west  == bw) & \
          (east  == bw)
    if n == 8:
        north_east = np.zeros((rows, cols))
        north_west = np.zeros((rows, cols))
        south_east = np.zeros((rows, cols))
        south_west = np.zeros((rows, cols))
        north_east[:-1, 1:]   = bw[1:, :-1]
        north_west[:-1, :-1] = bw[1:, 1:]
        south_east[1:, 1:]     = bw[:-1, :-1]
        south_west[1:, :-1]   = bw[:-1, 1:]
        idx &= (north_east == bw) & \
               (south_east == bw) & \
               (south_west == bw) & \
               (north_west == bw)
    return ~idx * bw

def signed_bwdist(im):
    '''
    Find perim and return masked image (signed/reversed)
    '''    
    im = -bwdist(bwperim(im))*np.logical_not(im) + bwdist(bwperim(im))*im
    return im


def bwdist(im):
    '''
    Find distance map of image
    '''
    dist_im = distance_transform_edt(1-im)
    return dist_im


def interp_shape(top, bottom, num):
    if num<0 and round(num) == num:
        print("Error: number of slices to be interpolated must be   integer>0")

    top = signed_bwdist(top)
    bottom = signed_bwdist(bottom)

    r, c = top.shape
    t = num+2

    print("Rows - Cols - Slices")
    print(r, c, t)
    print("")

    # rejoin top, bottom into a single array of shape (2, r, c)
    # MATLAB: cat(3,bottom,top)
    top_and_bottom = np.r_['0,3', top, bottom]
    #top_and_bottom = np.rollaxis(top_and_bottom, 0, 3)

    # create ndgrids 
    x,y,z = np.mgrid[0:r, 0:c, 0:t-1]  # existing data
    x1,y1,z1 = np.mgrid[0:r, 0:c, 0:t] # including new slice

    print("Shape x y z:", x.shape, y.shape, z.shape)
    print("Shape x1 y1 z1:", x1.shape, y1.shape, z1.shape)
    print(top_and_bottom.shape, len(x), len(y), len(z)) 

    # Do interpolation
    out = interpn((x,y,z), top_and_bottom, (x1,y1,z1))

    # MATLAB: out = out(:,:,2:end-1)>=0;
    array_lim = out[-1]-1    
    out[out[:,:,2:out] >= 0] = 1 

    return out

我这称之为:

new_image = interp_shape(image_1,image_2, 1)

我很确定这是80%的方式,但我在运行时仍然遇到此错误:

ValueError: The points in dimension 0 must be strictly ascending

同样,我可能没有正确构建我的网格。我相信np.mgrid应该产生与MATLAB ndgrid相同的结果。

有没有更好的方法来构建ndgrid等价物?

3 个答案:

答案 0 :(得分:1)

我不知道问题的解决方案,但我不认为可以使用interpn执行此操作。

我更正了您尝试过的代码,并使用了以下输入图像:

img1 img2

但结果是:

result

以下是更正后的代码:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy import interpolate

n = 8
img1 = np.zeros((n, n))
img2 = np.zeros((n, n))

img1[2:4, 2:4] = 1
img2[4:6, 4:6] = 1

plt.figure()
plt.imshow(img1, cmap=cm.Greys)

plt.figure()
plt.imshow(img2, cmap=cm.Greys)

points = (np.r_[0, 2], np.arange(n), np.arange(n))
values = np.stack((img1, img2))
xi = np.rollaxis(np.mgrid[:n, :n], 0, 3).reshape((n**2, 2))
xi = np.c_[np.ones(n**2), xi]

values_x = interpolate.interpn(points, values, xi, method='linear')
values_x = values_x.reshape((n, n))
print(values_x)

plt.figure()
plt.imshow(values_x, cmap=cm.Greys)
plt.clim((0, 1))

plt.show()

我认为您的代码与我的代码之间的主要区别在于xi的规范。 interpn使用起来有点混乱,我在older answer中更详细地解释了它。如果您对我如何指定xi的机制感到好奇,请参阅我的this answer解释我已做过的事情。

这个结果并不完全令人惊讶,因为interpn只是在两个图像之间进行线性插值:所以在一个图像中有1个而在另一个图像中有0的部分只是变成0.5。

在这里,由于一张图片是另一张图片的翻译,因此我们很清楚我们想要一张翻译过的图像&#34;介于&#34;之间。但interpn如何插入两个一般图像呢?如果你有一个小圆圈和一个大圆圈,那么它是否应该清楚地表明应该有一个中间尺寸的圆圈&#34;在&#34;之间。他们?在狗和猫之间进行插值怎么样?还是狗和建筑物?

我认为你基本上是在尝试画线&#34;连接两个图像的边缘,然后试图找出其间的图像。这类似于在半帧处对移动视频进行采样。您可能希望查看optical flow之类的内容,它使用向量连接相邻的帧。我不知道是否以及有哪些python包/实现可用。

答案 1 :(得分:1)

我想出来了。或者至少是产生理想结果的方法。

基于:Interpolating Between Two Planes in 3d space

def signed_bwdist(im):
    '''
    Find perim and return masked image (signed/reversed)
    '''    
    im = -bwdist(bwperim(im))*np.logical_not(im) + bwdist(bwperim(im))*im
    return im

def bwdist(im):
    '''
    Find distance map of image
    '''
    dist_im = distance_transform_edt(1-im)
    return dist_im

def interp_shape(top, bottom, precision):
    '''
    Interpolate between two contours

    Input: top 
            [X,Y] - Image of top contour (mask)
           bottom
            [X,Y] - Image of bottom contour (mask)
           precision
             float  - % between the images to interpolate 
                Ex: num=0.5 - Interpolate the middle image between top and bottom image
    Output: out
            [X,Y] - Interpolated image at num (%) between top and bottom

    '''
    if precision>2:
        print("Error: Precision must be between 0 and 1 (float)")

    top = signed_bwdist(top)
    bottom = signed_bwdist(bottom)

    # row,cols definition
    r, c = top.shape

    # Reverse % indexing
    precision = 1+precision

    # rejoin top, bottom into a single array of shape (2, r, c)
    top_and_bottom = np.stack((top, bottom))

    # create ndgrids 
    points = (np.r_[0, 2], np.arange(r), np.arange(c))
    xi = np.rollaxis(np.mgrid[:r, :c], 0, 3).reshape((r**2, 2))
    xi = np.c_[np.full((r**2),precision), xi]

    # Interpolate for new plane
    out = interpn(points, top_and_bottom, xi)
    out = out.reshape((r, c))

    # Threshold distmap to values above 0
    out = out > 0

    return out


# Run interpolation
out = interp_shape(image_1,image_2, 0.5)

示例输出: Example Output

答案 2 :(得分:1)

我遇到了一个类似的问题,我需要对帧之间的偏移进行插值,其中变化不仅构成平移,而且还改变了形状本身。我通过以下方法解决了这个问题:

  • 使用 scipy.ndimage.measurements 中的 center_of_mass 来计算我们要在每一帧中移动的对象的中心
  • 定义连续参数t,其中t = 0首先是帧,t = 1最后是帧
  • 通过从 scipy.ndimage.interpolation 进行 shift 向后/向前移动图像并叠加,从而在两个最近的帧之间插入运动(相对于特定的t值)他们。

代码如下:

def inter(images,t):
#input: 
# images: list of arrays/frames ordered according to motion
# t: parameter ranging from 0 to 1 corresponding to first and last frame 
#returns: interpolated image

#direction of movement, assumed to be approx. linear 
a=np.array(center_of_mass(images[0]))
b=np.array(center_of_mass(images[-1]))

#find index of two nearest frames 
arr=np.array([center_of_mass(images[i]) for i in range(len(images))])
v=a+t*(b-a) #convert t into vector 
idx1 = (np.linalg.norm((arr - v),axis=1)).argmin()
arr[idx1]=np.array([0,0]) #this is sloppy, should be changed if relevant values are near [0,0]
idx2 = (np.linalg.norm((arr - v),axis=1)).argmin()

if idx1>idx2:
    b=np.array(center_of_mass(images[idx1])) #center of mass of nearest contour
    a=np.array(center_of_mass(images[idx2])) #center of mass of second nearest contour
    tstar=np.linalg.norm(v-a)/np.linalg.norm(b-a) #define parameter ranging from 0 to 1 for interpolation between two nearest frames
    im1_shift=shift(images[idx2],(b-a)*tstar) #shift frame 1
    im2_shift=shift(images[idx1],-(b-a)*(1-tstar)) #shift frame 2
    return im1_shift+im2_shift #return average

if idx1<idx2:
    b=np.array(center_of_mass(images[idx2]))
    a=np.array(center_of_mass(images[idx1]))
    tstar=np.linalg.norm(v-a)/np.linalg.norm(b-a)
    im1_shift=shift(images[idx2],-(b-a)*(1-tstar))
    im2_shift=shift(images[idx1],(b-a)*(tstar))
    return im1_shift+im2_shift

Result example