<h3>概念</h3>
<p>我们可以使用<code>cv2.matchTemplate</code>方法来检测图像在另一个图像中的位置,但是对于第二组图像,您可以进行旋转。此外,我们还需要考虑颜色</p>
<p><code>cv2.matchTemplate</code>将接收图像、模板<em>(另一个图像)</em>和模板检测方法,并将返回灰度数组,其中灰度数组中最亮的点将是模板在该点处最有把握的点</p>
<p>我们可以在4个不同的角度使用模板,并使用产生最高置信度的模板。当我们检测到与模板匹配的可能点时,我们使用函数<em>(我们将自己定义)</em>来<strong>检查模板中最常见的颜色是否出现在我们检测到的图像中。如果不是,则忽略补丁,不管返回的可信度如何</p>
<h3>代码</h3>
<pre><code>import cv2
import numpy as np
def frequent_colors(img, vals=3):
colors, count = np.unique(np.vstack(img), return_counts=True, axis=0)
sorted_by_freq = colors[np.argsort(count)]
return sorted_by_freq[-vals:]
def get_templates(img):
template = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in range(3):
yield cv2.rotate(template, i)
def detect(img, template, min_conf=0.45):
colors = frequent_colors(template)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
conf_max = min_conf
shape = 0, 0, 0, 0
for tmp in get_templates(template):
h, w = tmp.shape
res = cv2.matchTemplate(img_gray, tmp, cv2.TM_CCOEFF_NORMED)
for y, x in zip(*np.where(res > conf_max)):
conf = res[y, x]
if conf > conf_max:
seg = img[y:y + h, x:x + w]
if all(np.any(np.all(seg == color, -1)) for color in colors):
conf_max = conf
shape = x, y, w, h
return shape
files = ["img1_2.png", "img2_2.png", "img3_2.png"]
template = cv2.imread("template2.png")
for name in files:
img = cv2.imread(name)
x, y, w, h = detect(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
</code></pre>
<h3>输出</h3>
<p><a href="https://i.stack.imgur.com/xxkGK.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xxkGK.png" alt="enter image description here"/></a></p>
<h3>解释</h3>
<ol>
<li>导入必要的库:</li>
</ol>
<pre><code>import cv2
import numpy as np
</code></pre>
<ol start=“2”>
<li>定义一个函数<code>frequent_colors</code>,它将接收图像并返回图像中最常见的颜色。可选参数<code>val</code>是返回多少颜色;如果<code>val</code>是<code>3</code>,则将返回3种最常见的颜色:</li>
</ol>
<pre><code>def frequent_colors(img, vals=3):
colors, count = np.unique(np.vstack(img), return_counts=True, axis=0)
sorted_by_freq = colors[np.argsort(count)]
return sorted_by_freq[-vals:]
</code></pre>
<ol start=“3”>
<li>定义一个函数<code>get_templates</code>,该函数将接收图像,并以4个不同角度(原始、顺时针90、180和逆时针90)生成图像<em>(灰度)</em>:</li>
</ol>
<pre><code>def get_templates(img):
template = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in range(3):
yield cv2.rotate(template, i)
</code></pre>
<ol start=“4”>
<li>定义一个函数<code>detect</code>,它将接收图像和模板图像,并返回图像上检测到的模板的边界框的x、y、w、h,对于这个函数,我们将使用前面定义的<code>frequent_colors</code>和<code>get_templates</code>函数。<code>min_conf</code>参数将是将检测分类为实际检测所需的最小置信度:</li>
</ol>
<pre><code>def detect(img, template, min_conf=0.45):
</code></pre>
<ol start=“5”>
<li>检测模板中最常见的三种颜色,并将它们存储在变量<code>colors</code>中。另外,定义主图像的灰度版本:</li>
</ol>
<pre><code> colors = frequent_colors(template)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
</code></pre>
<ol start=“6”>
<li>定义检测到的最大置信度的初始值,以及检测到的修补程序的初始值:</li>
</ol>
<pre><code> conf_max = min_conf
shape = 0, 0, 0, 0
</code></pre>
<ol start=“7”>
<li>以4个角度循环灰度模板,获得灰度模板的形状<em>(随着旋转改变形状)</em>,并使用<code>cv2.matchTemplate</code>方法获得图像上检测到的模板的灰度数组:</li>
</ol>
<pre><code> for tmp in get_templates(template):
h, w = tmp.shape
res = cv2.matchTemplate(img_gray, tmp, cv2.TM_CCOEFF_NORMED)
</code></pre>
<ol start=“8”>
<li>在置信度大于<code>conf_min</code>的检测模板的x,y坐标中循环,并将置信度存储在变量<code>conf</code>中。如果<code>conf</code>大于初始最大置信变量<em>(<code>conf_max</code>)</em>,则继续检测模板中所有三种最常见的颜色是否都存在于图像的补丁中:</li>
</ol>
<pre><code> for y, x in zip(*np.where(res > conf_max)):
conf = res[y, x]
if conf > conf_max:
seg = img[y:y + h, x:x + w]
if all(np.any(np.all(seg == color, -1)) for color in colors):
conf_max = conf
shape = x, y, w, h
</code></pre>
<ol start=“9”>
<li>最后,我们可以返回形状。如果在图像中未检测到模板,则形状将是为其定义的初始值,<code>0, 0, 0, 0</code>:</li>
</ol>
<pre><code> return shape
</code></pre>
<ol start=“10”>
<li>最后,循环遍历每个图像,并使用我们定义的<code>detect</code>函数获取边界框的x、y、w、h。使用<code>cv2.rectangle</code>方法在图像上绘制边界框:</li>
</ol>
<pre><code>files = ["img1_2.png", "img2_2.png", "img3_2.png"]
template = cv2.imread("template2.png")
for name in files:
img = cv2.imread(name)
x, y, w, h = detect(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
</code></pre>