在全球范围内,我的任务是确定两个 .jpg 文件的相似性/不相似性。 下面我将更详细地描述该过程。我有五个(实际上还有更多)模板 .jpg 文件。我有一个新的 .jpg 文件,我必须将其与每个模板 .jpg 文件相匹配才能做出决定 - 新的 .jpg 文件是否与任何模板 .jpg 文件相似。
在我的例子中关联整个文件是一个坏主意,因为错误很大。所以我想出了一种方法,将新文件“切割”成12个相等的部分(片段)(即分成12个.jpg文件),并在模板中搜索每个单独的片段。
为此,我使用了教程https://docs.opencv.org/4.x/dc/dc3/tutorial_py_matcher.html
但问题是新的 .jpg 文件中的片段与模板的匹配极其错误。
下面的文档作为一个新文档(示意性地我将其切成12个部分,即我收到一个文档作为输入,但我将这个文档切成12个部分(片段)(即12个新文件))
接下来,看看我的代码。其要点是我获取 12 个片段中的每一个并在模板中搜索该片段
def match_slices_in_template(path_template):
directory_in_str = 'slices'
directory = os.fsencode(directory_in_str)
img1 = cv.imread(path_template, cv.IMREAD_GRAYSCALE) # queryImage
good = []
for slice_image in os.listdir(directory):
print(slice_image)
filename = os.fsdecode(slice_image)
img2 = cv.imread(f'slices/{filename}', cv.IMREAD_GRAYSCALE) # trainImage
# Initiate SIFT detector
sift = cv.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# BFMatcher with default params
bf = cv.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
# Apply ratio test
for m, n in matches:
if m.distance < 0.3 * n.distance:
good.append([m])
# cv.drawMatchesKnn expects list of lists as matches.
img3 = cv.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3), plt.show()
print(match_slices_in_template('Bill_1.jpg'))
但是搜索结果完全不正确,看看 matplotlib 构建的一些示例图
在我的示例中,事实上,这两个文件是不同的(尽管它们有很多共同点)。但该程序认为它们非常相似。
所以你可能明白我问题的本质了:如何提高算法的性能,使其比现在更准确地确定相似性/差异性
我写了一个示例来指导您依靠文本而不是文档之间的结构相似性进行分类。
我的文件夹是这样组织的:
我有两张训练图像和三张测试图像。训练图像位于模板文件夹中,我要分类的图像位于 toBeClassified 文件夹中。这是我的输入图像:
现在我们已经解决了这个问题,这是一个可以从图像中获取文本的函数:
def text_from_image(filename):
'''
Load image as grayscale by using cv2.irmead(_, 0)
extract text using pytesseract, we use the text for classification
'''
imGray = cv2.imread(filename, 0)
text = pytesseract.image_to_string(imGray)
return text
现在我们有了文本,我们可以将其与一些标签一起使用,作为训练响应来训练模型:
texts = [text_from_image("Templates/"+filename) for filename in os.listdir("Templates/")] # get the texts
labels = ["Shipping", "Invoice"] # label the texts
这些行用于获取模型并在模板上训练它:
model = make_pipeline(TfidfVectorizer(), MultinomialNB()) # init the model
model.fit(texts, labels) # fit the model
现在我们可以采用两种方法,要么我们对概率最高的类别感到满意,要么我们预测概率。
第一种方法:
def classify_with_model(filename, model):
'''
We pass the filename of the images to be classified and the model to get the
class with the highest probability, not very good but good for now
'''
text = text_from_image(filename)
category = model.predict([text])[0]
return category
如果我们循环测试图像,我们会得到:
for filename in os.listdir("toBeClassified/"): # loop through the test images
category = classify_with_model("toBeClassified/"+filename, model) # get the class with the highest prob
print(f'Document {filename} is classified as {category}') # print out results
# Document Bill1.png is classified as Invoice
# Document Shipping1.jpg is classified as Shipping
# Document ShippingInvoice1.png is classified as Invoice
注意最后一张图片,是我在网上找到的混合图片。因此,在某些情况下查看概率非常重要。就像 SVM 学习者的笑话一样: 一个健康的人去看医生进行癌症筛查。医生使用最先进的 SVM 机器学习模型来识别不同类型的癌症,准确率达到 100%。测试结束后,医生回来说:“好消息和坏消息。好消息:我们的模型非常适合诊断每种类型的癌症。坏消息:它不知道如何说你是健康的。” “
无论如何,我没有时间幽默,也没有时间尝试幽默。我们以概率为依据:
def classify_with_probability(filename, model):
'''
This is to classify with probability, a bit better to see the confidences
'''
text = text_from_image(filename)
probabilities = model.predict_proba([text])[0] # get probabilities for each class
categories = model.classes_ # get the class labels
bestMatchIdx = probabilities.argmax() # index of the highest probability
bestMatch = categories[bestMatchIdx] # class with the highest probability
confidence = probabilities[bestMatchIdx] # probability of the best match
return bestMatch, dict(zip(categories, probabilities)) # return everythin
结果是:
for filename in os.listdir("toBeClassified/"):
category = classify_with_probability("toBeClassified/"+filename, model)
print(f'Document {filename} is classified as {category[0]} with the following confidence:')
print(category[1])
print("_______________________")
#Document Bill1.png is classified as Invoice with the following confidence:
#{'Invoice': 0.5693790615221315, 'Shipping': 0.4306209384778673}
#_______________________
#Document Shipping1.jpg is classified as Shipping with the following confidence:
#{'Invoice': 0.38458825025403914, 'Shipping': 0.6154117497459587}
#_______________________
#Document ShippingInvoice1.png is classified as Invoice with the following confidence:
#{'Invoice': 0.5519774748181495, 'Shipping': 0.4480225251818504}
#_______________________
在这里,我们也看到了概率,如果您想同时将图像分类为多个类别,例如发票和运输表格,这会很有帮助。
我希望这能以某种方式帮助您,正如我所说的,正如克里斯托夫提到的,在这些文档上使用功能将是一次疯狂的尝试。
进口:
import cv2
import pytesseract
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
import os
pytesseract.pytesseract.tesseract_cmd = "C:/Program Files/Tesseract-OCR/tesseract.exe"