├── requirements.txt ├── img ├── big.jpg └── small.jpg ├── output.jpg ├── .mkimage ├── image-20200611235323069.png ├── image-20200611235357312.png ├── image-20200612081116527.png └── image-20200612082712103.png ├── README.md └── main.py /requirements.txt: -------------------------------------------------------------------------------- 1 | pillow 2 | numpy 3 | -------------------------------------------------------------------------------- /img/big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/img/big.jpg -------------------------------------------------------------------------------- /output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/output.jpg -------------------------------------------------------------------------------- /img/small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/img/small.jpg -------------------------------------------------------------------------------- /.mkimage/image-20200611235323069.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/.mkimage/image-20200611235323069.png -------------------------------------------------------------------------------- /.mkimage/image-20200611235357312.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/.mkimage/image-20200611235357312.png -------------------------------------------------------------------------------- /.mkimage/image-20200612081116527.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/.mkimage/image-20200612081116527.png -------------------------------------------------------------------------------- /.mkimage/image-20200612082712103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CBaymax/TemplateMatching/HEAD/.mkimage/image-20200612082712103.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 模板匹配之SSDA算法 2 | 3 | # 配置环境 4 | 5 | 在`cmd/shell`中运行如下指令: 6 | 7 | ``` 8 | pip install -r requirements.txt 9 | ``` 10 | 11 | # 执行脚本 12 | 13 | 在img中放入需要匹配的大图和小图,名字必须为`big.jpg`和`small.jpg` 14 | 15 | 在`cmd/shell`中运行如下指令: 16 | 17 | ```powershell 18 | # 切换到当前目录下,命令 19 | # cd C:\Users\chu\Desktop\模板匹配 20 | python main.py 21 | ``` 22 | 23 | 得到如下运行结果: 24 | 25 | ![image-20200611235323069](.mkimage/image-20200611235323069.png) 26 | 27 | 并且会弹出已经保存好的图片: 28 | 29 | ![image-20200611235357312](.mkimage/image-20200611235357312.png) 30 | 31 | 图片保存在当前目录下,即`output.jpg` 32 | 33 | # SSDA算法的原理 34 | 35 | 设**大图**的尺寸为`(MxN)`,**小图**尺寸为`(mxn)`。 36 | 37 | 在大图中选取`((M-m+1)x(N-n+1))`个框作为**先验框**,也就是说每一个先验框都表示以`(i,j)`为左上定点,宽高均与小图一致的一个框框。 38 | 39 | ![image-20200612081116527](.mkimage/image-20200612081116527.png) 40 | 41 | (上图是论文中的原图,尺寸不一样,仅作示意) 42 | 43 | ## 算法具体描述: 44 | 45 | 循环遍历所有先验框 46 | 47 | * 在小图中找一个随机点,在先验框中相对位置对应一个点。两个点计算误差,`误差=先验框中的对应点灰度值 - 先验框所有点的平均值 - 小图对应点的灰度值 + 小图所有点的平均值` 48 | * 当误差小于上限时,继续找其他随机点,累加误差,直到累加的误差超过上限。记录此时先验框需要的随机点个数 49 | 50 | 所有先验框中需要**随机点个数最多**的先验框即为最匹配的框。 51 | 52 | ## 原理 53 | 54 | * 如果先验框和小图不相似,那么对应点的误差很大,很少次数的随机点的误差相加,就能超过门限值。 55 | 56 | * 如果先验框和小图相似,那么每次找的对应随机点的误差值会非常小,需要迭代累加很多次才能达到门限 57 | 58 | ![门限与随机点个数的关系](.mkimage/image-20200612082712103.png) 59 | 60 | 61 | 62 | # 探讨误差门限的选取依据 63 | 64 | 误差门限体现在源代码`main.py`中的第40行代码中: 65 | 66 | 即下面的数字150 67 | 68 | ```python 69 | # 当误差超过一定的限度,记录下使用的随机点个数 70 | if loss >= 150: 71 | R[i,j] = n 72 | ``` 73 | 74 | 可以修改数字,选定合适的误差门限。 75 | 76 | 大体上范围应该在【50~500】之间: 77 | 78 | * 门限数字越小,每个先验框平均检测的随机点越少,速度越快,精度越差,太小可能会判错。 79 | 80 | * 门限数字越大,每个先验框测试的随机点越多,速度越慢,但是精度越高,极端情况,随机点采遍所有先验框中的点,成了完全遍历 81 | 82 | 83 | 84 | > 理论上是超过阈值就停止,但是如果小图是大图的一部分,那么有可能存在所有对应点误差为0的先验框,在该先验框里,无论迭代多少次,误差都达不到上限,会成为死循环。为了避免这种情况,采用的方法是:预先存了50个随机点,只会迭代这些随机点,如果超过次数还未超过阈值,直接进行下一个框的判断,那么`R[i,j] = 50`,即每个框的计数上限是50。 85 | 86 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # 导入相应的库 2 | from PIL import Image,ImageDraw 3 | import numpy as np 4 | # 读取图像 5 | big_image = Image.open("./img/big.jpg") 6 | small_image = Image.open("./img/small.jpg") 7 | print("成功读取图像") 8 | # 将图片转换为灰度图 9 | big_image_rgb = big_image 10 | big_image = big_image.convert('L') 11 | small_image = small_image.convert('L') 12 | 13 | # 转换为矩阵格式 14 | big = np.array(big_image) 15 | small = np.array(small_image) 16 | 17 | print("打印图像尺寸:") 18 | print("big:",big.shape) 19 | print("small:",small.shape) 20 | #从small中随机取50个点,可能用不完,多了备用 21 | rand_point = [] 22 | for i in range(50): 23 | rand_point.append((np.random.randint(0,small.shape[0]-1) ,np.random.randint(0,small.shape[1]-1))) 24 | 25 | # 矩阵R 用来保存误差次数 26 | R = np.zeros([big.shape[0]-small.shape[0],big.shape[1]-small.shape[1]]) 27 | 28 | sMean = np.mean(small) # 计算small的均值 29 | print("正在进行计算,请稍后……") 30 | for i in range(R.shape[0]): 31 | for j in range(R.shape[1]): 32 | loss = 0 #误差 33 | mean = np.mean(big[i:i+small.shape[0],j:j+small.shape[1]]) # s[i,j]的均值 34 | for n in range(len(rand_point)): 35 | point = rand_point[n] #从备选的随机点中取出一个随机点 36 | # 计算并叠加误差 37 | # 误差 = |备选区域中随机点的灰度值-备选区域均值 - small中随机点的灰度值 + small均值 | 38 | loss += np.abs( big[i+point[0],j+point[1]] - mean - small[point[0],point[1]] + sMean) 39 | # 当误差超过一定的限度,记录下使用的随机点个数 40 | if loss >= 150 or n==len(rand_point)-1: 41 | R[i,j] = n 42 | break # 跳出n循环 43 | # 找到迭代次数最多的点,对应的索引即为小图对应左上角位置坐标 44 | index = np.unravel_index(R.argmax(), R.shape) 45 | 46 | print("左上角左边:",index) 47 | xy = [(index[1],index[0]),(index[1]+small.shape[1],index[0]+small.shape[0])] 48 | 49 | # 在big图中画出对应的位置 50 | big_image = big_image_rgb #将big还原为RGB图像 51 | draw = ImageDraw.Draw(big_image) 52 | draw.rectangle(xy,outline='red',width=3) 53 | big_image.show() 54 | print("已保存图像结果") 55 | big_image.save("output.jpg") 56 | --------------------------------------------------------------------------------