图像处理:将扫描的图像映射到具有许多相同功能的模板图像上 问题描述我们的方法问题

问题描述

问题描述

我们正在尝试将扫描的图像与模板图像进行匹配:

  • 扫描图像示例:

Example of a scanned image

Example of a template image

模板图像包含大小和轮廓属性关闭,向左打开和向右打开)变化的心形集合。模板中的每个心脏都是一个感兴趣的区域,我们知道该区域的位置,大小和轮廓类型。我们的目标是将扫描的图像匹配到模板,以便我们可以在扫描的图像中提取这些ROI。在扫描的图像中,这些心脏中的一些被交叉,它们将被呈现给分类器,以决定它们是否被交叉。

我们的方法

在关于PyImageSearch的教程之后,我们试图使用ORB查找匹配的关键点(下面包括代码)。这应该使我们能够计算透视变换矩阵,将扫描的图像映射到模板图像上。

我们尝试了一些预处理步骤,例如阈值化和/或模糊扫描图像。我们还尝试了尽可能增加功能的最大数量

问题

方法不适用于我们的图像集。可以在下图中看到:

in the following image

似乎很多关键点都映射到模板图像的错误部分,因此无法正确计算变换矩阵。

ORB是在此处使用的正确技术,还是算法的参数可以微调以提高性能?感觉好像我们错过了一些应该使它起作用的简单操作,但是我们真的不知道如何继续使用这种方法:)。

我们正在尝试一种替代技术,该技术可以使扫描与各个心脏形状互相关。这应该给出在心脏位置具有峰值的图像。通过围绕这些峰绘制边界框,我们希望将该边界框映射到模板的边界框上(我可以根据要求对此进行详细说明)

任何建议都将不胜感激!

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


# Preprocessing parameters
THRESHOLD = True
BLUR      = False

# ORB parameters
MAX_FEATURES = 4048
KEEP_PERCENT = .01
SHOW_DEBUG = True

# Convert both the input image and template to grayscale
scan_file = r'scan.jpg'
template_file = r'template.jpg'

scan     = cv.imread(scan_file)
template = cv.imread(template_file)

scan_gray     = cv.cvtColor(scan,cv.COLOR_BGR2GRAY)
template_gray = cv.cvtColor(template,cv.COLOR_BGR2GRAY)

if THRESHOLD:
    _,scan_gray     = cv.threshold(scan_gray,127,255,cv.THRESH_BINARY)
    _,template_gray  = cv.threshold(template_gray,cv.THRESH_BINARY)
    
if BLUR:
    scan_gray = cv.blur(scan_gray,(5,5))
    template_gray = cv.blur(template_gray,5))

# Use ORB to detect keypoints and extract (binary) local invariant features
orb = cv.ORB_create(MAX_FEATURES)

(kps_template,desc_template) = orb.detectAndCompute(template_gray,None)
(kps_scan,desc_scan)         = orb.detectAndCompute(scan_gray,None)

# Match the features
#method  = cv.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING
#matcher = cv.DescriptorMatcher_create(method)
#matches = matcher.match(desc_scan,desc_template)
bf = cv.BFMatcher(cv.norM_HAMMING)
matches = bf.match(desc_scan,desc_template)

# Sort the matches by their distances
matches = sorted(matches,key = lambda x : x.distance)

# Keep only the top matches
keep = int(len(matches) * KEEP_PERCENT)
matches = matches[:keep]


if SHOW_DEBUG:
    matched_visualization = cv.drawMatches(scan,kps_scan,template,kps_template,matches,None)
    plt.imshow(matched_visualization)

解决方法

基于@it_guy提供的说明,我试图仅使用扫描的图像找到所有交叉的心。我将不得不在更多图像上尝试该算法,以检查这种方法是否可以推广。

  1. 对扫描的图像进行二值化。

    data = json['data'] != null ? List<Data>.from(json["data"].map((x) => Data.fromJson(x))) : null;
    

enter image description here

  1. 执行dilation,以缩小心形轮廓中的小间隙,并代表十字形曲线。注意-可以通过在多幅图像中运行算法并找到最合适的结构元素来更改结构元素gray_image = cv2.cvtColor(rgb_image,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(gray_image,180,255,cv2.THRESH_BINARY_INV)
np.ones((1,2),np.uint8

enter image description here

  1. 找到图像中的所有轮廓。轮廓包括所有心形和底部的三角形。我们通过在轮廓的高度和宽度上设置约束以对其进行过滤来消除其他轮廓(如点)。此外,我们还使用contour hierachies来消除十字形心形中的内部轮廓。
closing_original = cv2.morphologyEx(original_binary,cv2.MORPH_DILATE,np.ones((1,np.uint8)). 

enter image description here

  1. 我们遍历每个滤波后的轮廓。

与正常心脏的轮廓-

enter image description here

轮廓交叉的心-

enter image description here

让我们观察一下这两种类型的心脏之间的区别。如果我们查看正常心脏内部从白到黑像素和黑到白像素(从上到下)的过渡,我们会看到,对于大多数图像列,此类过渡的数量为4。边框-2个过渡,底部边框-2个过渡)

白到黑像素-(255, 255,0 ,0,0)

黑白像素-(0, 0,255 ,255,255)

但是,对于交叉的心脏,大多数列的转换数必须为6,因为交叉的曲线/线会在心脏内部添加两个以上的转换(首先是黑白色,然后是白色-变黑)。因此,在所有具有大于或等于4个此类过渡的图像列中,如果40%以上的列具有6个过渡,则给定轮廓表示交叉轮廓。结果-

enter image description here

代码-

contours_original,hierarchy_original = cv2.findContours(closing_original,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

可以在更多图像上测试该方法以找到其准确性。