Подтвердить что ты не робот

Сравните исходное изображение с банком известных изображений

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

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

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

findContours(maskMat, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

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

Я изучил соответствие OpenCV Pattern Matching, а также Templating, но с ненадежными результатами.

Я прошу совета относительно наилучшего подхода, который обеспечит результат, на который я надеюсь.

Я построил рекламное видео для моей университетской лекции, чтобы лучше показать, что делает приложение. Если вас это интересует, вы можете посмотреть его здесь. https://youtu.be/ngMUUIsLHoc

Спасибо заранее.

4b9b3361

Ответ 1

Прежде всего, это выглядит как отличное приложение. И для фантастической цели. Хорошая работа!

В зависимости от вашего вопроса, просмотрев видео, кажется, что один подход будет следующим:

1. Разместите каждую область рисования в (скажем) сетку 3x3 и разрешите каждой области содержать примитив, например вертикальную линию, горизонтальную линию, квадрат, круг, треугольник или вообще ничего. (Это зависит от управления двигателем вашего родителя-друга)

  1. Когда изображение будет завершено, определите эти примитивы и закодируйте (скажем) 9-значный ключ, который можно использовать для получения соответствующего изображения. Например, если треугольник равен T, квадрат равен S, а пустой - знак подчеркивания, тогда код для "Я иду домой" в соответствии с видео будет "_T__S ____".

  2. Когда запускается новое изображение, вы можете определить каждый примитив, как он нарисован, и использовать его для построения ключа поиска, где ключ имеет?? для неизвестных символов. Затем вы можете быстро получить все возможные совпадения из своей базы данных.

Например, если пользователь рисует треугольник в верхней, средней области, это будет закодировано как '? T???????' и это будет соответствовать "_T__S____", а также "_TT ______"

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

Для этого вы можете вычислить центр масс каждого примитива, отсортировать их слева направо, сверху вниз и затем сохранить некоторое представление их относительных положений, например. треугольник над квадратом может быть TVS, где V означает, что S ниже T, треугольник слева от квадрата может быть T

Надеюсь, это поможет.

Удачи!

Ответ 2

  • Получение изображений на основе эскиза. Существует довольно обширная литература по поиску реальных изображений с использованием эскизов в качестве запроса. Не совсем то, что вы хотите, но некоторые из тех же методов, вероятно, могут быть адаптированы для поиска эскизов с использованием эскиза в качестве запроса. Они могут даже работать без изменений.

  • Автоматическое распознавание рукописных китайских иероглифов (или аналогичных письменных систем). Существует также довольно много литературы по этому поводу; проблема аналогична, система письма эволюционировала из эскизов изображений, но значительно упрощена и стилизована. Попробуйте применить одни и те же методы.

  • Число, порядок, расположение отдельных линий, вероятно, более информативны, чем готовый эскиз в качестве изображения. Есть ли способ захватить это? Если ваш пользователь рисует стилусом, вы, вероятно, можете записывать траектории стилуса для каждой строки. Это будет иметь гораздо больше информационный контент, чем сам образ. Подумайте о том, кто-то рисует (например) автомобиль с закрытыми глазами. Двигаясь по траекториям, вы можете легко найти себе автомобиль. Из рисунка это может быть намного сложнее.

  • Если вы можете захватить строки, как описано, то проблема совпадения может быть в некоторой степени сводимой к задаче сопоставления некоторых строк изображения A с наиболее похожими линиями на изображении B (возможно, деформированными, смещение и т.д.). Они также должны иметь сходные отношения с другими строками: если (например) две линии пересекаются на изображении A, они должны пересекаться на изображении B и под тем же углом и в аналогичном месте по длине каждого. Чтобы быть более надежным, в идеале это должно иметь дело с вещами вроде двух строк в одном изображении, соответствующих одной (объединенной) линии в другой.

Ответ 3

Подход, основанный на описании

Основываясь на вашем видео, я предполагаю, что вас больше всего интересует сравнение линейных чертежей и подробных эскизов. Для линейных чертежей я мог бы придумать следующий подход, основанный на описании. Предложенное описание основано на соотношении и не будет зависеть от абсолютных размеров/размеров формы и должно также хорошо обрабатывать изменения.

Вычисление описания для фигуры

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

  • Обнаружить все строки изображения с помощью преобразования Hough. Очень подробный пример приведен ниже здесь. Рисованные линии могут быть не совсем прямыми и могут не обнаруживать их как одну линию, а несколько разных сегментов с разными склонами и перехватами. Вы можете либо ограничить эскиз только в виде линий, либо использовать подход линии подгонки, который будет соответствовать одной прямой линии для слегка нерегулярной линии, нарисованной рукой.

  • Сначала сортируйте строки, основанные на координатах X, а затем на Y-координатах.

  • Проведите строки слева направо, снизу до верхнего порядка (или любой фиксированный порядок) и вычислите следующие свойства для каждой строки.

    • Угол между текущей и следующей строками. (http://www.vitutor.com/geometry/line/angle_lines.html)
    • Соотношение длин двух соседних (текущих и следующих) строк.
    • Пересечение двух линий (0 - без пересечения, 1 - найденное пересечение)
    • Если линии пересекаются, вычислите отношение длин двух сегментов, созданных точкой пересечения в текущей строке. (Пример здесь https://www.youtube.com/watch?v=G17Nbb1d3Yc)
  • Создайте вектор функции, объединив вышеуказанные значения, например, если у эскиза три строки, вектор функций будет иметь вид { theta1, theta2, theta3, lengthRatio1, lengthRatio2, lengthRatio3, segmentRatio1, segmentRatio2, segmentRatio3 }

Соответствие запросов и форм базы данных

Как объяснялось выше, вы можете создавать представления вектора признаков для всех фигур в своей базе данных, а также для любого нового запроса. Теперь вы можете написать простую функцию, которая вычисляет расстояние между двумя векторами. Ниже приведен псевдокод.

int numLines; // computed using hough transform
vector<float> featureVector1(vec_size);
vector<float> featureVector1(vec_size);
...
// feature vectors computed as explained//

// Compute error between two vectors //

float angleError = 0.0f, lengthRatioError = 0.0, segmentRatioError = 0.0;

float diff = 0.0;    
// (0,numLines-1) elements of the vector are angles
for(int i=0; i < numLines; i++) {
    diff = abs(featureVector1[i] - featureVector2[i]);
    angleError += diff;
}

diff = 0.0;
// (numLines,2numLines-1) elements of the vector are length ratios
for(int i=numLines; i < 2*numLines-1; i++) {
    diff = abs(featureVector1[i] - featureVector2[i]);
    lengthRatioError += diff;
}

diff = 0.0;
// (2*numLines,3numLines-1) elements of the vector are segment ratios
// These values should be zero for no intersection
for(int i=numLines; i < 2*numLines-1; i++) {
    diff = abs(featureVector1[i] - featureVector2[i]);
    segmentRatioError += diff;
}

// Weights for errors - you should play around with these. 
float w1 = 1.0, w2 = 1.0, w3 = 1.0;
totalError = w1*angleError + w2*lengthRatioError + w3*segmentRatioError;

Сопоставьте функцию запроса со всеми функциями базы данных и найдите форму с минимальным расстоянием (totalError). Если расстояние ниже порогового значения, объявите совпадение, иначе объявите запрос в виде новой формы и добавьте в базу данных.

Ответ 4

Один метод, который может помочь, из-за того, как долго эта библиотека была вокруг и насколько хорошо она поддерживается, является использование ImageMagick. Позвольте мне продемонстрировать.

1 установите Pod, используя

pod 'ImageMagick', '6.8.8-9'

2 в viewController или любом другом представлении, которое вы хотите сравнить с изображениями, импортируйте следующее:

#import <wand/MagickWand.h>

4 создайте исключение в режиме легкого режима "Макро" для проверки ошибок без необходимости кодирования метода исключения каждый раз, когда вам нужно проверить исключения:

#define ThrowWandException(wand) { \
char * description; \
ExceptionType severity; \
\
description = MagickGetException(wand,&severity); \
(void) fprintf(stderr, "%s %s %lu %s\n", GetMagickModule(), description); \
description = (char *) MagickRelinquishMemory(description); \
exit(-1); \
}

3 создайте метод сравнения для сравнения двух изображений:

-(void)compareTwoImages:(UIImage*)firstImage secondImage:(UIImage*)secondImage comparitorSize:(size_t)comparitorSize {

    double diff1, diff2, diff3, diff4, diff5, diff6, diff7, diff8, diff9, diff10, diff11, diff12;

    MagickWandGenesis();
    MagickWand *magick_wand_1 = NewMagickWand();
    NSData * dataObject1 = UIImagePNGRepresentation(firstImage);
    MagickBooleanType status1;
    status1 = MagickReadImageBlob(magick_wand_1, [dataObject1 bytes], [dataObject1 length]);

    if (status1 == MagickFalse) {
        ThrowWandException(magick_wand_1);
    }

    MagickWandGenesis();
    MagickWand *magick_wand_2 = NewMagickWand();
    NSData * dataObject11 = UIImagePNGRepresentation(secondImage);
    MagickBooleanType status11;
    status11 = MagickReadImageBlob(magick_wand_2, [dataObject11 bytes], [dataObject11 length]);

    if (status11 == MagickFalse) {
        ThrowWandException(magick_wand_2);
    }

    MagickScaleImage(magick_wand_2, comparitorSize, comparitorSize);
    MagickScaleImage(magick_wand_1, comparitorSize, comparitorSize);

    MagickWandGenesis();
    MagickWand *magick_wand_3 = NewMagickWand();

    MagickCompareImages(magick_wand_1, magick_wand_2, UndefinedMetric, &diff1);
    MagickCompareImages(magick_wand_1, magick_wand_2, AbsoluteErrorMetric, &diff2);
    MagickCompareImages(magick_wand_1, magick_wand_2, MeanAbsoluteErrorMetric, &diff3);
    MagickCompareImages(magick_wand_1, magick_wand_2, MeanErrorPerPixelMetric, &diff4);
    MagickCompareImages(magick_wand_1, magick_wand_2, MeanSquaredErrorMetric, &diff5);
    MagickCompareImages(magick_wand_1, magick_wand_2, PeakAbsoluteErrorMetric, &diff6);
    MagickCompareImages(magick_wand_1, magick_wand_2, PeakSignalToNoiseRatioMetric, &diff7);
    MagickCompareImages(magick_wand_1, magick_wand_2, RootMeanSquaredErrorMetric, &diff8);
    MagickCompareImages(magick_wand_1, magick_wand_2, NormalizedCrossCorrelationErrorMetric, &diff8);
    MagickCompareImages(magick_wand_1, magick_wand_2, FuzzErrorMetric, &diff10);
    MagickCompareImages(magick_wand_1, magick_wand_2, UndefinedErrorMetric, &diff11);
    MagickCompareImages(magick_wand_1, magick_wand_2, PerceptualHashErrorMetric, &diff12);

    NSLog(@"UndefinedMetric: %.21f", diff1);
    NSLog(@"AbsoluteErrorMetric: %.21f", diff2);
    NSLog(@"MeanAbsoluteErrorMetric: %.21f", diff3);
    NSLog(@"MeanErrorPerPixelMetric: %.21f", diff4);
    NSLog(@"MeanSquaredErrorMetric: %.21f", diff5);
    NSLog(@"PeakAbsoluteErrorMetric: %.21f", diff6);
    NSLog(@"PeakSignalToNoiseRatioMetric: %.21f", diff7);
    NSLog(@"RootMeanSquaredErrorMetric: %.21f", diff8);
    NSLog(@"NormalizedCrossCorrelationErrorMetric: %.21f", diff9);
    NSLog(@"FuzzErrorMetric: %.21f", diff10);
    NSLog(@"UndefinedErrorMetric: %.21f", diff11);
    NSLog(@"PerceptualHashErrorMetric: %.21f", diff12);

    DestroyMagickWand(magick_wand_1);
    DestroyMagickWand(magick_wand_2);
    DestroyMagickWand(magick_wand_3);

    MagickWandTerminus();
}

5 наблюдайте вывод в отладчике (очевидно, вам понадобится другой метод, который использует какой-то монитор "threshhold", чтобы определить, какой уровень показывает "точное или близкое к совпадению", и то, что вы считаете матч себя). Кроме того, ОЧЕНЬ ВАЖНОЕ ЗАМЕЧАНИЕ, причина, по которой у меня есть переменная size_t для размера в вышеперечисленных методах, заключается в том, что вы не можете сравнивать изображения разных размеров, поэтому сначала вы должны изменить размер изображений, сравнимых с размером, который вы чувствуете "разумно", а затем оба изображения будут изменены с помощью ImageMagick, а затем сравните изображения:

Пример 1:

[self compareTwoImages:[UIImage imageNamed:@"book.png"]
           secondImage:[UIImage imageNamed:@"book.png"]
        comparitorSize:32];

[76233: 1364823] UndefinedMetric: 0.866871957624008593335

[76233: 1364823] AbsoluteErrorMetric: 0.000000000000000000000

[76233: 1364823] Среднее значениеAbsoluteErrorMetric: 0.000000000000000000000

[76233: 1364823] Среднее значениеErrorPerPixelMetric: 0.000000000000000000000 **

[76233: 1364823] Среднее значениеSquaredErrorMetric: 0.000000000000000000000

[76233: 1364823] PeakAbsoluteErrorMetric: 0.000000000000000000000

[76233: 1364823] PeakSignalToNoiseRatioMetric: inf

[76233: 1364823] RootMeanSquaredErrorMetric: 0.866871957624008593335

[76233: 1364823] NormalizedCrossCorrelationErrorMetric: +0,000000000000000000000

[76233: 1364823] FuzzErrorMetric: 0.000000000000000000000

[76233: 1364823] UndefinedErrorMetric: 0.866871957624008593335

[76233: 1364823] PerceptualHashErrorMetric: 0.000000000000000000000

Пример 2:

[self compareTwoImages:[UIImage imageNamed:@"book.png"]
             secondImage:[UIImage imageNamed:@"arrow.png"]
          comparitorSize:32];

[76338: 1368754] UndefinedMetric: 0.074585376822533272501

[76338: 1368754] AbsoluteErrorMetric: 795.000000000000000000000

[76338: 1368754] MeanAbsoluteErrorMetric: 0.314410045058480136504

[76338: 1368754] MeanErrorPerPixelMetric: 328395.000000000000000000000

[76338: 1368754] MeanSquaredErrorMetric: 0.245338692857198115149

[76338: 1368754] PeakAbsoluteErrorMetric: 1.000000000000000000000000

[76338: 1368754] PeakSignalToNoiseRatioMetric: 6.102339529383479899138

[76338: 1368754] RootMeanSquaredErrorMetric: 0.074585376822533272501

[76338: 1368754] NormalizedCrossCorrelationErrorMetric: 0.000000000000000000000

[76338: 1368754] FuzzErrorMetric: 0.571942529580490965913

[76338: 1368754] UndefinedErrorMetric: 0.074585376822533272501

[76338: 1368754] PerceptualHashErrorMetric: 1827.005561849247442296473

Здесь довольно много происходит математически. У меня нет времени объяснять все эти переменные, но достаточно сказать, что это сравнение между двумя изображениями использует некоторые очень известные методы для сравнения двух изображений. Вам придется забрать это отсюда и проверить статистику, чтобы настроить это по своему усмотрению и выбрать порог ошибки, который подходит для ваших целей.

Краткое описание:

ImageMagick - это обработанная битками библиотека обработки изображений, и хотя эти методы, перечисленные выше, являются своего рода "черным ящиком", это черная шкала, которая позволяет вам экономить время и перемещаться к чему-то, что использует OpenCV. ImageMagick уже установил некоторые очень хорошие алгоритмы обработки изображений, и эта история вместе с готовым "этим методом" работает, что является одним из самых больших преимуществ ImageMagick, учитывая то, что вам понадобится для разработки собственного образа библиотеку распознавания/обработки или методы. (кстати, я не связан с ImageMagick, я просто очень доволен продуктом, что все)

Методы, используемые в выше описанных выше методах Objective-C, - это методы, которые основаны на библиотеке ImageMagick для IOS. Вам нужно будет прочитать эти методы, но так, чтобы вы знали, они написаны на C, а не Objective-C, что означает, что некоторые из них являются чуждыми для запуска миллионного Image I/O или других библиотек обработки, Тем не менее, единственная часть, которую я вижу как трудно понять (при условии, что человек не знаком с кодом C), заключается в том, как понять, что такое "&". символ перед некоторыми переменными. Кроме этого, возникает проблема объявления и использования структур, но на это можно легко ответить, используя Stack Overflow, там много информации об этом прямо здесь.

Мне не нравится идея, что вам нужно написать собственную библиотеку или алгоритмы обработки изображений для такого приложения. Я думаю, что вы делаете очень хорошо для нуждающегося человека, и я думаю, что это поможет большому количеству людей, если вы можете заблокировать MVP, и я думаю, что использование ImageMagick поможет вам добраться туда намного раньше, чем необходимо переписать некоторые вещи, которые ImageMagick уже делает для ваших собственных целей.

Последнее замечание: ImageMagick построен на некоторых очень низких библиотеках C. Мне нужно было бы выполнить какое-то сравнение обработки, чтобы определить, насколько хорошо ImageMagick работает на устройствах IOS, чем что-то вроде Image I/O. Тем не менее, я чувствую, что эти две библиотеки обработки изображений используют одни и те же функции обработки и скорости обработки изображений. Исправьте меня, если кто-то уверен, что Image I/O определенно быстрее в тестах, но я просто говорю вам об этом, чтобы вы знали, что это НЕ ваша средняя установка POD, это надежный кусок машинного перевода, который по достоинству некоторые разработчики IOS создали версию IOS, в основном, используя CoccoaPods. Помимо этого, ImageMagick используется на всех вычислительных платформах и в основном является инструментом командной строки

Статистика, что означают цифры:

Здесь ресурсы, которые вам понадобятся, чтобы понять математику за статистикой, которую я показываю выше в отладчике:

Этот первый старый, но все же релевантный:

http://www.ict.griffith.edu.au/anthony/info/graphics/image_comparing

Этот, кажется, обновлен:

http://www.imagemagick.org/Usage/compare/

Если вы решите использовать эту библиотеку, удачи вам, мне нравится, где вы собираетесь с этим приложением, и ваше демо выглядит потрясающе!

EDIT: Я почти забыл рассказать вам о самой важной части здесь. Если статистика вернет все "нули" для всего, кроме "UndefinedMetric", "PeakSignalToNoiseRatioMetric", "RootMeanSquaredErrorMetric" и "UndefinedErrorMetric", тогда у вас БОЛЬШЕ, чем, вероятно, есть MATCH!

Ответ 5

Я настоятельно рекомендую использовать сумку слов.

Если вы хотите использовать модель, подобную этой, чтобы распознать байк, он будет разделять изображение на части (седло, каблуки, кадр, рулевое управление). Если он видит другое изображение, содержащее два слоя, седло и рамку, он, скорее всего, поместит картинку на велосипед (так как многие слова похожи). Проблема с этим подходом заключается в том, что пространственное отношение слов не учитывается.

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

edit: OpenCV даже имеет реализацию для пакета слов. Здесь можно найти здесь