CV2 在图像上寻找模式
CV2 图像识别中的模式搜索
在这篇文章中,我使用了计算机视觉和神经网络来找到一百多年前草书中的一个单词。
在这个简短的示例中,我使用了CV2软件包,该软件包专注于计算机视觉,以解析包含草书文本的图像,并提取一个特定的单词,遵循使用Tensorflow / Keras训练的模型。
import cv2import numpy as npimport pandas as pdfrom google.colab.patches import cv2_imshowfrom statistics import meanimport tensorflow as tf
图像被读取并转换为双色调。
image = cv2.imread('test.jpg')#转换为灰度图像image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)cv2_imshow(image)
图像以张量的形式在后端进行解释,每个像素由三个点组成。这三个点表示颜色的饱和度。
image
现在,我使用inRange函数将每个像素设置为二进制分类形式,即白色或黑色。然后,我必须反转这些值,因为机器学习的标准是白底黑字。
lower = np.array([0, 0, 120])upper = np.array([0, 0, 255])msk = cv2.inRange(image, lower, upper)msk = cv2.bitwise_not(msk)cv2_imshow(msk)
接下来的两个函数是腐蚀和膨胀。第一个函数用于去除可能是噪声的白点(想象沙子侵蚀岩石)。第二个函数用于扩展白色区域,创建模糊的模式;根据理论上的预期,一个连续的块应当对应一个词或接近一个词。
In [5]:
#必须定义一个核,这里的核是我们想要提取的形状(矩形,椭圆,圆等)。在这个例子中是矩形kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2))#然后我腐蚀白色点以去除噪声点,或者至少减少噪声点的数量rrmsk = cv2.erode(msk,kernel,iterations = 1)kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(10,5))rrmsk = cv2.dilate(msk, kernel, iterations=1)cv2_imshow(rrmsk)
这一部分仅作为说明进行,用于识别初始图像中的单词。我还创建了一个word对象,该对象存储了通过这种方式挖掘出的每个单词的图片的矩形。
contours, hierarchy = cv2.findContours(rrmsk, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)minw = 50minh = 10image = cv2.imread('test.jpg')image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)cleancontours = []words = []for contour in contours: x,y,w,h = cv2.boundingRect(contour) if ((w>=minw) & (h>=minh)): cleancontours.append(contour) word = image[y-5:y+h+5,x-5:x+w+5] words.append(word)cleancontours = tuple(cleancontours)imageC = cv2.imread('test.jpg')imageC = cv2.cvtColor(imageC, cv2.COLOR_BGR2HSV)cv2.drawContours(imageC, cleancontours, -1, (0,255,0), 3)cv2_imshow(imageC)
我按照之前的步骤处理这些词语。
image = cv2.imread('test.jpg')image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)lower = np.array([0, 0, 150])upper = np.array([0, 0, 255])masks = []for word in words: if len(word) > 0: mask = cv2.inRange(word, lower, upper) mask = cv2.bitwise_not(mask) masks.append(mask)
在这里,我将处理正样本训练样本。我将在图像中寻找单词“Garcia”。我收集了同一人写的相同单词的其他图像样本。我使用之前描述的方式清理了这些图片,然后将它们转换为相同的大小(所有摄入样本的平均值)。这里显示的第三个图像是所有16个图像的平均值。
lower = np.array([0, 0, 150])upper = np.array([0, 0, 255])samples = ['sample1.jpg','sample2.jpg','sample3.jpg','sample4.jpg','sample5.jpg','sample6.jpg','sample7.jpg','sample8.jpg','sample9.jpg','sample10.jpg','sample11.jpg','sample12.jpg','sample13.jpg','sample14.jpg','sample15.jpg','sample16.jpg']train = []hs = []ws = []for sample in samples: im = cv2.imread(sample) im = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) im = cv2.inRange(im, lower, upper) im = cv2.bitwise_not(im) height, width = im.shape hs.append(height) ws.append(width) train.append(im)from statistics import meanhh = int(mean(hs))+1ww = int(mean(ws))+1trainr = []for im in train: im = cv2.resize(im, (ww, hh), interpolation = cv2.INTER_CUBIC) trainr.append(im)cv2_imshow(trainr[2])cv2_imshow(trainr[1])meanimg = np.mean(trainr, axis=0)cv2_imshow(meanimg)
我取平均图像并转换为二进制值。
lower = 80upper = 255meanimgT = cv2.inRange(meanimg, lower, upper)cv2_imshow(meanimgT)
在这一部分中,我取平均值并从文本中的每个单词中提取欧氏距离。让我们看看这种简单方法是否能产生良好的结果。
minh = min(hs)-20minw = min(ws)-20maxh = max(hs)+20maxw = max(ws)+20testr = []for im in masks: height, width = im.shape if ((height >= minh) & (width >= minw) & (height <= maxh) & (width <= maxw)): im = cv2.resize(im, (ww, hh), interpolation = cv2.INTER_CUBIC) testr.append(im)a,b=meanimg.shapeleng = a*bdistc = []i = 0for im in testr: distance = np.sqrt(np.sum(np.square(meanimg - im)))/leng dist = pd.DataFrame({'position':[i], 'distance':[distance]}) i = i + 1 distc.append(dist)distc = pd.concat(distc, axis=0, ignore_index=True)distc.sort_values('distance')
最接近的单词实际上是Garcia。
cv2_imshow(testr[23])
在下一部分中,我会对七个文本图像进行相同的处理,与测试图像一样。目标是提取一些虚构的单词图片,作为负面训练集。
lower = np.array([0, 0, 120])upper = np.array([0, 0, 255])samples2 = ['dummy1.jpg','dummy2.jpg','dummy3.jpg','dummy4.jpg','dummy5.jpg','dummy6.jpg','dummy7.jpg']minw = 50minh = 10train2r = []for sample in samples2: im = cv2.imread(sample) im = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) im = cv2.inRange(im, lower, upper) im = cv2.bitwise_not(im) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2)) im = cv2.erode(im,kernel,iterations = 1) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(10,5)) im = cv2.dilate(im, kernel, iterations=1) image = cv2.imread(sample) image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) for contour in contours: x,y,w,h = cv2.boundingRect(contour) if ((w>=minw) & (h>=minh)): word = image[y-5:y+h+5,x-5:x+w+5] if (len(word) > 0): try: mask = cv2.inRange(word, lower, upper) mask = cv2.bitwise_not(mask) height, width = mask.shape if ((height >= minh) & (width >= minw) & (height <= maxh) & (width <= maxw)): mask = cv2.resize(mask, (ww, hh), interpolation = cv2.INTER_CUBIC) train2r.append(mask) except Exception as error: print(error)
这是Keras中神经网络模型的核心。在这种情况下,使用的是支持二维输入和一维二进制输出的层。由于训练集非常小,模型很快进入了过拟合区域,所以需要多次尝试。我通过使用较小的批量大小处理,并允许每个时期选择数据时的“摇摆”,并在不同样本上运行多次来解决这个问题。
from random import sampletrain2r_s = sample(train2r,30)training = trainr + train2r_sys = ([1] * len(trainr)) + ([0] * len(train2r_s))training = np.array(training)ys = np.array(ys)#需要输入的形状nn, xx, yy = np.array(training).shape#初始化neur = tf.keras.models.Sequential()#层neur.add(tf.keras.layers.Conv2D(5,3, activation='relu', input_shape=(xx,yy,1)))neur.add(tf.keras.layers.Conv2D(15,3, activation='tanh'))neur.add(tf.keras.layers.Conv2D(15,3, activation='tanh'))neur.add(tf.keras.layers.Flatten())neur.add(tf.keras.layers.Dense(10, activation='tanh'))#输出层neur.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))neur.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])neur.fit(np.array(training), np.array(ys), batch_size=15, epochs=3)
为了减少过拟合,我设定了一个循环,在每次训练模型时都使用不同的随机训练集。这得益于我有大量负面样本。还需要考虑到正面样本和负面样本之间的不平衡。
for i in list(range(0,30)): train2r_s = sample(train2r,30) training = trainr + train2r_s ys = ([1] * len(trainr)) + ([0] * len(train2r_s)) training = np.array(training) ys = np.array(ys) print("training part: " + str(i+1)) neur.fit(np.array(training), np.array(ys), batch_size=30, epochs=3)
在测试中运行,使用未见数据。
test_out = neur.predict(np.array(testr))pd.DataFrame(test_out).sort_values(0, ascending=False)
提取最高的测试图像(最接近1的值),实际上是单词Garcia。
check = 23print(test_out[check])cv2_imshow(testr[check])