Примеры программирования OpenCV для Android: все 1–6
Примеры программирования OpenCV для Android: все 1–6

1. Готово

В этой главе я, Воля, шаг за шагом объясняю, как начать использовать. OpenCV Развивать зрительное восприятие Android приложение。

Компьютерное зрение с открытым исходным кодомOpenCV)программное обеспечение Библиотекаиметь 2500 Несколько алгоритмов оптимизации; Библиотека включает полный набор классических и современных алгоритмов компьютерного зрения и машинного обучения. Стоит на складе уже десять лет,и на основеРаспространение программного обеспечения БерклиBSD)许Может证выпускать,используйте 户易Использовать модификацию。

OpenCV Было скачано более 700 Тысячи раз и было Google,Yahoo,Microsoft,Intel,IBM,Sony и Honda И другие известные компании используют. Кроме того, OpenCV Поддержка различных настольных и мобильных операционных систем, включая Windows,Linux,Mac OSX,Android и iOS。

существоватьв этой книге,Мы Воляиспользовать Применимо к Android из OpenCV,этода Можетсуществовать Android запуститьиз на операционной системе OpenCV изодинчасть.

Я Воля представляю два варианта подготовки Установи. Во-первых, если вы начинаете новую установку Android, рекомендуется начать с Tegra Android развивать СумкаTADP)начинать。 Другая ситуация — вручную установить и запустить OpenCV из Android Требуется для каждого компонента. Если вы ранее установили Android среде разработки, этот вариант можно выбрать. Мы рассмотрим следующие темы:

  • Установите пакет разработки Tegra для Android
  • Вручную установите OpenCV и среду разработки Android.
  • учитьсяРодной развивающий комплектNDK)изметод работы
  • Создайте свой первый проект Android с помощью OpenCV.

Установите пакет разработки Tegra для Android

NVIDIA Опубликовано TADP, так что подготовка среды разработки Android становится гладкой.

NVIDIA Опубликовано TADP 3.0r4 версия для поддержки Android SDK(23.0.2),NDK(r10c)и OpenCV for Tegra 2.4.8.2, этот общепринятыйиз OpenCV4Android SDK, пройдено Tegra Были расширены конкретные оптимизации.

Загрузите и установите TADP

чтобы получить TADP,Пожалуйста, посетитеэта страницаи следуйте инструкциям, чтобыдлязарегистрироватьсяразвивать ВОЗ; Это бесплатное членство.

После активации членства войдите в систему и получите доступ к своей операционной системе. NVIDIA поддерживает следующие операционные системы:

  • Windows 64 бит
  • Mac OSX
  • Ubuntu Linux(32/64 Кусочек)

Сразу для меня,У меня на компьютере установлена ​​Windows 7 64 Кусочек., поэтому от начала существования сейчас все последующие шаги были проверены, и существование существует хорошо в этой операционной системе. Да,Если вы используете другую операционную систему,Я не ожидаю каких-либо серьезных изменений.

Уведомление

для Ubuntu Установка, ТАДП Воля Требуется, чтобы у вас былоrootпривилегия,поэтому Пожалуйста, убедитесь, что у вас есть。

После скачивания установщика TADP запустите его и выполните следующие действия:

  1. Прочитав и приняв лицензионное соглашение, следуйте инструкциям на экране.
  2. ты Волянуждатьсявыбирать Установитьдобрыйформа。 выберите «Определение»Установить и нажмите кнопку «Далее»:
  1. Как показано на рисунке, выберите компонент «Установитьиз» и нажмите кнопку «Далее»:
  1. Вам нужно назвать каталог «Установить» и скачать. Уведомление Пожалуйста, Уведомление: если вы уже установили «Установить», вы получите предупреждающее сообщение с предложением удалить предыдущую версию «Установить». хотеть卸载ранееиз Установить,Пожалуйста, перенеситеприезжатьранееиз Установить Каталог ибегатьtadp_uninstall.exe。 Иногда деинсталлятор не удаляет все. В этом случае вам необходимо вручную удалить предыдущую папку «Установить из содержимого».
  2. Теперь вы можете выбрать компонент. щелкнутьNextкнопка。
  3. Если вы используете прокси, вы можете ввести данные прокси; в противном случае,щелкнутьNextкнопка。
  4. Установить программу Воля начинает загрузку со всеми выбранными компонентами. Это может занять некоторое время, в зависимости от вашего интернет-соединения.
  5. После скачивания Заканчивать нажмите «Далее», чтобы начать установку выбранного компонента. Уведомление Иногда программа Установить окно Воля не отвечает. Не беда, через несколько минут Установка Воля продолжится в обычном режиме.
  6. выбиратьнеобходимыйиз Установитьпосле операции,ЗатемщелкнутьЗаканчиватькнопка。

Конфигурация после установки TADP

даиз,TADP Все будет скачано и установлено за вас; Однако вам все равно необходимо выполнить некоторую настройку после установки, чтобы убедиться, что все работает правильно.

Установите образ системы эмулятора

Если вы хотите использовать это SDK Платформа для Цель запуска эмулятора, необходимо для Установитьиз каждого Android SDK Платформа устанавливает образ системы.

Для этого просто выполните следующие простые шаги:

  1. изменятьприезжатьсуществовать Установить TADP часвыбиратьиз Установить Оглавление。
  2. Открыть SDK документпапка; существуют В этом случае,этодаandroid-sdk-windows
  3. бегать SDK Manager
  4. для У каждого есть Установитьиз AndroidX.X,дляэмуляторвыбиратьодин个образ системы,Например Образ системы ARM EABI V7a
  1. щелкнутьИнсталляционный пакет
  2. Прочтите и примите выбранный компонент лицензионного соглашения.
  3. щелкнутьУстановить

Теперь вы можете продолжить тестирование приложения на любом существующем эмуляторе Установить Цельиз.

Настройка Eclipse для использования с NDK

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

  1. отранее指定из Установить Оглавление Запустите Затмение.
  2. ОткрытьОкно | Настройки
  3. существуют в левой панели,Открыть Android-дерево
  4. выбиратьотметкадля NDK узел из Дерево.
  5. существоватьправая панель,щелкнутьПросматриватьивыбирать NDK Оглавление; Найдите его в своем каталоге «Волясуществовать Установить».
  6. щелкнутьNext

НДК-проверка

потому что OpenCV Библиотекадаиспользовать C/C++ писатьиз, поэтому проверьте свою среду да нормально проектировать Шаг да Убедитесь, что вы можете бегатьиспользовать собственный код из Android приложение:

  1. Запустите Затмение.
  2. от NDK Установить Оглавление(существоватьяизслучайдляC:\NVPACK\android-ndk-r10c\),отsamplesдокументпапкасерединаимпортироватьhello-jniПримерпроект,Сразу Нравитсяимпортироватьлюбойдругой Android То же, что проект.
  3. верно键щелкнутьHelloJniпроект。
  4. существоватьконтекстменюсередина,выбирать Android инструмент | добавить влокальная машинаподдерживать
  5. убеждаться Воля Библиотекаимянастраиватьдляhello-jni; По умолчанию он должен называться дляthis.
  6. использоватьтывыбиратьиз Цельзапускатьэмулятор。
  7. верно键щелкнутьпроект Просматриватьустройствосерединаизhello-jniпроект。 существоватьконтекстменюсередина,выбиратьбегатьдля | Android приложение

существуют консольные выводы,Должно быть.soдокументизсписок; это NDK использоватьприложение бинарный интерфейсABI)строитьиз Локальный обмен Библиотека,Библиотека точно определила машинный код по внешнему виду.

Android NDK поддерживает другую архитектуру. По умолчанию,еслисуществоватьapplication.mkдокументсередина指定Понятно.so,кроме MIPS и x86 Кроме того, также будет ARM EABI генерировать.so。 Мы «Волясуществовать» обсудим эту тему далее в этой главе.

Если все пойдет хорошо, ваш эмулятор должен иметь следующий код:

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

по сути,тысуществоватьэмулятор屏幕начальствосмотретьприезжатьиздаотлокальная машинакод返回и Зависит от Android существует отображение фрейма в виде строки в текстовом виде.

Вручную установите OpenCV и среду разработки Android.

Чтобы скачать OpenCV Вручную установку OpenCV для Android, на вашем компьютере могут быть следующие компоненты:

  • Комплект разработки Java SE 6
  • Android Studio
  • Android SDK
  • Eclipse IDE
  • Eclipse из ADT и CDT плагин
  • Android NDK
  • OpenCV4Android SDK

Вы можете выполнить действия по установке вручную.,Чтобы убедиться, что все необходимые компоненты готовы и исправны.,к便начинатьиспользовать OpenCV развивать Android приложение。

Комплект разработки Java SE 6

Вы можете начать сэта страницаскачать Применимо ктыиз OS из JDK Установщик.

Android Studio

Еще один очень хороший вариант да Android Studio。 Вы можете начать сэта страницаскачать Android Studio。 пожалуйста Уведомление,Android Studio и Android SDK Bundle существуют вместе, поэтому, если вы используете эту опцию, вам не нужно ее устанавливать. Кроме того, вы можете пропустить Eclipse и ADT из Установить,и Уведомлениеот Android Studio 1.3 начинать; Вы также найдете пары NDK извстроенныйподдерживать。

Android SDK

Чтобы загрузить и установить Android SDK, выполните следующие действия:

  1. доступэта страница
  2. прокрутите внизктолько SDK инструментчасть,Затемщелкнуть Windows Установить Ссылка на программуиз.exeдокумент。
  3. Прочитав и приняв условия, нажмите кнопку «Загрузить».
  4. Воля Установитьпрограммадержатьсуществоватьна диске,Затемщелкнуть.exeдокументкзапускать Установитьпрограмма,Затем следуйте инструкциям на экране.
  5. записать каталог SDK, чтобы позже можно было ссылаться на него из командной строки.
  6. Установить Заканчиватьназад,Менеджер Android SDKВолязапускать。
  7. выбирать Установить Android SDK инструмент,Версия 20 или Даже Высокий Версия。
  8. для Android из SDK Платформа, пожалуйста, выберите Android 3.0(API 11)или Даже Высокий Версия。 Сразу же можно использовать, использовать API 15,Рекомендуется это сделать.
  9. Прочтите и примите лицензионное соглашение,ЗатемщелкнутьУстановить

Eclipse IDE

для OpenCV 2.4。x,предположениеиспользовать Eclipse 3,7 (Индиго) или Eclipse 4.2(Juno); Вы можете начать с Eclipse изофициальный сайтскачатьтывыбиратьиз Версия。

Плагины ADT и CDT для Eclipse

Предполагая, что вы скачали Eclipse, вы можете скачать его, выполнив следующие действия. Android развиватьперсоналинструментADT)и C/C++ развиватьинструментCDT)плагин:

  1. запускать Eclipse,Затем Перейдите кпомощь | Установитьновыйпрограммное обеспечение
  2. щелкнутьверноначальстворогиздобавить вкнопка。
  3. существоватьдобавить вхранилище Библиотекаверно话框середина,существоватьимяПолесередина写入ADT Plug-in,Затем скопировать и вставить этот URL,существоватьРасположениеПолесередина.
  4. щелкнутьNext
  5. 选серединаразвиватьперсоналинструментфлажок。
  6. щелкнутьNext
  7. Следующее окно Воля показывает список для скачиванияинструмента. Толькохотетьубеждатьсяэто Сумка含локальная машинаподдерживатьинструмент(CDT),ЗатемщелкнутьNext
  8. Прочтите и примите лицензионное соглашение,ЗатемщелкнутьЗаканчивать
  9. Установить Заканчиватьназад,ты Волянуждаться重новый Запустите Затмение.

Android NDK

в соответствии сиз Требоватьдля C++ развивать Андроид, нужно установить Android NDK。

Уведомление

Не обязательно существовать использовать Android NDK во всех случаях. Как человек, занимающийся развитием, вам необходимо найти баланс между улучшениями производительности, которые приносят существующиеиспользовать собственные API, и сложностью, которую они привносят.

существуем из-за случая,потому чтоOpenCVБиблиотекадаиспользовать C/C++ писатьиз, поэтому нам, возможно, придется использовать NDK。 Однако именно потому,что программисты предпочитают использовать C/C++ писатькодиспользовать NDK 。

Скачать Android NDK

Вы можете выполнить следующие действия. Скачать Android NDK:

  1. изменятьприезжать Android NDK Домашняя страница
  2. существоватьскачатьчастьсередина,выбиратьитыиз Соответствующая операционная системаиз Версия。 Сразу я, этода Windows 64 бит
  3. Прочтите и согласитесь с условиями.
  4. щелкнутьскачатькнопка。

Установите и настройте Android NDK

После завершения загрузки вам необходимо выполнить следующие шаги для настройки NDK:

Перейдите к NDK Папка загрузки.

пункт Вдохновите известных ученых интерпретировать сюжет。

Переименуйте и переместите извлеченную папку; я Воляndkдокументпапкасказатьдля<ndk_home>。сейчассуществовать,ты Можеткиспользовать NDK Давайте построим проект.

Если вы хотите из командной строки для сборки,нонуждаться Воля<ndk_home>документпапка(существоватьяизслучайдляC:/android/android-ndk-r10d)добавить вприезжатьPATHпеременные средысередина. для Окна, пожалуйста, откройте CMD。 входитьк Вниз Заказ,и ВоляndkОглавлениезаменятьдлятыиз Оглавление:

Язык кода:javascript
копировать
set PATH=%PATH%;c:/android/android-ndk-r10d

Чтобы проверить NDK из Конфигурация верна, пожалуйста, перейдите в каталог проживания, содержащий проект. для Будьте проще,ты Можетксуществоватьhello-jniПримерпроект Тест на。 ты Можетксуществовать<ndk_home>/samples/Найдите нижеприезжатьэто。

проходитьвыполнить командуcd <your_project_directory>/Даже改Оглавление。 Выполните следующую команду:

Язык кода:javascript
копировать
ndk-build

Как показано в выводе консоли,расширениедля.soиздокументдаэтотпроектсерединаиспользоватьиз C/C++ Исходный код из компиляции Версия:

Создайте собственный код с помощью Eclipse.

Если вы предпочитаете от Eclipse строить, что удобнее, надо сказать Eclipse существовать哪里Можеткпопытаться найтиприезжать NDK,к便Можеткстроитьприложение:

  1. запускать Eclipse и ОткрытьОкно | Настройки
  2. существуют в левой панели,Открыть Android Дерево.
  3. выбирать NDK Деревоузел,Затем существовать в правой панелищелкнутьПросматривать,Затемвыбирать<ndk_home>Оглавление。
  4. щелкнутьNext
  5. от<ndk_home>/samples/импортироватьhello-jniПримерпроектделатьдля Android проект。
  6. Открытьпроектресурс Менеджер,Затемверно键щелкнутьhello-jniпроект。
  7. существоватьконтекстменюсередина,Навигацияприжатьез Android инструмент | добавить влокальная машинаподдерживатьВоляэтотпроект Конвертировать для C++ проект。
  8. Принять значение по умолчанию из Библиотеки,ЗатемщелкнутьЗаканчивать
  9. генерироватьприложение。

существовать控制台середина,ты Волясмотретьприезжать.soдокументизсписок,Этидокументдаэтотпроектизскомпилировано C++ часть. Однако если вы отимпортируете проект Открыть любой C/C++ В документе, который вы, Воля, видите, приезжать много выделенных ошибок. Вам просто нужно сделать кое-что и CDT шаги, связанные с подключением:

  1. НавигацияприжатьезПроект |。 существования На левой панели разверните Общие сведения о Си/С++узел。
  2. выбиратьпуть и символ
  3. существуют в правой панели,выбиратьвключатьвкладка。
  4. щелкнутьдобавить в,Затемщелкнутьфайловая системадобавить Следующий путь:
    • Если вы установите NDK r8 или Даже早Версия: <ndk_home>/platforms/android-9/arch-arm/usr/include<ndk_home>/sources/cxx-stl/gnu-libstdc++/include<ndk_home>/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include
    • Если вы установите NDK r8b или Даже Высокий Версия: <ndk_home> /platforms/android-9/arch-arm/usr/include <ndk_home>/sources/cxx-stl/gnu-libstdc++/4.6/include <ndk_home> /sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
  5. щелкнутьNext。 Eclipse Воля перестроит проект и снимет Eclipse Удалить все синтаксические ошибки в .
  6. Чтобы существовать, можно построить проект с Уиллом Java код и пакет нативного кода существуют один APK середина. хотеть Воляприложение Установитьсуществоватьтывыбиратьизэмуляторначальство,пожалуйстаиспользоватьменюэлементбегать | бегать Способдля | Android приложение

OpenCV4Android SDK

дляспособныйсуществоватьтыиз Android На устройстве используйте собственный код (C/C++) OpenCV коллекция, которую нужно установить OpenCV4Android СДК, это OpenCV отчасти может существовать Android работать в операционной системе.

  1. первый,изменятьприезжать OpenCV скачатьстраница
  2. скачать最новый Можетиспользовать Версия,существовать撰写本书часдля2.4.10
  3. Воля сжать документ распаковать удобно приехать по пути,НапримерC:\opencv\。 Уведомление 强烈предположениеиспользоватьнет пробеловизпуть,к避免ndk-build出сейчаслюбой问题。

Поймите, как работает NDK

Независимо от того, что вы даиспользовать TADP Выполните «Полное Установление», а также следуйте инструкциям по настройке, существующим на этом этапе. На этом этапе вы все должны улучшить визуальное восприятие. Android Все компоненты необходимые для приложения.

Прежде чем продолжить наш первый пример, давайте уточним NDK из Как работать. привычный Android NDK изосновы и привыкание к нимиспользовать это всегда хорошая идея, потому что для этого Воля становится для насиспользовать OpenCV развивать Android приложениеизкраеугольный камень。

Обзор НДК

Если вы решите использовать командную строку, скомпилируйте Android приложениеизлокальная машиначасть,тогда должениспользоватьndk-buildинструмент。 ndk-buildинструментна самом деледаодин个Скрипт,Именно Волязание отвечает за следующие скрипты сборки:

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

Кромеndk-buildинструментснаружи,Вам также следует познакомиться с некоторыми основными компонентами привычного друга.,Чтосерединавключать:

Java и собственный вызов:Android приложениедаиспользовать Java писать, после компиляции исходного кода байт-код для преобразуется так, что Android OS существовать Dalvik или Android бегатьчасART)Внизбегатьвиртуальная машина。

Уведомление

Пожалуйста, проверьте выполнение собственного кода приложения на виртуальной машине Dalvik.

использовать при реализации метода из в машинном коде,отвечатьиспользоватьnativeКлючевые слова。

Например,Вы можете объявить функцию «Воля умножить два числа»,И поручите компилятору сделать его нативным:

Язык кода:javascript
копировать
public native double mul(double x, double y);

Локальный обмен Библиотека:NDK использоватьрасширение.soстроить Эти Библиотека。 Как следует из названия,Они являются общими и ссылками из.

Собственная статическая библиотека:NDK такжекрасширение.aПриходитьстроить Эти Библиотека; Эта добрая библиотека на самом деле дасуществует компоновку во время компиляции.

Родной интерфейс JavaJNI):существоватьиспользовать Java писать Android приложение когда вам нужен способ Воля позвонить в загрузку приезжать с C/C++ написать из библиотеки, JNI Пригодится.

приложение бинарный интерфейс(ABI):Интерфейсопределение Понятноприложениекомпьютерный кодиз Появление,потому чтодляты Можетксуществоватьдругойизкомпьютерная архитектурабегатьприложение。 По умолчанию НДК для ARM EABI Создайте код. Но да, вы также можете выбрать желаемое для MIPS или x86 строитьиз。

Android.mk:Воляэтотдокумент Видетьдля Maven Создать сценарий или лучше из Makefile,Должендокументинструктироватьndk-buildМодули, связанные со скриптамиизопределениеиимя,Скомпилируйте необходимый исходный документ,И вам нужна ссылка из Библиотека. изучитьиспользовать Этот документ очень важен,Мы вернемся позже с дополнительной информацией.

Application.mk:создаватьэтотдокументда Может选из,использовать Всписоктыизприложениенеобходимыйизмодуль。 Эта информация может быть сгенерирована с использованием конкретной Целевой архитектуры, цепочки инструментов и стандартных Библиотек. ABI。

Рассмотрите эти компоненты проживания,Вы можете Подвести итоги для Android разработать собственное приложение из общего процесса,Как показано ниже:

  1. Определите, какие детали использовать Java написать, какие части Воля используют эту машину C/C++ писать。
  2. существовать Eclipse Создайте его в Android приложение。
  3. создаватьодин个Android.mkдокумент Приходитьопределениетыизмодуль,Список исходных кодов для компиляции документа,И перечислить ссылки из Библиотека.
  4. создаватьApplication.mk; Это необязательно из.
  5. ВолятыизAnrdoid.mkдокументкопироватьприезжатьпроектпутьсерединаизjniдокументпапка Вниз。
  6. использовать Eclipse строитьпроект。 Когда мы, Воля Eclipse Связьприезжатьуже Установитьиз NDK час,ndk-buildинструмент Волякомпилировать.soи.aБиблиотека,тыиз Java код Воля被компилироватьдля.dexдокумент,Весь контент упакован в Воля, существует в одном APK-документе.,Подготовить Установить。

Простой пример NDK

Когда вы развиваетесь, у вас есть местное одобрение Android приложениечас,тынуждатьсяпривычныйиспользовать NDK нетипичный Android приложениеиз общей структуры.

Обычно вы Android Приложение имеет следующую структуру клипа документа. проектrootдокументпапкаиметьк Внизребенок Оглавление:

  • jni/
  • libs/
  • res/
  • src/
  • AndroidManifest.xml
  • project.properties

Здесь и документальный клип, связанный с NDK, следующий:

  • jniдокументпапка Воля Сумка含приложениеизлокальная машиначасть. Другими словами, это да имеет NDK строить Скрипт(НапримерAndroid.mkиApplication.mk)из C/C++ Исходный код, необходимый для создания собственной библиотеки.
  • После успеха построить,libsдокументпапка Воля Сумка含локальная машина Библиотека。 Уведомление NDK строитьсистемануждатьсяAndroidManifest.xmlиproject.propertiesдокумент Приходитькомпилироватьприложениеизлокальная машиначасть. Поэтому, если какой-либо из этих документов отсутствует, необходимо сначала скомпилировать Java код, а затем скомпилировать C/C++ код.
Android.mk

существовать本节середина,Я Воля описывает грамматику стройдокумента. как упоминалось ранее,Android.mkна самом деледа GNU Makefile фрагмент, система сборки проанализирует его, чтобы изучитьсуществоватьпроект в build. Документ «Грамматика» предпочтительнее вашего модуля определения. Модуль — это одно из следующих:

  • статический Библиотека
  • общий Библиотека
  • Автономный исполняемый документ

тыуже经использоватьndk-buildПриходитьстроитьhello-jniпроект,поэтомупозволятьнассмотретьодин Вниз ДолженпроектAndroid.mkдокументизсодержание:

Язык кода:javascript
копировать
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

Теперь существуют, лучше вводить эти строки одну за другой:

  • LOCAL_PATH := $(call my-dir):здесь,Скриптопределение Понятноодин个имядляLOCAL_PATHизпеременная,ипроходить调использоватьmy-dirфункциянастраивать Чтоценить,Эта функция возвращает текущий рабочий каталог.
  • include $(CLEAR_VARS):существоватьэтот行середина,Скрипт Сумка含另один个имядляCLEAR_VARSиз GNU Makefile,использовать ВПрозрачныйвсе局部переменная-кLocal_XXXначалоизпеременная,ноLOCAL_PATHкроме。 Это необходимо потому, что для построениядокументасуществовать анализируется в едином контексте выполнения, где все переменные объявлены как Полные локальные переменные.
  • LOCAL_MODULE := hello-jni:существоватьздесь,Скриптопределение Понятноодин个имядляhello-jniизмодуль。 должно быть определеноЗначение LOCAL_MODULEпеременная,И переменная да уникальна из,к标识Android.mkсерединаизкаждыймодуль。 Уведомление строитьсистема Волясуществоватьтыопределениеизмодульсерединадобавить вlibпрефикси.soназад缀。 существовать Примерслучай,генерироватьиз Библиотека Волябыть названнымдляlibhello-jni.so
  • LOCAL_SRC_FILES := hello-jni.c:Как следует из названия,ты Волясуществоватьодин个модульсерединасписоквсенуждатьсястроитьи Собратьизисточникдокумент。 Уведомление Вы указываете только исходный документ, но не документ заголовка; Система сборки отвечает за расчет зависимостей.
  • include $(BUILD_SHARED_LIBRARY):здесьвключать另один个 GNU Makefile,это Воля收集тысуществоватьнаконецодин个include После команды определите всю информацию и определите, что строить и как строить модуль.

Создайте свой первый проект Android с помощью OpenCV.

существоватьразвиватьсредазапускатьибегатьи且иметь适когдаиз NDK Предыстория случая. Я могу начать собирать информацию о том, как существует Android приложениесерединаиспользовать OpenCV Библиотекаиз Полныйпейзаж。

Применимо к Android из OpenCV Поддерживается через местный API и Java Упаковка API получить доступ к его функциям. для родного API,ты Воляиспользовать Android NDK Определение этой книги — это библиотека, и она включена для вашего использования. OpenCV Библиотека。 Тогда Вам Воляиспользовать Родной интерфейс JavaJNI)от Java Код вызывает собственную Библиотеку.

另один个выбиратьдаиспользоватьобщепринятыйиз Java импортироватьпрямойсуществовать Java кодсерединаиспользовать OpenCV Java Упаковкаустройство。 Воля произойдет изда, Ява Упаковкаустройство Воляиспользовать JNI Воля вашего вызова, загрузите эту машину OpenCV Библиотека。

Конечно, это зависит от того, какой стиль вы выберете. Но да, вы должны понимать, что использование собственных вызовов можно сократить. JNI Накладные расходы, но требует дополнительной работы. С другой стороны, используйте Java Упаковочная машина может потребовать меньше работы по программированию и вызвать больше JNI накладные расходы.

Уведомление

Рассмотрим такую ​​ситуацию: вы обрабатываете видеокадры или кадры и зображение, и существуете вы изалгоритм, вы Воля звоните нескольким OpenCV функция. В данном случае лучше писать, чтобы все эти функции вызывались из родной Библиотеки. существоватьтыиз Android В приложении можно использовать только JNI Позвоните, чтобы получить доступ к этой родной Библиотеке.

HelloVisionWorld Android приложение

Мы Волястроить мы первые Android приложение, чтобы откамера в режиме реального времени получала кадры предварительного просмотра и использовала OpenCV из Java камера API На экране существования Полный отображается предварительный просмотр.

Создать проект в Eclipse

После да Создать проект в Затмениеиз шагов:

  1. запускать Eclipse и создайте новое рабочее пространство.
  2. Создать новый из Android проект,и ВолятыизприложениеимядляHelloVisionWorld
  3. настраиватьМинимальный SDK Версия。 хотетьиспользовать OpenCV4Android SDK руководитьстроить,Минимальный SDK Версиядля 11; Но да, настоятельно рекомендуется использовать API 15 или Даже Высокий Версия。 Сразу же можно использовать, использовать API 15
  4. выбиратьЦелевой SDK。 Сразу для меня,я Воля Чтонастраиватьдля API 19。 щелкнутьNext
  5. позволять Eclipse создаватьНовое из пустого действия,ииспользоватьимядляactivity_hello_visionизмакет Воля ЧтоимядляHelloVisionActivity
  6. ВоляOpenCVБиблиотекапроектимпортироватьприезжатьтыиз工делать区середина. Навигацияприжатьездокумент | импортировать | Существующий Android Код прибытия на рабочее местосередина.
  7. выбирать OpenCV4Android SDK изrootОглавление。 Отменить выбор всехПримерпроект,тольковыбиратьOpenCV Library,ЗатемщелкнутьFinish
  1. оттыиз Android Цитируется в проекте OpenCV Библиотека。 НажмитеПроект |。 от выбора узла «Android» Дерево на левой панели, затем существование на правой панели, в разделе «Библиотека» нажмите «добавить». в" и нажмите ОК:

Создайте проект в Android Studio.

之назадда Создайте проект в Android Studio.изшаг:

  1. запускать Android Studio
  2. Создать новый из Android Studio проект,и Воля ЧтоимядляHelloVisionWorld,и Волядомен компаниинастраиватьдляapp0.com
  3. выбиратьМинимальный SDK。 хотетьиспользовать OpenCV4Android SDK руководитьстроить,Минимальный SDK Версиядля11
  4. создаватьодин个нулевой白Активностьи Воля ЧтоимядляHelloVisionActivity
  5. хотеть ВоляOpenCVделатьдля Зависимостидобавить вприезжатьтыизпроект,пожалуйста Перейдите кдокумент | Новый | импортироватьмодульи<OpenCV4Android_Directoy>\sdk\java。 Затем,щелкнутьOK。 На данный момент это зависит от Android SDK Установив компонент, вы можете столкнуться с некоторыми проблемами при посещении. Android Studio Воля предлагает ссылку для быстрого исправления этой ошибки, которую легко исправить.
  6. существующийпроект щелкните правой кнопкой мыши по вновь созданному изприложению,ЗатемвыбиратьОткрытьмодульнастраиватьилив соответствии сF4
  7. существоватьЗависимостивкладкасередина,в соответствии с+кнопка,Затемвыбиратьмодуль Зависимости
  8. выбирать OpenCV Библиотека,Затемв соответствии сдобавить в。 Теперь существовать, вы сможете Воля OpenCV добрыйимпортироватьприезжатьтыизпроектсередина Понятно。

В дальнейшем, независимо от того, какую IDE вы выберете, вы сможете выполнить следующие шаги:

Открытьlayoutдокументиверно Чторуководитьредактироватьк匹配к Внизкод. Мы добавили OpenCV пространство имен и определение Java Схема обзора камеры:

Язык кода:javascript
копировать
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.hellovisionworld.HelloVisionActivity" >
    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"
        android:id="@+id/HelloVisionView"
        opencv:show_fps="true"
        opencv:camera_id="any" />
</RelativeLayout>

Уведомление

Скачать пример кода

Вы можете начать сэта страницаскачатьот Покупка аккаунтаизвсе Packt Книги из примера кода документа. еслитысуществоватьдругой地方购买Понятноэтот书,но Можеткдоступэта страницаируководитьзарегистрироваться,Для того, чтобы Волядокумент будет отправлен вам на электронную почту.

потому что Мы Воляиспользоватьоборудованиекамераруководить,поэтомунаснуждатьсясуществоватьAndroidManifestдокументсерединанастраиватьодин些权限:

Язык кода:javascript
копировать
</application>

<uses-permission android:name="android.permission.CAMERA"/>

<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>

существоватьAndroidManifestдокументсередина隐藏标题исистемакнопка:

Язык кода:javascript
копировать
<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >

Нам нужно существование, чтобы создать из инициализированной активности OpenCV Библиотека。 дляэтот,насиспользовать OpenCV Manager Serviceuse асинхронно инициализируется для доступа к внешнему Установитьсуществовать Цель из OpenCV Библиотека。 Сначала нам нужно существование Воли, чтобы использовать эмулятор при установке. OpenCV Manager。 дляэтот,пожалуйстасуществовать Заказ提示符Внизиспользоватьadb installЗаказ:

Язык кода:javascript
копировать
adb install <OpenCV4Android SDK_Home>\apk\OpenCV_2.4.X_Manager_2.X_<platform>.apk

использовать тебя OpenCV Установитьдокументпапказаменять<OpenCV4Android SDK_Home>,использоватьapkдокументпапкасерединаиз Можетиспользовать ВерсиязаменятьapkимясерединаизX

для<platform>,пожалуйстаиспользовать Вниз表в соответствии сэмуляторначальство Установитьизобраз системывыбиратьхотеть Установитьизплатформа:

Аппаратная платформа

имя пакета

Armeabi-v7a (ARMv7-A + неон)

OpenCV_2.4.X_Manager_2.X_armv7a-neon.apk

armeabi(ARMv5,ARMv6)

OpenCV_2.4.X_Manager_2.X_armeabi.apk

Интел х86

OpenCV_2.4.X_Manager_2.X_x86.apk

MIPS

OpenCV_2.4.X_Manager_2.X_mips.apk

Уведомление

существуют при тестировании приложения на реальном устройстве,Воля показывает сообщение,Требоватьтыот Google Play скачать OpenCV Менеджер,поэтомупожалуйстащелкнутьдаи检查Чтоподдерживатьиз OpenCV версию, чтобы вы могли загрузить ее посредством асинхронной инициализации.

существоватьActivityсередина,определение следующее и зафиксировано соответственно импортировать:

Язык кода:javascript
копировать
//A Tag to filter the log messages
private static final String  TAG = "Example::HelloVisionWorld::Activity";

//A class used to implement the interaction between OpenCV and the //device camera.
private CameraBridgeViewBase mOpenCvCameraView;

//This is the callback object used when we initialize the OpenCV //library asynchronously
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {

    @Override
       //This is the callback method called once the OpenCV //manager is connected
    public void onManagerConnected(int status) {
      switch (status) {
  //Once the OpenCV manager is successfully connected we can enable the camera interaction with the defined OpenCV camera view
      case LoaderCallbackInterface.SUCCESS:
        {
          Log.i(TAG, "OpenCV loaded successfully");
          mOpenCvCameraView.enableView();
        } break;
          default:
            {
              super.onManagerConnected(status);
            } break;
       }
    }
};

ДаженовыйonResumeАктивностьметод обратного вызовакнагрузка OpenCV Библиотека и исправьте соответственно импортировать:

Язык кода:javascript
копировать
@Override
public void onResume(){

  super.onResume();

//Call the async initialization and pass the callback object we //created later, and chose which version of OpenCV library to //load. Just make sure that the OpenCV manager you installed //supports the version you are trying to load.
  OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this, mLoaderCallback);
}

тыиз Активностьнуждаться实сейчасCvCameraViewListener2,Талантот OpenCV Вид камеры получает кадры камеры:

Язык кода:javascript
копировать
public class HelloVisionActivity extends Activity implements CvCameraViewListener2

Исправьте ошибку импортирования соответствующим образом и вставьте нереализованный метод в свою активность.

существоватьonCreateАктивностьметод обратного вызовасередина,Нам нужен вид камеры Воля OpenCV настройкидля видимого,А Воля, ваша регистрация активности для Воля, обрабатывает кадры камеры из объекта обратного вызова:

Язык кода:javascript
копировать
@Override
protected void onCreate(Bundle savedInstanceState) {
  Log.i(TAG, "called onCreate");

  super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  setContentView(R.layout.activity_hello_vision);

  mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloVisionView);

  //Set the view as visible
  mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);

  //Register your activity as the callback object to handle //camera frames
  mOpenCvCameraView.setCvCameraViewListener(this);
}

Последний этап – получение кадров с камеры. дляэтот,пожалуйста Даже改onCameraFrameметод обратного вызоваиз实сейчас:

Язык кода:javascript
копировать
```java
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

  //We're returning the colored frame as is to be rendered on //thescreen.
  return inputFrame.rgba();
}
```
  1. Теперь вы можете использовать существующий эмулятор и установить Установить приложение на свое реальное устройство.
  2. этотдасуществоватьмоделированиекамераначальствобегатьизприложение:
Язык кода:javascript
копировать
![Creating a project in Android Studio](https://img-blog.csdnimg.cn/img_convert/e65df8cd175c80ce60243e2f738b739e.png)

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

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

К тому же ты уже учишься NDK из Основы и как это работает. Наконец, вы изучили, как использовать OpenCV Режим «Вид с камеры» захватывает кадры с камеры и отображает их на экране вашего устройства. Этот пример Воля станет для нас основой для реализации более интересных идей.

2. Приложение 1. Создайте собственную темную комнату.

существовать本главасередина,ты Воляузнать, каксуществовать OpenCV серединахранилищеивыражатьизображение,И как использовать это представление для достижения интересных изалгоритмов.,Этиалгоритм Воля Усиливатьизображениеиз Появление。

Мы, Воля, сначала объясним, как обозначаются цифры и изцветовое изображение. космос, исследовать OpenCV середина重хотетьизMatдобрый。

Затем,Мы Воля шаг за шагом от мобильной картинки Библиотека загружаем изображение и Воля его отображение существует из операций на экране вашего устройства,Независимо от разрешения изображения.

наконец,Ваша гистограмма Воляучитьсяизображение,И как рассчитать ииспользовать их для Усиления изображения (будь то черно-белое и зображение или цветное изображение).

Наша Волясуществовать В этой главе рассматриваются следующие темы:

  • цифровое изображение
  • Обработка изображений, хранящихся на вашем телефоне
  • Рассчитать гистограмму изображения
  • Увеличьте контрастность изображения

цифровое изображение

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

цветовое пространство

Мы живем в непрерывном мире,поэтомухотетьсуществовать Дискретныйизчисло字传感устройствосередина捕获сцена,Сразу необходимо сделать дискретное отображение пространства (макета) и интенсивности (информации о цвете).,Так что воля реального мира и хранилища данных существует и зображение.

2D числаизображениеD(i, j)отлевыйначальствоначинатьпредставлятьсуществоватьпо номеру строкиiи Номер столбцаjвыражатьизпиксельиз传感устройство响отвечатьценить,рогдляi = j = 0

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

еслииспользоватьот 0 приезжать2^8 - 1изценитьизбез подписи字符выражатьглубина цвета信息,нона пиксель Можеткхранилищеот от 0 (черный) до 255 (белый) изсилоценить.

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

Уведомление

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

и RGB Выразить сравнение, рассмотреть изцветовое пространстваи людям добрый понимание и восприятие цветов более актуально. этодаХюэ,НасыщенностьиценитьHSV)цветовое пространство。

Каждый размер цвета можно понимать следующим образом:

  • ХюэH):этодасам цвет,красный,Синий или зеленый.
  • НасыщенностьS):это测量цветизчистота; Например, это темно-красный или темно-красный? Только представьте, насколько белый цвет скрывает цвет.
  • ценитьV):этодацветизяркость,Также называется яркостью.

наконецхотеть考虑изизображениедобрыйформададвоичныйизображение。 Это двумерный массив пикселей. Но да, каждый пиксель можно хранить только ноль или одно изценить. этот种добрыйформаиливыражать形式для解决Видеть觉问题(Напримеробнаружение края)很重хотеть。

Иметь двумерный массив пикселей и три двумерные плоскости для представления каждой единицы, где каждая единица представляет собой пиксель. RGB В случае цветового пространства из, оно содержит цвет по интенсивностиценить, в случае существования оно содержит Хюэ, насыщенность ценить. HSV цветовое пространствоизразмер,Воляизображениеуменьшить масштабдлячислоценитьматрица。 потому что OpenCV основное внимание уделяется обработке, манипулированию и зображению, поэтому вам нужно учиться в первую очередь да OpenCV 如何хранилищеииметь дело сизображение。

Matдобрый

существоватьиспользовать OpenCV развивать Видеть觉感知приложениечас,Воляиспользоватьиз最重хотетьиз Базовыйчисло据结构даMatдобрый。

Matдобрыйвыражатьn维密集число字одинрядили多рядчисло组。 по сути,еслитыиспользоватьMatдобрыйвыражать灰степеньизображение,ноMatобъект Волядахранилище Интенсивность пикселейценитьиз二维число组(иметьодин个ряд)。 еслииспользоватьMatдобрыйхранилище Полныйцветизображение,ноMatобъект Воляда Имеет три каналаиз二维число组(один个рядиспользовать Вкрасныйсила,Один канал окрашен в зеленый цвет,Один канал использует синий цвет),и且такой же适использовать HSV цветовое пространство.

любой Java добрыйодин样,Matдобрыйиметь构造устройствосписок,И существовать большую часть времени,Конструктора по умолчанию сразу достаточно. Но да,существоватьнекоторыйдругойслучай,Вы можете использовать определенный размер,добрыйформаирядчисло ПриходитьинициализацияMatобъект。

существуют В этом случае,Можно использовать следующие конструкторы:

Язык кода:javascript
копировать
int numRow=5;
int numCol=5;
int type=org.opencv.core.CvType.CV_8UC1;
Mat myMatrix=newMat(numRow,numCol,type);

Этот конструктор принимает три целочисленных параметра:

  • int Rows:новыйматрица行изчисло量
  • int Cols:новыйматрица列изчисло量
  • int type:новыйматрицадобрыйформа

Уведомление

для Понятно指定Matдобрыйхранилищеиздобрыйформак及有多少个ряд,OpenCV длятыпоставлять ПонятноCvTypeдобрыйиstatic intПоле,и имеет следующее соглашение об именах:

Язык кода:javascript
копировать
CV_[данные добрый размер шрифта, 8 | 16 | 32 | 64] [Целое число со знаком, без знака и число с плавающей запятой, S | U | F][количество каналов, C1 | C2 | C3 | C4]

Например,ты Волядобрыйформа参число指定дляorg.opencv.core.CvType.CV_8UC1; Это означает, что один канал матрицы Воляпрохождения занимает 8 Кусочек беззнакового символа интенсивности цвета. Другими словами, эта матрица интенсивности Воляхранилище для от 0 (черный) до 255 (белый) и оттенки серого и зображение.

БазовыйMat操делать

В дополнение к представлению в OpenCV Библиотека,ты还нуждатьсяпривычный МожетксуществоватьMatобъектначальство执行изодин些Базовый操делать。

Вы можете выполнять самые основные операции — доступ на уровне пикселей.,чтобы получить пиксель ценить,无论тыизцветовое пространствода в оттенках серого также да Полный RGB。 Предположим, у вас есть 1 глава,“Сразунить”изприложение,и且ужезапускатьибегать,ты Можетк回想起существоватьonCameraFrame()метод обратного вызовасередина,настолькосуществоватьиспользоватьinputFrame.rgba()Поиск метода Полныйцветкамерарамка。

Используя рамку камеры, мы можем получить доступ к пикселюценить с помощью следующего кода:

Язык кода:javascript
копировать
@Override
  public Mat onCameraFrame(CvCameraViewFrameinputFrame) {
    Mat cameraFram=inputFrame.rgba();
    double [] pixelValue=cameraFram.get(0, 0);
    double redChannelValue=pixelValue[0];
    double greenChannelValue=pixelValue[1];
    double blueChannelValue=pixelValue[2];
    Log.i(TAG, "red channel value: "+redChannelValue);
    Log.i(TAG, "green channel value: "+greenChannelValue);
    Log.i(TAG, "blue channel value: "+blueChannelValue);
    return inputFrame.rgba();
  }

Лучше наш Просматривать важно из нескольких строк, остальное на самом деле довольно просто:

Язык кода:javascript
копировать
double [] pixelValue=cameraFram.get(0, 0);

существоватьэтотодин行середина,нас调использоватьget(0,0)функцияи Воля Чтопередача给行ииндекс столбца; существуют В этом случае,Это верхний левый угол пикселей.

пожалуйста Уведомление,get()метод返回один个пара精степеньчисло组,потому чтодляMatобъект最多Можетк容纳Четыре个ряд。

существуем из примера,этода Полныйцветизображение,поэтому Кромеодин个透明степеньряд Alpha(a)снаружи,на пиксельизкрасный(r),зеленый(g)исиний(b)цветрядизсила Все Волядругой,поэтому Долженметодизимядляrgba()

ты Можеткиспользоватьчисло组索引Операция符[]独立доступкаждыйрядсила,поэтомудлякрасный,интенсивность зеленого и синего,соответственноиспользовать0,1и2

Язык кода:javascript
копировать
double redChannelValue=pixelValue[0];
double greenChannelValue=pixelValue[1];
double blueChannelValue=pixelValue[2];

Вниз表список Понятнотынуждатьсяпривычныйиз БазовыйMatдобрый操делать:

Функция

Пример кода

Получить номер канала

Mat myImage; //declared and initialized

int numberOfChannels=myImage.channels();

Сделайте глубокую копию данных матрицы, существующих изMatobjectiz.

Mat newMat=existingMat.clone();

Получить количество столбцов матрицы

Первый метод: Mat myImage //объявлен и инициализирован;

int colsNum=myImage.cols();

Второй метод: int colsNum=myImage.width();

Третий метод: //И да, это публичная переменная экземпляра.

int colsNum=myImage.size().width;

Получить количество строк матрицы

Первый метод: Mat myImage //объявлен и инициализирован;

int rowsNum=myImage.rows();

Второй метод: int rowsNum=myImage.height();

Третий метод: //И да, это публичная переменная экземпляра.

int rowsNum=myImage.size().height;

Чтобы получить глубину элемента матрицы (для каждого канала издоброго типа):

Mat myImage; //declared and initialized

int depth=myImage.depth()

CV_8U: 8-битное целое число без знака (от 0 до 255).

CV_8S: 8-битное целое число со знаком (от -128 до 127).

CV_16U: 16-битное целое число без знака (от 0 до 65 535).

CV_16S: 16-битное целое число со знаком (от -32 768 до 32 767).

CV_32S: 32-битное целое число со знаком (от -2 147 483 648 до 2 147 483 647).

CV_32F: 32-битное число с плавающей запятой.

CV_64F: 64-битное число с плавающей запятой.

Получить общее количество элементов матрицы (количество пикселей в изображении)

Mat myImage; //declared and initialized

long numberOfPixels=myImage.total()

Обработка изображений, хранящихся на вашем телефоне

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

ВоляизображениенагрузкаприезжатьMatобъект

первый Создать новый из Android Проект, начнем. Как вы видели в предыдущей главе, для начала использования OpenCV алгоритм, вам нужно OpenCV Библиотекадобавить вприжатьтыизпроект в:

  1. запускать Eclipse
  2. Создать новый из Android проектприложение; насимядляDarkRoom
  3. выбирать Сумкаимя。 существоватьэтот Примерсередина,я Воля Чтовыбиратьдляcom.example.chapter2.darkroom
  4. Волянеобходимыйиз Минимальный SDK настраиватьдля API 11(Android 3.0) или выше. Сразу лично я очень рекомендую Воляитвыбратьдля API 16(Android 4.1)。 для Целевой SDK,отвечать Долженвыбирать API 19,потому чтодляеслииспользоватьиз Целевой SDK Высокий В 19,носуществоватьнагрузка OpenCV Проблемы возникают, когда Библиотека.
  5. щелкнутьNext
  6. позволять Eclipse длятысоздаватьодин个нулевой白Активностьи Воля ЧтоимядляIODarkRoom
  7. Заканчиватьсоздаватьпроект。
  8. Воля OpenCV Библиотекапроектимпортироватьприезжатьтыиз工делать区документсередина,меню | импортировать | Существующий Android Код прибытия на рабочее место
  9. щелкнутьПросматриватьиизменятьприезжатьтыиз OpenCV Установить主Оглавление。
  10. выбирать OpenCV 主Оглавление,ЗатемщелкнутьNext
  11. Отменавыбиратьвсепроект,Затемтольковыбирать OpenCV Библиотекапроект。
  12. НажмитеЗаканчивать
  13. сейчассуществовать,Вам нужна Воля, недавно созданная из Android-проектов, просто импортируйте ссылку на библиотеку OpenCV.,поэтому,существоватьновыйпроектначальство,верно键щелкнутьсвойство
  14. существоватьлевый窗格середина,выбиратьAndroidДеревоузел,Затем существовать в правой панели,щелкнутьдобавить в
  15. выбирать OpenCV Библиотека,ЗатемщелкнутьNext

определение пользовательского интерфейса

существоватьэтотпроектсередина,Ваша Воля загружает хранилище вашего телефона,Воля Что Конвертировать для Кусочеккартинаизображение,И показано существование изображения.

Предпочитайте наше приложение отнастройки, событие макета начинается:

Язык кода:javascript
копировать
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
  <ImageView
  android:id="@+id/IODarkRoomImageView"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:src="@drawable/ic_launcher"
  android:layout_marginLeft="0dp"
  android:layout_marginTop="0dp"
  android:scaleType="fitXY"/>
</LinearLayout>

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

существовать Контрольный списокдокументсередина,добавить вк Вниз行:

Язык кода:javascript
копировать
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Это разрешение на запись; Да,Вам также неявно предоставляются разрешения на чтение.,Потому что для него меньше ограничений.

Сейчас мы продолжаем нашу деятельность:

Язык кода:javascript
копировать
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
  <activity
  android:name=".IODarkRoom"
  android:label="@string/app_name"
  android:screenOrientation="portrait">
    <intent-filter>
      <actionandroid:name="android.intent.action.MAIN"/>

      <categoryandroid:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
  </activity>
</application>

Это очень простое определение; Но да, без потери общности, моя Воля активна из пределов ориентации для портретной, что означает, что ваша активная Воля не поддерживает альбомный режим. Эта Воля фокусируется на существующем изображении, а не на работе с различными моделями активности. Да,Рекомендую внимательно прочитать эту главу,Воля Долженприложение Расширятьдлятакжеподдерживать Боковая установка Кусочек,Потому что для этого Волядля принесет вам хороший практический опыт.

Для каждой операции в приложении в Воляподдержании нам Воля нужен один пункт меню. Наша первая акция дасуществовать на мобильном Открыть картинку Библиотека, выбрать ьконкретное изображение, для этого вам понадобится существующий документ в дополнении Следующие предметы:

Язык кода:javascript
копировать
res/menu/iodark_room.xml
<item
android:id="@+id/action_openGallary"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_OpenGallary"/>

Добавит соответствующее определение строки вприезжатьres/values/strings.xml

Язык кода:javascript
копировать
<stringname="action_OpenGallary">Open Gallary</string>

Мы уже подготовили этот раздел для приложения из определение пользовательского интерфейса,поэтомупозволятьнас继续Чтоназадизкод.

Чтение изображений с помощью OpenCV

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

Язык кода:javascript
копировать
private BaseLoaderCallback mLoaderCallback = newBaseLoaderCallback(this) {
  @Override
  public void onManagerConnected(int status) {
    switch (status) {
      case LoaderCallbackInterface.SUCCESS:
      {
        Log.i(TAG, "OpenCV loaded successfully");
      } break;
      default:
      {
        super.onManagerConnected(status);
      } break;
    }
  }
};

@Override
  public void onResume()
  {
    super.onResume();
    OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_8, this, mLoaderCallback);
  }

Далее нужно обработать клик пользователя по нашему элементу перед определением изменения:

Язык кода:javascript
копировать
private static final int SELECT_PICTURE = 1;
private String selectedImagePath;
@Override
  public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_openGallary) {
      Intent intent = newIntent();
      intent.setType("https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/opencv-android-prog-example/img/*");
      intent.setAction(Intent.ACTION_GET_CONTENT);
      startActivityForResult(Intent.createChooser(intent,"Select Picture"), SELECT_PICTURE);
      return true;
    }
    return super.onOptionsItemSelected(item);
  }

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

Язык кода:javascript
копировать
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (resultCode == RESULT_OK) {
    if (requestCode == SELECT_PICTURE) {
      Uri selectedImageUri = data.getData();
 selectedImagePath = getPath(selectedImageUri);
      Log.i(TAG, "selectedImagePath: " + selectedImagePath);
 loadImage(selectedImagePath);
 displayImage(sampledImage);
    }
  }
}

Убедитесь, что операция «Открытие» возвращает желаемые результаты (существовать в данном случае для изображения). URI)之назад,нас调использоватьпомощьпрограммаметодgetPath()Приходить检索нагрузкапутьнеобходимыйиз Форматизизображениепуть。 использовать OpenCV изизображение:

Язык кода:javascript
копировать
private String getPath(Uri uri) {
  // just some safety built in 
  if(uri == null ) {
    return null;
  }
  // try to retrieve the image from the media store first
  // this will only work for images selected from gallery
  String[] projection = { MediaStore.Images.Media.DATA };
  Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
  if(cursor != null ){
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    return cursor.getString(column_index);
  }
  return uri.getPath();
}

После подготовки пути,нас Воля调использоватьloadImage()метод:

Язык кода:javascript
копировать
private void loadImage(String path)
{
 originalImage = Highgui.imread(path);
  Mat rgbImage=new Mat();

 Imgproc.cvtColor(originalImage, rgbImage, Imgproc.COLOR_BGR2RGB);

  Display display = getWindowManager().getDefaultDisplay();
  //This is "android graphics Point" class
  Point size = new Point();
  display.getSize(size);

  int width = size.x;
  int height = size.y;
  sampledImage=new Mat();

 double downSampleRatio= calculateSubSampleSize(rgbImage,width,height);

 Imgproc.resize(rgbImage, sampledImage, new Size(),downSampleRatio,downSampleRatio,Imgproc.INTER_AREA);

  try {
    ExifInterface exif = new ExifInterface(selectedImagePath);
    int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

    switch (orientation)
    {
      case ExifInterface.ORIENTATION_ROTATE_90:
        //get the mirrored image
 sampledImage=sampledImage.t();
        //flip on the y-axis
 Core.flip(sampledImage, sampledImage, 1);
        break;
      case ExifInterface.ORIENTATION_ROTATE_270:
        //get up side down image
 sampledImage=sampledImage.t();
        //Flip on the x-axis
 Core.flip(sampledImage, sampledImage, 0);
        break;
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

Давайте рассмотрим код шаг за шагом:

Язык кода:javascript
копировать
originalImage = Highgui.imread(path);

Этот метод считывает изображение по заданному пути и возвращает его. этодаHighguiдобрыйсерединаизстатическийстановиться员。

Уведомление

Если вы хотите загрузить цветное изображение, очень важно изучить цветовой канал по порядку. существоватьimread()изслучай,Раскодируйте изображение Воля нажатием B,G,R Заказать хранилищеиз гл.

Теперь давайте посмотрим на следующий фрагмент кода:

Язык кода:javascript
копировать
Mat rgbImage=new Mat();

Imgproc.cvtColor(originalImage, rgbImage, Imgproc.COLOR_BGR2RGB);

для Понятно Воляизображениенагрузкадля RGB Кусочек изображения, нам сначала нужно декодировать изображение изображенияот цветового пространства Воли. B,G,R Преобразование в цветовое пространство R,G,B。

первый,нас实例化один个нулевойизMatобъектrgbImage,ЗатемиспользоватьImgproc.cvtColor()метод执行цветовое пространствокартографирование. Метод принимает три параметра: исходное изображение, код изображения и карты. Счастливая изда, OpenCV поддерживать 150 Множественные отображения, существующие в ситуациях, которые нам нужны. BGR приезжать RGB картографирование.Теперь давайте посмотрим на следующий фрагмент кода:

Язык кода:javascript
копировать
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);

int width = size.x;
int height = size.y;
double downSampleRatio= calculateSubSampleSize(rgbImage,width,height);

потому что лимит памяти,Отображение изображения в исходном разрешении было бы очень расточительно.,иногдаоченькда不Может能из。

Например,еслитыиспользоватьсотовый телефониз 8 Изображение, снятое мегапиксельной камерой, затем предположим 1 Байт глубины цвета,цветизображениеизхранилищерасходыдля8 x 3(RGB) = 24 MB

для решения этой проблемы,Рекомендуется изменить размер (уменьшить разрешение) разрешения экрана вашего мобильного телефона. для этого,Сначала мы получаем разрешение дисплея мобильного телефона.,ЗатемиспользоватьcalculateSubSampleSize()辅助методвычислить Вниз采样соотношение:

Язык кода:javascript
копировать
private static double calculateSubSampleSize(Mat srcImage, int reqWidth, int reqHeight) {
  // Raw height and width of image
  final int height = srcImage.height();
  final int width = srcImage.width();
  double inSampleSize = 1;

  if (height > reqHeight || width > reqWidth) {

    // Calculate ratios of requested height and width to the raw
    //height and width
    final double heightRatio = (double) reqHeight / (double) height;
    final double widthRatio = (double) reqWidth / (double) width;

    // Choose the smallest ratio as inSampleSize value, this will 
    //guarantee final image with both dimensions larger than or 
    //equal to the requested height and width.
    inSampleSize = heightRatio<widthRatio ? heightRatio :widthRatio;
  }
  return inSampleSize;
}

calculateSubSampleSize()метод采использовать三个参число:источникизображение,Требуется по ширине и Требуется по высоте,Затем рассчитайте уровень субдискретизации. Сейчас существуют,предпочтение Давайте посмотрим на следующий фрагмент кода:

Язык кода:javascript
копировать
sampledImage=new Mat();
Imgproc.resize(rgbImage, sampledImage, new Size(),downSampleRatio,downSampleRatio,Imgproc.INTER_AREA);

сейчассуществовать,Мы готовы изменить размер изображенияиз загрузки под размер экрана устройства. первый,нассоздаватьодин个нулевойизMatобъектsampledImage,сохранить изображение после изменения размера. Затем,нас Воля Чтопередача给Imgproc.resize()

  • источникMatобъект,Нам нужно изменить его размер
  • ЦельMatобъект
  • Новое изображениеиз размера; существоватьнасиз例ребеноксередина,один个новыйизнулевойSizeобъект,Потому что для нас Воля посылает частоту субдискретизации
  • Скорость субдискретизации в два раза (ширина) в направлении X
  • Частота дискретизации в направлении Y увеличивается вдвое (высота)
  • Вставить метод ценить из целого числа; по умолчаниюценитьдляINTER_LINEAR,Это соответствует линейной вставкеценить

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

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

В обоих случаях OpenCV существует. Есть несколько вариантов, как вычислить этот добрый пиксель. по умолчаниюизINTER_LINEARметодпроходитьв соответствии систочник Пиксельи Цель Пиксельизблизость к2 x 2周围источник Пиксельизценитьруководить线性Взвешенный,рассчитать Цельпиксельценить. или человек,INTER_NEARESTотисточникизображениесередина最接近из Пиксель获Выбирать Цель Пиксельизценить。 INTER_AREA选элементна самом деле Воля Цель Пиксель放существоватьисточник Пиксельначальство,Тогда средний охват изпикселить. наконец,нас Можетквыбиратьсуществоватьисточникизображениеиз4×4周围Пиксельмеждупримерка三次样条,Затемотпримеркаиз样条середина读Выбирать Взаимноотвечатьиз Цельценить; этотдавыбиратьINTER_CUBIC内Вставлятьметодизрезультат。

Уведомление

Чтобы уменьшить изображение,в целомсуществоватьINTER_AREAВставлятьценить Внизсмотреть起Приходить最хороший,И увеличить изображение,в целомсуществоватьINTER_CUBIC(медленный)илиINTER_LINEAR(Даже快,но все равно выглядит хорошо) выглядит лучше всего.

Язык кода:javascript
копировать
try {
  ExifInterface exif = new ExifInterface(selectedImagePath);
  int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

  switch (orientation)
  {
    case ExifInterface.ORIENTATION_ROTATE_90:
      //get the mirrored image
      sampledImage=sampledImage.t();
      //flip on the y-axis
      Core.flip(sampledImage, sampledImage, 1);
      break;
    case ExifInterface.ORIENTATION_ROTATE_270:
      //get upside down image
      sampledImage=sampledImage.t();
      //Flip on the x-axis
      Core.flip(sampledImage, sampledImage, 0);
      break;
  }
} catch (IOException e) {
  e.printStackTrace();
}

сейчассуществовать,Нам нужно обработать направление изображения.,и且потому что Активность толькосуществовать работает в портретном режиме, поэтому мы Воля 90 или 270 Степень обработки изображения степени вращения.

существовать旋изменять 90 В данном случае это означает, что вы сделали снимок, когда телефон был в портретной ориентации; наспроходить调использоватьt()метод Воляизображение逆час针旋изменять 90 степень,кизменять置Matобъект。

транспонированный результат да исходное изображениеиз зеркала Версия,поэтомунаснуждаться执行另одиншагкпроходить调использоватьCore.flip()и Воля Чтопередачаприезжатьисточникизображениеи Цельизображениеи调использовать翻изменятькод Приходить指定如何沿垂直轴翻изменятьизображение; 0выражать围绕 x 轴翻изменять,толькоценить(Например1)выражать围绕 y 轴翻изменять,грузценить(Например-1)выражать围绕два个轴翻изменять。

для 270 поворот на градус, что означает, что вы держите телефон вверх ногами, чтобы сделать снимок. Следуем тому же иалгоритму, транспонируем и затем переворачиваем изображение. Да,существовать после транспонирования изображения,Это Воляда вокруг горизонтального направления из зеркала Версия,поэтомунас Воля0и0翻изменятькододин起вызов.

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

Язык кода:javascript
копировать
private void displayImage(Mat image)
{
  // create a bitMap
  Bitmap bitMap = Bitmap.createBitmap(image.cols(), image.rows(),Bitmap.Config.RGB_565);
  // convert to bitmap:
  Utils.matToBitmap(image, bitMap);

  // find the imageview and draw it!
  ImageView iv = (ImageView) findViewById(R.id.IODarkRoomImageView);
  iv.setImageBitmap(bitMap);
}

первый,Создаем объект диаграммы Кусочек,Его цветовые каналы в порядке и загрузка изображения цветовых каналов в порядке RGB соответствуют. Тогда.,насиспользоватьUtils.matToBitmap()ВоляMatобъект Конвертировать для Кусочеккартинаобъект。 наконец,насиспользоватьновыйсоздаватьиз Кусочеккартинаобъектнастраиватьизображение Видетькартина Кусочеккартина。

Рассчитать гистограмму изображения

Мы находимся в одном шаге от изучения изображения содержания и одного из основных методов анализа изображения. Сразуда Рассчитать гистограмму изображения.

Что такое гистограмма?

Гистограмма да — это распределение интенсивности данного изображения по общему графику с В. как упоминалось ранее,существовать x 轴начальство,绘картина Воляиметь0к255в пределах досягаемостиизценить,Это зависит от глубины,и y Ось Воля представляет количество появлений соответствующей интенсивности ценитьиз.

После того, как изображение-гистограмма будет рассчитана и отображена,Вы сразу можете легко получить информацию о контрастности изображения.,силаточка布ждатьизодин些见解。 Фактически, если гистограмму Воли нормализовать так, что она всегда идля 1, вы можете просмотреть гистограмму Воли для функции плотности вероятности и ответить на такие вопросы, как, например, сколько из вероятности да для данной интенсивности, ценить появление существующего изображения, ответить сразуда y Прочтите ось при этом значении интенсивности. На рисунке ниже вы можете увидеть интенсивность проживания для 50 из пикселя появляется существующее изображениеиз слева 5,000 Второсортный:

Понимание компонентов гистограммы

Прежде чем мы начнем изучать и начинать рассчитывать гистограмму, нам нужно изучить некоторые компоненты и термины, чтобы рассчитать гистограмму:

  • Гистограмма из коробки:как упоминалось ранее,直方картинаиз x Ось представляет интенсивность, которую может сохранитьизценить каждый пиксель. Например, если вы хотите отобразить интенсивность 0 приезжать 255 изгистограмма, то нужна Воля 256 сундуки для хранения количества появлений каждой силы ценитьиз. Но обычно это не так, поскольку считается, что гистограмма очень точная, и результат может не давать много информации. Чтобы решить эту проблему, вы можете разделить гистограмму на ячейки, каждая из которых имеет определенный диапазон интенсивностей. дляusizeexample,от 0 приезжать 255, мы можем иметь 25 коробки, в каждой из которых находится Воля 10 Непрерывное изменение интенсивностиценитьизценить,от 0 приезжать 9,от 10 приезжать 19. Согласно этой доброй рекомендации. Однако если гистограмма по-прежнему не очень репрезентативна, вы можете уменьшить количество интервалов, чтобы увеличить диапазон интенсивности в каждом интервале.
  • Размер гистограммы:существуем из примера,Размеры Количество для 1. Поскольку длясуществовать оттенки серого и зображениеиз случая, для одного канала, мы воля рассматриваем только каждый Интенсивность пикселей ценить; существующие оттенки серого и зеркальное отражение Воля считает одним цветовым каналом. Полноцветные изображения.
  • Диапазон гистограммы:этотдахотеть测量изценитьизпредел。 существование в примере, интенсивность в диапазоне да 0 приезжать 255,поэтомунасхотеть测量изценитьизобъем Воляда(0, 255),Прямо сейчасвсесила。

Теперь мы готовы показать вам, как использовать OpenCV Библиотека Расчет изображениежистограммы.

определение пользовательского интерфейса

Наша Воля продолжает существование, начатое в предыдущем разделе из того же приложения «Строить» из изменений «дасуществовать менюдокумент в добавлении». Добавлен еще один термин меню для запуска расчета гистограммы.

изменятьприезжатьres/menu/iodark_room.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
android:id="@+id/action_Hist"
android:orderInCategory="101"
android:showAsAction="never"
android:title="@string/action_Hist">
</item>

Вот и все, что касается изменений пользовательского интерфейса.

Рассчитать гистограмму изображения

существоватьIODarkRoom В активности нам необходимо обработать пользователя, нажав пункт меню Показать гистограмму.

如ВнизредактироватьonOptionesItemSelected()метод:

Язык кода:javascript
копировать
@Override
public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_openGallary) {
    Intent intent = newIntent();
    intent.setType("https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/opencv-android-prog-example/img/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent,"Select Picture"), SELECT_PICTURE);
    return true;
  }
 else if (id == R.id.action_Hist) {
 if(sampledImage==null)
 {
 Context context = getApplicationContext();
 CharSequence text = "You need to load an image first!";
 int duration = Toast.LENGTH_SHORT;

 Toast toast = Toast.makeText(context, text, duration);
 toast.show();
 return true;
 }
 Mat histImage=new Mat();
 sampledImage.copyTo(histImage);
 calcHist(histImage);
 displayImage(histImage);
 return true;
  }
  return super.onOptionsItemSelected(item);
}

пожалуйста Уведомление,Если нажат пункт меню «Показать гистограмму»,Сначала мы проверяем, загрузил ли пользователь да изображение.,Если пользователь не загружается,затем покажи дружеское сообщение,Затем Воля Что返回。

Сейчас существует часть гистограммы,Как показано ниже:

Язык кода:javascript
копировать
Mat histImage=new Mat();
sampledImage.copyTo(histImage);

calcHist(histImage);

displayImage(histImage);
return true;

наспервый制делатьиспользовать户нагрузкаизуменьшить масштабизображениеизкопировать。 Это необходимо, поскольку мы изменили гистограмму, чтобы отобразить гистограмму, поэтому нам необходимо получить копию оригинала. получатькопироватьназад,нас Воля调использоватьcalcHist()и Воля Чтопередача给новыйизображение:

Язык кода:javascript
копировать
private void calcHist(Mat image)
{
  int mHistSizeNum = 25;
  MatOfInt mHistSize = new MatOfInt(mHistSizeNum);
  Mat hist = new Mat();
  float []mBuff = new float[mHistSizeNum];
  MatOfFloat histogramRanges = new MatOfFloat(0f, 256f);
  Scalar mColorsRGB[] = new Scalar[] { new Scalar(200, 0, 0, 255), new Scalar(0, 200, 0, 255), new Scalar(0, 0, 200, 255) };
  org.opencv.core.PointmP1 = new org.opencv.core.Point();
  org.opencv.core.PointmP2 = new org.opencv.core.Point();

  int thikness = (int) (image.width() / (mHistSizeNum+10)/3);
  if(thikness> 3) thikness = 3;
  MatOfInt mChannels[] = new MatOfInt[] { new MatOfInt(0), new MatOfInt(1), new MatOfInt(2) };
  Size sizeRgba = image.size();
  int offset = (int) ((sizeRgba.width - (3*mHistSizeNum+30)*thikness));
  // RGB
  for(int c=0; c<3; c++) {
 Imgproc.calcHist(Arrays.asList(image), mChannels[c], new Mat(), hist, mHistSize, histogramRanges);
 Core.normalize(hist, hist, sizeRgba.height/2, 0, Core.NORM_INF);
    hist.get(0, 0, mBuff);
    for(int h=0; h<mHistSizeNum; h++) {
      mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
      mP1.y = sizeRgba.height-1;
      mP2.y = mP1.y - (int)mBuff[h];
 Core.line(image, mP1, mP2, mColorsRGB[c], thikness);
    }
  }
}

calcHist()методточкадлядвачасть.

Часть 1. Конфигурация гистограммы. Внешний вид. Определение компонентов гистограммы, связанных с:

Язык кода:javascript
копировать
int mHistSizeNum = 25;
MatOfInt mHistSize = new MatOfInt(mHistSizeNum);

первый,насопределение直方картина箱из个число。 существуют. В данном случае мы изгистограммы Воля имеем 25 коробка. Затем,насинициализацияодин个MatOfInt()объект,ДолженобъектдаMatдобрыйизребенокдобрый,нотолькохранилище带有直方картина箱числоиз整число。 инициализацияизрезультатдаразмердля1 x 1 x 1 (row x col x channel)изMatOfIntобъект,Чтосередина保留число字25

Уведомление

Нам нужно инициализировать вот так изобъект,Потому что по техническим характеристикам,OpenCV вычислить直方картинаметод采использоватьодин个Matобъект,Объект сохраняет количество интервалов гистограммы.

Затем,насиспользоватьк Вниз Заказинициализацияодин个новыйизMatобъекткдержать直方картинаценить:

Язык кода:javascript
копировать
Mat hist = newMat();

на этот раз,Matобъектизразмердля1 x 1 x nbins

Язык кода:javascript
копировать
float []mBuff = new float[mHistSizeNum];

Напомним, что начало этой главы существовало в середина,Мы посетили отдельные пиксели на изображении. жить здесь,У нас есть та же технология для доступа к ячейкам гистограммы.,и Воляэто们хранилищесуществоватьfloatдобрыйформаизчисло组середина. Здесь мы определяем еще одну составляющую гистограммы, а именно Диапазон. гистограммы:

Язык кода:javascript
копировать
MatOfFloat histogramRanges = new MatOfFloat(0f, 256f);

насиспользоватьMatOfFloat()добрый; этодаMatдобрыйизребенокдобрый,Как следует из названия,Он содержит только числа с плавающей запятой.

инициализацияизрезультат Волядаразмердля2 x 1 x 1изMatобъект,Чтоценитьсоответственнодля0и256

Язык кода:javascript
копировать
Scalar mColorsRGB[] = new Scalar[] { new Scalar(200, 0, 0, 255), new Scalar(0, 200, 0, 255), new Scalar(0, 0, 200, 255) };

При создании гистограммы для каждого канала мы воляпроходим рисуем линии с соответствующими цветами каналов, чтобы различать каждый канал из гистограммы. насинициализацияодин个Зависит от三个Scalarобъекткомпозицияизчисло组,Долженобъект Толькодаодин个长степень最多для 4 прецизионный массив изпара, представляющий три цвета: красный, зеленый и синий. Инициализируйте две точки, чтобы нарисовать линию для каждого интервала гистограммы:

Язык кода:javascript
копировать
org.opencv.core.PointmP1 = new org.opencv.core.Point();
org.opencv.core.PointmP2 = new org.opencv.core.Point();

Для нашего поля гистограммы рисуется каждая линия, нам нужно указать толщину линии:

Язык кода:javascript
копировать
int thikness = (int) (image.width() / (mHistSizeNum+10)/3);
if(thikness> 3) thikness = 3;

использоватьценить0,1и2инициализация三个MatOfIntобъект,Чтобы проиндексировать каждый канал изображения независимо:

Язык кода:javascript
копировать
MatOfInt mChannels[] = new MatOfInt[] { new MatOfInt(0), new MatOfInt(1), new MatOfInt(2) };

Рассчитайте смещение, чтобы начать рисовать гистограмму:

Язык кода:javascript
копировать
Size sizeRgba = image.size();
int offset = (int) ((sizeRgba.width - (3*mHistSizeNum+30)*thikness));

предпочтение Переходим ко второй части существования, которая рассчитывает и рисует гистограмму:

Язык кода:javascript
копировать
// RGB
for(int c=0; c<3; c++) {
  Imgproc.calcHist(Arrays.asList(image), mChannels[c], new Mat(), hist, mHistSize, histogramRanges);

  Core.normalize(hist, hist, sizeRgba.height/2, 0, Core.NORM_INF);

  hist.get(0, 0, mBuff);

  for(int h=0; h<mHistSizeNum; h++) {
    mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
    mP1.y = sizeRgba.height-1;
    mP2.y = mP1.y - (int)mBuff[h];
    Core.line(image, mP1, mP2, mColorsRGB[c], thikness);
  }
}

Уведомление Из Первое дело Мы можем рассчитывать гистограмму только одного канала за раз. этот Сразудадля什么насдлятри каналабегатьодин个forциклиз原потому что。 к Вциклизосновная часть,Нет.один步да调использоватьImgproc.calcHist(),Воля выполняет всю тяжелую работу после прохождения следующих параметров:

  • Matобъектизсписок。 Imgproc.calcHist()вычислитьизображениесписокиз直方картина,существуем из примера,настолькосуществоватьпередачатолько Сумка含один个изображениеизMatобъектсписок。
  • Индекс каналаизMatOfIntобъект。
  • Если вы хотите рассчитать гистограмму конкретной области, изображение,но ВоляMatобъектиспользоватьделатьмаска。 Да,существовать本例середина,Нам нужно вычислить всю гистограмму изображения.,этот Сразудадля什么нас发送один个нулевойизMatобъектиз原потому что。
  • один个Matобъект,использовать Вхранилище直方картинаценить。
  • один个MatOfIntобъект,Используйте В, чтобы сохранить количество ящиков.
  • один个MatOfFloatобъект,использовать Вдержать Диапазон гистограммы。

Теперь существуют мы рассчитали гистограмму,Надо это нормализовать ценить,к便Можетксуществоватьоборудование屏幕начальство显示это们。 Core.normalize()Можеткк几种другойиз Способиспользовать:

Язык кода:javascript
копировать
Core.normalize(hist, hist, sizeRgba.height/2, 0, Core.NORM_INF);

Здесь используйте метод даиспользовать входной массив из нормы для нормализации.,Этот пример изгистограммыоценить,и передайте следующие параметры:

  • делатьдляхотеть归один化изценитьизMatобъект。
  • делатьдля归один化назадиз ЦельизMatобъект。
  • пара Alpha。 нормализация существования нормы из падежа, альфа Воляиспользоватьделать范числоценить。 для другого случая (нормализация диапазона), альфа Волядаскоп изсамый маленькийценить。
  • параре бета-версия. Параметр толькосуществовать используется как максимальный диапазон цен в случае нормализации диапазона. существоватьнасиз例ребеноксередина,наспроходить Понятно0,Потому что для не заменяется наиспользовать.
  • Целочисленная норма добрый тип. Этот параметр указывает, что использованиеиз должно быть нормализовано. существоватьнасиз例ребеноксередина,наспередача ПонятноCore.NORM_INF,это告诉 OpenCV использовать бесконечную норму для нормализации, воля входного массива максимально ценитьнастройки и т. д. alpha Параметры (существовать в данном случае для изображения высоты пополам). Вы можете использовать другую спецификацию, например L2 Нормы L1 спецификация,этотсоответственно Взаимнокогда ВпередачаCore.NORM_L2илиCore.NORM_L1。 另снаружи,ты МожеткпроходитьпередачаCore.MINMAXПриходитьиспользоватьобъем归один化,этот会Готов оценить 归один化 для альфа и бета между параметрами.

После стандартизации,нассуществоватьfloatчисло组середина检索直方картина箱ребенокценить:

Язык кода:javascript
копировать
hist.get(0, 0, mBuff);

наконец,насиспользоватьCore.line()для直方картинасерединаизкаждый箱ребенокрисоватьодин条线:

Язык кода:javascript
копировать
for(int h=0; h<mHistSizeNum; h++) {
  //calculate the starting x position related to channel C plus 10 //pixels spacing multiplied by the thickness
  mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
  mP1.y = sizeRgba.height-1;
  mP2.y = mP1.y - (int)mBuff[h];
  Core.line(image, mP1, mP2, mColorsRGB[c], thikness);
}

КCore.line()передачак Вниз参число:

  • хотетьсуществовать ЧтоначальстворисоватьизMatобъект
  • выражать行起точкаизPointобъект
  • выражать行终точкаизPointобъект
  • выражатьлинияцветизScalarобъект
  • Представляет ширину линии из целого числа

Конечный результат Воляда загружает изображение, которое содержит гистограмму для каждого цветового канала:

Увеличьте контрастность изображения

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

Понимание выравнивания гистограммы

от абстрактной перспективы,Гистограмма сбалансированной функции: найти место для функции,Долженфункция获Выбиратьизображениеиз原始直方картинаи Воля Что Конвертировать дляиметьизображениесилаценитьравномерно распределенныйиз拉伸直方картина,оти Увеличьте контрастность изображения。

на самом деле,Гистограмма «Сбалансированный» не дает полной выходной гистограммы «сбалансированный». Но да,Это обеспечивает хорошее приближение требуемого преобразования.,от И может распределить интенсивность более равномерно в пределах ценить

Улучшение изображений в оттенках серого

С самого начала этой книги,На самом деле мы не делаем различий между Воляприложениеизалгоритмприложение в оттенках серого и Полноцветные изображения. Но даа.,Воля Гистограмма сбалансированное приложение В Оттенки серого и зображение Воля ее приложения В Полный Цвет и зображение с различными эффектами.

нас Воляпервыйот Воля直方картинасбалансированныйприложение В灰степеньизображение。

определение пользовательского интерфейса

нас Волясуществовать Переднийразвиватьизпроектизбаза础начальство,добавить в Даже多менюэлементк触发изображение Усиливать Функция。

Открытьменюдокументres/menu/iodark_room.xml,Затемдобавить вновыйизребенокменю:

Язык кода:javascript
копировать
<item android:id="@+id/enhance_gs"android:title="@string/enhance_gs"android:enabled="true"android:visible="true"android:showAsAction="always"android:titleCondensed="@string/enhance_gs_small">
  <menu>
  <item android:id="@+id/action_togs"android:title="@string/action_ctgs"/>
  <item android:id="@+id/action_egs"android:title="@string/action_eqgsistring"/>
  </menu>
</item>

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

Преобразовать изображение в оттенки серого

OpenCV поддерживает несколько преобразований цветового пространства,поэтому Воля Полныйцветизображение Конвертировать для灰степень级необходимыйиз工делать量非常Маленький。

наснуждатьсясуществовать Активностьсередина ДаженовыйonOptionsItemSelected(MenuItem item)методкиметь дело св соответствии с Внизновыйменюэлементиз操делать,к便Конвертировать для灰степень:

Язык кода:javascript
копировать
else if (id == R.id.action_togs) {
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  greyImage=new Mat();
 Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);
  displayImage(greyImage);
  return true;
}

Проверяем, загрузился ли образец изображения,Затем调использоватьImgproc.cvtColor()А Воля ему передаются следующие параметры:

  • делатьдлянасизисточникизображениеизMatобъект。
  • делатьдля ЦельизображениеизMatобъект。
  • Укажите, какой цвет вы хотите пространство Конверсии и какие цвета от пространство Конвертировать из целого числа. существовать本例середина,насвыбирать Понятноот RGB Конвертировать для灰степень。

Наконец, мы отображаем изображение в оттенках серого.

Гистограмма выровненного изображения в оттенках серого

нас Даже改ПонятноonOptionsItemSelected(MenuItem item)методкиметь дело с直方картинасбалансированныйменюэлемент:

Язык кода:javascript
копировать
else if (id == R.id.action_egs) {
  if(greyImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to convert the image to greyscale first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat eqGS=new Mat();
 Imgproc.equalizeHist(greyImage, eqGS);
  displayImage(eqGS);
  return true;  
}

Мы Воля еще раз проверяем, является ли пользователь да уже Преобразовать изображение в оттенки серогоизображение。 В противном случае метод выравнивания гистограмм не сработает. Затем,нас调использоватьImgproc.equalizeHist()и传入два个参число:

  • делатьдляисточникизображениеизMatобъект
  • делатьдля ЦельизображениеизMatобъект

наконец,нас调использоватьdisplayImage()к显示Усиливатьназадизизображение:

Улучшение изображений HSV

Чтобы использовать сбалансированный гистограмму, приходите Усиливать полное цветное изображение и получите тот же эффект.,Прямо сейчас Увеличьте контрастность изображения,наснуждаться Воляизображениеот RGB трансформация пространствадля HSV,Затем Волятакой жеизалгоритмприложение Вполныйи(S)иценить(V)ряд。

определение пользовательского интерфейса

Внесенные изменения в меню новый пункт для запуска HSV Улучшения, связанные с:

Язык кода:javascript
копировать
<item android:id="@+id/action_HSV"android:titleCondensed="@string/action_enhanceHSV"android:title="@string/action_enhanceHSV"android:enabled="true"android:showAsAction="ifRoom"android:visible="true"/>
Выравнивание гистограммы изображения HSV

Вам необходимо овладеть основными навыками дасуществовать на основе каждого канала:

Язык кода:javascript
копировать
else if (id == R.id.action_HSV) {
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

первый,ДаженовыйonOptionsItemSelected(MenuItem item)киметь дело сновыйизменюэлемент:

Язык кода:javascript
копировать
Mat V=new Mat(sampledImage.rows(),sampledImage.cols(),CvType.CV_8UC1);
Mat S=new Mat(sampledImage.rows(),sampledImage.cols(),CvType.CV_8UC1);

инициализациядва个новыйизMatобъекткдержатьизображениеценитьи Насыщенностьряд:

Язык кода:javascript
копировать
Mat HSV=new Mat();
Imgproc.cvtColor(sampledImage, HSV, Imgproc.COLOR_RGB2HSV);

сейчассуществовать,нас Воля RGB изображение Конвертировать для HSV Цветовое пространство:

Язык кода:javascript
копировать
byte [] Vs=new byte[3];
byte [] vsout=new byte[1];
byte [] ssout=new byte[1];

for(int i=0;i<HSV.rows();i++){
  for(int j=0;j<HSV.cols();j++)
  {
    HSV.get(i, j,Vs);
    V.put(i,j,new byte[]{Vs[2]});
    S.put(i,j,new byte[]{Vs[1]});
  }
}

Затем,нас逐Пиксельдоступизображениеккопировать Насыщенностьиценитьряд:

Язык кода:javascript
копировать
Imgproc.equalizeHist(V, V);
Imgproc.equalizeHist(S, S);

调использоватьImgproc.equalizeHist()к Усиливатьценитьи Насыщенностьряд:

Язык кода:javascript
копировать
for(int i=0;i<HSV.rows();i++){
  for(int j=0;j<HSV.cols();j++)
  {
    V.get(i, j,vsout);
    S.get(i, j,ssout);
    HSV.get(i, j,Vs);
    Vs[2]=vsout[0];
    Vs[1]=ssout[0];
    HSV.put(i, j,Vs);
  }
}

сейчассуществовать,нас Воля Усиливатьиз Насыщенностьиценитькопировать Вернуться к оригиналуизображение:

Язык кода:javascript
копировать
Mat enhancedImage=new Mat();
Imgproc.cvtColor(HSV,enhancedImage,Imgproc.COLOR_HSV2RGB);
displayImage(enhancedImage);
return true;

Наконец, мы будем HSV цветтрансформация пространствадля RGB и显示Усиливатьизизображение:

Улучшение изображений RGB

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

определение пользовательского интерфейса

Мы Волядобавить В новом изменяю элемент, который будет выполняться на одном канале или группе каналов. RGB Улучшение:

Язык кода:javascript
копировать
<item android:id="@+id/action_RGB"android:title="@string/action_RGB"android:titleCondensed="@string/action_enhanceRGB_small"android:enabled="true"android:showAsAction="ifRoom"android:visible="true">
  <menu>
    <item android:id="@+id/action_ER"android:titleCondensed="@string/action_enhance_red_small"android:title="@string/action_enhance_red"android:showAsAction="ifRoom"android:visible="true"android:enabled="true"android:orderInCategory="1"/>
    <item android:id="@+id/action_EG" android:showAsAction="ifRoom"android:visible="true"android:enabled="true"android:titleCondensed="@string/action_enhance_green_small"android:title="@string/action_enhance_green"android:orderInCategory="2"/>
    <item android:id="@+id/action_ERG" android:showAsAction="ifRoom"android:visible="true"android:enabled="true"android:titleCondensed="@string/action_enhance_red_green_small"android:title="@string/action_enhance_red_green"android:orderInCategory="3"/>
  </menu>
</item>
Выравнивает гистограмму цветовых каналов изображения.

У вас может быть очень медленный попиксельный доступ.,Особенно когда разрешение высокое. существуют В этом разделе,Мы, Воля, исследуем еще один канал использования изображений из технологий.,Технология работает быстрее,Как показано ниже:

Язык кода:javascript
копировать
else if(id==R.id.action_ER)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat redEnhanced=new Mat();
  sampledImage.copyTo(redEnhanced);
 Mat redMask=new Mat(sampledImage.rows(),sampledImage.cols(),sampledImage.type(),new Scalar(1,0,0,0));

этотв重хотетьизодин行даинициализацияredMask(этотдаодин个Matобъект),всерядвсенастраиватьдля0,Кроме первого канала,Нет.один个рядда RGB изображениесерединаизкрасныйряд。

Затем,нас调использоватьenhanceChannel()метод,И передадим созданную нами копию загруженной маски канала изображения:

Язык кода:javascript
копировать
enhanceChannel(redEnhanced,redMask);

существоватьenhanceChannel()методсередина,наспервый Волянагрузкаизизображениекопироватьприезжать另один个Matобъект:

Язык кода:javascript
копировать
private void enhanceChannel(Mat imageToEnhance,Mat mask)
{
  Mat channel=new  Mat(sampledImage.rows(),sampledImage.cols(),CvType.CV_8UC1);
 sampledImage.copyTo(channel,mask);

 Imgproc.cvtColor(channel, channel, Imgproc.COLOR_RGB2GRAY,1);
 Imgproc.equalizeHist(channel, channel);
 Imgproc.cvtColor(channel, channel, Imgproc.COLOR_GRAY2RGB,3);
 channel.copyTo(imageToEnhance,mask);
}

Да,На этот раз мы передаем маску Воли методу копировать.,Извлеките указанный канал только с помощью.

Затем,нас Волякопироватьизряд Конвертировать для灰степеньцветнулевой间, так что深степеньдля 8 Кусочек,и且equalizeHist()не подведет。

Наконец, мы будем Что Конвертировать для RGB Matобъект,Воля Усиливатьизрядкопироватьприезжатькрасный,зеленый и синий,Затемиспользоватьтакой жеизмаска Воля Усиливатьизрядкопироватьприезжатьпередачаиз参число。

Вы можете легко создавать свои собственные маски для использования различных каналов или комбинаций каналов.

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

приезжатьв настоящий моментдляконец,Вы уже должны знать, как выражать существование в OpenCV. Вы также создали свою собственную темную комнату.,откартина Библиотека Загрузка изображения,Рассчитайте и отобразите его гистограмму,И выполнить гистограмму сбалансированной на разных цветовых пространствах.,к Усиливатьизображениеиз Появление。

существовать Внизодинглавасередина,Мы Воляразвиваем новое изприложение,к利использовать Даже多из OpenCV Алгоритм обработки изображений и компьютерного зрения. Мы воляиспользуемалгоритм для сглаживания и зображения и обнаружения возраста, линий и кругов.

3. Приложение 2 – Программный сканер

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

Долженприложение Воляточкадлядваглава。 существовать本главасередина,Мы, Воля, знакомим с двумя важными темами.,Этими темами Воляпомощь мы достигаем конечной Цели.

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

Вторая тема Воля посвящена еще одному известному методу анализа форм, который называется «Преобразование». Хафа。 Вы Воляучитесь в основной идее технологии, которая сделала ее очень популярной, а мы Воляиспользовать OpenCV Реализация для начала воля прямой линии и подгонки круга позволяет получить набор краевых пикселей.

пространственная фильтрация

существовать Нет. 2 глава,“приложение 1-Постройте свою собственную темную комнату» мы обсудили, как использовать сбалансированную гистограмму и другие методы Усиливать данное изображение, так что изображение приятнее. Усиливатьдругойцветовое пространствосерединаизизображениеверно比степень。 существовать本节середина,Мы с Волей обсуждаем еще одну технологию Усиливать,Обычно используется в качестве этапа предварительной обработки во многих алгоритмах компьютерного зрения.,Прямо сейчаснулевой间滤波。

существуют До начала реализации концепции,позволятьнаспервый Создать новый из Android приложение。 Мы выполним те же шаги, что и в предыдущей главе; Да,Мы, Воля, перечисляем различные шаги, связанные с присвоением имени.,Согласно этой доброй рекомендации:

  1. Создать новый из Android проекти Воля ЧтоимядляSoftScanner
  2. выбирать Упаковкаимя; существуем из примера,насиспользовать Понятноcom.app2.softscanner
  3. При создании пустого действия,Только需Воля ЧтоимядляSoftScannerПрямо сейчас Может。
  4. Перейдите к новому приложению «Библиотека OpenCV» по ссылке из шагов.
  5. Для получения разрешений выполните те же действия, что и в предыдущей главе.
  6. Для асинхронной загрузки OpenCV Библиотека и устройство для чтения изображения, следуйте инструкциям 2 глава“использовать OpenCV Читать приложение "из" Выполните те же действия, что и в разделе 1: Создайте свою собственную темную комнату.

Уведомление

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

Узнайте о свертке и линейных фильтрах

Усиление изображенияиз основной цели делает изображение более привлекательным и визуально приемлемым.,И обычно нужно делать изда, чтобы подчеркнуть края.,уменьшать少噪точкаииногда Добавьте эффект размытия。

Эти операции Усиливать, как и многие другие операции Усиливать, могут быть реализованы с применением пространственной фильтрации. Мы используем пространство «использовать существующий проект», чтобы подчеркнуть, что процесс фильтрации происходит на фактическом изображении пикселей, и воля, от которой отличаются идругой фильтры (например, фильтры частотной области). существуя, двигаясь вперед из процесса, мы, Воля, больше не обсуждаем фильтры частотной области, поэтому от настоящего времени мы, Воля, пространственная фильтрацияустройствосказатьдля过滤устройство。

Независимо от того, какой фильтр вы хотите использовать,в целомследоватьиз Воля过滤устройствоприложение Визображениеиз Процесс почтидастандартныйиз。 Короче говоря, с помощью линейного фильтра мы рассматриваем каждый пиксель исходного изображенияиз (обычно Воля его называют пикселем для Цель), а Воля его ценит заменяет для его окружения указанную окрестность из взвешенных и. 之Местоксказатьдля линейного фильтра,да Потому что для Цель пикселя из нового ценится его ближайшие пиксели из линейной комбинации (взвешенной и) из результата.

Взвешенный общий вес определяется ядром фильтра (маской); Для этого нам нужно учитывать размер окрестности. Метод расчета нового Цель пикселя определяет ядро ​​Кусочка так, чтобы центральный вес из Расположениеи Цель пикселей совпадал. Затем мы объединяем взвешенные значения соседних пикселей (включаем Цель-пиксели и соответствующие им веса), чтобы получить Цель-пиксель из новой цены. наконец,Продолжаем повторять этот процесс для каждого пикселя в пикселе.

приложение дискретной формы из линейного фильтра из механизма, также называемого для свертки,иногда Воля过滤устройствоядерныйописыватьдля Ядро свертки。

наконец,Мы можем подвести итог линейному процессу свертки,Как показано ниже:

  1. определение Ядро свертки(Прямо сейчас,Укажите пиксель окрестности (вес).
  2. Воляразмещение ядерного оружиясуществовать Цельизображениеначальство, так что Цель Пиксельиядерныйизсередина心重合。
  3. Соответствующие веса пикселей ниже ядра Воля и в ядре умножаются,Затемиспользоватьрезультатзаменять Цель Пиксель。
  4. Для каждого пикселя Цельизображение из шагов 2 и 3.

устранить шум

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

средний фильтр

Проектирование ядра свертки,Воля Цельпиксель изценить заменяет на субатомное соседство изсреднеценить,Можетк得приезжатьсредний фильтр。

размердля3 x 3нетипичный Ядро сверткиkКак показано ниже:

Следуйте процедуре иммиграции, упомянутой выше.,каждый Цель Пиксель Воля被Что3 x 3Районизсреднийценитьзаменять,Изменение размера ядра Воля делает изображение более размытым,Потому что окрестности содержат все больше и больше пикселей.

Гауссов фильтр

Обращайтесь с каждым пикселем среди его соседей одинаково,так, чтобы каждый пиксель в окрестности имел одинаковый вес,Прямо сейчасверноновый Цель Пиксельценитьиз影响такой же。

Да,существоватьфактическая ситуация,и非如этот。 Как правило, влияние соседства становится все слабее и слабее по мере удаления от пикселя из Расположения; Следовательно, чем дальше от целевого пикселя, тем меньшим должен быть эффект, т. е. тем меньше вес.

использовать Гауссов фильтр может реализовать эту связь. Как следует из названия,Этот фильтр использует функцию Гаусса, применяя одномерную формулу, определяющую распределение весов для данной окрестности из:

Эта Воля выдает колоколообразную кривую,Чтосерединаaдапик кривойиз高степень,bда峰середина心иливсеценитьиз Расположение,cдастандартный差или сигма, которая указывает ширину пика. Колоколообразная кривая. иметь参числоизколоколообразная криваяиз Примерследующее:a = 1, b = 0, c = 1

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

сейчассуществовать,Воляx轴Видетьдляядерныйсерединаизиндекс веса(Чтосередина 0 дасередина心масса),Воляy轴Видетьдлямассаценить。 поэтому,еслинас移动ядерныйделать Чтосередина心(существоватьx = 0виз曲线середина心)и Цель Пиксель重合,Тогда наибольший вес Воли (кривая из пика ценить) присваивается Целью пикселя.,а затем отойти от основного центра,Вес будет продолжать снижаться,поэтому,Присвойте меньшую важность пикселям, находящимся дальше от цели пикселей.

медианный фильтр

существуют в этом фильтре,Пиксели по соседству сортируются по их интенсивности ценить,Цель Пиксель被排序назадиз Районизсередина Кусочекчисло代替。 медианный фильтрдляшкала исключениядлясоль и перецшумизодин种шум非常有效,Как показано ниже:

определение пользовательского интерфейса

для каждого типа фильтра добрый,нас Волясуществоватьприложениесерединадобавить в разных предметах меняю. изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
  android:id="@+id/img_blurr"
  android:enabled="true"
  android:orderInCategory="4"
  android:showAsAction="ifRoom"
  android:title="@string/list_blurr"
  android:titleCondensed="@string/list_blurr_small"
  android:visible="true">
  <menu>
    <item
      android:id="@+id/action_average"
      android:title="@string/action_average"/>
    <item
      android:id="@+id/action_gaussian"
      android:title="@string/action_gaussian"/>
    <item
      android:id="@+id/action_median"
      android:title="@string/action_median"/>
   </menu>
</item>

Фильтр приложения для уменьшения шума изображения

OpenCV для нашего существования Это обсуждение Каждый фильтр предоставляет готовую к использованию реализацию. наснуждаться做из Сразуда指定один些идентификация В过滤устройствоиз参число,Затемнас Сразу Можеткначинать Понятно。

существоватьSoftScannerАктивностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методидобавить в Следующие ситуации:

Язык кода:javascript
копировать
else if(id==R.id.action_average)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat blurredImage=new Mat();
  Size size=new Size(7,7);
 Imgproc.blur(sampledImage, blurredImage, size);

  displayImage(blurredImage);
  return true;  
}
else if(id==R.id.action_gaussian)
{
  /* code to handle the user not loading an image**/

  /**/
  Mat blurredImage=new Mat();
  Size size=new Size(7,7);
 Imgproc.GaussianBlur(sampledImage, blurredImage, size, 0,0);

  displayImage(blurredImage);
  return true;
}
else if(id==R.id.action_median)
{
  /* code to handle the user not loading an image**/

  /**/
  Mat blurredImage=new Mat();
  int kernelDim=7;
 Imgproc.medianBlur(sampledImage,blurredImage , kernelDim);

  displayImage(blurredImage);
  return true;
}

для Для каждого выбранного фильтра мы выполняем один и тот же процесс:

Если пользователь не загружает картинку в картинке, мы обрабатываем следующую ситуацию:

Язык кода:javascript
копировать
if(sampledImage==null)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to load an image first!";
  int duration = Toast.LENGTH_SHORT;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

длясредний фильтр,нас调использоватьImgproc.blur()метод,И передаем следующие параметры:

входитьизображениеизMatобъект; Он может иметь любое количество каналов, которые обрабатываются независимо.

После фильтра приложения,Выходное изображениеизMatобъект。

инструктироватьхотетьиспользоватьизядерный(Сосед)размеризSizeобъект。 существоватьнасиз例ребеноксередина,ядерныйизразмердля7 x 7

Язык кода:javascript
копировать
Mat blurredImage=new Mat();
Size size=new Size(7,7);
Imgproc.blur(sampledImage, blurredImage, size);
displayImage(blurredImage);
return true;

хотетьприложение Гауссов фильтр,насиспользоватьк Вниз参число调использоватьImgproc.GaussianBlur()метод:

входитьизображениеизMatобъект。

Выходное изображениеизMatобъект。

инструктироватьядерныйразмеризSizeобъект。 Вы можете использовать разную высоту и ширину сердечника. Да,Оба должны быть для нечетных и положительных чисел.

представлятьx方Кначальствостандартный差изпара精степеньформа。 существоватьнасиз例ребеноксередина,нас Воля Чтонастраиватьдля0,к便 OpenCV По ширине ядра мы рассчитываем цену.

представлятьy方Кстандартный差изпара精степеньформа,настакже Воля Чтонастраиватьдля0,к便 OpenCV Рассчитайте значение, исходя из высоты ядра:

Язык кода:javascript
копировать
Mat blurredImage=new Mat();
Size size=new Size(7,7);
Imgproc.GaussianBlur(sampledImage, blurredImage, size, 0,0);
displayImage(blurredImage);
return true;

наконец,хотетьиспользоватьмедианный фильтр,насиспользоватьк Вниз参число调использоватьImgproc.medianBlur()

входитьизображениеизMatобъект。

Выходное изображениеизMatобъект。

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

Язык кода:javascript
копировать
Mat blurredImage=new Mat();
int kernelDim=7;
Imgproc.medianBlur(sampledImage,blurredImage , kernelDim);
displayImage(blurredImage);
return true;

На изображении ниже показаны три варианта использования ядерных изсредний разных размеров. фильтриз Пример(левый:11,середина心:25иверно:35)。 ты会смотретьприезжать,По мере увеличения размера ядра,Детали начинают выпадать:

Ниже приведен пример медиафильтрационного удаления шума соли и перца в эффекте:

ищу края

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

для Рассчитать скорость изменения,Наша концепция производных в дискретной области,потому чтодлядляразмердляn x nизизображение,нас Только有Номер строки1, 2, ..., nи Номер столбца1, 2, ..., n,инас没有Номер строки1.1, 1.2, ...

позволятьнас考虑изображениеI(x, y),Чтосерединаxда Номер столбца,yда Номер строки。 потому чтоэтодадва个переменнаяизфункция,поэтомунас Воляв соответствии сxиз Дискретный导число逼近чиновник,Вычислите частные производные для каждой переменной независимо:

этотдаизображение Взаимнодляxизодинуровень导число,и且для Понятновычислитьизображение Взаимнодляyизодинуровень导число,Мы используем следующую формулу:

поэтому,дляxВыбиратьизображениеиз导числоочень просто。 нас Выбиратьx + 1из Пиксельценить,иотx-1из Пиксельсерединауменьшать去это,Это называется центральной разностью,yтакжеда如этот。

наконец,потому что изображение имеет два измерения (строки и столбцы).,поэтомудляна пиксель(один个использовать Вx方К,один个использовать Вy方К),нас得приезжатьодин个梯степень К量[∂I/∂x; ∂I/∂y],И потому что это вектор,Таким образом, это может сказать нам две вещи:

  • Представляет интенсивность края и величину градиента пикселя.
  • Представляет направление края в направлении градиента.

Взгляд в будущее,Мы можем разработать простое ядро ​​для расчета средней центральной разницы,кпопытаться найтиприезжатьизображениесуществоватьxиy方Кначальствоиз导число,Как показано ниже:

Теперь мы можем выполнить следующие шаги. Подвести итог процесс обнаружения первого производного края:

  1. Наш сглаживающий фильтр сглаживает изображение (чтобы убрать шум).
  2. вычислитьx方Киз导число; выход Воляда被ядерный过滤дляK[x]изизображение。
  3. вычислитьy方Киз导число; выход Воляда另один个кK[y]ядерный过滤изизображение。
  4. Рассчитайте размер градиента для каждого пикселя.
  5. порог ценить количество градиента,Прямо сейчас,Если градиент пикселей велик, определенный порог,нодлякрай。 В противном случае это не так.

На рисунке ниже показан пример,это针верно原始изображение(левый)существоватьx方Кначальствовычислитьодинуровень导числок Обнаружение垂直край(середина心),дляy方КПриходитьвычислить水平край(верно):

Детектор границ Собеля

OpenCV для предлагает различные издетекторы края。 Наше устройство «Воля startuseiz» названо для Детектор границ Собеля。 Основная идея здесь — конструкция ядра свертки:

ядерный Даже加强调K[x]изсередина心行иK[y]изсередина心列。

Детектор края Канни

Еще один очень хороший издетектор край (также известный как лучший детектор) да Детектор края Канни

существования Детектора края Канни, мы определяем краевые пиксели следующим образом:

  1. насиспользовать Гауссов фильтргладкийиметь дело сизображение。
  2. использовать Например Sobel Фильтр для вычисляет вектор градиента для каждого пикселя.
  3. проводим Воля каждого пикселя из размера градиента из его существующего направления градиента из соседей сравниваются,для подавления немаксимальных пикселей. Мы идентифицируем его по краю из части,поэтому,Если его амплитуда градиента наибольшая,Тогда Воля останется.
  4. Наконец, Канни Симметрично для гистерезиса процесса исполь зовать два порога ценообразования (низкий и высокий), чтобы определить количество оставшихся пикселей:
    • Если пиксель с амплитудой градиента велик, высокий порог ценить,Тогда пиксель принимается как краевой пиксель.
    • Если амплитуда градиента пикселей мала, низкий порог ценить,но立Прямо сейчас拒绝Пиксель。
    • Если амплитуда градиента пикселей существует между высоким порогом ценить и низким порогом ценить,и且этосоединятьприезжать梯степень幅степень Высокий ВТакаоценитьиз Пиксель,Тогда пиксель Воля рассматривается как крайний пиксель.

определение пользовательского интерфейса

нас Волясуществоватьнасизприложениесерединадобавить в Некоторые пункты меню для запуска нашего Воляиспользоватьиз разных детекторов края。 изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
  android:id="@+id/img_edge_detection"
  android:enabled="true"
  android:orderInCategory="5"
  android:showAsAction="ifRoom"
  android:title="@string/list_ed"
  android:titleCondensed="@string/list_ed_small"
  android:visible="true">
  <menu>
    <item
      android:id="@+id/action_sobel"
      android:title="@string/action_sobel"/>
    <item
      android:id="@+id/action_canny"
      android:title="@string/action_canny"/>
  </menu>
</item>

приложение Sobel Фильтр для поиска ребер

существовать本节середина,нас Воля同часиспользовать Sobel и Детектор края Канни найти изображение в изедже. Мы начнем с Sobel Краевой фильтр начинается.

существоватьSoftScannerАктивностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методидобавить в Следующие ситуации:

Язык кода:javascript
копировать
else if(id==R.id.action_sobel)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat blurredImage=new Mat();
  Size size=new Size(7,7);
 Imgproc.GaussianBlur(sampledImage, blurredImage, size, 0,0);

  Mat gray = new Mat();
 Imgproc.cvtColor(blurredImage, gray, Imgproc.COLOR_RGB2GRAY);

  Mat xFirstDervative =new Mat(),yFirstDervative =new Mat();
  int ddepth=CvType.CV_16S;

 Imgproc.Sobel(gray, xFirstDervative,ddepth , 1,0);
 Imgproc.Sobel(gray, yFirstDervative,ddepth , 0,1);

  Mat absXD=new Mat(),absYD=new Mat();

 Core.convertScaleAbs(xFirstDervative, absXD);
 Core.convertScaleAbs(yFirstDervative, absYD);

  Mat edgeImage=new Mat();
 Core.addWeighted(absXD, 0.5, absYD, 0.5, 0, edgeImage);

  displayImage(edgeImage);
  return true;
}

потому что Sobel да Первый детектор производных края,поэтомунас Воляследовать Передний Обзоризпроцесс:

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

Язык кода:javascript
копировать
Mat blurredImage=new Mat();
Size size=new Size(7,7);
Imgproc.GaussianBlur(sampledImage, blurredImage, size, 0,0);

Волягладкийизображение Конвертировать для灰степеньизображение:

Язык кода:javascript
копировать
Mat gray = new Mat();
Imgproc.cvtColor(blurredImage, gray, Imgproc.COLOR_RGB2GRAY);

использоватьImgproc.Sobel()и传入к Вниз参число,вычислить灰степеньизображениеизxиyодинуровень导число:

делатьдляисточникизображениеизMatобъект。

делатьдля Выходное изображениеизMatобъект。

целочисленная глубина,использовать Винструктировать Выходное изображениеиз深степень。 Большую часть времени существуют, вход и выход, а также глубина зображения одинакова. Да,Когда мы существуем, вычисляем производную в некоторых случаях,Долженценитьдлягруз(Прямо сейчас,от Белый(255)Изменятьдлячерный(0,derivative = -255 - 0 = -255)。 поэтому,еслинасиспользоватьизMatобъектиз深степеньдлябез подписи 8 бит (серые изображения сохраняют только 0 приезжать 255 между ценить),ногруз导числоизценить Воля溢出инастраиватьдля0,Прямо сейчас错过этот个边。 Чтобы обойти эту проблему, мы используем символ из 16 Выходное изображение разрядности для хранения отрицательных производных.

насхотетьвычислитьизxуровеньиз整число。 нас Воля Чтонастраиватьдля1квычислитьxизодинуровень导число。

насхотетьвычислитьизyуровеньиз整число。 нас Воля Чтонастраиватьдля1квычислитьyизодинуровень导число。

Уведомление

Уведомление,хотетьвычислитьx方Кначальствоиз梯степень,насиспользоватьx-order = 1иy-order = 0。 насверноy方Кдобрыйсделай это как。

Вот код:

Язык кода:javascript
копировать
Mat xFirstDervative =new Mat(),yFirstDervative =new Mat();
int ddepth=CvType.CV_16S;

Imgproc.Sobel(gray, xFirstDervative,ddepth , 1,0);
Imgproc.Sobel(gray, yFirstDervative,ddepth , 0,1);

нас调использоватьCore.convertScaleAbs()существоватьвходитьMatобъектначальство依次执行三个操делать:

  • УвеличитьвходитьMatобъектизценить; Да,потому что мы не передаем какой-либо коэффициент масштабирования,поэтому跳过Понятно Увеличитьшаг。
  • ВыбиратьвходитьMatобъектсерединакаждый元素изабсолютныйценить。 наснуждатьсяэтотшаг,потому чтодлянасхранилище Понятноxиyодинуровень导числоизгрузценить,Но на самом деле нас волнует производная изабсолютная цена.,и且нас希望能够Воля Этиценитьхранилищесуществоватьбез подписииз 8 КусочекMatобъектсередина(хранилищеот 0 приезжать 255 изценить)。
  • Конвертировать длябез подписииз 8 Кусочек深степеньMatобъект。

Core.convertScaleAbs()из参числодавходитьивыходMatобъект:

Язык кода:javascript
копировать
Mat absXD=new Mat(),absYD=new Mat();
Core.convertScaleAbs(xFirstDervative, absXD);
Core.convertScaleAbs(yFirstDervative, absYD);

нас尝试использоватьCore.addWeighted()Приходить估计梯степеньразмерк显示крайизображение,Долженфункциявычислитьдва个изображениеиз Взвешенныйи。 Мы передаем следующие параметры для достижения:

  • Нет.один张картина片изMatобъект。 нассуществоватьx方Кпередача Понятноабсолютныйодинуровень导число。
  • Первая картинка имеет вдвое больший вес; существоватьнасиз例ребеноксередина,два个изображениевседля0.5
  • Нет.二个изображениеизMatобъект。 нас沿y方Кпередача Понятноабсолютныйодинуровень导число。
  • Второй весит в два раза больше.
  • Каждая сумма и добавляет пару точностиценить. Нам не нужно доп. влюбойсодержание,поэтомунас发送0
  • один个Matобъект,использовать Вхранилище Выходное изображение。

Уведомление

Это количество градиентов примерно стоит. Сразу для этого примера это дахорошийиз. Да,Если вам нужно рассчитать фактическую величину градиента,тогда должениспользоватьэтотчиновникgradient magnitude = √(f[x]² + f[y]²),Чтосерединаf[x], f[y]соответственнодаxиy方Кначальствоизодинуровень导числоизценить。

Вот код:

Язык кода:javascript
копировать
Mat edgeImage=new Mat();
Core.addWeighted(absXD, 0.5, absYD, 0.5, 0, edgeImage);

наконец,нас显示edgeImage

Язык кода:javascript
копировать
displayImage(edgeImage);

Приложение Пример обнаружения границ фильтра Собеля

использовать Детектор края Канни

приложение Детектор края Канни проще; На самом деле нам просто нужно существовать OpenCV выполнить функцию, Детектор края Каннииз Все этапы выполняем мы. проходитьэтот种抽象水平,нас Тольконуждаться指定один些алгоритм参число Прямо сейчас Может。

существоватьSoftScannerАктивностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методидобавить в Следующие ситуации:

Язык кода:javascript
копировать
else if(id==R.id.action_canny)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat gray = new Mat();
 Imgproc.cvtColor(sampledImage, gray, Imgproc.COLOR_RGB2GRAY);

  Mat edgeImage=new Mat();
 Imgproc.Canny(gray, edgeImage, 100, 200);

  displayImage(edgeImage);
  return true;  
}

Чтобы облегчить задачу, вы можете посмотреть, как добраться:

нас Волявходитьизображение Конвертировать для灰степень,потому чтодля Canny только Применимо к灰степеньизображение:

Язык кода:javascript
копировать
Mat gray = new Mat();
Imgproc.cvtColor(sampledImage, gray, Imgproc.COLOR_RGB2GRAY);

нас调использоватьImgproc.Canny()и передайте следующие параметры:

  • делатьдлявходить灰степеньизображениеизMatобъект
  • выходкрайизображениеизMatобъект
  • Шаг гистерезиса нижний порог ценитьиз двукратного
  • Верхний предел шага гистерезиса в два раза

Уведомление

Canny предположение Воляначальство限порогценитьи Вниз限порогценитьизсоотношениенастраиватьдля 2:1 приезжать 3:1。

Вот код:

Язык кода:javascript
копировать
Mat edgeImage=new Mat();
Imgproc.Canny(gray, edgeImage, 100, 200);

наконец,нас显示edgeImage

Язык кода:javascript
копировать
displayImage(edgeImage);

приложение Детектор края Каннииз Пример

Обнаружение формы

поэтому,насуже经смотретьприезжать如何Обнаружениекрай; Но да, этот процесс происходит попиксельно, отвечает на вопрос «нет для пикселя». В дальнейшем нам понадобится не тестирование кромок для анализа формы, а более конкретная информация. Нам, Воля, нужно больше представителей.

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

Понимание преобразования линии Хафа

Есть много способов провести линию через несколько точек.,и且Преобразование Хафапризнанныйдлядаодин种约束不足изметод,где мы толькоиспользуем точку, чтобы найти все линии, которые могут провести эту точку из,Мы используем еще одну точку, чтобы найти все, что вы можете извлечь из линии.,И продолжаем делать это по всем пунктам.

В итоге у нас появилась система голосования,где каждая точка голосуется за линию,И чем больше точек на одной линии,верно Должен行изголосование Сразу Чем выше。 Короче говоря, Преобразование Хафа Можеткописыватьдля Воляx, yнулевой间серединаиз Отображение точекприезжатьформа интересаиз参числонулевой间。

利использоватьxиyнулевой间серединаиз Уравнение прямойy = ax + b,Воля Что Изменять换длясклон(a)изнулевой间и截Выбиратьнулевой间(b),и учитывая это преобразование,Приходите вxиyнулевой间серединаизточка,На самом деле да наклон и з линия в пространстве,Чтоуравнение式дляb = -ax + y

существовать Внизкартинасередина,нассуществоватьxиyнулевой间середина有五个точка(левый)。 когда Конвертировать длясклониперехватыватьнулевой间час,Мы получаем пять элементов приезжать (справа):

сейчассуществовать,xиyнулевой间серединаизкаждыйточка Все Воляголосование给один个склон,И существуют наклон и перехват в пространстве,поэтомунасхотеть做из Сразудасуществовать参числонулевой间серединапопытаться найтиприезжатьмаксимумценить,Эта Сразуда нас устраивает по пунктам:

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

для вертикальной линии,Наклон до бесконечности,Это сразу же означает, что использование линии из уравнения полярных координат вместо наклона и формы пересечения более практично по причине. существуют В этом случае,насхотетьиметь дело сизуравнениедаr = x cosθ + y sinθ,нас又有два个参числоr(ρ)иθ,Мы, Воля, придерживаемся тех же идей,Толькодасейчассуществоватьизнулевой间дляrиθи不дасклониперехватывать。

Опять следуем системе голосования,попытаться найтиприезжатьпредставлятьнасизточкаизпрямая линияизrиθмаксимумценить。 Да,этотодин次xиyнулевой间серединаизточка Волядатолько弦曲线,еслидва个или多个только弦曲线существовать同одинrиθв Взаимно交,Это означает, что они принадлежат одной строке:

Уведомление

ты Можетксуществоватьэта страницаначальствоиспользовать Маленькийпрограмма查смотреть Преобразование Хафаиз продумать ситуацию.

Обнаружение прямых линий с помощью преобразования Хафа

существуют В OpenCV у нас есть две реализации линейного преобразования Хафа:

  1. стандартный Преобразование Хафа:Долженпроцессиранее说明изпроцесс非常Взаимнопохожий; Да,потому что алгоритм должен проверять все краевые точки на данном изображении,поэтомупризнанныйдляда较медленныйизвыбирать。
  2. Вероятностное преобразование линии Хафа:этот选элементданас Волясуществовать Примерсерединаиспользоватьиз选элемент。 существующая версия вероятности, алгоритм использует разницу в баллах голосов, необходимую для обнаружения линий, маленький Объем вычислений, необходимый для обнаружения линий. Интуитивно понятно, что прежде чем «за» создаст длинную линию, существование определит, достигнет ли аккумуляторная ячейка Кусочекда неслучайно приезжать, нам нужно только подтверждение очков из небольшого числа людей, которые сразу могут проголосовать. Но да, для более коротких строк необходимо определить более высокую часть. Короче говоря, метод пытается минимизировать количество краевых точек, необходимых для определения подобранной линии.

определение пользовательского интерфейса

Мы Волядобавить водин个новыйизменюэлементкзапускать Преобразование Хафаалгоритм。 изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item android:id="@+id/action_HTL"
  android:enabled="true"
  android:visible="true"
  android:title="@string/action_HL">
</item>

Обнаружение и рисование линий

Процесс преобразования линии Хафа разделен на четыре этапа:

  1. Загрузка интересного изображения.
  2. использовать Canny Обнаружение краев изображения; выход Волядадвоичныйизображение。
  3. существоватьдвоичныйизображениеначальство调использоватьстандартныйили Вероятностное преобразование линии Хафа。
  4. Нарисуйте линию обнаружения прибытия.

существоватьSoftScannerАктивностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методидобавить в Следующие ситуации:

Язык кода:javascript
копировать
else if(id==R.id.action_HTL)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat binaryImage=new Mat();
  Imgproc.cvtColor(sampledImage, binaryImage, Imgproc.COLOR_RGB2GRAY);

 Imgproc.Canny(binaryImage, binaryImage, 80, 100);

  Mat lines = new Mat();
  int threshold = 50;

 Imgproc.HoughLinesP(binaryImage, lines, 1, Math.PI/180, threshold);

  Imgproc.cvtColor(binaryImage, binaryImage, Imgproc.COLOR_GRAY2RGB);
  for (int i = 0; i < lines.cols(); i++) 
  {
    double[] line = lines.get(0, i);
    double xStart = line[0], 
    yStart = line[1],
    xEnd = line[2],
    yEnd = line[3];
    org.opencv.core.Point lineStart = new org.opencv.core.Point(xStart, yStart);
    org.opencv.core.Point lineEnd = new org.opencv.core.Point(xEnd, yEnd);

    Core.line(binaryImage, lineStart, lineEnd, new Scalar(0,0,255), 3);
  }
  displayImage(binaryImage);

  return true;
}

Долженкодна самом очень просто,к Внизшагиспользовать ВОбнаружение и рисование линий:

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

Язык кода:javascript
копировать
if(sampledImage==null)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to load an image first!";
  int duration = Toast.LENGTH_SHORT;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

Затем,насинициализацияодин个новыйизMatобъект,и Волянагрузкаизизображениеот Полныйцветтрансформация пространствадля灰степеньнулевой间。 наконец,нас调использоватьImgproc.Canny()Воля灰степеньизображение Конвертировать длятолько显示крайиздвоичныйизображение:

Язык кода:javascript
копировать
Mat binaryImage=new Mat();
Imgproc.cvtColor(sampledImage, binaryImage, Imgproc.COLOR_RGB2GRAY);
Imgproc.Canny(binaryImage, binaryImage, 80, 100);

Внизодин步да调использоватьImgproc.HoughLinesP(),Это оригинальный метод Преобразования Хафа из вероятностной версии.,И передаем следующие параметры:

  • один个Matобъект,Представляет загрузку изображенияиз двоичного изображения Версия
  • один个Matобъект,использовать ВВоля Обнаружениеприезжатьиз Линия зарезервированадля参числоx_start, y_start, x_end, y_end
  • 参числоρизточка辨率(к Пиксельдляодин Кусочек)из倍число; существуют мы из примера, мы воля его настройкидля одного пикселя
  • 参числоθиз弧степеньточка辨率изпара精степень; существуем из-за случая,нас Воля Чтонастраиватьдля 1 степень(pi / 180)
  • Порог аккумулятораценитьиз целого числа,возвращает только строки с достаточным количеством голосов из

Уведомление

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

Вот код:

Язык кода:javascript
копировать
Mat lines = new Mat();
int threshold = 50;
Imgproc.HoughLinesP(binaryImage, lines, 1, Math.PI/180, threshold);

Наконец, мы будем бинарное и зображение Конвертировать для полного и цветового пространствок显示Обнаружениеприезжатьизлиния,Затем существование обнаруживает строки прибытия в цикле и использует параметры, рисуя их одну за другой.,x_start, y_start, x_end, y_end

Язык кода:javascript
копировать
Imgproc.cvtColor(binaryImage, binaryImage, Imgproc.COLOR_GRAY2RGB);
for (int i = 0; i < lines.cols(); i++) 
{
  double[] line = lines.get(0, i);
  double xStart = line[0], 
  yStart = line[1],
  xEnd = line[2],
  yEnd = line[3];
  org.opencv.core.Point lineStart = new org.opencv.core.Point(xStart, yStart);
  org.opencv.core.Point lineEnd = new org.opencv.core.Point(xEnd, yEnd);

  Core.line(binaryImage, lineStart, lineEnd, new Scalar(0,0,255), 3);
}  
displayImage(binaryImage);

Вы можете обнаружить линии Хафа «прибытие» в следующем входном изображении в сетке «существующие»:

по краю изображения обнаружить местонахождение толстая линия (синяя)

Обнаружение кругов с помощью преобразования Хафа

OpenCV для предоставляет еще одну реализацию Преобразования Хафаиз.,Но на этот раз,У нас нет тестовой линии,идав соответствии с Воляx, yтрансформация пространствадля参числонулевой间изтакой же思想Приходить Обнаружениекруглый。

длякруглыйизуравнениеr² = (x - a)² + (y - b)²,нас Есть три参числоr, a, b,Чтосерединаaиbсоответственнодакруглыйсуществоватьxиy方Кначальствоизсередина心 ,rдарадиус。

сейчассуществовать,Пространство параметров трехмерное из,Каждая краевая точка круга имеет право голоса в этом трехмерном пространстве.,Затемнассуществовать参числонулевой间середина搜索максимумценитьк Обнаружениекруглыйизсередина心ирадиус。

Этот процесс очень,Занимает много памяти и вычислений,и且三维нулевой间Воляочень редкий。 Хорошей новостью является то, что OpenCV использоватьсказатьдляГрадиентный метод Хафаизметод实сейчас Понятнокруглый形Преобразование Хафа。

Градиентный метод Как работает Хафаиз следующее: Для первого шага мы приложениедетектор край, например Детектор края Канни。 существуют На втором этапе мы увеличиваем аккумуляторную единицу (в двумерном пространстве) вдоль направления градиента для каждого краевого пикселя. Интуитивно понятно, что если мы сталкиваемся с кругом, то накопительная единица с более высоким правом голоса фактически является центром круга. Теперь, когда мы создали список потенциальных центров, нам нужно найти радиус круга. поэтому,для каждого центра,Мы учитываем краевые пиксели, сортируя их по центру их расположения и расстоянию.,И сохраняйте максимальное количество краевых пикселей (голосования) за пределами одного радиуса:

определение пользовательского интерфейса

для триггерного раунда Преобразование Хафа,нас Воляодин个менюэлементдобавить Вприжатьез теперь доступен в меню середина. изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item android:id="@+id/action_CHT"
  android:enabled="true"
  android:visible="true"
  android:title="@string/action_CHT">
</item>

Обнаружение и рисование кругов

Процесс обнаружения круга и процесс обнаружения линии очень похожи:

  1. Загрузка интересного изображения.
  2. Воля Чтоот Полныйцветтрансформация пространствадля灰степеньнулевой间。
  3. Круглый метод Преобразование Хафа называется существующим изображением в оттенках серого.
  4. Нарисуйте круг обнаружения прибытия.

насредактироватьonOptionsItemSelected()киметь дело скруглый Преобразование Ситуация в Хафаизе:

Язык кода:javascript
копировать
else if(id==R.id.action_CHT)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat grayImage=new Mat();
  Imgproc.cvtColor(sampledImage, grayImage, Imgproc.COLOR_RGB2GRAY);

  double minDist=20;
  int thickness=5;
  double cannyHighThreshold=150;
  double accumlatorThreshold=50;
  Mat circles = new Mat();
 Imgproc.HoughCircles(grayImage, circles, Imgproc.CV_HOUGH_GRADIENT, 1, minDist,cannyHighThreshold,accumlatorThreshold,0,0);

  Imgproc.cvtColor(grayImage, grayImage, Imgproc.COLOR_GRAY2RGB);
  for (int i = 0; i < circles.cols(); i++) 
  {
    double[] circle = circles.get(0, i);
    double centerX = circle[0], 
      centerY = circle[1],
      radius = circle[2];
    org.opencv.core.Point center = new org.opencv.core.Point(centerX, centerY);
 Core.circle(grayImage, center, (int) radius, new Scalar(0,0,255),thickness);
  }
  displayImage(grayImage);
  return true;
}

Код круга Преобразование Хафаиз тот же,Используйте тестовую линию В,За исключением следующих частей:

Язык кода:javascript
копировать
double minDist=20;
int thickness=5;
double cannyHighThreshold=150;
double accumlatorThreshold=50;

Mat circles = new Mat();
Imgproc.HoughCircles(grayImage, circles, Imgproc.CV_HOUGH_GRADIENT, 1, minDist,cannyHighThreshold,accumlatorThreshold,0,0);

Imgproc.cvtColor(grayImage, grayImage, Imgproc.COLOR_GRAY2RGB);
for (int i = 0; i < circles.cols(); i++) 
{
  double[] circle = circles.get(0, i);
  double centerX = circle[0], 
    centerY = circle[1],
    radius = circle[2];
  org.opencv.core.Point center = new org.opencv.core.Point(centerX, centerY);
 Core.circle(grayImage, center, (int) radius, new Scalar(0,0,255),thickness);
}

наспроходить调использоватьImgproc.HoughCircles()и Воляк Вниз参числопередача给это Приходить Обнаружениекруглый:

  • один个Matобъект,выражать 8 Кусочек одноканальный ввод в оттенках серого и зображение.
  • один个Matобъект,Волядержать Обнаружениеприезжатьизкруглый。 матрицаиз每один列Воля Сумка含один个Зависит от Эти参числоx, y, rвыражатьизкруглый。
  • Метод обнаружения целочисленный. В настоящее время OpenCV реализует только алгоритм градиента Хафа.
  • Соотношение между входом и размером аккумулятора с точностью Визпара настраивается. Например,еслинаспередача1,но累加устройство Воляиметьивходитьизображениетакой жеизразмер(宽степеньи高степень)。 еслинаспроходить3,Тогда размер аккумулятора Воля для входного изображения будет равен одной трети.
  • Обнаружение перемещения между центрами изсамых кругов маленькийрасстояниеиздва倍ценить。 Пожалуйста Уведомление, чем больше расстояние, тем больше верных кругов вы Воля пропустите; Чем короче расстояние, тем больше ложных кругов вы обнаружите.
  • использовать Ввнутренний Детектор края Канниначальство限порогценитьиз double ценить; Вниз限порогценить Воляданачальство限порогценитьизодин半。
  • Порог накопителя ценитьиз в два раза представляет собой количество голосов за каждое обнаружение прибытия в центр.
  • Мы ищем изсамого маленький радиус из целого числа; еслиты不知道,но Можеткпроходить0
  • Чтобы определить максимальный радиус из целого числа; если未知,нопроходить0

наконец,насцикл ОбнаружениеприезжатьизкруглыйииспользоватьCore.circle()逐одинрисовать。

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

существовать本главасередина,Мы ввели понятие пространственной фильтрации,и展示Понятноот Снижение шумаприезжатьобнаружение краясуществовать Ядро сверткисерединаиздругойприложение。 Мы видели, как приезжатьиспользовать OpenCV проходитьсредний,Гауссовский фильтр Приходитьгладкийизображение。 Мы также будем OpenCV Реализовано для Sobel и Детектор края Канни。 В дополнение к обнаружению сглаживания краев изображений, мы также представляем метод под названием «Преобразование». Хафаиз знаменитая технология анализа формы, так что линии и круги соответствуют граничным пикселям.

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

4. Приложение 2 — применить коррекцию перспективы.

В этой главе мы продолжаем существование главы № 3 «Приложение 2: Программный сканер» в запуске приложения для основ.

Мы Воляиспользоватьуже经讨论过изконцепция(Прямо сейчасобнаружение краяи霍夫线Изменять换)в четырехугольникобъектруководить透Видеть Коррекция。 Воляперспективная трансформация приложения Вобъект Воля меняет то, как мы видим объектиз. когдатыдлядокумент,При фотографировании чеков и т. д.,Если вы хотите лучше просмотреть снимок, изображениеилидобрый, как отсканированное из копии,Эта идея Воле пригодилась.

Мы, Воля, рассмотрим, как приехать исполь зовать три разных способа реализации этой идеи:

  • Жесткая коррекция перспективы
  • Гибкая коррекция перспективы
  • Ручная коррекция перспективы

Трансформация изображения и коррекция перспективы

Изображения могут претерпевать ряд преобразований. 最简одинизсписоксуществоватьздесь。

Кастрюля

по сути,существоватьизображениекоординировать Кастрюлясередина,насхотеть做изда Воляна пиксель移Кусочекp = [x, y],Что量дляt = [t[x], t[y]]。 Например,нас Можетк Воля Пиксельpизизменять换写дляp' = p + t

Ротация и Кастрюля

существующее Это конвертируется,Мы, Воля, вращаем приложение В каждый пиксель,Затем отправляйтесь в Кастрюлю. потому что сохраняется евклидово расстояние,поэтому Должен Изменять换такжесказатьдля二维欧几里得Изменять换。

нас Можетк Воляэтот Изменять换写дляp' = Rp + t,ЧтосерединаRда2×2матрица,ждать ВR = [cosθ, -sinθ; sinθ, cosθ],θда旋изменятьрогстепень。

Вращение масштаба

Это также называется для,для преобразования подобия,существовать Это меняется,Мы добавилимасштабный коэффициентs,к便Можетк Воля Изменять换выражатьдляp' = sRp + t。 Это преобразование Воля сохраняет углы между линиями.

Аффинный

существовать Аффинныйизменять换середина,параллельные линии остаются параллельными,и且Можетквыражатьдляp' = Ap*,Чтосерединаp* = [x, y, 1]иA = [a, b, c; d, e, f]

перспективная трансформация

Это также называется для,для проективной трансформации,существовать Это меняется,насиспользовать3×3матрицаи不да2×3матрица Приходить Даже改Пиксельиз Видетьточка。 Аффинное преобразование иперспективное Основное отличие трансформации от изда состоит в том, что последняя не сохраняет параллельных линий, а сохраняет только свою линейность.

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

Чтобы найти приезжатьэту матрицу,Сначала нам нужно использовать нашу идею существования для обнаружения интересующих объектов, обсуждаемых в главе 3 главы «Приложение 2: Программный сканер».,выборгруппа достопримечательностей,Тогда укажите эти достопримечательности из Расположение,Для лучшего обзора объекта.

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

Согласно предыдущему примеру,Мы с Волей обсуждаем три пути перспективы Коррекцияиз,И продемонстрируем разные способы найти этих персонажей, приезжать.,к Учреждатьнеобходимыйизверноотвечать关系кпопытаться найтиприезжатьподходящийизперспективная матрица преобразования.

Жесткая коррекция перспективы

Мы проводим первый эксперимент перспективной Коррекцияиз Воляда неизменно из пробы. Мы выполним следующие шаги:

  1. Волявходитьизображение Конвертировать для灰степень。
  2. использовать Детектор края Канни获Выбиратькрайизображение。
  3. использовать вероятность Преобразование Хафа обнаружить края изображения из линий.
  4. Найти приезжать, интересующих объектиз пограничного значения.
  5. Ориентировочная процентная ставка объектаиз границы напротив; поэтому,для называется жестким да, потому что дляобъект не обязательно должен иметь параллельные и противоположные стороны.,Данас Воляпроходитьв четырехугольникобъектиспользоватьпрямоугольник估计Приходить强制执行этот操делать。
  6. Составьте список валютиз-уголизм.
  7. Соответствие накладывается между углами экрана.
  8. использоватьверноотвечать关系получатьперспективная матрица преобразования.
  9. Матрица преобразования Воли приложение В входное изображение,Узнать точку зрения заинтересованных объектиз Коррекция.

определение пользовательского интерфейса

Мы добавим дополнительные пункты меню, чтобы начать процесс коррекции перспективы. изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
  android:id="@+id/action_rigidscan"
  android:enabled="true"
  android:orderInCategory="6"
  android:title="@string/action_rigidscan"
  android:visible="true">
</item>

использоватьобъектоценка ограничивающей рамкиперспективная трансформация

существовать Активностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методипроходитьвыбирать刚性扫描选элемент Приходитьдобавить в случае для работы с пользователями.

Первый шаг — убедиться, что пользователь загрузил изображение:

Язык кода:javascript
копировать
else if(id==R.id.action_rigidscan)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;

  }

Волявходитьизображение Конвертировать для灰степеньизображение:

Язык кода:javascript
копировать
Mat gray = new Mat();
Imgproc.cvtColor(sampledImage, gray, Imgproc.COLOR_RGB2GRAY);

использовать Детектор края Каннистроитькрайизображение:

Язык кода:javascript
копировать
Mat edgeImage=new Mat();
Imgproc.Canny(gray, edgeImage, 100, 200);

строитькрайизображениеназад,Нам нужны линии обнаружения,поэтомунасиспользовать Вероятностное преобразование линии Хафа:

Язык кода:javascript
копировать
Mat lines = new Mat();
int threshold = 50;
Imgproc.HoughLinesP(edgeImage, lines, 1, Math.PI/180, threshold,60,10);

Объявите и инициализируйте необходимые переменные.,Найти приезжающих, заинтересованных в изобъектиз до четырех пограничных линий,и отбросить все строки о самом существующем объекте, обнаруженном приездеиз,Для более точной оценки границы:

Язык кода:javascript
копировать
boolean [] include=new boolean[lines.cols()];
double maxTop=edgeImage.rows();
double maxBottom=0;
double maxRight=0;
double maxLeft=edgeImage.cols();
int leftLine=0;
int rightLine=0;
int topLine=0;
int bottomLine=0;
ArrayList<org.opencv.core.Point> points=new ArrayList<org.opencv.core.Point>();

существовать Вниз面изforциклсередина,Мы проверяем каждую строку, чтобы найти крайнюю левую границу интересующего объекта. После поиска места,нас Воля Чтоверноотвечатьизincludeчисло组元素настраиватьдляtrue,Чтобы избежать поиска других пограничных линий, выберите ту же строку еще раз:

Язык кода:javascript
копировать
for (int i = 0; i < lines.cols(); i++) 
{
  double[] line = lines.get(0, i);
  double xStart = line[0], xEnd = line[2];
  if(xStart<maxLeft && !include[i])
  {  
    maxLeft=xStart;
    leftLine=i;

  }
  if(xEnd<maxLeft && !include[i])
  {
    maxLeft=xEnd;
    leftLine=i;

  }
}
include[leftLine]=true;

Найдя линию приезжать, мы с Воля ее две точки добавляем. вприезжатьpointsчисло组списоксередина. Позже, когда мы оценим границу границы, Воляиспользовать этот список массивов:

Язык кода:javascript
копировать
double[] line = lines.get(0, leftLine);
double xStartleftLine = line[0], 
    yStartleftLine = line[1],
    xEndleftLine = line[2],
    yEndleftLine = line[3];

org.opencv.core.Point lineStartleftLine = new org.opencv.core.Point(xStartleftLine, yStartleftLine);
org.opencv.core.Point lineEndleftLine = new org.opencv.core.Point(xEndleftLine, yEndleftLine);

points.add(lineStartleftLine);
points.add(lineEndleftLine);

Ту же операцию проделаем, чтобы найти крайнюю правую границу проживания:

Язык кода:javascript
копировать
for (int i = 0; i < lines.cols(); i++) 
{
  line = lines.get(0, i);
  double xStart = line[0], xEnd = line[2];

  if(xStart>maxRight && !include[i])
  {
    maxRight=xStart;
    rightLine=i;

  }
  if(xEnd>maxRight && !include[i])
  {
    maxRight=xEnd;
    rightLine=i;

  }
}
include[rightLine]=true;

Воля принадлежит к крайней правой граничной линии из точки добавить вприезжатьpointsчисло组списоксередина:

Язык кода:javascript
копировать
line = lines.get(0, rightLine);
double xStartRightLine = line[0], 
    yStartRightLine = line[1],
    xEndRightLine = line[2],
    yEndRightLine = line[3];

org.opencv.core.Point lineStartRightLine = new org.opencv.core.Point(xStartRightLine, yStartRightLine);
org.opencv.core.Point lineEndRightLine = new org.opencv.core.Point(xEndRightLine, yEndRightLine);

points.add(lineStartRightLine);
points.add(lineEndRightLine);

Найдите верхнюю границу проживания:

Язык кода:javascript
копировать
```java
for (int i = 0; i < lines.cols(); i++) 
{
  line = lines.get(0, i);
  double yStart = line[1],yEnd = line[3];

  if(yStart<maxTop && !include[i])
  {
    maxTop=yStart;
    topLine=i;
  }
  if(yEnd<maxTop && !include[i])
  {
    maxTop=yEnd;
    topLine=i;

  }
}
include[topLine]=true;
```
  1. Воля В верхнюю границу из точки добавить вприезжатьpointsчисло组списоксередина:
Язык кода:javascript
копировать
```java
line = lines.get(0, topLine);
double xStartTopLine = line[0], 
    yStartTopLine = line[1],
    xEndTopLine = line[2],
    yEndTopLine = line[3];

org.opencv.core.Point lineStartTopLine = new org.opencv.core.Point(xStartTopLine, yStartTopLine);

org.opencv.core.Point lineEndTopLine = new org.opencv.core.Point(xEndTopLine, yEndTopLine);

points.add(lineStartTopLine);
points.add(lineEndTopLine);
```
  1. Найдите нижнюю границу проживания:
Язык кода:javascript
копировать
```java
for (int i = 0; i < lines.cols(); i++) 
{
  line = lines.get(0, i);
  double yStart = line[1],yEnd = line[3];
  if(yStart>maxBottom && !include[i])
  {
    maxBottom=yStart;
    bottomLine=i;

  }
  if(yEnd>maxBottom && !include[i])
  {
    maxBottom=yEnd;
    bottomLine=i;
  }
}
include[bottomLine]=true;
```
  1. Воля底线точкадобавить вприезжатьpointsчисло组списоксередина:
Язык кода:javascript
копировать
```java
line = lines.get(0, bottomLine);
double xStartBottomLine = line[0], 
    yStartBottomLine = line[1],
    xEndBottomLine = line[2],
    yEndBottomLine = line[3];

org.opencv.core.Point lineStartBottomLine = new org.opencv.core.Point(xStartBottomLine, yStartBottomLine);

org.opencv.core.Point lineEndBottomLine = new org.opencv.core.Point(xEndBottomLine, yEndBottomLine);

points.add(lineStartBottomLine);
points.add(lineEndBottomLine);
```
  1. насиспользоватьот Обнаружениеприезжатьизграница线серединавыбиратьизточкасписок ПриходитьинициализацияточкаматрицаMatOfPoint2fобъект:
Язык кода:javascript
копировать
```java
MatOfPoint2f mat=new MatOfPoint2f();
mat.fromList(points);
```
  1. наспроходить调использоватьImgproc.minAreaRect()и传入насранееинициализацияизточкаизматрица Приходитьпопытаться найтиприезжатьграницапрямоугольник。 Функция пытается найти жилье, которое соответствует набору точек и имеет все возможные суммы. маленькийобластьизпрямоугольник。 Когда нас интересует точка на линии границы, мы получаем линию границы:
Язык кода:javascript
копировать
```java
RotatedRect rect= Imgproc.minAreaRect(mat);

```
  1. Теперь мы оцениваем прямоугольник из четырех угловых точек, извлеченных из массива точек прибытия:
Язык кода:javascript
копировать
```java
org.opencv.core.Point rect_points[]=new org.opencv.core.Point [4];
rect.points(rect_points);
```
  1. После рентгеноскопии Коррекция,Инициализируйте Воля с помощью В, чтобы показать интерес к объекту изображения. Мы также будемиспользовать изображение из уголка найти жилье Transform,ксамый Эти углы соответствуют расстоянию между интересующими углами. поэтому,по сути,Мы пытаемся осуществить трансформацию (масштабирование,Ротация или Кастрюля),Преобразование Воля максимально приближает интересный объектиз Четыреугол к новому инициализирующему изображениюиз Четыреугол.
Язык кода:javascript
копировать
```java
Mat correctedImage=new Mat(sampledImage.rows(),sampledImage.cols(),sampledImage.type());
```
  1. сейчассуществовать,насинициализациядва个Matобъект,Интересующийся Вхранилищеобъектиз Четыреуголь,Еще один соответствующий ракурс с Вхранилищеизображениеиз.,существует перспектива Коррекция нашего Волясуществовать, который проявляет интерес изобъектов:
Язык кода:javascript
копировать
```java
Mat srcPoints=Converters.vector_Point2f_to_Mat(Arrays.asList(rect_points));
Mat destPoints=Converters.vector_Point2f_to_Mat(Arrays.asList(new org.opencv.core.Point[]{
  new org.opencv.core.Point(0, correctedImage.rows()),
    new org.opencv.core.Point(0, 0),
    new org.opencv.core.Point(correctedImage.cols(),0),
  new org.opencv.core.Point(correctedImage.cols(), correctedImage.rows())
}));
```
  1. наспроходить调использоватьImgproc.getPerspectiveTransform()и Воля Чтопередачаприезжатьисточники Цельрогточка Приходитьвычислитьнеобходимыйизизменять换матрица:
Язык кода:javascript
копировать
```java
Mat transformation=Imgproc.getPerspectiveTransform(srcPoints, destPoints);

```

наконец,насприложениепроходитьImgproc.warpPerspective()методипередачак Вниз参числовычислить出из Изменять换: * источникизображениеизMatобъект; В данном случае сразуда содержит объект изображения изображения. * Выходное изображениеизMatобъект * насхотетьприложениеизизменять换изMatобъект * один个Sizeобъект,использовать Вдержать Выходное изображениеизразмер

Язык кода:javascript
копировать
Imgproc.warpPerspective(sampledImage, correctedImage, transformation, correctedImage.size());

наконецодин步дасуществующееприложение целесообразно после переоборудования显示насзаинтересованныйизобъект:

Язык кода:javascript
копировать
```java
displayImage(correctedImage);
```

![Estimating the perspective transformation using the object bounding box](https://img-blog.csdnimg.cn/img_convert/630e6742b0ebbc8e52967ff35e32a70d.png)

До (слева) и после (справа) конвертации

Гибкая коррекция перспективы

сейчассуществовать,Мы выполнили жесткую Коррекцию,нас希望получать Дажехорошийизрезультат。 Как упоминалось ранее, используйте перспективную коррекцию основных причин поиска объекта с четырьмя углами угла. существовать“Жесткая коррекция В разделе «виды» мы используем оценку из границы, чтобы найти интересующий угол объекта; Да,Как вы знаете,пропорциональноиз Каждая противоположная сторона параллельна,Это может снизить перспективные результаты Коррекцияиз.,потому чтодлясейчас实世界серединаизпараллельные линиисуществовать投影час必须существоватьсказатьдлякартина片平面източка сходаиз地方Взаимно交。

поэтому,использовать параллельные линии, оценивать угловые точки, не отвлекаясь от лучшего выбора,Мы можем реализовать проектную линию «Воля» (от Преобразование Хафа найти место проживанияиз проекционных линий) сохранить существование на картинке и использовать простые геометрические фигуры найти место пересечения между ними, чтобы добиться большего,Для того, чтобы найти жилье Четыреуголь.

Мы Воля выполняем из шагов следующие:

  1. использовать Гауссов фильтр Волявходитьизображение Конвертировать для灰степеньигладкий。
  2. использовать Детектор края Каннипопытаться найтиприезжатькрайизображение。
  3. Приезжающий заинтересован в объектировании краевых линий с использованием вероятностного линейного преобразования Хафа.
  4. Проходит вычисление точек пересечения между всеми обнаруженными линиями проживания и находит изображение границ проживания в каждом углу.
  5. Используйте Найдите углы (вершины) приезда на предыдущем шаге, чтобы аппроксимировать другой многоугольник. Этот шаг необходим, чтобы минимизировать количество вершин и исключить бесполезные углы. Да,Мы по-прежнему сохраняем ту же структуру исходного многоугольника.
  6. Сейчас у нас существуют представители, заинтересованные в объектизсамом маленькие угловые наборы, нам нужно их рассортировать, так чтолевыйначальстворогпервый出сейчас,Затем в правом верхнем углу,нижний правый угол,наконецдалевый Внизрог。
  7. существования накладывает соответствие между сортировкой и з углом и углом экрана.
  8. использоватьверноотвечать关系получатьперспективная матрица преобразования.
  9. Матрица преобразования Воли приложение В входное изображение Узнать точку зрения заинтересованных объектиз Коррекция.

определение пользовательского интерфейса

Мы Воляиспользоватьодин个меню Приходитьзапускать Гибкая коррекция перспективыпроцесс。 изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
  android:id="@+id/action_flexscan"
  android:enabled="true"
  android:orderInCategory="7"
  android:title="@string/action_flexscan"
  android:visible="true">
</item>

приложение Гибкая коррекция перспективы

существоватьSoftScanner Активностьсередина,наснуждатьсяредактироватьonOptionesItemSelected()методидля Гибкое сканированиедобавить вновыйиз прописных и строчных букв:

Первый шаг — убедиться, что пользователь загрузил изображение:

Язык кода:javascript
копировать
else if(id==R.id.action_flexscan)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

Выполняем те же действия, чтобы получить края:

Язык кода:javascript
копировать
Mat gray = new Mat();
Imgproc.cvtColor(sampledImage, gray, Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(gray, gray, new Size(7,7), 0);

Mat edgeImage=new Mat();
Imgproc.Canny(gray, edgeImage, 100, 300);

Mat lines = new Mat();
int threshold = 100;
Imgproc.HoughLinesP(edgeImage, lines, 1, Math.PI/180, threshold,60,10);

насиспользоватьчиновник:

и:

Язык кода:javascript
копировать
ArrayList<org.opencv.core.Point> corners=new ArrayList<org.opencv.core.Point>();
for (int i = 0; i < lines.cols(); i++) 
{
  for (int j = i+1; j < lines.cols(); j++) 
  {
    org.opencv.core.Point intersectionPoint = getLinesIntersection(lines.get(0, i), lines.get(0, j));
    if(intersectionPoint!=null)
    {
      corners.add(intersectionPoint);
    }
  }
}

Сейчас существуют у нас пересечение,Нам нужно найти приезжающий другой полигон, чтобы обнаружить приезжающий полигон с такой же структурой, но с меньшим количеством вершин. для этого,насиспользоватьImgproc.approxPolyDP()метод,А Воля ему передаются следующие параметры:

один个Matобъект,С помощью Вхранилище мы находим угловые списки для проживания.

один个Matобъект,Это Воляхранилище аппроксимирует многоугольник из новых вершин.

Представляет максимальное расстояние между исходным многоугольником и приблизительным многоугольником и числом точности. существуют В этом случае,насиспользоватьImgproc.arcLength()методвычислить原始多边形изпериметр,Затем Воля Что乘кодин个Маленькийпотому чторебенок0.02,Затемиспользоватьрезультатнастраиватьдва个形状междуизмаксимумрасстояние。

логическое значение цены,Указывает, закрыта ли форма да.,существуем из примерадля:

Язык кода:javascript
копировать
MatOfPoint2f cornersMat=new MatOfPoint2f();
cornersMat.fromList(corners);

MatOfPoint2f approxConrers=new MatOfPoint2f();
Imgproc.approxPolyDP(cornersMat, approxConrers, Imgproc.arcLength(cornersMat, true)*0.02, true);

существуют. На этом этапе мы просто проверяем, что приближение из многоугольника k имеет как минимум четыре угла:

Язык кода:javascript
копировать
if(approxConrers.rows()<4)
{
  Context context = getApplicationContext();
  CharSequence text = "Couldn't detect an object with four corners!";
  int duration = Toast.LENGTH_LONG;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

Наш примерный список углов воля копироватьприезжать,Затем используйте этот список, чтобы найти центроид многоугольника.,Воляиспользовать Вверно近похожийрогточкаруководить排序。 Хорошо из аппроксимации центроида ценить все угловые точки аппроксимации из средней цены.

Язык кода:javascript
копировать
corners.clear();
Converters.Mat_to_vector_Point2f(approxConrers,corners);
org.opencv.core.Point centroid=new org.opencv.core.Point(0,0);
for(org.opencv.core.Point point:corners)
{
  centroid.x+=point.x;
  centroid.y+=point.y;
}
centroid.x/=corners.size();
centroid.y/=corners.size();

Теперь существуют, начинаем сортировать угловые точки по центроиду многоугольника. Сначала мы разбили их на два списка, в одном списке будут храниться Y Координаты малых В центра масс и з угла при вершине, второй список Воля Y Координаты: В центр масс и з базовый угол. Затем на основе списка в верхнем углу мы X Отсортируйте координаты сверху слева и сверху справа, и то же самое проделайте с нижним списком:

Язык кода:javascript
копировать
ArrayList<org.opencv.core.Point> top=new ArrayList<org.opencv.core.Point>();
ArrayList<org.opencv.core.Point> bottom=new ArrayList<org.opencv.core.Point>();

for (int i = 0; i < corners.size(); i++)
{
  if (corners.get(i).y < center.y)
    top.add(corners.get(i));
  else
    bottom.add(corners.get(i));
}

org.opencv.core.Point topLeft = top.get(0).x > top.get(1).x ? top.get(1) : top.get(0);

org.opencv.core.Point topRight = top.get(0).x > top.get(1).x ? top.get(0) : top.get(1);

org.opencv.core.Point bottomLeft = bottom.get(0).x > bottom.get(1).x ? bottom.get(1) :bottom.get(0);

org.opencv.core.Point bottomRight = bottom.get(0).x > bottom.get(1).x ? bottom.get(0) : bottom.get(1);

corners.clear();
corners.add(topLeft);
corners.add(topRight);
corners.add(bottomRight);
corners.add(bottomLeft);

Затем,картинасуществовать“Жесткая коррекция Как сделано в разделе «перспективы», устанавливаем соответствие между отсортированными ракурсами и изображением из:

Язык кода:javascript
копировать
Mat correctedImage=new Mat(sampledImage.rows(),sampledImage.cols(),sampledImage.type());
Mat srcPoints=Converters.vector_Point2f_to_Mat(corners);

Mat destPoints=Converters.vector_Point2f_to_Mat(Arrays.asList(new org.opencv.core.Point[]{
  new org.opencv.core.Point(0, 0),
  new org.opencv.core.Point(correctedImage.cols(), 0),
  new org.opencv.core.Point(correctedImage.cols(),correctedImage.rows()),new org.opencv.core.Point(0,correctedImage.rows())}));

наспроходить调использоватьImgproc.getPerspectiveTransform()и Воля Чтопередачаприезжатьисточники Цельрогточка Приходитьвычислитьнеобходимыйиз Изменять换матрица:

Язык кода:javascript
копировать
Mat transformation=Imgproc.getPerspectiveTransform(srcPoints, destPoints);

насприложениепроходитьImgproc.warpPerspective()методвычислить出из Изменять换:

Язык кода:javascript
копировать
```java
Imgproc.warpPerspective(sampledImage, correctedImage, transformation, correctedImage.size());

```
  1. наконец,существующееприложение целесообразно после переоборудования,Мы проявляем интерес изобъектов:
Язык кода:javascript
копировать
```java
displayImage(correctedImage);
```

![Applying flexible perspective correction](https://img-blog.csdnimg.cn/img_convert/63ce737ec34ff5efdb289fab8fcfc13c.png)

До (слева) и после (справа) конвертации

Ручная коррекция перспективы

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

Наша Воля следует шагам и приезжатьиз, как показано в разделе «Жесткая коррекция перспектив», очень похоже:

  1. позволятьиспользовать户выбиратьзаинтересованныйобъектиз Четыреугол。
  2. Найдите центр тяжести объекта.
  3. Отсортируйте выбранные углы по их центроидам объектов.
  4. существования накладывает соответствие между сортировкой и з углом и углом экрана.
  5. использоватьверноотвечать关系получатьперспективная матрица преобразования.
  6. Матрица преобразования Воли приложение В входное изображение,Узнать точку зрения заинтересованных объектиз Коррекция.

определение пользовательского интерфейса

Как только пользователь выберет четыре угла, мы добавим еще один пункт меню, чтобы запустить ручной процесс. изменятьприезжатьres/menu/soft_scanner.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<item
  android:id="@+id/action_manScan"
  android:enabled="true"
  android:orderInCategory="8"
  android:title="@string/action_manscan"
  android:visible="true">
</item>

Вручную выберите угловые точки

Пользователь выбирает, что интересует его из углового поста,Мы в «Воля» следуем тому же процессу. Но да,Советы по отображению координат на экране вашего устройства:

существовать АктивностьonCreate()методсередина,нас ВоляonTouch()事件иметь дело сустройство附加приезжатьImageView。 существовать事件иметь дело сустройствосередина,Сначала загружаем изображениеиз масштабного коэффициента с отображением В.,ВоляImageViewсередина Место选рогизкоординировать投影приезжатьнагрузкаизображение。 существования. После загрузки изображения для получения правильных координат следующие шаги такие же, как и до Воляи:

Язык кода:javascript
копировать
final ImageView iv = (ImageView) findViewById(R.id.SSImageView);
iv.setOnTouchListener(new OnTouchListener() {

  @Override
  public boolean onTouch(View view, MotionEvent event) {

    int projectedX = (int)((double)event.getX() * ((double)sampledImage.width()/(double)view.getWidth()));

    int projectedY = (int)((double)event.getY() * ((double)sampledImage.height()/(double)view.getHeight()));

    org.opencv.core.Point corner = new org.opencv.core.Point(projectedX, projectedY);

    corners.add(corner);

    Core.circle(sampledImage, corner, (int) 5, new Scalar(0,0,255),2);

    displayImage(sampledImage);
    return false;
  }
}); 

Нам нужно убедиться, что пользователь загружает изображение и выбирает четыре угла:

Язык кода:javascript
копировать
if(sampledImage==null)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to load an image first!";
  int duration = Toast.LENGTH_SHORT;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}
if(corners.size()!=4)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to select four corners!";
  int duration = Toast.LENGTH_LONG;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

Рассчитайте центр масс объекта и расположите четыре угла соответственно:

Язык кода:javascript
копировать
org.opencv.core.Point centroid=new org.opencv.core.Point(0,0);
for(org.opencv.core.Point point:corners)
{
  centroid.x+=point.x;
  centroid.y+=point.y;
}
centroid.x/=corners.size();
centroid.y/=corners.size();
sortCorners(corners,centroid);

Затем,картинасуществовать“Жесткая коррекция перспективы”часть:

Язык кода:javascript
копировать
Mat correctedImage=new Mat(sampledImage.rows(),sampledImage.cols(),sampledImage.type());
Mat srcPoints=Converters.vector_Point2f_to_Mat(corners);

Mat destPoints=Converters.vector_Point2f_to_Mat(Arrays.asList(new org.opencv.core.Point[]{
  new org.opencv.core.Point(0, 0),
  new org.opencv.core.Point(correctedImage.cols(), 0),
  new org.opencv.core.Point(correctedImage.cols(),correctedImage.rows()),
  new org.opencv.core.Point(0,correctedImage.rows())}));

Как сделано в из, строим сортировку по угловым точкам изображение соответствия между угловыми точками.

наспроходить调использоватьImgproc.getPerspectiveTransform()и Воля Чтопередачаприезжатьисточники Цельрогточка Приходитьвычислитьнеобходимыйиз Изменять换матрица:

Язык кода:javascript
копировать
Mat transformation=Imgproc.getPerspectiveTransform(srcPoints, destPoints);

насприложениепроходитьImgproc.warpPerspective()методвычислить出из Изменять换:

Язык кода:javascript
копировать
Imgproc.warpPerspective(sampledImage, correctedImage, transformation, correctedImage.size());

наконец,Мы существуемприложение после того, как соответствующее из преобразований показывает, что нас интересует из объекта:

Язык кода:javascript
копировать
displayImage(correctedImage);

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

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

существовать Внизодинглавасередина,нас Воля探讨другойдобрыйформаиз Особенности изображения, как их найти и почему они для них важны.

5. Приложение 3-Просмотр панорамы

существовать本главасередина,нас Воляначинатьразвиватьновыйизприложение。 Приложениеиз Цельда Воля две Сшивка изображениясуществовать вместе, чтобы сформировать мнение; нас Воляпредставлять Особенности изображенияизконцепцияи重хотеть性,Затем Воляэто们付诸实践。

Мы можем подвести итог следующим темам:

  • Обнаружение функций
  • Описание функции
  • сопоставление функций
  • Сшивка изображений

Особенности изображения

В этом разделе мы Волячимся Особенности изображенийиз значений и почему они для них важны.

представлять себе,遇见один个людии立Прямо сейчас Обнаружениеприезжать Долженлюдииз Лицо(Глаз,鼻ребенокимногодругойлюди Лицоособенность)из Состояние。 Вопрос в том, что нам делать? Чем мы руководствуемся при обнаружении этих черт лица? Как мы их опишем? Более того, когда мы смотрим на другого человека с такими же чертами лица, мы легко можем обнаружить различия между ними. функции。 Какую метрику мы используем для измерения этого сходства?

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

Эти характеристики считаются хорошими из характеристик.,Измерить преимущества и недостатки характеристик.,Мы должны учитывать его надежность и инвариантность (особенно инвариантность к масштабированию и вращению; например.,насизлюди Лицоособенность(Например Глаз)不Изменять) Пропорции лица, независимо от того, большое ли лицо или маленькое, вы легко определите, где находятся глаза). в целом,для Для достижения этой надежности,Наше обнаружение воли при использовании свойств качества рассматривается вместе с описанием функций метода из свойства качества.

Например,нас Волясмотретьприезжатьодин些детектор функций,Прямо сейчасХарриси FAST,Найдите объекты в одном масштабе (один масштаб),идругойдетектор функции (например, ORB)проходитьстроитьтак называемыйиз尺степеньнулевой间,существуют Находите объекты в нескольких масштабах.

Я нашел это прекрасной возможностью,Он знакомит с основными понятиями масштабного пространства.,Прямо сейчасиспользоватьдругойиз Пропорция尺уменьшить масштабметод Приходитьстроитьизображениепирамида。 Самый простой способ удалить X и Y направление из всех других пикселей. поэтому,Например,еслиты有один个100x100изизображение,ноот x и y серединаудалитьвседругой Пиксель Волягенерировать100x100изизображение。 Вы продолжаете повторять этот шаг, пока программа не будет готова. небольшой приемлемый диапазон.

детектор функций

первыйхотеть问изда,существуют Компьютерное зрение из фона,Какие характеристикидахорошийизособенность? Для ответа на этот вопрос лучше всего возьмем в качестве примера вершину горы. Мы можем начать рассматривать этот горный массив (прямоугольный 2)граница内изособенность,Но дапроблемане может быть найдена неоднократноприезжатьилине может адекватно описать эти характеристики,поэтомуэто们Воле сложно соответствовать.

Еще один кандидат, которого стоит искать, — это Да, используйте это. Мы уже существуем 3 глава,“приложение 2-Программный Узнайте, как обнаруживать края в «сканере», чтобы вы могли легко находить «добрый тип» среди таких функций, как приезжать. Но да, существует вопрос, как их однозначно описать, потому что для того, чтобы увидеть 1.1 и 1.2, их легко спутать с одним и тем же краем. этот个问题被сказатьдля Проблема с диафрагмой,такой же,Воле сложно соответствовать.

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

Узнайте об угловом детекторе Harris

Мы отвечаем на вопрос, какие характеристики являются хорошими, и приводим пример хорошей функции. Теперь нам нужно найти способ легко их обнаружить. Итак, давайте рассмотрим изображение вершины горы. Если начать со сканирования квадратного окна и зображения, то угол Воли имеет максимальное изменение интенсивности, так как переди края разные, два ортогональных направления Воли меняются, а край только по одному направлению (х или у) изменения 。

Основная идея этого углового точечного детектора да Харрис. нас试картинапопытаться найтиприезжатьодин个补丁,Если мы существуем в этом окне патча, перемещаем скан в разных направлениях,Это Волясуществовать производит максимум изменений или изменений интенсивности.

Детектор угла Харриса, инвариант вращения. Да,Он не является масштабно-инвариантным.

определение пользовательского интерфейса

создаватьиметьнулевой白АктивностьPanoActivityизновыйприложениеидобавить вот оборудование Библиотека нагрузки и зображенияиз Функция а также нагрузка OpenCV Библиотека之назад,нас Волясуществоватьменюэлементсерединадобавить в первом пункте меню, чтобы существовать, загрузить изображение и выполнить Харрисугловой детектор。 изменятьприезжатьres/menu/pano.xmlдокументи Открытьэток Сумка含к Внизменюэлемент:

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_harris" 
  android:orderInCategory="2" 
  android:title="@string/action_harris">
</item>
использовать Харрисугловой детектор

OpenCV для Вы предоставили разные из Объекты интересаилидетектор функции, и API иметьочень простоизинтерфейс,Можетиспользовать Вдобрыйorg.opencv.features2dFeatureDetectorиметь工厂метод,и присвоен идентификатор детектора, возвращает фабричный метод Воля и это ID Издетектор корреспонденции функциииз примеров.

нас ДаженовыйonOptionsItemSelectedкиметь дело с Харрисменюэлемент:

Язык кода:javascript
копировать
if(sampledImage==null)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to load an image first!";
  int duration = Toast.LENGTH_SHORT;

  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

Mat greyImage=new Mat();
MatOfKeyPoint keyPoints=new MatOfKeyPoint();
Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);

FeatureDetector detector = FeatureDetector.create(FeatureDetector.HARRIS);
detector.detect(greyImage, keyPoints);

Features2d.drawKeypoints(greyImage, keyPoints, greyImage);

displayImage(greyImage);

Действия очень просты и заключаются в следующем:

наспервый Волявходитьизображение Конвертировать для灰степеньи实例化关键точкаобъектизматрица:

Язык кода:javascript
копировать
Mat greyImage=new Mat();
MatOfKeyPoint keyPoints=new MatOfKeyPoint();
Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);

насиспользоватьFeatureDetector.create工厂метод实例化насвыбиратьиздетектор функции и передайте их ID:

Язык кода:javascript
копировать
FeatureDetector detector = FeatureDetector.create(FeatureDetector.HARRIS);

использоватьк Вниз Заказ调использоватьdetectметод:

Язык кода:javascript
копировать
detector.detect(greyImage, keyPoints);

调использоватьdetectметодк查попытаться найтииметьк Вниз参числоиз兴趣точка:

  • представлятьвходитьизображениеизMatобъект
  • MatOfKeyPointобъект,Обнаружение приездов в точки интереса с помощью Вхранилище

для появился, чтобы обнаружить местонахождение POI,нас调использоватьFeature2d.drawKeypoints()

Язык кода:javascript
копировать
Features2d.drawKeypoints(greyImage, keyPoints, greyImage);

насиспользоватьк Вниз参число调использоватьFeature2d.drawKeypoints()

  • делатьдлявходитьизображениеизMatобъект
  • хотетьрисоватьизMatOfKeyPoint
  • Выходное изображениеизMatобъект

наконец,Обнаружение дисплея. Изображение POI:

Язык кода:javascript
копировать
displayImage(greyImage);
Позвоните в местный угловой детектор Harris.

существует много ситуаций,Вам нужен ответ в режиме реального времени,Например Обнаружениесотовый телефон摄картинаголоваиз Видеть频источниксерединаизособенность。 полагаться только на Java Вызов может не обеспечить требуемой производительности и, следовательно, не соответствовать установленным срокам. существования В этом случае каждая секунда превышает 20 рамка; Это сразу для того, что я думаю, это познакомит вас с этой машиной OpenCV API из Хорошая возможность. Тебе не нужен привычный C++。 Да,Изучить структуру языка Воля - это очень помогает.

Первое, что нам нужно сделать, — это добавить в проект поддержку C++.

существовать Eclipse серединаиспользоватьлокальная машина OpenCV Библиотека
  1. Щелкните правой кнопкой мыши по существупроект Просматривать изпроектимя.
  2. Перейдите кНовый | другой | C/C++ | Конвертировать для C/C++ проект
  3. выбирать“Makefile проект”,выбирать“другойинструментцепь”,Затемщелкнуть“Заканчивать”:
  1. определениепеременные средыNDKROOT,Наведите курсор на NDK из основной папки документов.,НапримерC:\NVPACK\android-ndk-r10c
  2. Щелкните правой кнопкой мыши по существупроект Просматриватьизпроектимя.,Затемвыбиратьсвойство
  3. Нажмите на узел дерева Сборка C/C++。 существоватьстроитьустройствонастраиватьвкладкасередина,Прозрачныйиспользоватьпо умолчанию Команда сборкифлажок,ЗатемсуществоватьКоманда сборки文本框серединавходитьк Внизсодержание:${NDKROOT}/ndk-build.cmd
  4. Перейдите на вкладку «ОК для», затем выберите «Существовать в группе «Workbench build OK для», выберите «База в сохранении ресурсов» и снимите текстовое поле «Сделать строить Цель». Снимите флажок «строить (инкрементную сборку)» и текстовое поле «Сделать сборку Цель»:
  1. В это время позвоните NDK генерироватьпроект Волянеудача,и решить эту проблему,Нам нужно создать новую папку документов в папке существующего документа.,и Воля Чтоимядляjni
  2. существоватьэтотдокументпапкасередина,нас Воля Есть тридокумент:
  1. Android.mkизсодержаниеотвечатьследующее:
Язык кода:javascript
копировать
```java
LOCAL_PATH := $(call my-dir)

include$(CLEAR_VARS)

# Must include the opencv.mk file, change the path accordingly include C:\NVPACK\OpenCV-2.4.8.2-Tegra-sdk\sdk\native\jni\OpenCV-tegra3.mk

# Name the library and list the cpp source files
LOCAL_MODULE    := Pano
LOCAL_SRC_FILES := Pano.cpp
LOCAL_LDLIBS +=  -llog -ldl
include$(BUILD_SHARED_LIBRARY)
```
  1. Application.mkиз含量отвечатьследующее:
Язык кода:javascript
копировать
```java
APP_PLATFORM := android-9
APP_ABI := armeabi-v7a
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
```
  1. дляcppдокумент,оно может быть пустым,И содержит только заголовок:
Язык кода:javascript
копировать
```java
#include <jni.h>
```
  1. Создать проект.
  2. Нам нужно включить несколько каталогов, чтобы мы могли писать C++ кодииспользоватьСтандартный шаблон БиблиотекаSTL)и OpenCV。 для Для этого нажмите правой кнопкой мыши на "проектимя | свойство | C/C++ общепринятый | путь и символ”。
Язык кода:javascript
копировать
### Уведомление

STL для Вы предоставляете набор готовых издобрых решений, реализующих различные структуры данных и алгоритмы.
  1. выбирать GNU C++,добавить вк Вниз Оглавление,Затемв соответствии стыиз Установить Даже改путь:
Язык кода:javascript
копировать
```java
${NDKROOT}/platforms/android-9/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/ armeabi-v7a/include
C:\NVPACK\OpenCV-2.4.8.2-Tegra-sdk\sdk\native\jni\include
```
существовать Android Studio серединаиспользоватьлокальная машина OpenCV Библиотека

существоватьпроект Видетькартинасередина,Щелкните правой кнопкой мыши узел приложения.,ЗатемвыбиратьОткрытьмодульнастраиватьилив соответствии сF4

Выберите «SDK. Расположение". существовать“Android NDK Местоположение, выберите NDK Местосуществоватьиз Оглавление。 Пожалуйста Уведомление, мы Воляиспользовать экспериментально Gradle Версия плагина 2.5 построить проект; Поэтому нам нужно NDK Версия r10e:

Если вы используетеизда Android Studio 1.3.2,нонуждаться Даженовыйgradle-wrapper.propertiesи Даже改точка发 URL-адрес, как показано ниже:

Язык кода:javascript
копировать
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

существоватьпроектизbuild.gradleдокументсередина,Обновите путь Зависимостидобрый следующим образом:

Язык кода:javascript
копировать
dependencies {classpath 'com.android.tools.build:gradle-experimental:0.2.0'}

существоватьпроектдокументпапкасередина,существоватьapp\src\mainВнизсоздаватьдва个документпапкаjniиjniLibs

существоватьjniдокументпапкасередина,Создать новый документ,и Воля ЧтоимядляPano.cpp

сейчассуществовать,Навигацияприжатьез<OpenCV4AndroidSDKFolder>\sdk\native\libs\,и ВолявседокументпапкакопироватьприезжатьновыйсоздаватьизjniLibsдокументпапкасередина. тыизпроект Деревоотвечать Как показано ниже:

наснуждаться Даженовыйbuild.gradleсерединаизязык, специфичный для доменаDSL),Чтобы наш модуль мог использовать и Gradle 2.5 вместе. для этого,пожалуйста Даженовыйстроитьдокументтак, чтобы оно имяо Следующее сопоставление и оставляет зависимые методы без изменений. Пожалуйста, Уведомление, вашей Воля необходимо обновить абсолютный путь, чтобы он соответствовал вашему из Установить:

Язык кода:javascript
копировать
applyplugin: 'com.android.model.application' model {
  android {
    compileSdkVersion = 23 buildToolsVersion = "23.0.1" defaultConfig.with {
      applicationId = "com.app3.pano" minSdkVersion.apiLevel = 15 targetSdkVersion.apiLevel = 19 versionCode = 1 versionName = "1.0"
    }
  }
  //Make sure to build with JDK version 7
  compileOptions.with {
    sourceCompatibility=JavaVersion.VERSION_1_7 targetCompatibility=JavaVersion.VERSION_1_7
  }
  android.ndk {
    moduleName = "Pano" ldLibs += ['log']
    cppFlags += "-std=c++11" cppFlags += "-fexceptions" cppFlags  += "-I${file("<OpenCV4AndroidSDK_Home>/sdk/native/jni/include")}".toString()
    cppFlags  += "-I${file("<OpenCV4AndroidSDK_Home>/sdk/native/jni/include/opencv")}".toString()
    ldLibs += ["android", "EGL", "GLESv2", "dl", "log", "z"]// , "ibopencv_core" stl = "gnustl_shared}
    android.buildTypes {
      release {
        minifyEnabled= false proguardFiles+= file('proguard-rules.pro')
      }
    }
    android.productFlavors {
      create("arm") {
        ndk.with {
          abiFilters += "armeabi" File curDir = file('./')
          curDir = file(curDir.absolutePath)
          String libsDir = curDir.absolutePath+"\\src\\main\\jniLibs\\armeabi\\" //"-L" + ldLibs += libsDir + "libopencv_core.a" ldLibs += libsDir + "libopencv_imgproc.a" ldLibs += libsDir + "libopencv_java.so" ldLibs += libsDir + "libopencv_features2d.a"
        }
      }
      create("armv7") {
        ndk.with {
          abiFilters += "armeabi-v7a" File curDir = file('./')
          curDir = file(curDir.absolutePath)
          String libsDir = curDir.absolutePath+"\\src\\main\\jniLibs\\armeabi-v7a\\" //"-L" + ldLibs += libsDir + "libopencv_core.a" ldLibs += libsDir + "libopencv_imgproc.a" ldLibs += libsDir + "libopencv_java.so" ldLibs += libsDir + "libopencv_features2d.a" 
        }
      }
      create("x86") {
        ndk.with {
          abiFilters += "x86"
        }
      }
      create("mips") {
        ndk.with {
          abiFilters += "mips"
        }
      }
      create("fat") {
      }
    }
  }
}

наконец,наснуждатьсядля OpenCV модуль Даженовыйbuild.gradleдокумент,Чтобы следующие совпадения:

Язык кода:javascript
копировать
apply plugin: 'com.android.model.library' model {
  android {
    compileSdkVersion = 23 buildToolsVersion = "23.0.1" defaultConfig.with {
      minSdkVersion.apiLevel = 15 targetSdkVersion.apiLevel = 19
    }
  }
  //Make sure to build with JDK version 7
  compileOptions.with {
    sourceCompatibility=JavaVersion.VERSION_1_7 targetCompatibility=JavaVersion.VERSION_1_7
  }
  android.buildTypes {
    release {
      minifyEnabled= false proguardFiles+= file('proguard-rules.pro')
    }
  }
}

Существовать и в то же время строить проект.

Обработка родной части

Что бы вы ни выбрали IDE , вы можете выполнить следующие действия: добавить собственный код Воля вприажатьприложениесередина:

ОткрытьPano.cppидобавить Следующий код; Наш код Воляпрохождения позже:

Язык кода:javascript
копировать
#include<jni.h>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<vector>

extern"C" {

  JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindHarrisCorners(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
  {
    cv::Mat& mGr  = *(cv::Mat*)addrGray;
    cv::Mat& mRgb = *(cv::Mat*)addrRgba;

    cv::Mat dst_norm;
    cv::Mat dst = cv::Mat::zeros(mGr.size(),CV_32FC1);

    //the size of the neighbor in which we will check 
    //the existence of a corner
    int blockSize = 2;
    //used for the Sobel kernel to detect edges before 
    //checking for corners
    int apertureSize = 3;
    // a free constant used in Harris mathematical formula
    double k = 0.04;    
    //corners response threshold
    float threshold=150;

 cv::cornerHarris( mGr, dst, blockSize, apertureSize, k, cv::BORDER_DEFAULT );

    cv::normalize( dst, dst_norm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat() );

    for( unsignedint i = 0; i < dst_norm.rows; i++ )
    {
      float * row=dst_norm.ptr<float>(i);
      for(int j=0;j<dst_norm.cols;j++)
      {
        if(row[j]>=threshold)
        {
          cv::circle(mRgb, cv::Point(j, i), 10, cv::Scalar(255,0,0,255));
        }
      }
    }
  }
}

нассуществоватьPanoActivityдобрыйсередина声明Понятнолокальная машинаметод,чтобы нативный код можно было вызвать позже:

Язык кода:javascript
копировать
public native void FindHarrisCorners(long matAddrGr, long matAddrRgba);

Мы объявляем нативные методы при создании нативной Библиотеки и существующих активностей.,Но да, когда мы пытаемся вызвать собственный метод,будет взимать платуприезжатьjava.lang.UnsatisfiedLinkError,Потому что для еще не загрузил родную Библиотеку. для этого,нас Даже改onManagerConnected()методксуществовать OpenCV Загрузите собственную библиотеку после инициализации:

Язык кода:javascript
копировать
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  @Override
  public void onManagerConnected(int status) {
    switch (status) {
      case LoaderCallbackInterface.SUCCESS:
      {
        Log.i(TAG, "OpenCV loaded successfully");
        // Load native library after(!) OpenCV initialization
 System.loadLibrary("Pano");
      } break;

      default:
      {
        super.onManagerConnected(status);
      } break;
    }
  }
};

Теперь мы готовим элемент меню «Использовать встроенную Библиотеку», чтобы вызвать встроенный угловой детектор Харриса. поэтому,Открытьres/menu/pano.xmlидобавить Следующие предметы:

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_nativeHarris" 
  android:orderInCategory="2" 
  android:title="@string/action_nativeHarris">
</item>

существоватьPanoActivityсередина,Даже改onOptionsItemSelected()киметь дело слокальная машина Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_nativeHarris)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  Mat greyImage=new Mat();
  Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);

  FindHarrisCorners(greyImage.getNativeObjAddr(),sampledImage.getNativeObjAddr());

  displayImage(sampledImage);
}

У нас есть список звонков Харрисугловой. Детектиз самостоятельно реализует необходимые шаги; Однако нам все равно нужно внимательно прочитать C++ Кодируйте детали, чтобы изучать то, что мы делаем, чтобы вы могли расширять и развивать Всуществовать идеи здесь. Конечно, с C++ Основная идея построения языка из Воля очень полезна.

наспервый Сумка含необходимыйизголовадокументсписок:

Язык кода:javascript
копировать
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<vector>

в соответствии сэтотимя约定Java_Fully_Qualified_Class_Name_MethodName声明Мы Воляиспользоватьизфункция. нассуществоватьPanoActivityсередина声明изметодтолько采использоватьдва个参число:灰степеньицветизображениеизадрес; Однако собственный метод требует четырех. Первые два всегда существуют, какие-либо JNI метод声明серединаиспользовать。 Последние два соответствуют букве В, которую мы отправляем по адресу (существовать). Java серединаjlongкартографированиеприезжатьlong):

Язык кода:javascript
копировать
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindHarrisCorners(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)

нас Воля Ссылка отправлена ​​наMatссылка,Один из них использует изображение в оттенках серого.,Другой использует Вцветное изображение:

Язык кода:javascript
копировать
cv::Mat& mGr  = *(cv::Mat*)addrGray;
cv::Mat& mRgb = *(cv::Mat*)addrRgba;

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

Язык кода:javascript
копировать
cv::Mat dst_norm;
cv::Mat dst = cv::Mat::zeros(mGr.size(),CV_32FC1);
int blockSize = 2;
intapertureSize = 3;
double k = 0.04;
float threshold=150;

Мы Воля Харрисугловой детекторизместный实сейчассказатьдля“实сейчас”,и Волярогточкаиз响отвечать归один化для0и255между:

Язык кода:javascript
копировать
cv::cornerHarris( mGr, dst, blockSize, apertureSize, k, cv::BORDER_DEFAULT );
cv::normalize( dst, dst_norm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat() );

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

Язык кода:javascript
копировать
for( unsignedint i = 0; i < dst_norm.rows; i++ )
{
  float * row=dst_norm.ptr<float>(i);
  for(int j=0;j<dst_norm.cols;j++)
  {
    if(row[j]>=threshold)
    {
    cv::circle(mRgb, cv::Point(j, i), 10, cv::Scalar(255,0,0,255));
    }
  }
}

На фото слева даиспользовать Java Упаковкапрограммаиз HCD, на фото справа эта машина HCD

Узнайте больше об угловом детекторе FAST.

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

позволятьнас考虑один个ПиксельP。еслинассуществовать ПиксельPизкруглый形Районсередина测试 16 пикселей, и среди них 12 个Пиксельизсила大Вили Маленький ВPизсила加/уменьшатьa,но说Pдаодин个潜существоватьиз兴趣точкаилирог。 порог.

Этот процесс очень трудоемкий,поэтомудля Понятно加快Обнаружение速степень,предлагать Понятно另один种测试метод。 Долженалгоритмпервыйсуществоватьидентификация Расположение(1、9、5、13)только测试 4 пиксели; если Чтосередина三个大Вили Маленький ВPизсила加/уменьшатьпорогценить,но继续другой 8 пиксели; В противном случае этот пиксель будет отброшен:

определение пользовательского интерфейса

Воляк Внизменюэлементдобавить вприезжатьres/menu/pano.xml

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_fast"
  android:orderInCategory="4"
  android:title="@string/action_fast">
</item>
использовать FAST угловой детектор

ОткрытьPanoActivityиредактироватьonOptionsItemSelected()квключатьк Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_fast)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  Mat greyImage=new Mat();
 Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);

MatOfKeyPoint keyPoints=new MatOfKeyPoint();
  FeatureDetector detector=FeatureDetector.create(FeatureDetector.FAST);

  detector.detect(greyImage, keyPoints);
  Features2d.drawKeypoints(greyImage, keyPoints, greyImage);  
  displayImage(greyImage);
}

Как упоминалось ранее, OpenCV Имеет очень простой интерфейс и заводской метод для создания различных детекторов. Детекторы Харриса FAST Единственная разница между изда, который мы отправляем в фабричный метод, заключается в следующих параметрах:

Язык кода:javascript
копировать
FeatureDetector detector = FeatureDetector.create(FeatureDetector.FAST);

Остальная часть кода точно такая же.

использоватьлокальная машина FAST

существовать本节середина,нас Воля КPanoActivityдобрыйдобавить Еще один нативный метод, называемый приезжать в нативной реализации Воли. FAST угловой детектор:

Откройте класс активности и добавьте следующее объявление:

Язык кода:javascript
копировать
public native void FindFastFeatures(long matAddrGr, long matAddrRgba);

Этот метод имеет два параметра. Первый — для изображения адреса в оттенках серого, второй — для цветной версии адреса.

Воляк Внизметоддобавить вприезжатьPano.cppдокументсередина:

Язык кода:javascript
копировать
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindFastFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
{
  cv::Mat& mGr  = *(cv::Mat*)addrGray;
  cv::Mat& mRgb = *(cv::Mat*)addrRgba;
  std::vector<cv::KeyPoint> v;

 cv::FastFeatureDetector detector(50);
 detector.detect(mGr, v);
  for( unsignedint i = 0; i < v.size(); i++ )
  {
    const cv::KeyPoint& kp = v[i];
    cv::circle(mRgb, cv::Point(kp.pt.x, kp.pt.y), 10, cv::Scalar(255,0,0,255));
  }
}

существовать Переднийизкодсередина,наспервый实例化关键точкаиз К量ипорогценитьдля50изFastFeatureDetectorобъект,ипроходить传入灰степеньизображениеи关键точкаизнулевой К量Приходить调использоватьdetectionметод。 Затем,Мы рисуем круг для каждой обнаруженной ключевой точки прибытия.

нассуществоватьres/menu/pano.xmlсерединадобавить Добавлен еще один пункт меню:

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_nativefast" 
  android:orderInCategory="5" 
  android:title="@string/action_fastnative">
</item>

наконец,ОткрытьPanoActivityиредактироватьonOptionsItemSelected()к Сумка含к Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_nativefast)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  Mat greyImage=new Mat();
  Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);
 FindFastFeatures(greyImage.getNativeObjAddr(),sampledImage.getNativeObjAddr());

  displayImage(sampledImage);
}

На фото слева даиспользовать Java Упаковкапрограммаиз БЫСТРО, изображение справа — локальная машина FAST

учиться ORB детектор функций

OpenCV Labs из Еще один важный детектор,Также дескриптор,дадва个非常有имяноуже申пожалуйста专利изалгоритм(преобразование масштабно-инвариантного объектаSIFT)иНадежные функции ускоренияSURF)из替代物 ORB。 хотетьиспользовать SIFT и СЕРФ, вы платите; Однако ОРБ Существование обеспечивает бесплатную выборку с точки зрения затрат на расчеты и соответствия производительности.

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

  • ORBпервыйиспользовать FAST алгоритм Обнаружение точек интереса или угловых точек
  • Он использует Харрис (Harris) для каждого угла указанной фракции (на основе B обнаруживает перемещение угла по изменениям интенсивности вблизи угла)
  • Он ранжирует точки интереса по баллам и рассматривает верхние N°
  • Использование изображения пирамиды генерирует точки интереса в нескольких масштабах, а FAST обнаруживает точки интереса в одном масштабе.
  • Он вычисляет окрестность точки интереса на основе центроида, взвешенного по интенсивности.
  • Алгоритм использует точку интереса и центроид для расчета направления этого вектора, а воля указывает направление для точки интереса; Описывающая часть этого шага очень важна.
определение пользовательского интерфейса

Воляк Внизменюэлементдобавить вприезжатьres/menu/pano.xml

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_orb"
  android:orderInCategory="6"
  android:title="@string/action_orb">
</item>
использовать ORB детектор функций

наснуждатьсясуществоватьPanoActivityдобрыйсерединаредактироватьonOptionsItemSelected()квключатьк Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_orb)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  Mat greyImage=new Mat();
  Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);
  MatOfKeyPoint keyPoints=new MatOfKeyPoint();

 FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);

  detector.detect(greyImage, keyPoints);
  Features2d.drawKeypoints(greyImage, keyPoints, greyImage);  
  displayImage(greyImage);
}

существоватьдругойдетектор Переключаться между функциями очень легко. мы просто ORB из ID передача给factoryметод,Затем调использоватьdetectметод。

использоватьлокальная машина ORB

существовать本节середина,Мы Воляиспользовать ORB Детектор реализован изначально, а этап предварительной обработки перенесен. CPP файл, чтобы JNI Стоимость звонка из снижается, если приезжать только за один звонок:

ОткрытьPanoActivityдобрыйидобавить Следующее заявление:

Язык кода:javascript
копировать
public native void FindORBFeatures(long matAddrRgba, int featuresNumber);

Этот метод принимает два параметра,Прямо сейчасместныйобъектизадресихотеть Обнаружениеизмаксимумособенностьчисло。

существоватьPano.cppсередина,добавить Реализованы следующие методы:

Язык кода:javascript
копировать
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindORBFeatures(JNIEnv*, jobject, jlong addrRgba, jint featuresNumber)
{
  cv::Mat& mRgb = *(cv::Mat*)addrRgba;
  cv::Mat grayImg;
  std::vector<cv::KeyPoint> v;

 cv::cvtColor(mRgb,grayImg,cv::COLOR_RGBA2GRAY);

 cv::OrbFeatureDetector detector(featuresNumber);

 detector.detect(grayImg, v);

 cv::drawKeypoints(grayImg,v,mRgb,cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
}

нас Воля Воляцветизображение Конвертировать дляPano.cppиз预иметь дело сшагруководить Понятноиметь дело с。 наспроходить调использоватьcv::cvtColorипередачавходитьизображение,Выходные изображения и код сопоставления для достижения. Затем,нас实例化один个ORBFeatureDetectorобъект,Объектиз максимального количества признаков и т.д. В отправляем из параметров.

существовать Внизодин行,нас调использоватьdetectметод。 наконец,насиспользоватьcv::drawKeypointsметодрисовать关键точка,и передать входное изображение (определить ключевые точки с помощью Визизображения),KeyPointиз К量,Выходное изображение,использовать Врисовать关键точкаизцвет(using cv::Scalar::all(-1)выражать (использоватьизцвет Волядаслучайныйиз),Последний знак да используется в качестве каждой ключевой точки и круга.,Его размер равен размеру характерной точки, а направление рисуется характерной точки.

Воляк Внизменюэлементдобавить вприезжатьres/menu/pano.xml

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_nativeorb" 
  android:orderInCategory="7" 
  android:title="@string/action_orbnative">
</item>

наконец,ОткрытьPanoActivityиредактироватьonOptionsItemSelected()к Сумка含к Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_nativeorb)
{
  if(sampledImage==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an image first!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  Mat copy=sampledImage.clone();
 FindORBFeatures(copy.getNativeObjAddr(),100);
  displayImage(copy);  
}

На фото слева даиспользовать Java Упаковкапрограммаиз ORB, картинка справа да имеет характерные пропорции и ориентацию, родные ORB

Характеристика и сопоставление

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

Вы можете следить за местностью по форме (прямоугольникили круг).,Режим выборки (плотная выборка,Чтосередина局部областьсерединаизвсе Пиксель Все Воляверноособенностьописыватьили Разреженная выборка способствует)верно Дескриптор функциируководитьточкадобрый ) и спектр (двоичный, который описывает вектор Волятолькодля 1 и 0 илииспользоватьлюбойскалярценитьилидругойценитьизскаляр)。

OpenCV Предоставляет различные виды продукции функции。 Да,существовать本节середина,потому что SIFT и Дескрипторы SURF (плотные и скалярные) — чтобы запатентовать алгоритм, за их использование нужно платить, поэтому мы только сосредоточимся на разреженном двоичном дескрипторе (также называемом локальным двоичным дескриптором).

использовать метод выборки пар пикселей,Независимо от формы дескриптора,может вычислять локальные двоичные дескрипторы,существоватьэтотметодсередина,сравнить выбранныеиз Пиксельвернокгенерироватьвыражатьописывать К量издвоичный字符串。 Например,еслинас有одинверно Пиксель(P1, P2),но比较P1иP2изсила。 еслиP1изсила大ВP2,но Воля1放入описывать К量середина,в противном случае Воля Вставлять入0

учиться BRIEF и ORB Дескриптор функции

Двоичные робастные независимые базовые характеристикиBRIEF)дескрипторпризнанныйдлядапредлагатьиз最简одинтакжеда Нет.один个местныйдвоичныйдескриптор。 для Понятноиспользовать长степеньдля N изописывать К量описывать兴趣точка,Алгоритмпрохождения несколькими случайными методами (единообразный,Гауссовскийждать)существовать31x31色块областьсерединавыбирать Понятно N пары случайных пикселей и сравнивают их, чтобы построить двоичную строку.

для ORB, дескриптор проводит Воля точки интереса поворачивается в каноническое направление (при условии, что наш существующий этап обнаружения знает доминирующее направление точки интереса) Направление Воли добавить вприезжать BRIEF, затем вычисляет описание. В результате мы достигаем некоторой вращательной инвариантности. Например, если точка интереса доминирует в направлении 90 степень,носуществоватьиспользовать ORB Перед описанием точку интереса Воли и ее окрестности поворачивают так, чтобы она была направлена ​​вверх (направление = 0), а затем описать интересующую точку, чтобы добиться инвариантности вращения.

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

использовать метод рандомизации (BRIEF) или метод обучения из выборки (ORB) Выбор пар пикселей приводит к асимметричным формам дескрипторов,Как показано ниже:

учиться BRISK Дескриптор функции

Ключевые моменты: бинарный, надежный, инвариантный, масштабируемыйBRISK)Создание дескрипторасуществоватьк Четыре个同心环排列из 60 точек, поэтому форма выборки пары точек круглая и симметричная. каждыйточкапредставлятьодин个круглый形采样область(использовать Ввыбирать采样верно),По мере удаления от точки интереса,Площадь увеличится.

для вычисляет направление,использовать Гауссов Фильтр сглаживает каждую выбранную область, а затем вычисляет локальный градиент. Разделение выборки пополам разделено на две группы: длинные сегменты, расстояние между двумя парами велико до определенного порога, а локальный градиент используется вместе для расчета угла направления для направления точки интереса, обеспечивая при этом инвариантность вращения. Второй добрыйда короткий отрезок, в котором расстояние между двумя парами низкое В другом пороге ценить и сравнивать с Впроходить 512 построить 512 Битовый двоичный дескриптор. На картинке ниже описано BRISK Зона отбора проб:

учиться FREAK Дескриптор функции

Быстрые клавиши сетчаткиFREAK)дескрипторизкруглый形形状дабаза Влюдииз Видеть网膜система,Среди них центр плотности рецепторных клеток является самым высоким.,и随着нас离开и降低。 для режима выборки используйте алгоритм автономного обучения, чтобы изучить оптимальные пары пикселей, чтобы максимизировать дисперсию пар точек и самые корреляция миниатюризации.

соответствующие функции

После того, как вы определили соответствующий дескриптор для своих нужд, сразу же необходимо выбрать функцию расстояния, чтобы определить функций。 В зависимости от вашего дескриптора выбора, можно использовать множество функций расстояния. дляместныйдвоичныйособенность,любимыйизвыбиратьдаРасстояние Хэммингак测量два个ждать长двоичный字符串междуизразница。 Операция очень эффективна и быстра, поскольку для нее можно использовать инструкции машинного языка или XOR За операцией следует подсчет битов, которые необходимо выполнить.

использоватьсопоставление функций

существовать本частьсередина,Обновления нашей Воля,Чтобы можно было Воля смешивать разные детекторы с разными дескрипторами использовать,Чтобы найти жилье, подходящее по характеристикам.

определение пользовательского интерфейса

Мы волясуществоватьприложениеменю в определении двух групп. Один с набором детекторов, а другой с набором дескрипторов. Мы также будемдобавить В пункте меню вы можете существовать там, где депутаты хотят найти проживание из объекта в данном сценарии. Открытьres/menu/pano.xmlидобавить вк Внизпроект:

Язык кода:javascript
копировать
<item android:orderInCategory="8" android:id="@+id/detector" android:title="@string/list_detector">
  <menu><group android:checkableBehavior="single">
    <item android:id="@+id/harris_check"
      android:title="@string/action_harris"/>
    <item android:id="@+id/fast_check"
      android:title="@string/action_fast" android:checked="true"/>
    <item android:id="@+id/orbD_check"
      android:title="@string/action_orb" />
  </group></menu>
</item>

<item android:orderInCategory="9" android:id="@+id/descriptor" android:title="@string/list_descriptor">
  <menu><group android:checkableBehavior="single">
    <item android:id="@+id/BRIEF_check"
      android:title="@string/action_brief"/>
    <item android:id="@+id/ORB_check"
      android:title="@string/action_orb" android:checked="true"/>
    <item android:id="@+id/BRESK_check"
      android:title="@string/action_brisk"/>
    <item android:id="@+id/FREAK_check"
      android:title="@string/action_freak"/>
  </group></menu>
</item>

<item android:id="@+id/action_match"
  android:orderInCategory="10"
  android:title="@string/action_match">
</item>

<item
  android:id="@+id/action_selectImgToMatch"
  android:orderInCategory="1"
  android:showAsAction="never"
  android:title="@string/action_selectImgToMatch"/>
Найдите объекты на сцене

Мы, Воля, следуем этой процедуре, чтобы найти приезжатьобъект в данном сценарии.первый,Загрузить сцену,Затем загрузите объектизображение,наконецвыбиратьmatch。 хотеть执行匹配процесс,насредактироватьonOptionsItemSelected()квключатьк Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_match)
{
  if(sampledImage==null || imgToMatch==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an object and a scene to match!";
    int duration = Toast.LENGTH_SHORT;
    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }

  int maximumNuberOfMatches=10;
  Mat greyImage=new Mat();
  Mat greyImageToMatch=new Mat();

  Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);
  Imgproc.cvtColor(imgToMatch, greyImageToMatch, Imgproc.COLOR_RGB2GRAY);

  MatOfKeyPoint keyPoints=new MatOfKeyPoint();
  MatOfKeyPoint keyPointsToMatch=new MatOfKeyPoint();

  FeatureDetector detector=FeatureDetector.create(detectorID);
  detector.detect(greyImage, keyPoints);
  detector.detect(greyImageToMatch, keyPointsToMatch);

  DescriptorExtractor dExtractor = DescriptorExtractor.create(descriptorID);
  Mat descriptors=new Mat();
  Mat descriptorsToMatch=new Mat();

  dExtractor.compute(greyImage, keyPoints, descriptors);
  dExtractor.compute(greyImageToMatch, keyPointsToMatch, descriptorsToMatch);

  DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
  MatOfDMatch matches=new MatOfDMatch();
  matcher.match(descriptorsToMatch,descriptors,matches);

  ArrayList<DMatch> goodMatches=new ArrayList<DMatch>();
  List<DMatch> allMatches=matches.toList();

  double minDist = 100;
  for( int i = 0; i < descriptorsToMatch.rows(); i++ )
  { 
    double dist = allMatches.get(i).distance;
    if( dist < minDist ) minDist = dist;
  }

  for( int i = 0; i < descriptorsToMatch.rows() && goodMatches.size()<maximumNuberOfMatches; i++ )
  { 
    if(allMatches.get(i).distance<= 2*minDist)
    {     
      goodMatches.add(allMatches.get(i)); 
    }
  }

  MatOfDMatch goodEnough=new MatOfDMatch();
  goodEnough.fromList(goodMatches);
  Mat finalImg=new Mat();
  Features2d.drawMatches(greyImageToMatch, keyPointsToMatch, greyImage, keyPoints, goodEnough, finalImg,Scalar.all(-1),Scalar.all(-1),new MatOfByte(), Features2d.DRAW_RICH_KEYPOINTS + Features2d.NOT_DRAW_SINGLE_POINTS);
  displayImage(finalImg);
}

наспервыйубеждатьсясценаиобъектизображениеуженагрузка:

Язык кода:javascript
копировать
if(sampledImage==null || imgToMatch==null)
{
  Context context = getApplicationContext();
  CharSequence text = "You need to load an object and a scene to match!";
  int duration = Toast.LENGTH_SHORT;
  Toast toast = Toast.makeText(context, text, duration);
  toast.show();
  return true;
}

Волясценаиобъектизображение Все Конвертировать для灰степень:

Язык кода:javascript
копировать
Imgproc.cvtColor(sampledImage, greyImage, Imgproc.COLOR_RGB2GRAY);
Imgproc.cvtColor(imgToMatch, greyImageToMatch, Imgproc.COLOR_RGB2GRAY);

Детектор объекта строится согласно отприложению извыбирать, и его использование используется для обнаружения признаков в сцене и изображения объекта:

Язык кода:javascript
копировать
MatOfKeyPoint keyPoints=new MatOfKeyPoint();
MatOfKeyPoint keyPointsToMatch=new MatOfKeyPoint();

FeatureDetector detector = FeatureDetector.create(detectorID);
detector.detect(greyImage, keyPoints);
detector.detect(greyImageToMatch, keyPointsToMatch);

Мы выполняем одну и ту же операцию для всех типов дескрипторов. OpenCV Имеет детектороподобный интерфейс и интерфейс дескриптора. тысуществоватьDescriptorExtractorдобрыйначальство调использоватьодин个createметод,И перейдите к использованию дескриптора ID. существуют В этом случае,ID база Внасотприложениеменюсередина Место做извыбирать。

Язык кода:javascript
копировать
DescriptorExtractor dExtractor = DescriptorExtractor.create(descriptorID);

Следующий,Мы существуем создать объект-дескриптор, для которого вызываем метод расчета и передаем изображение.,Обнаружениеприезжатьиз关键точкаинулевойизMatобъект Приходитьхранилищесуществоватьсценаиобъектизображениесередина Обнаружениеприезжатьизкаждыйособенностьизописывать,Описано хранилищем:

Язык кода:javascript
копировать
Mat descriptors=new Mat();
Mat descriptorsToMatch=new Mat();
dExtractor.compute(greyImage, keyPoints, descriptors);
dExtractor.compute(greyImageToMatch, keyPointsToMatch, descriptorsToMatch);

Затем,наспроходитьсуществоватьDescriptorMactherдобрыйначальство调использоватьcreateметодипередачатывыбиратьизdistanceфункцияиз ID для создания объекта сопоставления. В нашем примере существует наш локальный двоичный дескриптор. поэтому,Расстояние Хэмминга Воляда Наши любимые извыбирать:

Язык кода:javascript
копировать
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);

сейчассуществовать,нас Подготовитьпроходитьсуществовать匹配устройствообъектначальство调использоватьmatchметод,и передайте описание функции объекта,сценаособенностьописыватьиDMatchобъектизнулевойматрица,отсценаиобъектизображениесерединапопытаться найтиприезжатьсоответствующие функции。 DMatchобъектдаодин个简одинизчисло据结构,использовать Вхранилищедва个匹配издескрипторирасстояние(существуем из примерадля Расстояние Хэмминга):

Язык кода:javascript
копировать
MatOfDMatch matches=new MatOfDMatch();
matcher.match(descriptorsToMatch,descriptors,matches);

Наконец, мы выбираем лучшие точки соответствия и рисуем их:

Язык кода:javascript
копировать
ArrayList<DMatch> goodMatches=new ArrayList<DMatch>();
List<DMatch> allMatches=matches.toList();

double minDist = 100;
for( int i = 0; i <descriptorsToMatch.rows(); i++ )
{ 
  double dist = allMatches.get(i).distance;
  if( dist < minDist ) minDist = dist;
}

for( int i = 0; i <descriptorsToMatch.rows() && goodMatches.size()<maximumNuberOfMatches; i++ )
{ 
  if( allMatches.get(i).distance<= 2*minDist)
  {     
    goodMatches.add(allMatches.get(i)); 
  }
}

MatOfDMatch goodEnough=new MatOfDMatch();
goodEnough.fromList(goodMatches);

Mat finalImg=new Mat();
Features2d.drawMatches(greyImageToMatch, keyPointsToMatch, greyImage, keyPoints, goodEnough, finalImg,Scalar.all(-1),Scalar.all(-1),new MatOfByte(),Features2d.DRAW_RICH_KEYPOINTS + Features2d.NOT_DRAW_SINGLE_POINTS);

displayImage(finalImg);

использовать ORB Carry. Описание функций внешнего вида для масштабирования и вращения без изменений.

Сопоставление встроенных функций

Мы видели, как приезжатьиспользовать Java Упаковкаустройство Обнаружение,описыватьисоответствующие функции。 Но да, если Воля эти шаги совместить, приезжать один JNI 调использоватьсередина会Даже快,Потому что для этого процесса требуется много шагов,и且каждыйшаг Все Конвертировать длявернолокальная машинакодизодин个 JNI вызов.

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

определение пользовательского интерфейса

Мы добавим новый пункт меню для выполнения собственных процессов. Открытьres/menu/pano.xmlидобавить вк Внизпроект:

Язык кода:javascript
копировать
<itemandroid:id="@+id/action_native_match" 
  android:orderInCategory="11" 
  android:title="@string/action_native_match">
</item>
собственный процесс сопоставления

существуют В этом разделе мы переносим процесс Воля Воля и этапы предварительной обработки на нативную сторону. оти Воляобщийиз JNI Сокращение накладных расходов маленький:

наспервыйсуществовать Активностьдобрыйсередина声明один个новыйизлокальная машинаметод。 Собственные методы ссылаются на изображение объекта, изображение сцены, детектор. ID и дескриптор ID и возвращает результаты с совпадающим изображением:

Язык кода:javascript
копировать
public native void FindMatches(long objectAddress, long sceneAddress,int detectorID, int descriptorID,long matchingResult);

нассуществоватьPano.cppдокументсерединаопределение Понятнолокальная машинаметод:

Язык кода:javascript
копировать
JNIEXPORT void JNICALL Java_com_app3_pano_PanoActivity_FindMatches(JNIEnv*, jobject, jlong objectAddress, jlong sceneAddress,jint detectorID, jint descriptorID,jlong matchingResult)
{
  cv::Mat& object  = *(cv::Mat*)objectAddress;
  cv::Mat& scene = *(cv::Mat*)sceneAddress;
  cv::Mat& result = *(cv::Mat*)matchingResult;
  cv::Mat grayObject;
  cv::Mat grayScene;

  //Convert the object and scene image to grayscale
  cv::cvtColor(object,grayObject,cv::COLOR_RGBA2GRAY);
  cv::cvtColor(scene,grayScene,cv::COLOR_RGBA2GRAY);

  std::vector<cv::KeyPoint> objectKeyPoints;
  std::vector<cv::KeyPoint> sceneKeyPoints;
  cv::Mat objectDescriptor;
  cv::Mat scenceDescriptor;

  //Construct a detector object based on the input ID
  if(detectorID==1)//FAST
  {
    cv::FastFeatureDetector detector(50);
    detector.detect(grayObject, objectKeyPoints);
    detector.detect(grayScene, sceneKeyPoints);
  }
  else if(detectorID==5)//ORB
  {
    cv::OrbFeatureDetector detector;
    detector.detect(grayObject, objectKeyPoints);
    detector.detect(grayScene, sceneKeyPoints);
  }

  //Construct a descriptor object based on the input ID
  if(descriptorID==3)//ORB
  {
    cv::OrbDescriptorExtractor descriptor;
    descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
    descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
  }
  else if(descriptorID==4)//BRIEF
  {
    cv::BriefDescriptorExtractor descriptor;
    descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
    descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
  }
  else if(descriptorID==5)//BRISK
  {
    cv::BRISK descriptor;
    descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
    descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
  }
  else if(descriptorID==6)//FREAK
  {
    cv::FREAK descriptor;
    descriptor.compute(grayObject,objectKeyPoints,objectDescriptor);
    descriptor.compute(grayScene,sceneKeyPoints,scenceDescriptor);
  }

  //Construct a brute force matcher object using the 
  //Hamming distance as the distance function
  cv::BFMatcher matcher(cv::NORM_HAMMING);
  std::vector< cv::DMatch> matches;
  matcher.match( objectDescriptor, scenceDescriptor, matches);

  //Select the best matching points and draw them
  double min_dist = 100;
  for( int i = 0; i < objectDescriptor.rows; i++ )
  {
    double dist = matches[i].distance;
    if( dist < min_dist ) min_dist = dist;
  }
  std::vector< cv::DMatch> good_matches;
  for( int i = 0; i < objectDescriptor.rows; i++ )
  {
    if( matches[i].distance <= 3*min_dist )
    {
      good_matches.push_back( matches[i]);
    }
  }
  drawMatches( grayObject, objectKeyPoints, grayScene, sceneKeyPoints,good_matches, result, cv::Scalar::all(-1), cv::Scalar::all(-1),std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS+cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
}

существовать Активностьдобрыйсередина,редактироватьonOptionsItemSelected()квключатьк Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_native_match)
{
  if(detectorID==FeatureDetector.HARRIS)
  {
    Context context = getApplicationContext();
    CharSequence text = "Not a valid option for native matching";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  if(sampledImage==null || imgToMatch==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an object and a scene to match!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat finalImg=new Mat();

 FindMatches(imgToMatch.getNativeObjAddr(),sampledImage.getNativeObjAddr(),detectorID,descriptorID,finalImg.getNativeObjAddr());

  displayImage(finalImg);
} 

использовать ORB Сделайте нативное сопоставление для Обнаружение функциии Описание

Сшить два изображения

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

в целом,Сращивание делится на два этапа:

  • регистрация и выравнивание изображений:существоватьэтот,Приводим два изображения-одно для исходников.,Еще один для Цель,и且Долженпроцесс涉及существоватьнулевой间начальствозарегистрироваться Цельизображениекиисточникизображениеверно齐。 Процесс можно разделить на базовую интенсивность В и выравнивание, базовую особенность В и выравнивание. В нашей базе Воляиспользовать функцию выравнивания, так как для нас есть привычный метод из компонента (существовать два изображения, найти, описание соответствующие функции)。 этотпроцессизрезультатдаиметьуже知参число(Прямо сейчас3x3одинотвечатьматрица)из运动模форма,Должен模формаиспользовать ВВоляодин个изображениеизкоординироватькартографированиеприезжать另один个изображение。 После расширения приложения сращивания для использования двух или более изображений вы, Воля, начнете сталкиваться с полной регистрацией на доске. проблему и найдите набор согласованных параметров выравнивания, чтобы минимизировать рассогласование между всеми парами. Чтобы решить эту добрую проблему с помощью методов: регулировка луча (прогулкасамый уменьшает ошибку перепроецирования между каждой парой изображений для улучшения оценки) и wave Коррекция (конечный результат с помощью ВКоррекция, поскольку для обычно будет иметь волнистый эффект, обнаруживаемый в выходных данных существующей формы волны) панорамный.
  • Коррекция:верно齐иверно齐всеизображениеназад,Волянуждатьсяверновходитьизображениеруководить曝光Коррекция, так что смешивание выглядит более естественным. Нам также понадобится процесс многополосного смешивания для удаления видимых швов и артефактов сшивания.

вернонас Приходить说Счастливая изда, OpenCV иstitcherдобрыйпучоксуществоватьодин起,Интерфейс «Добрый Воляпроход» очень прост для выполнения соединения проводов. Но да,OpenCV4Android SDK Не включено Java Реализатор упаковки, я думаю, для этого вам следует использовать привычный существовать приложение, использовать собственную реализацию, чтобы ее можно было расширить и добавить вприжать текущую версиюиз по другой причине. OpenCV Java Упаковка может удовлетворить ваши потребности. поэтому,Чтобы решить эту проблему,нас Воля КPano.cppдобавить в另один个функцияк调использоватьstitcherдобрыйи返回результат。

определение пользовательского интерфейса

Мы Волядобавить В новый изменён пункт для выполнения родного конвейера сшивания. Открытьres/menu/pano.xmlидобавить вк Внизпроект:

Язык кода:javascript
копировать
<item android:id="@+id/action_native_stitcher" android:orderInCategory="11" android:title="@string/action_native_stitch">
</item>
местныйstitcher

существовать本节середина,нас Волядляместныйstitcherдобрый实сейчас Java Упаковкаустройство,к便Можетксуществоватьприложениесерединаиспользоватьэто:

наспервыйсуществоватьactivityдобрыйсередина声明один个новыйизлокальная машинаметод。 Нативный метод обращается к первой и второй сцене и возвращает результат с конкатенацией и изображением:

Язык кода:javascript
копировать
public native void Stitch(long sceneOneAddress, long sceneTwoAddress,long stitchingResult);

нассуществоватьPano.cppсерединаопределение Понятноновыйиз拼接метод:

Язык кода:javascript
копировать
JNIEXPORTvoid JNICALL Java_com_app3_pano_PanoActivity_Stitch(JNIEnv*, jobject, jlong sceneOneAddress, jlong sceneTwoAddress,jlong stitchingResult) {
  cv::Mat& sceneOne  = *(cv::Mat*)sceneOneAddress;
  cv::Mat& sceneTwo = *(cv::Mat*)sceneTwoAddress;
  cv::Mat& result = *(cv::Mat*)stitchingResult;
  /* The core stitching calls: */
  //a list to store all the images that need to be stitched
  std::vector<cv::Mat> natImgs;
  natImgs.push_back(sceneOne);
  natImgs.push_back(sceneTwo);
  //create a stitcher object with the default pipeline
  cv::Stitcher stitcher = cv::Stitcher::createDefault();
  //stitch and return the result
  stitcher.stitch(natImgs, result);
}

существовать Активностьдобрыйсередина,редактироватьonOptionsItemSelectedквключатьк Вниз Состояние:

Язык кода:javascript
копировать
else if(id==R.id.action_native_stitcher)
{
if(sampledImage==null || imgToMatch==null)
  {
    Context context = getApplicationContext();
    CharSequence text = "You need to load an two scenes!";
    int duration = Toast.LENGTH_SHORT;

    Toast toast = Toast.makeText(context, text, duration);
    toast.show();
    return true;
  }
  Mat finalImg=new Mat();
  Stitch(imgToMatch.getNativeObjAddr(),sampledImage.getNativeObjAddr(),finalImg.getNativeObjAddr());
  displayImage(finalImg);
}

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

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

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

6. Приложение 4 – Автоматическое селфи

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

Мы, Воля, представляем из темы:

  • Обнаружить из Каскадный с помощью Вобъекта классификатор
  • использовать OpenCV Манипулировать кадрами камеры
  • использоватьобученныйиз Каскадный классификатор Обнаружениеобъект

Каскадный классификатор

существуют В этом разделе мы обсуждаем Волямощныйиз Каскадный классификатор и его компоненты, Хаар особенность,积точкаизображение,адаптивный импульсAdaboost)икаскадкстроитьодин个物体Обнаружениеустройство。

суммируя,Чтобы построить детектор объектов,ты Можеткиспользоватьтолько样本(Например,размердля24x24излюди Лицо)игруз样本(любойдругой非люди Лицоизизображение)верно Чторуководитьтренироваться。 Вы, Воля, постоянно совершенствуете тренировочный процесс, чтобы минимизировать ошибки обучения (мин добрыйдля нелиц и з всего лиц и добры йдля лиц и з всего нелиц).

тренироваться Заканчиватьназад,Мы получили новое изображение,нас Требовать Обнаружениеустройство检查Чтода否有только面样本(Прямо сейчаслицо)。 Сделайте это, выполнив следующие шаги:

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

Характеристики, подобные Хаару

Характеристики, аналогичные Хааруде, являются еще одной хорошей характеристикой.,Используйте Â, чтобы распознавать лица, например,Пешеходы и другие твердые объекты.

Статья «Простая функция изусиливаскаскади быстрого обнаружения целей», автор: Paul Viola и Michael Jones В 2001 Предложено в 2001 году введение доброго Haar изособенностьизиспользоватьадаптивный импульсикаскад для распознавания лиц. от С тех пор многие другие характеристики и Усиливать вариации были использованы для создания многих других объектов доброго из подразделений доброго.

Учреждать Обнаружить из Каскадный с помощью Вобъекта классификатор Из Первый шаг — попытаться закодировать обширную информацию о положительных и отрицательных образцах. Другими словами, нам нужно определить, какие черты распознаются достаточно хорошо, чтобы отличить лица от нелиц. существуют В этом разделе мы обсуждаем Воля и Главу 5 глава,“App 3. Просмотрите функции проживания в «Полном просмотре просмотра» с различными типами функций. Здесь используются функции фиксированного размера из пиксельной сетки, существующие в данном случае, потому что что固定размеризсеткаопределение Понятноописыватьобласть,поэтому无需Обнаружение兴趣точка。

добрыйпохожий Haar из функций фиксированный размер пиксельной сетки, разделенной на черные и белые области, и существует 2 глава,“приложение Ядро свертки, обсуждаемое в разделе «1-Build Your Own Darkroom», очень похоже. 。 Воля Функцииприложение Вданныйизизображениеобластьчас,Соответствующую область можно описать путем вычитания суммы интенсивности пикселей под белой областью из общей интенсивности под черной областью.,от того, чтобы приехать по цене.

добрыйпохожий Haar гибкий дизайн функций; Например, вы можете иметь несколько Type 1 Характеристики, но да Воля разные по высоте и/или ширине приложение Визуализацияиз разных областей. Следовательно, учитывая эти параметры (характеристика добрый тип (1, 2, 3, 4 или 5),Ширина объекта,Высота объекта иприложение. Этот объект изображает область),Вы Воля получаете большое количество доступных описаний положительных и отрицательных образцов характеристик.

Уведомление

существовать Viola и Jones из工делатьсередина,Долженалгоритмиспользовать24x24окноделатьдля Базовыйокноразмер(вселицои非лицоизразмер Все调整для24x24Пиксель),Если учтены все параметры (добрый тип,(пропорции и расположение),нонас最终拥有размердля 160,000 Особенности бассейна.

На следующем рисунке показан пример пула компонентов функции:

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

Технику оптимизации «адаптивный импульс» можно использовать для устранения избыточных функций или для того, чтобы правильно выделить подмножества функций; позже в этой главе Воля вернется к подробностям алгоритма.

另один种优化技术использовать Ввычислитьособенностьценить(Прямо сейчасотчерныйобластьсерединауменьшать去Белыйобласть),А добиться этого можно, рассчитав так называемое интегральное изображение.

полная картина

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

Точки изображения и входное изображение того же размера,Дакаждый积точка Пиксель(i, j)дасуществоватьвходить Пиксель(i, j)начальство方илевый侧извсевходить Пиксельизобщийи。 Например,когдалевыйначальство Пиксельиспользовать(0, 0)索引час,ценить6из整число Пиксель(1, 2)давсевходить Пиксель(i, j)изобщийи,Чтосерединаi <= 1,j <= 2

После подсчета очков изображение,получатьизображениесерединалюбойобластьизвходить Пиксельизобщийи ВолястановитьсядляO(1)Операция。

Например,考虑иметь Четыре个областьиз积точкаизображение:A,B,CиD1выражатьиз整число Пиксель Волявсевходить ПиксельизобщийихранилищесуществоватьобластьAсередина,2выражатьиз整число ПиксельдаобластьAиBсерединавсевходить Пиксельизобщийи,Зависит от3выражатьиз整число ПиксельдаобластьAсерединавсевходить Пиксельизобщийи。CиCтакой же,积точка Пиксель4такой же,это们Волявходить ПиксельизобщийихранилищесуществоватьA,B,CиD

сейчассуществовать,хотетьполучатьобластьDсерединавходить Пиксельизобщийи,Вам нужно всего лишь четыре целочисленных угловых пикселя 1, 2, 3 и 4 изценить.,ииспользовать简одиниз算术ОперацияD = 4 + 1 - 2 - 3,Вы Воля получаете общую площадь ввода,Как показано ниже:

адаптивный импульс

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

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

В своей простейшей форме алгоритм Adaboost может описать следующее:

  1. от Uniform из Начните с положительных и отрицательных весов выборки. Все образцы (положительные и отрицательные) одинаково важны.
  2. Пройдитесь по пулу признаков/слабых классификаторов, а затем взвесьте взвешенную ошибку классификатора. маленькийизточкадобрыйустройство。 Уведомление Ошибка Divide добрый даиспользовать эту функцию Воля сколько лиц разделено на добрыйдля нелиц, Воля сколько нелиц разделено на добрыйдля лиц.
  3. Увеличьте вес неправильно классифицированных образцов (отрицательных и положительных образцов), чтобы подчеркнуть важность правильной классификации этих образцов на следующей итерации.
  4. Повторите шаги 2 и 3. До тех пор, пока не дойдет до прибытия. Многие случаи могут привести к необходимости собирать большинство N характеристики сходятся.

Как только у нас появятся эти функции (слабые разделители) и список,Затем их можно линейно объединить, чтобы сформировать более сильный сепаратор.,Он работает лучше, чем любой слабый анализатор.,наконец Конечноодин个порогценитьк最佳地Волялицои非лицоточка开。

дляхотетьточкадобрыйизновыйизображение,насвычислитьсуществоватьвходитьизображениеначальствоиспользовать Adaboost выбиратьизN个Взаимно关особенностьизчисло量,И определить, человеческое ли это лицо или нет, исходя из выбранного порога.

каскад

наконецодин个技巧дляэтот种добрыйформаизточкадобрыйустройство起Понятноимя字,для ускорения обнаружения любого изображенияиз,Что依据данаснуждатьсяиспользоватьразмердля24x24изокно扫描входитьизображение,Например Viola и Jones из работ. Да,мы знаем,существоватьмногоэтотдобрыйокносередина,Не существуетсуществоватьзаинтересованныйизобъект,поэтомунуждатьсяверноалгоритмруководить修改,Для того, чтобы как можно быстрее отбросить негативное окно и сосредоточиться на «Возможном из позитивного окна».

дляэтот,У нас есть серия мощных сепараторов,и不датренироватьсяодин个强точкадобрыйустройство。 Таким образом, все выбранные функции группируются по нескольким этапам, где каждый этап определяет с помощью В, что данное окно содержит интересующий объект из отрицательного окна или возможное окно. по сути,Это обновление позволяет нам как можно раньше устранить большое количество негативных окон, использовать меньший набор сопутствующих характеристик.,Как показано ниже:

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

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

использовать Каскадный классификатор Обнаружениеобъект

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

Получите доступ к камере вашего телефона с помощью OpenCV

Мы, Воля, выполняем те же действия, что и в предыдущей главе.,первыйсоздаватьодин个имядляAutoSelfieизнулевой白Активностьновыйприложение。

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

Язык кода:javascript
копировать
<uses-permissionandroid:name="android.permission.CAMERA"/>
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

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

Предварительный просмотр камеры

OpenCV для Предварительный просмотр камерыдобрыйпоставлять Java Реализация, добрый обрабатывается оборудованием камеры. OpenCV Библиотека взаимодействия. org.opencv.android.JavaCameraViewдобрыйделатькамера Можетксуществоватьоборудование屏幕начальствоиметь дело сирисоватьрамка。

приезжатьв настоящий моментдляконец,использоватьJavaCameraViewПредварительный просмотр кадров камеры достаточно; Да,Нам, Воля, нужно определиться самому изкамера посмотреть добрый,к便кназад能够РасширятьJavaCameraViewдобрыйиз Функция。Теперь существуем, давайте посмотрим, как определить себя изкамера, вид добрый:

создаватьодин个имядляcom.app4.autodselfie.CamViewизновый Java добрый。

делатьновыйдобрый Расширятьприезжатьorg.opencv.android.JavaCameraView

如ВнизопределениеCamViewдобрый构造устройство:

Язык кода:javascript
копировать
public CamView(Context context, AttributeSet attrs) {
  super(context, attrs);
}

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

определение пользовательского интерфейса

существоватьприложениемакетдокументactivity_auto_selfie.xmlсередина,нас Воля主ВидетькартинаопределениедляCamViewдобрый(потому чтодляэтодаandroid.view.SurfaceViewдобрыйизребенокдобрый):

Язык кода:javascript
копировать
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.app4.autoselfie.CamView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/auto_selfie_activity_surface_view"/>

</LinearLayout>
Предварительный просмотр кадров камеры

返回АктивностьAutoSelfie,Мы, Воля, выполняем следующие действия, чтобы начать с устройства с камеры получать кадры:

Даже改Активностьдобрыйк实сейчасCvCameraViewListener2интерфейс,Интерфейс会Воля Активностьдобрыйизменять Изменятьдляиспользовать В监听насCamViewдобрый,камераView Start,Взгляд останавливается и получает три жизненных события:

Язык кода:javascript
копировать
public class AutoSelfie extends Activity implements CvCameraViewListener2

нас声明два个нулевойизMatобъект-один个использовать Вдержатькогда前камерарамкаиз RGB Версия, еще одно сохранение версии в оттенках серого с буквой В:

Язык кода:javascript
копировать
private Mat mRgba;
private Mat mGray;

нас实сейчас ПонятноCvCameraViewListener2из三个缺失事件иметь дело сустройство。 начинать摄影机Видетькартинаназад,нас Воляинициализациядва个Matобъект。 Когда вид камеры прекращается, мы отпускаем ее, а когда начинаем получать кадры камеры, возвращаем текущий кадр для отрисовки на экране. RGB Версия:

Язык кода:javascript
копировать
public void onCameraViewStarted(int width, int height) {
  mGray = new Mat();
  mRgba = new Mat();
}

public void onCameraViewStopped() {
  mGray.release();
  mRgba.release();
}

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
  mRgba=inputFrame.rgba();
  return mRgba;
}

ДаженовыйonCreate()методкпопытаться найтиприезжатьнассуществоватьприложениемакетдокументсерединаопределениеизCamViewобъект,Волякамеранастраиватьдлясоединять(только面илиназад),существовать本例середина,Мы Воляконнекткфронтальная камера,наконец,Волянасиз АктивностьзарегистрироватьсядляCamViewобъектжизненные событияиз监听устройство:

Язык кода:javascript
копировать
mOpenCvCameraView = (CamView) findViewById(R.id.auto_selfie_activity_surface_view);
mOpenCvCameraView.setCameraIndex(1);
mOpenCvCameraView.setCvCameraViewListener(this);

наконец,Библиотека OpenCV успешно загружена,нас МожеткделатьCamView объектConnect приезжать в камеру устройства; Только有этот样onCameraViewStarted()才会被调использовать,CamViewобъект Изменятьдля Активность状态:

Язык кода:javascript
копировать
private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
  @Override
  public void onManagerConnected(int status) {
    switch (status) {
      case LoaderCallbackInterface.SUCCESS:
      {
        Log.i(TAG, "OpenCV loaded successfully");
 mOpenCvCameraView.enableView();
      } break;
      default:
      {
        super.onManagerConnected(status);
      } break;
    }
  }
};

Уведомление

ты会Уведомлениеприезжать,Воля При вертикальном положении устройства,рисоватьиз框架会翻изменять。 Не волнуйтесь, мы позаботимся об этом позже.

Обнаружение сомкнутых ладоней в кадре камеры

Приложение «Автоматическое селфи» Следующее предложение обнаружения для захвата текущего кадра камеры. я发сейчас,Близко к ладони, достаточно хороших советов,Вы можете рассмотреть другие советы,Например笑Лицождать。

только如нассуществовать“Каскадный в разделе "классификатор" упомянуто перемещениеиз, наш детектор Волядаиспользоватьдобрый аналогичен Haar Особенности из Каскадный классификатор。

Уведомление

Хорошо обученный на этапе и выбранные характеристики Воля сохраняет существование XML в файле. ты Можеткпрямойотэта страницаскачатьдокумент,Вы также можете найти документ «Прибытие» в папке «Изпроект-документ», прилагаемой к этой главе.

использоватьбаза В Java из Каскадный классификатор

один旦делатьтренироваться有素източкадобрыйустройство Обнаружениеприезжатьтывыбиратьизобъект(существуем из-за случаядлязакрытая ладонь),OpenCV предоставит многомасштабный детектор раздвижных окон,Это Волясуществовать раздвижное окно средний проект хорошо обученный из перегородки добрый,И существуют входные изображенияиз в нескольких масштабах,Возвращает ограничивающую рамку вокруг обнаруженного объекта выезда в разных масштабах.

Уведомление

Глава нашего существования № 5 «Приложение 3: Полный просмотр просмотра» столкнулась с концепцией создания изображения пирамиды, построенной с использованием нескольких масштабов.

использоватьorg.opencv.objdetect.CascadeClassifierдобрыйделатьдлясейчасстановитьсяизслайдокно Обнаружениеустройство非常容易。 Сначала нам нужен хорошо обученный сепаратор XML документкопироватьприезжатьприложение原始ресурсдокументпапка\res\raw\haarhand.xmlсередина.

Следующий,наспроходить如Вниз Даже改BaseLoaderCallback实сейчас Приходить声明иинициализацияorg.opencv.objdetect.CascadeClassifierобъект:

Язык кода:javascript
копировать
private File cascadeFile;
private CascadeClassifier cascadeClassifier;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  @Override
  public void onManagerConnected(int status) {
    switch (status) {
      case LoaderCallbackInterface.SUCCESS:
      {
        Log.i(TAG, "OpenCV loaded successfully");
        try {
          // load cascade file from application resources
          InputStream is = getResources().openRawResource(R.raw.haarhand);
          File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
 cascadeFile = new File(cascadeDir, "haarhand.xml");
          FileOutputStream os = new FileOutputStream(cascadeFile);

          byte[] buffer = new byte[4096];
          int bytesRead;
          while ((bytesRead = is.read(buffer)) != -1) {
            os.write(buffer, 0, bytesRead);
          }
          is.close();os.close();
          //Initialize the Cascade Classifier object using the 
          // trained cascade file
 cascadeClassifier = new CascadeClassifier(cascadeFile.getAbsolutePath());
          if (cascadeClassifier.empty()) {
            Log.e(TAG, "Failed to load cascade classifier");
            cascadeClassifier = null;
          } else
            Log.i(TAG, "Loaded cascade classifier from " + cascadeFile.getAbsolutePath());
          cascadeDir.delete();
        } catch (IOException e) {
          e.printStackTrace();
          Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
        }
        mOpenCvCameraView.enableView();
      } break;
      default:
      {
        super.onManagerConnected(status);
      } 
      break;
    }
  }
};

сейчассуществовать,Мы готовы обработать каждый кадр камеры,для обнаружения закрытых ладоней и автоматической съемки фотографий.

Наша Воляиспользоватьзалгоритм Подведите итоги ниже:

  1. Рассчитаем, что мы ищем изобъектизсамый маленькие габариты (ширина и высота). существуем из-за случая,самый маленький размер Волядля размера рамы из 20%。 Конечно, вы можете изменить его, если хотите. маленький размер, но, пожалуйста, Уведомление, мы существуем ищем изобъект. Чем меньше, тем медленнее он обнаруживает алгоритм Воля строить.
  2. бежать Мы существуем инициализируем детектор скользящего окна на текущем кадре до первого 1 Укажите изсамый шаг маленький размер: найдите то, что вас интересует, из объектов.
  3. Игнорируйте ложноположительное обнаружение. Ложноположительные обнаружения происходят, когда детектор скользящего окна возвращает ограничивающую рамку, которая на самом деле не содержит интересующий объект. для Чтобы минимизировать ложные срабатывания и стабилизировать обнаружение, мы делаем следующее:
    • Прежде всего, мы для каждого 100 пиксельно-квантованная ограничивающая рамка. другими словами,нас Волякамерарамка划точкадля100x100Пиксельизнулевой间хранилищеведро,Каждая ограничивающая рамка размещается в соответствии с ее положением, соответствующим местоположению хранилища в пространстве хранилища середина.
    • Во-вторых,существоватьN个рамка之назад,нас检查да否有один个Сумка含Nграница框изхранилищеведро。 этотиметь в видудляN个连续рамка Приходить说,Обнаружение стабильности,поэтому,Вероятность ложных срабатываний очень мала.
  1. Как только происходит стабильное истинное положительное обнаружение (фактическое закрытие ладони), текущий кадр изкамеры сохраняется.

Чтобы начать реализацию этого алгоритма,наспервыйнуждаться Даже改CamViewдобрыйк实сейчасandroid.hardware.Camera.PictureCallback,к便дляonPictureTaken()метод обратного вызовапоставлять实сейчаскдержатьданныйизкамерарамка。

новыйизCamViewдобрый Как показано ниже:

Язык кода:javascript
копировать
public class CamView extends JavaCameraView implements PictureCallback {
  private static final String TAG = "AutoSelfie::camView";
  private String mPictureFileName;
  public CamView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public void onPictureTaken(byte[] data, Camera camera) {
    Log.i(TAG, "Saving a bitmap to file");
    // The camera preview was automatically stopped. Start it
    // again.
    mCamera.startPreview();
    mCamera.setPreviewCallback(this);

    // Write the image in a file (in jpeg format)
    try {
      FileOutputStream fos = new FileOutputStream(mPictureFileName);
      fos.write(data);
      fos.close();
    } catch (java.io.IOException e) {
      Log.e("PictureDemo", "Exception in photoCallback", e);
    }
  }

  public void takePicture(final String fileName) {
    Log.i(TAG, "Taking picture");
    this.mPictureFileName = fileName;
    // Postview and jpeg are sent in the same buffers if the 
    //queue is not empty when performing a capture.
    // Clear up buffers to avoid mCamera.takePicture to be stuck 
    //because of a memory issue
    mCamera.setPreviewCallback(null);
    // PictureCallback is implemented by the current class
    mCamera.takePicture(null, null, this);
  }
}

Если у вас есть функция сохранения кадра камеры,Сразу Можеткпроходить Даже改onCameraFrame()из实сейчас Приходить ДаженовыйAutoSelfie Действия добрый, чтобы обнаружить закрытые ладони:

Язык кода:javascript
копировать
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
  //Flip around the Y axis
  Core.flip(inputFrame.rgba(), mRgba, 1);
  Core.flip(inputFrame.gray(),mGray,1);

  if (mAbsoluteFaceSize == 0) {
    int height = mGray.rows();
    if (Math.round(height * mRelativeFaceSize) > 0) {
      mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
    }
  }

  MatOfRect closedHands = new MatOfRect();
  if (cascadeClassifier != null)
    cascadeClassifier.detectMultiScale(mGray, closedHands, 1.1, 2, 2,new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());

  Rect[] facesArray = closedHands.toArray();
  for (int i = 0; i < facesArray.length; i++)
  {
    Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), HAND_RECT_COLOR, 3);
    Point quatnizedTL=new Point(((int)(facesArray[i].tl().x/100))*100,((int)(facesArray[i].tl().y/100))*100);

    Point quatnizedBR=new Point(((int)(facesArray[i].br().x/100))*100,((int)(facesArray[i].br().y/100))*100);

    int bucktID=quatnizedTL.hashCode()+quatnizedBR.hashCode()*2;
    if(rectBuckts.containsKey(bucktID))
    {
      rectBuckts.put(bucktID, rectBuckts.get(bucktID)+1);
      rectCue.put(bucktID, new Rect(quatnizedTL,quatnizedBR));
    }
    else
    {
      rectBuckts.put(bucktID, 1);
    }
  }
  int maxDetections=0;
  int maxDetectionsKey=0;
  for(Entry<Integer,Integer> e : rectBuckts.entrySet())
  {
    if(e.getValue()>maxDetections)
    {
      maxDetections=e.getValue();
      maxDetectionsKey=e.getKey();
    }
  }
  if(maxDetections>5)
  {
    Core.rectangle(mRgba, rectCue.get(maxDetectionsKey).tl(), rectCue.get(maxDetectionsKey).br(), CUE_RECT_COLOR, 3);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
    String currentDateandTime = sdf.format(new Date());
    String fileName = Environment.getExternalStorageDirectory().getPath() + "/sample_picture_" + currentDateandTime + ".jpg";

    mOpenCvCameraView.takePicture(fileName);

    Message msg = handler.obtainMessage();
    msg.arg1 = 1;
    Bundle b=new Bundle();
    b.putString("msg", fileName + " saved");
    msg.setData(b);
    handler.sendMessage(msg);
    rectBuckts.clear();
  }
  return mRgba;
}

Лучше Мы объясняем код киз шаг за шагом:

нассуществовать y Переверните поле ввода по оси, чтобы устранить эффект зеркального отображения:

Язык кода:javascript
копировать
    //Flip around the Y axis
    Core.flip(inputFrame.rgba(), mRgba, 1);
    Core.flip(inputFrame.gray(),mGray,1);

Рассчитайте размер самого маленького объекта, исходя из высоты поля ввода:

Язык кода:javascript
копировать
if (mAbsoluteFaceSize == 0) {
int height = mGray.rows();
if (Math.round(height * mRelativeFaceSize) > 0) {
  mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);}}

нассуществовать Каскадный классификаторобъектначальство调использоватьdetectMultiScale()метод Приходитьстроитьизображениепирамидаисуществоватькаждый Пропорция尺начальствобегатьслайдокно Обнаружениеустройство:

Язык кода:javascript
копировать
MatOfRect closedHands = new MatOfRect();
if (cascadeClassifier != null)
cascadeClassifier.detectMultiScale(mGray, closedHands, 1.1, 2, Objdetect.CASCADE_SCALE_IMAGE,new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());

насиспользоватьк Вниз参число调использоватьdetectMultiScale()

  • камерарамкаиз灰степень Версия
  • нулевойизMatOfRectобъект,Обнаружение прибытияиз ограничивающих рамок с Вхранилищами
  • масштабный коэффициент,использовать ВКонечносуществоватькаждый Пропорция Вниз Волявходитьрамкауменьшать少多少(1.1иметь в виду Волякогда前Пропорцияуменьшать少 10% Построить следующую шкалу в пирамиде, имеющую более высокую оценку, значит быстрее провести расчеты) Если масштабирование существования не закрывает ладонь при определенных размерах, распознавание лицевой стороны может быть потеряно ценить)
  • самый маленький Размер соседства, используйте B, чтобы указать, сколько соседей должно сохраняться для каждого обнаружения; В противном случае он отбрасывается (этот параметр используется для уменьшения количества ложных срабатываний, поскольку попотому чтоиспользоватьдругойиз Пропорция,потому чтосуществовать Обнаружение множества реальных соседей в одном районе,поэтому真实误报会出сейчас)-flagCASCADE_SCALE_IMAGEУвеличитьизображениекстроитьизображениепирамида (Потому что для есть другой способ ввести характеристики масштаба для обнаружения различных масштабов и объектов) Следовательно, для Чтобы улучшить производительность и упростить операции, мы Воля придерживаемся существующих 5 глава,“приложение 3- Просмотр панорам"
  • Вы можете найти жилье, заинтересованное в объектизсамый маленький максимальный размер

После наличия списка обнаружения,нас希望Воляэто们точка组дляразмердля100 x 100Пиксельизнулевой间точка区,Стабильное обнаружение и устранение ложных срабатываний при использовании различных кадров:

Язык кода:javascript
копировать
Rect[] facesArray = closedHands.toArray();
for (int i = 0; i < facesArray.length; i++){
  //draw the unstable detection using the color red
  Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), HAND_RECT_COLOR, 3);
  //group the detections by the top-left corner
  Point quatnizedTL=new Point(((int)(facesArray[i].tl().x/100))*100,((int)(facesArray[i].tl().y/100))*100);
  //group the detections by the bottom-right corner
  Point quatnizedBR=new Point(((int)(facesArray[i].br().x/100))*100,((int)(facesArray[i].br().y/100))*100);
  //get the spatial bucket ID using the grouped corners hashcodes
  int bucktID= quatnizedTL.hashCode()+quatnizedBR.hashCode()*2;
  //add or increase the number of grouped detections per bucket
  if(rectBuckts.containsKey(bucktID)){
    rectBuckts.put(bucktID, rectBuckts.get(bucktID)+1);
    rectCue.put(bucktID, new Rect(quatnizedTL,quatnizedBR));
  }
  else{
    rectBuckts.put(bucktID,1);
  }
}

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

Язык кода:javascript
копировать
int maxDetections=0;
int maxDetectionsKey=0;
for(Entry<Integer,Integer> e : rectBuckts.entrySet()){
  if(e.getValue()>maxDetections){
    maxDetections=e.getValue();
    maxDetectionsKey=e.getKey();
    }
  }
  //Threshold for a stable detection
  if(maxDetections>5){
    //Draw the stable detection in green
    Core.rectangle(mRgba, rectCue.get(maxDetectionsKey).tl(), rectCue.get(maxDetectionsKey).br(), CUE_RECT_COLOR, 3);
    //build the file name
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
    String currentDateandTime = sdf.format(new Date());
    String fileName = Environment.getExternalStorageDirectory().getPath() +"/sample_picture_" + currentDateandTime + ".jpg";
    //take the picture
    mOpenCvCameraView.takePicture(fileName);
    //show a notification that the picture is saved
    Message msg = handler.obtainMessage();msg.arg1 = 1;
    Bundle b=new Bundle();b.putString("msg", fileName + " saved");
    msg.setData(b);handler.sendMessage(msg);
    //clear the spatial buckets and start over
    rectBuckts.clear();
  }
  return mRgba;
}

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

В этой главе мы создали новое приложение, основанное на известном каскадном инструменте Build, который может автоматически делать фотографии. Мы уже рассмотрели особенности проживанияотиспользоватьиз. импульсизучатьалгоритмикаскадстроить Каскадный классификаторизпроцесс。 ты还изучать Понятно如何использовать经过тренироватьсяизточкадобрыйустройство Приходитьинициализацияииспользоватьбаза В多尺степеньслайдокноиз Обнаружениеустройство,распознавать жесты закрытия ладони,И Воля эти обнаружения делает для подсказки с устройства и зкамеры захвата кадров.

boy illustration
RasaGpt — платформа чат-ботов на основе Rasa и LLM.
boy illustration
Nomic Embed: воспроизводимая модель внедрения SOTA с открытым исходным кодом.
boy illustration
Улучшение YOLOv8: EMA основана на эффективном многомасштабном внимании, основанном на межпространственном обучении, и эффект лучше, чем у ECA, CBAM и CA. Малые цели имеют очевидные преимущества | ICASSP2023
boy illustration
Урок 1 серии Libtorch: Тензорная библиотека Silky C++
boy illustration
Руководство по локальному развертыванию Stable Diffusion: подробные шаги и анализ распространенных проблем
boy illustration
Полностью автоматический инструмент для работы с видео в один клик: VideoLingo
boy illustration
Улучшения оптимизации RT-DETR: облегченные улучшения магистрали | Support Paddle облегченный rtdetr-r18, rtdetr-r34, rtdetr-r50, rtdet
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | Деформируемое внимание с большим ядром (D-LKA Attention), большое ядро ​​​​свертки улучшает механизм внимания восприимчивых полей с различными функциями
boy illustration
Создано Datawhale: выпущено «Руководство по тонкой настройке развертывания большой модели GLM-4»!
boy illustration
7B превышает десятки миллиардов, aiXcoder-7B с открытым исходным кодом Пекинского университета — это самая мощная модель большого кода, лучший выбор для корпоративного развертывания.
boy illustration
Используйте модель Huggingface, чтобы заменить интерфейс внедрения OpenAI в китайской среде.
boy illustration
Оригинальные улучшения YOLOv8: несколько новых улучшений | Сохранение исходной информации — алгоритм отделяемой по глубине свертки (MDSConv) |
boy illustration
Второй пилот облачной разработки | Быстро поиграйте со средствами разработки на базе искусственного интеллекта
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция с нулевым кодированием и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
Решенная Ошибка | Загрузка PyTorch медленная: TimeoutError: [Errno 110] При загрузке факела истекло время ожидания — Cat Head Tiger
boy illustration
Brother OCR, библиотека с открытым исходным кодом для Python, которая распознает коды проверки.
boy illustration
Новейшее подробное руководство по загрузке и использованию последней демонстрационной версии набора данных COCO.
boy illustration
Выпущен отчет о крупной модели финансовой отрасли за 2023 год | Полный текст включен в загрузку |
boy illustration
Обычные компьютеры также могут работать с большими моделями, и вы можете получить личного помощника с искусственным интеллектом за три шага | Руководство для начинающих по локальному развертыванию LLaMA-3
boy illustration
Одной статьи достаточно для анализа фактора транскрипции SCENIC на Python (4)
boy illustration
Бросая вызов ограничениям производительности небольших видеокарт, он научит вас запускать большие модели глубокого обучения с ограниченными ресурсами, а также предоставит полное руководство по оценке и эффективному использованию памяти графического процессора!
boy illustration
Команда Fudan NLP опубликовала 80-страничный обзор крупномасштабных модельных агентов, в котором в одной статье представлен обзор текущего состояния и будущего агентов ИИ.
boy illustration
[Эксклюзив] Вы должны знать о новой функции JetBrains 2024.1 «Полнострочное завершение кода», чтобы решить вашу путаницу!
boy illustration
Краткое изложение базовых знаний о регистрации изображений 1.0
boy illustration
Новейшее подробное руководство по установке и использованию библиотеки cv2 (OpenCV, opencv-python) в Python.
boy illustration
Легко создайте локальную базу знаний для крупных моделей на основе Ollama+AnythingLLM.
boy illustration
[Решено] ошибка установки conda. Среда решения: не удалось выполнить первоначальное зависание решения. Повторная попытка с помощью файла (графическое руководство).
boy illustration
Одна статья поможет вам понять RAG (Retrival Enhanced Generation) | Введение в концепцию и теорию + практику работы с кодом (включая исходный код).
boy illustration
Эволюция архитектуры шлюза облачной разработки
boy illustration
Docker и Kubernetes [Разработка контейнерных приложений с помощью Python]