尝试使打印的链接可点击

2021-08-09 04:59:58

在本文中,我们将开始解决通过智能手机摄像头制作可点击的印刷链接(即书籍或杂志中的链接)的问题。我们将使用 TensorFlow 2 对象检测 API 来训练自定义对象检测器模型,以在文本图像(即智能手机相机流)中查找子字符串(如 https://)的位置和边界框。每个链接的文本(https:// 边界框的右侧延续)将使用 Tesseract 库进行识别。本文不会涉及识别部分,但您可以在 links-detector 存储库中找到该应用程序的完整代码示例。 📝 在 GitHub 上打开 links-detector 存储库以查看应用程序的完整源代码。 ⚠️ 目前该应用程序处于实验 Alpha 阶段,存在许多问题和限制。所以在这些问题得到解决之前,不要把你的期望值提高得太高🤷🏻‍。此外,本文的目的更多是关于学习如何使用 TensorFlow 2 对象检测 API,而不是想出一个生产就绪的模型。如果本文中的 Python 代码块在此平台上缺乏正确的格式,请随时阅读 GitHub 上的文章我是一名软件工程师,在我自己的时间里,我将学习机器学习作为一种爱好。但这还不是问题。

我最近买了一本关于机器学习的印刷书,当我阅读前几章时,我在文本中遇到了许多看起来像 https://tensorflow.org/ 或 https://some-url.com 的印刷链接/which/may/be/even/longer?and_with_params=true。我看到了所有这些链接,但由于它们被打印出来,我无法点击它们(谢谢,帽子!)。要访问这些链接,我需要在浏览器的地址栏中逐个字符地键入它们,这非常烦人且容易出错。所以,我在想,如果类似于二维码检测,我们会尝试“教”智能手机(1)检测和(2)为我们识别打印的链接并使它们可点击?这样,您只需单击一次,而不是多次击键。 “点击”打印链接的操作复杂度从 O(N) 到 O(1)。正如我之前提到的,我只是将机器学习作为一种爱好来学习。因此,本文的目的更多是关于学习如何使用 TensorFlow 2 对象检测 API,而不是想出一个生产就绪的应用程序。在 iPhone X 这样的设备上,检测和识别过程应该具有接近实时的性能(即每秒 0.5-1 帧)。这意味着整个检测 + 识别过程最多需要 2 秒(相当可以忍受)至于业余项目)。仅应支持浅色背景(即白色或浅灰色)上的深色文本(即黑色或深灰色)。目前只支持 https:// 链接(如果我们的模型无法识别 http://、ftp://、tcp:// 或其他类型的链接,也可以)。

在后端进行链接检测和识别,并将响应发送回客户端。 💚 检测性能不受客户端设备限制。我们可以通过水平(添加更多实例)和垂直(添加更多内核/GPU)扩展服务来加快检测速度。 💚 模型可能更大,因为不需要上传到客户端。在客户端下载 ~10Mb 模型可能没问题,但加载 ~100Mb 模型对于客户端的网络和应用程序 UX(用户体验)来说可能是一个大问题。 💚 可以控制谁在使用模型。模型受 API 保护,因此我们可以完全控制其调用方/客户端。 💔 系统复杂性增长。应用程序技术堆栈从 JavaScript 发展到 JavaScript + Python。我们需要注意自动缩放。 💔 应用程序的离线模式是不可能的,因为它需要互联网连接才能工作。 💔 客户端和服务器之间过多的 HTTP 请求可能会在某个时候成为瓶颈。想象一下,如果我们想要提高检测的性能,比方说,从每秒 1 帧到 10+ 帧。这意味着每个客户端每秒将发送 10 个以上的请求。对于 10 个并发客户端,每秒已经有 100 多个请求。在这种情况下,HTTP/2 双向流和 gRPC 可能很有用,但我们将回到这里增加的系统复杂性。

💔 系统变得更加昂贵。几乎所有来自 Pros 部分的积分都需要支付。 💚 系统不那么复杂。我们不需要设置服务器、构建 API 以及向系统引入额外的 Python 堆栈。 💚 离线模式是可能的。该应用程序不需要互联网连接即可工作,因为模型已完全加载到设备上。因此,可能会构建渐进式 Web 应用程序 (PWA) 来支持这一点。 💚 系统“有点”自动缩放。您拥有的客户端越多,它们带来的内核和 GPU 就越多。但这不是一个合适的缩放解决方案(更多关于下面的缺点部分)。 💚 系统更便宜。我们只需要一个用于静态资产(HTML、JS、CSS、模型文件等)的服务器。这可能是免费的,比方说,在 GitHub 上。 💚 每秒发送到服务器端的 HTTP 请求数量不断增加,这没有问题。 💔 只能进行水平缩放(每个客户端都有自己的 CPU/GPU)。垂直缩放是不可能的,因为我们无法影响客户端的设备性能。因此,我们无法保证对低性能设备进行快速检测。

💔 无法保护模型的使用并控制模型的调用者/客户端。每个人都可以下载模型并重新使用它。 💔 客户端设备的电池消耗可能会成为一个问题。为了使模型工作,它需要计算资源。因此,当应用程序运行时,客户可能不满意他们的 iPhone 越来越热。由于该项目的目的更多是关于学习而不是提出生产就绪的解决方案,因此我决定采用从客户端为模型提供服务的第二种选择。这使整个项目的成本大大降低(实际上,使用 GitHub 可以免费托管它),而且我可以将更多精力放在机器学习上,而不是自动缩放后端基础设施上。好的,所以我们决定采用无服务器解决方案。现在我们有一个来自相机流的图像作为输入,看起来像这样:第一个也是最明显的方法是通过使用识别图像的整个文本来解决光学字符识别 (OCR) 任务,比方说, Tesseract.js 库。它返回段落、文本行和文本块的边界框以及识别的文本。然后我们可以尝试使用这样的正则表达式从识别的文本行或文本块中提取链接(示例在 TypeScript 上): const URL_REG_EXP = / https?:\/\/(www\.)?[-a -zA-Z0-9@:%._+~#=]{2,256}\.[az]{2,4}\b([-a-zA-Z0-9@:%_+.~#? &/=]*) / gi ; const extractLinkFromText = (text : string ) : string | null => { const urls : string [] |空 = 文本。匹配 ( URL_REG_EXP ) ; if ( !urls || !urls .length ) { return null ; } 返回网址 [ 0 ] ; };

💔 问题是识别+检测时间可能会从 2 秒到 20+ 秒不等,具体取决于文本的大小、图像上“看起来像文本的东西”的数量、图像质量和其他因素.因此,要达到每秒 0.5-1 帧以让用户体验至少接近实时将非常困难。 💔 此外,如果我们考虑一下,我们要求图书馆为我们识别图像中的整个文本,即使它可能只包含一两个链接(即只有约 10% 的文本可能对我们有用)我们),或者它甚至可能根本不包含链接。在这种情况下,这听起来像是在浪费计算资源。如果我们在链接文本识别之前使用一些额外的“顾问”算法,我们可以使 Tesseract 工作得更快。这个“顾问”算法应该检测但不识别图像上每个链接的最左边位置(如果有的话)。这将使我们能够通过遵循以下规则来加速识别部分:如果图像不包含任何链接,我们根本不应调用 Tesseract 检测/识别。如果图像确实有链接,那么我们需要让 Tesseract 仅识别图像中包含链接的那些部分。我们不想花时间识别不包含链接的不相关文本。将在 Tesseract 之前发生的“顾问”算法应该以恒定的时间工作,无论图像质量如何,或者图像上是否存在文本。它也应该非常快,并且在不到 1 秒的时间内检测到链接最左边的位置,以便我们能够满足“接近实时”的要求(即在 iPhone X 上)。 💡 那么,如果我们使用另一个对象检测模型来帮助我们找到图像中所有出现的 https:// 子字符串(每个安全链接都有这个前缀,不是吗)怎么办?然后,在文本中有这些 https:// 边界框,我们可以提取它们的右侧延续并将它们发送到 Tesseract 进行文本识别。

您可能会注意到 Tesseract 需要做的工作要少得多,以防万一它有一些关于链接可能位于何处的提示(请参阅两张图片上的蓝色框的数量)。所以现在的问题是我们应该选择哪种对象检测模型以及如何重新训练它以支持自定义 https:// 对象的检测。训练一个新的对象检测模型在我们的上下文中不是一个合理的选择,因为以下原因:💔我们很可能无法收集数十万张带有链接的书籍的标记图像(我们可能会虽然生成它们,但稍后会详细介绍)。因此,与其创建新模型,不如教一个现有的对象检测模型为我们进行自定义对象检测(进行迁移学习)。在我们的例子中,“自定义对象”将是带有 https:// 文本的图像。这种方法有以下好处: 💚 数据集可能要小得多。我们不需要收集成千上万的标记图像。相反,我们可能会做大约 100 张图片并手动标记它们。这是因为模型已经在 COCO 数据集等通用数据集上进行了预训练,并且已经学会了如何提取通用图像特征。 💚 训练过程会更快(GPU 上的分钟/小时而不是几天/几周)。同样,这是因为较小的数据集(较小的批次)和较少的可训练参数。

我们可以从 TensorFlow 2 Detection Model Zoo 中选择现有模型,该模型提供了一系列在 COCO 2017 数据集上预训练的检测模型。现在它包含大约 40 个模型变体可供选择。为了在自定义数据集上重新训练和微调模型,我们将使用 TensorFlow 2 对象检测 API。 TensorFlow 对象检测 API 是一个构建在 TensorFlow 之上的开源框架,可以轻松构建、训练和部署对象检测模型。如果您点击 Model Zoo 链接,您将找到每个模型的检测速度和准确度。当然,我们希望在选择模型时在检测速度和准确性之间找到适当的平衡。但是在我们的例子中可能更重要的是模型的大小,因为它将被加载到客户端。存档模型的大小可能在 ~20Mb 到 ~1Gb 之间变化很大。以下是几个例子: 💚 它使用 MobileNet v2 网络作为特征提取器,该网络针对移动设备的使用进行了优化,以降低能耗。 💚 无论图像内容如何,​​它都会一次性对整个图像和其中的所有对象进行对象检测(不涉及区域提议步骤,这使得检测速度更快)。

💔 虽然它不是最准确的模型(一切都是权衡⚖️)。模型名称编码了一些重要特征,如果需要,您可以阅读更多相关信息: 在本文中,我们将把 Tensorflow 2 对象检测 API 作为 Python 包安装。如果您在 Google Colab(推荐)或 Jupyter 中进行试验,这会很方便。对于这两种情况,都不需要本地安装,您可以直接在浏览器中进行试验。如果您更喜欢通过 Docker 安装对象检测 API,您也可以遵循官方文档。如果您在 API 安装或数据集准备期间遇到问题,请尝试通读 TensorFlow 2 对象检测 API 教程,其中为该过程添加了许多有用的细节。克隆到“模型”...远程:枚举对象:2301,done.remote:计数对象:100%(2301/2301),done.remote:压缩对象:100%(2000/2000),done.remote:总计2301 (delta 561), 重用 922 (delta 278), 打包重用 0Receiving objects: 100% (2301/2301), 30.60 MiB | 13.90 MiB/s,完成。解析增量:100% (561/561),完成。由于某些依赖项错误,最后一步可能会失败。在这种情况下,您可能需要运行 pip install 。 ——再安静一次。

TensorFlow 对象检测 API 已安装!您现在可以使用 API 提供的脚本进行模型推理、训练或微调。让我们从 TensorFlow Model Zoo 下载我们选择的 ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8 模型,并检查它如何进行一般对象检测(从 COCO 数据集中检测类的对象,如“猫”、“狗”、“汽车”等)。我们将使用 get_file() TensorFlow 帮助程序从 URL 下载存档模型并将其解压缩。将张量流导入为 tf 导入路径libMODEL_NAME = 'ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8'TF_MODELS_BASE_PATH = 'http://download.tensorflow.org/models/object_detection/tf2/202007111/202007111/202007111/202007111/20200711_model_cache_model_cache_model_cache_model_cache_model_fpnlite_640x640_coco17_tpu-8'TF_MODELS_BASE_PATH = 'http://download.tensorflow.org/models/object_detection/tf2 = TF_MODELS_BASE_PATH + model_name + '.tar.gz' model_dir = tf .keras .utils .get_file ( fname =model_name , origin =model_url , untar = True , cache_dir =pathlib .Path (cache_folder ) .absolute ( ) ) return model_dir #开始模型 download.model_dir = download_tf_model (MODEL_NAME , CACHE_FOLDER ) 打印 (model_dir ) pipeline.config 文件包含模型的检测设置。当我们需要微调模型时,我们稍后会回到这个文件。目前,该模型可以检测 90 个 COCO 数据集类的对象,如汽车、鸟、热狗等。让我们看看模型在包含这些类对象的一些通用图像上的表现如何。

Object Detection API 已经为我们定义了一套完整的 COCO 标签(类)。 import os # 导入对象检测 API 助手。 from object_detection .utils import label_map_util # 加载 COCO 标签数据(类名和索引关系)。 def load_coco_labels() : # Object Detection API 已经为我们定义了一套完整的 COCO 类。 label_map_path = os .path .join ( 'models/research/object_detection/data' , 'mscoco_complete_label_map.pbtxt' ) label_map = label_map_util .load_labelmap (label_map_path ) # 类 ID 到类名的映射。 category = label_map_util .convert_label_map_to_categories ( label_map , max_num_classes =label_map_util .get_max_label_map_index (label_map ) , use_display_name = True ) category_index = label_map_util .create_category_index (categories) #Class Name to Class ID mapping label_map_dict = label_map_util .get_label_map_dict (label_map , use_display_name = True ) return category_index , label_map_dict # 加载 COCO 标签.coco_category_index , coco_label_map_dict = load_coco_labels ( ) print ( 'coco_category_index :'coco_category_index :'coco_category_index_codict_index:'coco_category_index_codict_map_index:'coco_categ_c : {'id': 1, 'name': 'person'}, 2: {'id': 2, 'name': 'bicycle'}, ... 90: {'id': 90, 'name' : 'toothbrush'},}coco_label_map_dict:{ 'background': 0, 'person': 1, 'bicycle': 2, 'car': 3, ... 'toothbrush': 90,} 我们需要创建一个检测该函数将使用我们下载的预训练模型进行对象检测。 import tensorflow as tf # 导入对象检测 API 助手。 from object_detection .utils import config_util from object_detection .builders import model_builder # 为特定模型和特定模型的检查点生成检测函数 def detection_fn_from_checkpoint (config_path , checkpoint_path ) : # 构建模型。 pipeline_config = config_util .get_configs_from_pipeline_file (config_path) model_config = pipeline_config [ 'model' ] model = model_builder .build ( model_config =model_config , is_training = False , ) # 恢复检查点。 ckpt = tf .compat .v2 .train .Checkpoint (model =model ) ckpt .restore (checkpoint_path ) .expect_partial ( ) # 这是一个进行检测的函数。 @tf .function def detect_fn(图像):图像,形状=模型.预处理(图像)预测_dict=模型.预测(图像,形状)检测=模型.后处理(预测_dict,形状)返回检测,预测_dict,tf .reshape(形状) , [ - 1 ] ) return detect_fninference_detect_fn = detection_fn_from_checkpoint ( config_path =os .path .join ( 'cache' , 'datasets' , MODEL_NAME , 'pipeline.config' ) , checkpoint_path =os .path .join ('datasets' , 'datasets' ' , MODEL_NAME , 'checkpoint' , 'ckpt-0' ) , ) 为此,让我们将图像保存到我们项目的 inference/test/ 文件夹中。如果您使用的是 Google Colab,则可以创建此文件夹并手动上传图像。 import matplotlib .pyplot as plt %matplotlib inline # 创建一个只有一个 image.inference_ds = tf .keras .p​​reprocessing .image_dataset_from_directory ( directory = 'inference' , image_size = ( 640 , 640 ) , batch_size = 1 , False shuffle 的 TensorFlow 数据集, label_mode = None ) # Numpy 版本的数据集.inference_ds_numpy = list (inference_ds .as_numpy_iterator() ) # 你可以像这样预览数据集中的图像.plt .figure (figsize = ( 14 , 14 ) ) for i , image in枚举(inference_ds_numpy):plt.subplot(2,2,i+1)plt.imshow(image[0].astype(“uint8”))plt.axis(“off”)plt.show()

现在我们已准备好运行检测。 inference_ds_numpy[0] 数组以 Numpy 格式存储第一张图像的像素数据。 box = detections ['detection_boxes'] .numpy ()scores = detections ['detection_scores'] .numpy()classes = detections ['detection_classes'] .numpy()num_detections = detections ['num_detections'] .numpy() [0 ] 打印('boxes.shape:',boxes .shape)打印('scores.shape:',scores.shape)打印('classes.shape:',classes.shape)打印('num_detections:',num_detections)模型为我们做了 100 次检测。但这并不意味着它在图像上找到了 100 个对象。这意味着该模型有 100 个槽,并且它最多可以在单个图像上检测 100 个对象。每个检测都有一个分数,代表模型对其的置信度。每个检测的边界框都存储在 box 数组中。模型关于每次检测的分数或置信度是......