Я пытаюсь понять, какие технологии мне нужны для обработки изображений для персонажей.
В частности, в этом примере мне нужно извлечь хэштаг, который был обведен. Вы можете увидеть это здесь:
Любая реализация будет очень полезной.
Я пытаюсь понять, какие технологии мне нужны для обработки изображений для персонажей.
В частности, в этом примере мне нужно извлечь хэштаг, который был обведен. Вы можете увидеть это здесь:
Любая реализация будет очень полезной.
хотя я думаю, что могут быть более простые способы. OpenCV - это библиотека с открытым исходным кодом, используемая для создания приложений для просмотра компьютеров, а Tesseract - механизм OCR с открытым исходным кодом.
Прежде чем начать, позвольте мне кое-что уточнить: это не круг, его округленный прямоугольник.
Я делюсь исходным кодом приложения, которое я написал, чтобы продемонстрировать, как проблема может быть решена, а также некоторые советы о том, что происходит. Этот ответ не должен никому обучать цифровой обработке изображений, и ожидается, что у читателя будет минимальное понимание в этой области.
Я расскажу очень кратко о том, что делают большие разделы кода. Большая часть следующего фрагмента кода взята из squares.cpp, пример приложения, которое поставляется с OpenCV для обнаружения квадратов в изображениях.
#include <iostream>
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
// angle: helper function.
// Finds a cosine of angle between vectors from pt0->pt1 and from pt0->pt2.
double angle( cv::Point pt1, cv::Point pt2, cv::Point pt0 )
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
// findSquares: returns sequence of squares detected on the image.
// The sequence is stored in the specified memory storage.
void findSquares(const cv::Mat& image, std::vector<std::vector<cv::Point> >& squares)
{
cv::Mat pyr, timg;
// Down-scale and up-scale the image to filter out small noises
cv::pyrDown(image, pyr, cv::Size(image.cols/2, image.rows/2));
cv::pyrUp(pyr, timg, image.size());
// Apply Canny with a threshold of 50
cv::Canny(timg, timg, 0, 50, 5);
// Dilate canny output to remove potential holes between edge segments
cv::dilate(timg, timg, cv::Mat(), cv::Point(-1,-1));
// find contours and store them all as a list
std::vector<std::vector<cv::Point> > contours;
cv::findContours(timg, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
for( size_t i = 0; i < contours.size(); i++ ) // Test each contour
{
// Approximate contour with accuracy proportional to the contour perimeter
std::vector<cv::Point> approx;
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
// Square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if( approx.size() == 4 &&
fabs(cv::contourArea(cv::Mat(approx))) > 1000 &&
cv::isContourConvex(cv::Mat(approx)) )
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// Find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
// If cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if( maxCosine < 0.3 )
squares.push_back(approx);
}
}
}
// drawSquares: function draws all the squares found in the image
void drawSquares( cv::Mat& image, const std::vector<std::vector<cv::Point> >& squares )
{
for( size_t i = 0; i < squares.size(); i++ )
{
const cv::Point* p = &squares[i][0];
int n = (int)squares[i].size();
cv::polylines(image, &p, &n, 1, true, cv::Scalar(0,255,0), 2, CV_AA);
}
cv::imshow("drawSquares", image);
}
Итак, наша программа начинается с:
int main(int argc, char* argv[])
{
// Load input image (colored, 3-channel)
cv::Mat input = cv::imread(argv[1]);
if (input.empty())
{
std::cout << "!!! failed imread()" << std::endl;
return -1;
}
// Convert input image to grayscale (1-channel)
cv::Mat grayscale = input.clone();
cv::cvtColor(input, grayscale, cv::COLOR_BGR2GRAY);
//cv::imwrite("gray.png", grayscale);
Как выглядит оттенки серого:
// Threshold to binarize the image and get rid of the shoe
cv::Mat binary;
cv::threshold(grayscale, binary, 225, 255, cv::THRESH_BINARY_INV);
cv::imshow("Binary image", binary);
//cv::imwrite("binary.png", binary);
Что выглядит двоичный:
// Find the contours in the thresholded image
std::vector<std::vector<cv::Point> > contours;
cv::findContours(binary, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
// Fill the areas of the contours with BLUE (hoping to erase everything inside a rectangular shape)
cv::Mat blue = input.clone();
for (size_t i = 0; i < contours.size(); i++)
{
std::vector<cv::Point> cnt = contours[i];
double area = cv::contourArea(cv::Mat(cnt));
//std::cout << "* Area: " << area << std::endl;
cv::drawContours(blue, contours, i, cv::Scalar(255, 0, 0),
CV_FILLED, 8, std::vector<cv::Vec4i>(), 0, cv::Point() );
}
cv::imshow("Countours Filled", blue);
//cv::imwrite("contours.png", blue);
Как выглядит синий:
// Convert the blue colored image to binary (again), and we will have a good rectangular shape to detect
cv::Mat gray;
cv::cvtColor(blue, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, binary, 225, 255, cv::THRESH_BINARY_INV);
cv::imshow("binary2", binary);
//cv::imwrite("binary2.png", binary);
Какая двоичная выглядит следующим образом:
// Erode & Dilate to isolate segments connected to nearby areas
int erosion_type = cv::MORPH_RECT;
int erosion_size = 5;
cv::Mat element = cv::getStructuringElement(erosion_type,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size));
cv::erode(binary, binary, element);
cv::dilate(binary, binary, element);
cv::imshow("Morphologic Op", binary);
//cv::imwrite("morpho.png", binary);
Какая двоичная выглядит следующим образом:
// Ok, let go ahead and try to detect all rectangular shapes
std::vector<std::vector<cv::Point> > squares;
findSquares(binary, squares);
std::cout << "* Rectangular shapes found: " << squares.size() << std::endl;
// Draw all rectangular shapes found
cv::Mat output = input.clone();
drawSquares(output, squares);
//cv::imwrite("output.png", output);
Какой вывод выглядит следующим образом:
Хорошо! Мы решили первую часть проблемы, которая находила округленный прямоугольник. Вы можете видеть на изображении выше, что прямоугольная форма была обнаружена, а зеленые линии были нарисованы поверх исходного изображения для образовательных целей.
Вторая часть намного проще. Он начинается с создания ROI (Region of Interest) в исходном изображении, поэтому мы можем обрезать изображение в области внутри закругленного прямоугольника. Как только это будет сделано, обрезанное изображение будет сохранено на диске в виде файла TIFF, которое затем будет отправлено в Tesseract, сделав его волшебным:
// Crop the rectangular shape
if (squares.size() == 1)
{
cv::Rect box = cv::boundingRect(cv::Mat(squares[0]));
std::cout << "* The location of the box is x:" << box.x << " y:" << box.y << " " << box.width << "x" << box.height << std::endl;
// Crop the original image to the defined ROI
cv::Mat crop = input(box);
cv::imshow("crop", crop);
//cv::imwrite("cropped.tiff", crop);
}
else
{
std::cout << "* Abort! More than one rectangle was found." << std::endl;
}
// Wait until user presses key
cv::waitKey(0);
return 0;
}
Как выглядит обрезка:
Когда это приложение завершает работу, оно создает на диске файл с именем cropped.tiff
. Перейдите в командную строку и вызовите Tesseract для обнаружения текста на обрезанном изображении:
tesseract cropped.tiff out
Эта команда создает файл с именем out.txt
с обнаруженным текстом:
Tesseract имеет API, который можно использовать для добавления функции OCR в ваше приложение.
Это решение не является надежным, и вам, вероятно, придется внести некоторые изменения здесь и там, чтобы заставить его работать для других тестовых случаев.
Существует несколько альтернатив: Реализация Java OCR
Они упоминают следующие инструменты:
И еще несколько.
Этот список ссылок также может быть полезен: http://www.javawhat.com/showCategory.do?id=2138003
Обычно такой тип задачи требует большого количества проб и испытаний. Вероятно, лучший инструмент гораздо больше зависит от ваших входных данных, чем любого другого.
Вы можете проверить эту статью: http://www.codeproject.com/Articles/196168/Contour-Analysis-for-Image-Recognition-in-C
Он поставляется с математической теорией и реализацией на С# (к сожалению, но не так много, чтобы переписать, если вы решили реализовать его в java) + opencv. Таким образом, вам придется использовать Visual Studio и перестроить свою версию opencv, если вы хотите проверьте его, но он того стоит.
OCR хорошо работает со сканированным документом. То, что вы имеете в виду, - это обнаружение текста в общих изображениях, для чего требуются другие методы (иногда OCR используется как часть потока)
Я не знаю никаких "готовых к производству" реализаций.
для общей информации попробуйте google scientar с: "обнаружение текста в изображениях"
конкретный метод, который хорошо работал у меня, - "преобразование ширины штриха" (SWT) это не сложно реализовать, и я считаю, что там также доступны некоторые версии, доступные в Интернете.