简体   繁体   English

如何检测用于将显微镜图像旋转对准模板的良好特征

[英]How to detect good features for rotationally aligning microscope images to a template

I'm working on a project to automatically rotate microscope image stacks of a fluid experiment so that they are lined up with images of the CAD template for the microfluidic chip. 我正在开展一个项目,自动旋转流体实验的显微镜图像堆栈,以便它们与微流控芯片的CAD模板图像对齐。 I am using the OpenCV package in Python for image processing. 我在Python中使用OpenCV包进行图像处理。 Having the correct rotational orientation is necessary so that the images can be masked properly for analysis. 具有正确的旋转方向是必要的,以便可以适当地掩蔽图像以进行分析。 Our chips have markers filled with fluorescent dye that are visible in every frame. 我们的芯片上有标记,每个框架都可以看到荧光染料。 The template and a sample image look like the following (the template can be scaled to arbitrary size, but the relevant region of the images is typically ~100x100 pixels or so): 模板和示例图像如下所示(模板可以缩放到任意大小,但图像的相关区域通常约为100x100像素):

样本模板标记 包含对齐标记的样本图像

I have not been able to rotationally align the image to the CAD template. 我无法将图像旋转对齐到CAD模板。 Typically, the misalignment between the CAD template and the images is less than a few degrees, which is still sufficient to interfere with analysis, so I need to be able to measure the rotational difference even if it is relatively small. 通常,CAD模板和图像之间的不对准小于几度,这仍然足以干扰分析,因此我需要能够测量旋转差异,即使它相对较小。

Following examples online I am using the following procedure: 以下示例在线我使用以下过程:

  1. Scale up the image to approximately the same size as the template using cubic interpolation (~800 x 800) 使用三次插值(~800 x 800)将图像放大到与模板大致相同的大小
  2. Threshold both images using Otsu's method 使用Otsu方法对两个图像进行阈值处理
  3. Find keypoints and extract descriptors using a built-in method (I've tried ORB, AKAZE, and BRIEF). 使用内置方法查找关键点并提取描述符(我尝试过ORB,AKAZE和BRIEF)。
  4. Match descriptors using a brute-force matcher with Hamming distance. 使用具有汉明距离的强力匹配器匹配描述符。
  5. Take the best matches and use them to compute a partial affine transformation matrix 采用最佳匹配并使用它们来计算部分仿射变换矩阵
  6. Use that matrix to infer a rotational shift, warping the one image to the other as a check. 使用该矩阵推断旋转移位,将一个图像扭曲为另一个图像作为检查。

Here's a sample of my code (borrowed in part from here ): 这是我的代码示例(从这里借来的部分):

import numpy as np
import cv2
import matplotlib.pyplot as plt

MAX_FEATURES = 500
GOOD_MATCH_PERCENT = 0.5

def alignImages(im1, im2,returnpoints=False):
    # Detect ORB features and compute descriptors.
    size1 = int(0.1*(np.mean(np.shape(im1))))
    size2 = int(0.1*(np.mean(np.shape(im2))))
    orb1 = cv2.ORB_create(MAX_FEATURES,edgeThreshold=size1,patchSize=size1)
    orb2 = cv2.ORB_create(MAX_FEATURES,edgeThreshold=size2,patchSize=size2)
    keypoints1, descriptors1 = orb1.detectAndCompute(im1, None)
    keypoints2, descriptors2 = orb2.detectAndCompute(im2, None)

    matcher = cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=True)
    matches = matcher.match(descriptors1,descriptors2)
    # Sort matches by score
    matches.sort(key=lambda x: x.distance, reverse=False)

    # Remove not so good matches
    numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
    matches = matches[:numGoodMatches]

    # Draw top matches
    imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None)
    cv2.imwrite("matches.jpg", imMatches)

    # Extract location of good matches
    points1 = np.zeros((len(matches), 2), dtype=np.float32)
    points2 = np.zeros((len(matches), 2), dtype=np.float32)
    for i, match in enumerate(matches):
        points1[i, :] = keypoints1[match.queryIdx].pt
        points2[i, :] = keypoints2[match.trainIdx].pt
    # Find homography
    M, inliers = cv2.estimateAffinePartial2D(points1,points2)
    height, width = im2.shape
    im1Reg = cv2.warpAffine(im1,M,(width,height))
    return im1Reg, M

if __name__ == "__main__":
    test_template = cv2.cvtColor(cv2.imread("test_CAD_cropped.png"),cv2.COLOR_RGB2GRAY)
    test_image = cv2.cvtColor(cv2.imread("test_CAD_cropped.png"),cv2.COLOR_RGB2GRAY)
    fx = fy = 88/923
    test_image_big = cv2.resize(test_image,(0,0),fx=1/fx,fy=1/fy,interpolation=cv2.INTER_CUBIC)
    ret, imRef_t = cv2.threshold(test_template,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    ret, test_big_t = cv2.threshold(test_image_big,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    imReg, M = alignImages(test_big_t,imRef_t)
    fig, ax = plt.subplots(nrows=2,ncols=2,figsize=(8,8))
    ax[1,0].imshow(imReg)
    ax[1,0].set_title("Warped Image")
    ax[0,0].imshow(imRef_t)
    ax[0,0].set_title("Template")
    ax[0,1].imshow(test_big_t)
    ax[0,1].set_title("Thresholded Image")
    ax[1,1].imshow(imRef_t - imReg)
    ax[1,1].set_title("Diff")
    plt.show()

In this example, I get the following bad transformation because there are only 3 matching keypoints and they are all incorrect: 在这个例子中,我得到了以下错误的转换,因为只有3个匹配的关键点,它们都是不正确的:

转化的形象

I find that regardless of my keypoint/descriptor parameters I tend to get too few "good" features. 我发现无论我的关键点/描述符参数如何,我都倾向于获得太少的“好”功能。 Is there anything I can do to pre-process my images better to get good features more reliably, or is there a better method to align my images to this template that doesn't involve keypoint matching? 我能做些什么来更好地预处理我的图像以更可靠地获得好的功能,还是有更好的方法将我的图像对齐到这个不涉及关键点匹配的模板? The specific application of this experiment means that I can't use the patented keypoint extractor/descriptors like SURF and SIFT. 该实验的具体应用意味着我不能使用专利的关键点提取器/描述符,如SURF和SIFT。

A good method to align two images based on rotation, translation and scaling only is the Fourier Mellin transform . 仅基于旋转,平移和缩放来对齐两个图像的好方法是傅里叶梅林变换

Here is an example using the implementation in DIPlib (disclosure: I'm an author): 以下是使用DIPlib中实现示例 (披露:我是作者):

import PyDIP as dip

# load data
image = dip.ImageRead('image.png')
template = dip.ImageRead('template.png')
template = template.TensorElement(0) # this one is RGB, take any one channel

# pad the two images with zeros so they have equal sizes
sz = [max(image.Size(0), template.Size(0)), max(image.Size(1), template.Size(1))]
image = image.Pad(sz)
template = template.Pad(sz)

# match
res = dip.FourierMellinMatch2D(template, image)

# display
dip.JoinChannels((template,res,res)).Show()

模板和变换图像的颜色叠加


However, there are many other approaches. 但是,还有许多其他方法。 A key thing here is that both the template and the image are quite simple, and very similar. 这里的关键是模板和图像都非常简单,非常相似。 This makes registration very easy. 这使得注册变得非常容易。

For example, assuming you have the proper scaling of the template (this should not be a problem I presume), all you need to do is find the rotation and the translation. 例如,假设您具有适当的模板缩放(这不应该是我认为的问题),您需要做的就是找到旋转和平移。 You can brute-force the rotations, simply rotating the image over a set of small angles, and matching each of the results with the template (cross-correlation). 您可以强制旋转,只需在一组小角度上旋转图像,并将每个结果与模板匹配(互相关)。 The one with the best match (largest cross-correlation value) has the appropriate rotation. 具有最佳匹配(最大互相关值)的那个具有适当的旋转。 If you need to have a very precise rotation estimation, you can do a second set of angles close to the best choice in the first set. 如果您需要进行非常精确的旋转估计,则可以在第一组中选择接近最佳选择的第二组角度。

Cross-correlation is cheap and easy to compute, and leads to high precision translation estimates (the Fourier Mellin method makes extensive use of it). 互相关是便宜且易于计算的,并且导致高精度的翻译估计(傅里叶梅林方法广泛使用它)。 Don't just find the pixel with the largest value in the cross-correlation output, you can fit a parabola to the few pixels around this one and use the location of the maximum of the fitted parabola. 不要只是在互相关输出中找到具有最大值的像素,您可以将抛物线拟合到该像素周围的几个像素,并使用拟合抛物线的最大值的位置。 This leads to sub-pixel estimates of translation. 这导致转换的子像素估计。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM