Мы все бывали в такой ситуации раньше - когда мы пытаемся вспомнить, где что-то видели или читали, но не можем. Иногда то, что мы пытаемся вспомнить, может быть в текстовой форме, поэтому это легко найти, но что, если бы это было на изображении?

В этой статье я опишу, как создать приложение для поиска по всем изображениям в папке фрагмента текста с помощью C ++.

Получение текста в изображении

Чтобы определить, содержит ли изображение фрагмент текста, нам нужно сначала извлечь весь текст из изображения, а затем выполнить поиск в извлеченном тексте для запроса. Чтобы извлечь текст из изображения, мы будем использовать код из этой статьи, где я описал, как выполнить оптическое распознавание символов (OCR) на изображении, которое даст текст на изображении. Чтобы повторно использовать код, я создал для него файл заголовка (названный basicOCR.h):

#ifndef BASIC_OCR_H
#define BASIC_OCR_H
#include <string>
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include <opencv2/opencv.hpp>
std::string getText(std::string imagePath);
 
#endif

где getText () - функция, извлекающая текст из изображения. Он принимает путь к изображению в качестве аргумента, выполняет распознавание текста на изображении и возвращает текст в изображении.

Теперь мы можем повторно использовать эту функцию getText (), включив basicOCR.h:

#include <iostream>
#include "basicOCR.h"
using namespace std;
bool contains(string str1, string str2) {
 // Returns true if str1 contains str2, else false
return (str1.find(str2) != string::npos);
}
bool imgContainsText(string imgPath, string text) {
 // Returns true if the text in the image contains the specified text, else false
 
string imgText = getText(imgPath);
return contains(imgText, text);
}

Выше мы включили заголовочный файл basicOCR, затем мы определили две функции - contains (), которая просто определяет, содержит ли одна строка другую, и imgContainsText (), которая получает текст в изображении с помощью функции getText (), а затем проверяет, содержит ли этот текст запрос, который ищет пользователь.

Поиск нескольких изображений

Мы хотим, чтобы это приложение могло искать все изображения в папке по тексту, введенному пользователем. Чтобы помочь нам поместить изображения в папку, мы будем использовать библиотеку dirent.h.

#include <dirent.h>
...
int main(int argc, char **argv) {
 DIR *dir;
 struct dirent *ent;
 char *imFolder = argv[1];
 string fileName;
if ((dir = opendir(imFolder)) != NULL)
 {
  while ((ent = readdir(dir)) != NULL)
  {
   fileName = ent->d_name;
   // Do something...
  }
  closedir(dir);
 }
 else
 {
  /* could not open directory */
  perror("");
  return EXIT_FAILURE;
 }

В приведенном выше коде мы позволяем пользователю передавать путь к папке, содержащей изображения, в качестве аргумента командной строки, argv[1], затем мы вызываем opendir () - функцию из библиотеки dirent - по указанному пути, который возвращает указатель на каталог.

Если результат не NULL, мы вызываем readdir () для каталога, который возвращает коллекцию всех файлов и папок в каталоге. Свойство ent->d_name дает нам имя каждого файла или папки.

Однако, если результат NULL, мы возвращаем сообщение об ошибке, в котором говорится, что мы не можем открыть каталог.

С помощью вышесказанного мы можем получить все файлы в папке, но нам нужны только файлы, содержащие изображения. Итак, мы создадим следующие функции:

bool endsWith(string s, string endString) {
 // Returns true if s ends with endString, else false
 
 return s.length() >= endString.length() && s.substr(s.length() - endString.length()) == endString;
}
bool isImage(string fileName) {
 // Returns true if the file is a GIF, PNG or a JPG image, else false
return endsWith(fileName, ".gif") || endsWith(fileName, ".png") || endsWith(fileName, ".jpg");
}

Первая функция, ndsWith (), помогает нам определить, заканчивается ли строка другой строкой, а вторая функция, isImage (), помогает проверить, соответствует ли имя файла файл заканчивается расширением изображения. Для простоты мы будем работать только с тремя расширениями изображений: GIF, PNG и JPG.

Теперь мы можем искать все изображения в папке по заданному запросу:

int main(int argc, char **argv) {
...
string query, results = "";
cout << "Enter the text to search for: ";
cin >> query;
while ((ent = readdir(dir)) != NULL)
  {
   fileName = ent->d_name;
   if (isImage(fileName) && imgContainsText(imFolder + fileName, query)) {
    results += fileName + "\n";
   }
  }
closedir(dir);
...

}

и выводим результат в консоль:

if (results.length() > 0) {
  cout << "The text was found in: \n" + results;
 }
 else
 {
  cout << "Sorry, the text was not found in any image.";
 }

Подведение итогов

Мы успешно создали приложение, которое может искать по всем изображениям в папке некоторый фрагмент текста, который предоставит пользователь. Теперь давайте протестируем приложение. Ниже приведены изображения, которые мы будем искать:

Вот вывод консоли, когда я запускаю приложение и ищу текст «кому»:

Enter the text to search for: to
The text was found in:
asleep.jpg
wallpaper.jpg

и если я ищу «текст», я получаю:

Enter the text to search for: text
The text was found in:
asleep.jpg
random.jpg

Однако, если я ищу «средний», которого нет ни на одном из изображений, я получаю:

Enter the text to search for: Medium
Sorry, the text was not found in any image.

Это показывает, что приложение работает должным образом. Спасибо, что зашли так далеко. Вы можете найти полный код этого проекта здесь. Если у вас есть предложения или комментарии, пожалуйста, оставьте их ниже.