如何检测图像的那一部分被移动了?
How to detect that PART of an image was shifted?
我有几张证件照片,我需要确定证件是否被更改过。
因此,此任务由两部分组成:1. 文本移位。 检测到特殊文本被移位:
注意铭文:TEST TEXT HERE
有一个缺口(在S
字母处)。
- 第二个任务:背景图像的类似移动,如下所示:
我的问题:
有一些方法可以检测到相同的变化吗?它们可以是水平的和垂直的。
我总是知道测试铭文(在第一个任务中),而且我总是有质量好的装饰品样本。我有一组照片需要检查。
我的解决方法。
1. 第一个想法:在每张照片上叠加正确的题词并检查其周围的像素颜色 - 如果有很多红色像素,则有偏移。所以我可以选择文本但不能选择图像。
2.关于图像我想到了傅立叶变换。如果图像上有间隙,则在同一坐标处进行函数跳转。但是我不知道这个方法的一些实现。
首先我知道,这类问题对于 SO 来说太宽泛了。
How to detect a shift between images
但是我已经找到类似的了,所以希望它不会被关闭!
第二点——我对任何算法都持开放态度——包括经典和机器学习。
我已经按照处理问题的方式制作了一个脚本。这可能不是最好的方法,但我希望这对您有所帮助,或者让您对如何进行有新的看法。
首先,我会将图像转换为 HSV 色彩空间,因为转换为二进制并不是最好的方法,因为 tekst 周围有很多噪音。转换后,您可以使用 cv2.inRange
和阈值提取文本。然后我搜索轮廓并将它们绘制在一个新的空白蒙版上。
下一步是执行开运算,将附近的轮廓合并为一个大轮廓。开运算后进行膨胀,去除左上角剩余的字符T。
接下来我将再次搜索轮廓并绘制一个边界矩形。如果轮廓是一个完美的正方形,那么您将看不到矩形,但是如果轮廓移动了,它将使矩形内部有两个较小的矩形(相反的颜色):
最后再次搜索具有阈值大小的轮廓并将其绘制在图像上。
结果:
代码:
# Import modules
import cv2
import numpy as np
# Read the image and transform to HSV colorspace.
img = cv2.imread('ID.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Extract the red text.
lower_red = np.array([150,150,50])
upper_red = np.array([200,255,255])
mask_red = cv2.inRange(hsv, lower_red, upper_red)
# Search for contours on the mask.
_, contours, hierarchy = cv2.findContours(mask_red,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Mask for processing.
mask = np.ones(img.shape, np.uint8)*255
# Iterate through contours and draw them on mask.
for cnt in contours:
cv2.drawContours(mask, [cnt], -1, (0,0,0), -1)
# Perform opening to unify contours.
kernel = np.ones((15,15),np.uint8)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# Perform dilation to remove some noises.
kernel_d = np.ones((2,2),np.uint8)
dilation = cv2.dilate(opening,kernel_d,iterations = 1)
# Seraching for contours on the new mask.
gray_op = cv2.cvtColor(dilation, cv2.COLOR_BGR2GRAY)
_, threshold_op = cv2.threshold(gray_op, 150, 255, cv2.THRESH_BINARY_INV)
_, contours_op, hierarchy_op = cv2.findContours(threshold_op, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate through contours and draw a bounding rectangle.
for cnt in contours_op:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(threshold_op,(x,y),(x+w,y+h),(255,255,255),1)
# Seraching for contours again on the new mask.
_, contours_f, hierarchy_f = cv2.findContours(threshold_op, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate through contours and add size for thresholding out the rest.
for cnt in contours_f:
size = cv2.contourArea(cnt)
if size < 1000:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),-1)
# Display the result.
cv2.imshow('img', img)
第二张图不行,因为图片的复杂度不同。
在第二张图片上,我会尝试扩大它,这样唯一剩下的就是底部的 3 条线(或者在移位的情况下是 4 条线)并计算轮廓的数量。如果存在 4 个轮廓,则将其移动。
或者第二张图片的第二种方法。使用 cv2.reactangle()
将等高线拆分为 3 个单独的等高线,并计算从它们到您创建的线的最小距离。这样你就可以计算出即使拆分发生在底线移动之前。
第二张图片的代码:
# Import modules
import cv2
import numpy as np
import scipy
from scipy import spatial
# Read image
img_original = cv2.imread('ID_sec4.png')
img = img_original.copy()
# Get height and weight of the image
h1, w1, ch = img.shape
# Draw line somewhere in the bottom of the image
cv2.line(img, (10, h1-10), (w1-10, h1-10), (0,0,0), 3)
# Search for contours and select the biggest one (the pattern)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
_, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt_big = max(contours, key=cv2.contourArea)
# Draw white rectangles to seperate the extreme left and extreme right side of the contour
x, y, w, h = cv2.boundingRect(cnt_big)
cv2.rectangle(img,(0,0),(x+20,y+h+20),(255,255,255),2)
cv2.rectangle(img,(w1,0),(w, y+h+20),(255,255,255),2)
# Search for contours again
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
_, cnts, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate over the list and calculate minimum distance from the line (line you drew)
# and contours then make a bounding box if it fits the criteria
for i in cnts:
reshape1 = np.reshape(i, (-1,2))
ref = max(cnts, key=lambda cnts: cv2.boundingRect(cnts)[1])
reshape2 = np.reshape(ref, (-1,2))
tree = spatial.cKDTree(reshape2)
mindist, minid = tree.query(reshape1)
distances = np.reshape(mindist, (-1,1))
under_min = [m for m in distances if 1 < m < 70]
if len(under_min) > 1:
x, y, w, h = cv2.boundingRect(i)
cv2.rectangle(img_original,(x-10,y-10),(x+w+10,y+h+10),(0,255,0),2)
# Display the result
cv2.imshow('img', img_original)
结果:
希望对您有所帮助。干杯!
我有几张证件照片,我需要确定证件是否被更改过。 因此,此任务由两部分组成:1. 文本移位。 检测到特殊文本被移位:
注意铭文:TEST TEXT HERE
有一个缺口(在S
字母处)。
- 第二个任务:背景图像的类似移动,如下所示:
我的问题: 有一些方法可以检测到相同的变化吗?它们可以是水平的和垂直的。 我总是知道测试铭文(在第一个任务中),而且我总是有质量好的装饰品样本。我有一组照片需要检查。
我的解决方法。 1. 第一个想法:在每张照片上叠加正确的题词并检查其周围的像素颜色 - 如果有很多红色像素,则有偏移。所以我可以选择文本但不能选择图像。 2.关于图像我想到了傅立叶变换。如果图像上有间隙,则在同一坐标处进行函数跳转。但是我不知道这个方法的一些实现。
首先我知道,这类问题对于 SO 来说太宽泛了。 How to detect a shift between images 但是我已经找到类似的了,所以希望它不会被关闭! 第二点——我对任何算法都持开放态度——包括经典和机器学习。
我已经按照处理问题的方式制作了一个脚本。这可能不是最好的方法,但我希望这对您有所帮助,或者让您对如何进行有新的看法。
首先,我会将图像转换为 HSV 色彩空间,因为转换为二进制并不是最好的方法,因为 tekst 周围有很多噪音。转换后,您可以使用 cv2.inRange
和阈值提取文本。然后我搜索轮廓并将它们绘制在一个新的空白蒙版上。
下一步是执行开运算,将附近的轮廓合并为一个大轮廓。开运算后进行膨胀,去除左上角剩余的字符T。
接下来我将再次搜索轮廓并绘制一个边界矩形。如果轮廓是一个完美的正方形,那么您将看不到矩形,但是如果轮廓移动了,它将使矩形内部有两个较小的矩形(相反的颜色):
最后再次搜索具有阈值大小的轮廓并将其绘制在图像上。
结果:
代码:
# Import modules
import cv2
import numpy as np
# Read the image and transform to HSV colorspace.
img = cv2.imread('ID.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Extract the red text.
lower_red = np.array([150,150,50])
upper_red = np.array([200,255,255])
mask_red = cv2.inRange(hsv, lower_red, upper_red)
# Search for contours on the mask.
_, contours, hierarchy = cv2.findContours(mask_red,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Mask for processing.
mask = np.ones(img.shape, np.uint8)*255
# Iterate through contours and draw them on mask.
for cnt in contours:
cv2.drawContours(mask, [cnt], -1, (0,0,0), -1)
# Perform opening to unify contours.
kernel = np.ones((15,15),np.uint8)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# Perform dilation to remove some noises.
kernel_d = np.ones((2,2),np.uint8)
dilation = cv2.dilate(opening,kernel_d,iterations = 1)
# Seraching for contours on the new mask.
gray_op = cv2.cvtColor(dilation, cv2.COLOR_BGR2GRAY)
_, threshold_op = cv2.threshold(gray_op, 150, 255, cv2.THRESH_BINARY_INV)
_, contours_op, hierarchy_op = cv2.findContours(threshold_op, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate through contours and draw a bounding rectangle.
for cnt in contours_op:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(threshold_op,(x,y),(x+w,y+h),(255,255,255),1)
# Seraching for contours again on the new mask.
_, contours_f, hierarchy_f = cv2.findContours(threshold_op, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate through contours and add size for thresholding out the rest.
for cnt in contours_f:
size = cv2.contourArea(cnt)
if size < 1000:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),-1)
# Display the result.
cv2.imshow('img', img)
第二张图不行,因为图片的复杂度不同。
在第二张图片上,我会尝试扩大它,这样唯一剩下的就是底部的 3 条线(或者在移位的情况下是 4 条线)并计算轮廓的数量。如果存在 4 个轮廓,则将其移动。
或者第二张图片的第二种方法。使用 cv2.reactangle()
将等高线拆分为 3 个单独的等高线,并计算从它们到您创建的线的最小距离。这样你就可以计算出即使拆分发生在底线移动之前。
第二张图片的代码:
# Import modules
import cv2
import numpy as np
import scipy
from scipy import spatial
# Read image
img_original = cv2.imread('ID_sec4.png')
img = img_original.copy()
# Get height and weight of the image
h1, w1, ch = img.shape
# Draw line somewhere in the bottom of the image
cv2.line(img, (10, h1-10), (w1-10, h1-10), (0,0,0), 3)
# Search for contours and select the biggest one (the pattern)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
_, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt_big = max(contours, key=cv2.contourArea)
# Draw white rectangles to seperate the extreme left and extreme right side of the contour
x, y, w, h = cv2.boundingRect(cnt_big)
cv2.rectangle(img,(0,0),(x+20,y+h+20),(255,255,255),2)
cv2.rectangle(img,(w1,0),(w, y+h+20),(255,255,255),2)
# Search for contours again
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
_, cnts, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Iterate over the list and calculate minimum distance from the line (line you drew)
# and contours then make a bounding box if it fits the criteria
for i in cnts:
reshape1 = np.reshape(i, (-1,2))
ref = max(cnts, key=lambda cnts: cv2.boundingRect(cnts)[1])
reshape2 = np.reshape(ref, (-1,2))
tree = spatial.cKDTree(reshape2)
mindist, minid = tree.query(reshape1)
distances = np.reshape(mindist, (-1,1))
under_min = [m for m in distances if 1 < m < 70]
if len(under_min) > 1:
x, y, w, h = cv2.boundingRect(i)
cv2.rectangle(img_original,(x-10,y-10),(x+w+10,y+h+10),(0,255,0),2)
# Display the result
cv2.imshow('img', img_original)
结果:
希望对您有所帮助。干杯!