Scipy优化指定参数步长,误差收敛

时间:2016-01-16 18:23:35

标签: python image optimization scipy

我正在使用scipy optimize进行生物医学成像应用。我正在尝试编写一种算法,通过在两者之间找到仿射变换(在这种情况下是移位,缩放和旋转)来对齐两个图像。我正在使用优化器来最小化两个图像之间的差异。我想要优化的参数是x和y移位,围绕图像中心的旋转,以及x和y维度的缩放。理想情况下,步长将全部接近整数而不是1e-6(当前步长)。有没有办法在scipy optimize中指定用于参数的最小步长?

我目前正在将它们映射到正确的数量级,但这仅适用于Nelder-Mead方法一次或两个参数。 (我的目标是使用L-BFGS-B,因为渐变,但这是目前另外一组问题。)添加更多参数会导致优化认为错位图像是正确的答案。 scipy优化的这种“假阳性”结果是否典型?除了改变公差外,还有什么方法可以提高准确度吗?

工作代码:

class toy():
    def __init__(self, img1, img2):
        self.original = img1
        self.shifted = img2

    def find_affine(self, parameters):
        # extract parameters
        x = 0
        y = 0
        r = 0.0
        sx = 1.0
        sy = 1.0
        # x = int(parameters[0]*10000)
        # y = int(parameters[1]*10000)
        # r = int(parameters[2]*10000)
        # sx = 1.0 + abs(round(parameters[3]*1000, 1))
        # sy = 1.0 + abs(round(parameters[4]*1000, 1))

        print "Updated parameters: [" + str(x) + ", " + str(y) + ", " + str(r) + ", " + str(sx) + ", " + str(sy) + "]"

        # image data
        original_dims = [len(self.original), len(self.original[0])]
        shifted_dims = [len(self.shifted), len(self.shifted[0])]
        mod_orig = self.original

        # set up rotation
        if r != 0.0:
            rotation = cv2.getRotationMatrix2D((original_dims[0]/2, original_dims[1]/2), r, 1)
            mod_orig = cv2.warpAffine(mod_orig, rotation, (len(self.original), len(self.original[0])))

        # set up scaling
        if sx != 1.0 or sy != 1.0:
            dims = [int(round(sx*original_dims[0])), int(round(sy*original_dims[1]))]
            mod_orig = cv2.resize(mod_orig, (dims[0], dims[1]), interpolation=cv2.INTER_CUBIC)
            original_dims = [len(mod_orig), len(mod_orig[0])]

        print "New dimensions: " + str(len(mod_orig)) + " " + str(len(mod_orig[0]))

        # set up translation
        shift = [y*sy, x*sx, 0]  # does this need to be scaled?

        # convert to pixels
        # set the overlap bounds: x
        if (x <= 0):
            rows_orig = [abs(shift[1]), original_dims[1]]
            rows_shift = [0, shifted_dims[1]+shift[1]]
        else:
            rows_shift = [shift[1], shifted_dims[1]]
            rows_orig = [0, original_dims[1]-shift[1]]

        # set the overlap bounds: y
        if (y <= 0):
            cols_orig = [abs(shift[0]), original_dims[0]]
            cols_shift = [0, shifted_dims[0]+shift[0]]
        else:
            cols_shift = [shift[0], shifted_dims[0]]
            cols_orig = [0, original_dims[0]-shift[0]]

        # get the relevant pixels
        original_overlap = mod_orig[cols_orig[0]:cols_orig[1], rows_orig[0]:rows_orig[1]]
        shifted_overlap = self.shifted[cols_shift[0]:cols_shift[1], rows_shift[0]:rows_shift[1]]
        self.show_overlap(original_overlap, shifted_overlap)

        return (original_overlap, shifted_overlap)

    def objective(self, parameters):
        stable_pts, warped_pts = self.find_affine(parameters)
        stable_pts = stable_pts.flatten()
        warped_pts = warped_pts.flatten()
        N = max(len(stable_pts), len(warped_pts))
        print "Number of points: " + str(N)

        if N == 0:
            return 99999999999

        score = 0.0
        for s_pt, w_pt in zip(stable_pts, warped_pts):
            score = score + ((float(s_pt) - float(w_pt))**2)

        score = score/N
        print "Objective score: " + str(score) + " for x = " + str(parameters)
        return score

    def show_overlap(self, img1, img2):
        print "Dims"
        print "   Img1 Dims: " + str(len(img1)) + " " + str(len(img1[0]))
        print "   Img2 Dims: " + str(len(img2)) + " " + str(len(img2[0]))

        dims = [min(len(img1), len(img2)), min(len(img1[0]), len(img2[0]))]

        overlap = numpy.zeros(dims)

        for x in xrange(len(overlap)):
            for y in xrange(len(overlap[0])):
                overlap[x][y] = abs(int(img1[x][y])-int(img2[x][y]))

        overlap = numpy.uint8(overlap)
        cv2.imshow("Overlapping Image", overlap)
        cv2.waitKey(500)
        cv2.destroyAllWindows()

def _main(status):
    # read in images
    img1 = cv2.imread("data/smile_orig.png")
    img2 = cv2.imread("data/smiletest.png")
    # convert to grayscale
    img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    test = toy(img1, img2)
    initial_conditions = [0.0]
    transform = optimize.minimize(test.objective, initial_conditions, method='nelder-mead', tol=1e-8)

    # Show minimization at the end
    params = transform.x
    original_img, shifted_img = test.find_affine(params)
    print "COMPLETED MINIMIZATION"
    print transform

    # show difference between transformed image and desired image
    overlap = numpy.zeros([min(len(original_img), len(shifted_img)),
                            min(len(original_img[0]), len(shifted_img[0]))])
    print "shape of overlap: " + str(overlap.shape)
    for x in xrange(len(overlap)):
        for y in xrange(len(overlap[0])):
            overlap[x][y] = abs(int(original_img[x][y])-int(shifted_img[x][y]))

    overlap = numpy.uint8(overlap)
    cv2.imshow("Overlapping Image", overlap)
    print "Final Image Shown"
    print "Dimensions: " + str(len(overlap)) + " " + str(len(overlap[0]))
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    _main(status)

使用的图片:

原始图片: original image

移位和旋转图像: shifted and rotated image

结果:

COMPLETED MINIMIZATION
  status: 0
    nfev: 95
 success: True
     fun: 11.051886006332982
       x: array([ -1.29629630e-04,   8.42592593e-04,   2.77777778e-05])
 message: 'Optimization terminated successfully.'
     nit: 29

通过参数映射,实现的变换是ax尺寸偏移-1,ay尺寸偏移8,旋转0。两幅图像之间的实际仿射变换是ax尺寸偏移10,ay尺寸偏移20,旋转-10。

找到转换后两个图像之间的差异: difference between two images after transform

它很接近,它的分数很低,但它需要更接近(理想情况下完美得分为0.0,或至少笑脸没有留下任何东西)。任何建议都会受到欢迎。

相关:

Integer step size in scipy optimize minimize

0 个答案:

没有答案