Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Image processing with Android using OpenCV (Ger...
Search
Ralf
September 02, 2014
Programming
310
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Image processing with Android using OpenCV (German)
(German)
Ralf
September 02, 2014
More Decks by Ralf
See All by Ralf
Scale your Kotlin Multiplatform projects using dependency injection
vrallev
1
450
Extending kotlin-inject for fun & profit
vrallev
0
970
Extending kotlin-inject for fun & profit
vrallev
2
1k
Managing State Beyond ViewModels and Hilt (Updated)
vrallev
4
1.7k
Managing State Beyond ViewModels and Hilt
vrallev
5
1.8k
Isolated Development
vrallev
0
1.4k
Dagger + Anvil: Learning to Love Dependency Injection
vrallev
4
1.9k
Android at Scale @Square
vrallev
7
8.3k
Delegation in Kotlin
vrallev
3
830
Other Decks in Programming
See All in Programming
in-process GraphQL のすすめ #ginzajs
izumin5210
4
1.5k
freeeにおけるEvalsの実践例の紹介
freee
PRO
0
160
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
170
AI Readyの正体はデータマネジメントだ メダリオン2.0の最前線
freee
PRO
0
430
承認済みなのに差戻しできてしまうバグ、型で潰せます
shinchit
0
120
Go 1.27 における memory allocation の高速化
andpad
0
350
高専、大学編入、そして未踏へ〜プロダクト開発とキャリアの歩み - Technical College, University Transfer, and On to “Mitou” / My Journey in Product Development and Career
pkmiya
0
100
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
530
片田舎のおっさん、 Swift Buildのダイアモンド問題解決の不具合修正PRを出すが、解決方法がキャッシュをしないようにすることであり、ビルド時間が伸びると言われてマージされないので高速化もする/swiftbuild
yimajo
0
320
ソフトウェアラスタライザ
fadis
1
720
Webエンジニアなのにブラウザの仕組みがわからないので、Pythonで自作してみた
tatsuki12
4
1.1k
Go 1.27からのGODEBUG / Go 1.27 リリースパーティ #go127party
mazrean
0
260
Featured
See All Featured
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
480
A better future with KSS
kneath
240
18k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
220
A designer walks into a library…
pauljervisheath
211
24k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
500
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
950
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.5k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
290
Producing Creativity
orderedlist
PRO
348
40k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
800
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
690
Transcript
Ralf Wondratschek | adorsys GmbH & Co. KG Bildbearbeitung für
Android mit OpenCV
Inhalt OpenCV API Pixel Zugriff Effektbeispiele
Inhalt Einbindung in Android Kamerazugriff bei Android Best Practices
cv::Scalar provideCode() { return cv::Scalar(CPP, Java); } void showDemo(cv::Mat& mat)
{ Android(mat); iOS(mat); Windows(mat); }
OpenCV API
Open Source Computer Vision BSD Lizenz kommerzielle Produkte erlaubt
C++, C, Python und JAVA API Fokus auf Performance und Echtzeitanwendungen
Entwicklung begann 1999 durch Intel Version 1.0 erschien 2006 Version
2.0 mit C++ API erschien 2009 Seit 2012 durch OpenCV.org gepflegt und verwaltet
class Mat
class Mat Repräsentiert ein n-dimensionales Array Bei Bildern 2-dimensional
rows und cols Variablen Überladene Operatoren A+B, A-B, A*alpha, A*B, …
class Mat Anzahl der Kanäle und Bittiefe frei wählbar
z.B. CV_8UC1, CV_8UC3, CV_32F Konvertierung mittels cvtColor(src, dst, code) Funktion Standardformat ist BGR und nicht RGB
Mat bgr(4, 4, CV_8UC3) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0)
(0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0)
class Mat_ Template Klasse für Mat Sinnvoll, wenn man zu
Compile-Zeit den Typ des Mat Objektes kennt Mat mat(24, 24, CV_8U); … Mat_<uchar>& other = (Mat_<uchar>&) mat;
class Size cols rows
class Size Gibt die Anzahl der Spalten und Reihen an
Mat::size() Größe auch über cols und rows abfragbar Bei höher dimensionalen Matrizen sind Werte (-1, -1)
class Vec Klasse für kurze numerische Vektoren Vec2b, Vec4i,
Vec6d Überladene Operatoren v1+v2, v1-v2, v1*scale, … 255 0 0 Mat_<Vec3b> mat(240, 320, Vec3b(255, 0, 0));
class Scalar Erbt von Vec und stellt Vec<type, 4> dar
4 dimensionaler Vektor
Pixel Zugriff
at(row, col) Mat mat(240, 320, CV_8UC3); mat.at<Vec3b>(row, col)[0] = 255;
mat.at<Vec3b>(row, col)[1] = 255; mat.at<Vec3b>(row, col)[2] = 255; Mat mat(240, 320, CV_8UC1); mat.at<uchar>(row, col) = 255;
at(row, col) Mat mat(4, 4, CV_8UC3); mat.at<Vec3b>(1, 2)[0] = 255;
at(row, col) Typ der Matrix muss zur Compile Zeit bekannt
sein Gut lesbar Sinnvoll bei einzelnen Pixelzugriffen Langsam beim Iterieren über das gesamte Bild
Iterator Mat_<Vec3b>::iterator it = mat.begin<Vec3b>(); Mat_<Vec3b>::iterator itend= mat.end<Vec3b>(); for (
; it != itend; ++it) { (*it)[0] = 255; (*it)[1] = 255; (*it)[2] = 255; }
Iterator Typ der Matrix muss zur Compile Zeit bekannt sein
Gut lesbar, objektorientiert Sinnvoll, wenn die Bearbeitungszeit nicht ausschlaggebend ist Langsam beim Iterieren über das gesamte Bild (aber schneller als at() Methode)
int nr = mat.rows; int nc = mat.cols; if (mat.isContinuous())
{ nc = nc * nr; nr = 1; } for (int row = 0; row < nr; ++row) { uchar* pointer = mat.ptr<uchar>(row); for (int col = 0; col < nc; ++col) { *pointer++ = 255; // B *pointer++ = 255; // G *pointer++ = 255; // R } } Pointer
Pointer Kann als Template Methode verwendet werden Pointerarithmetik fehleranfällig Effizient,
Vorteile bei Speicherstruktur können genutzt werden Sinnvoll beim Iterieren über das gesamte Bild
Beispiele
Allgemeines Vorgehen 1.) App lädt plattformspezifisch das Bild und stellt
es dar 2.) Aus dem Bild wird ein Mat Objekt im BGR Format erstellt 3.) Im geteilten Code wird das Mat Objekt in-place bearbeitet
Schwarz-Weiß void OpenCvDemo::convertGray (cv::Mat& mat) { cv::Mat gray(mat.rows, mat.cols, CV_8UC1);
cvtColor(mat, gray, CV_BGR2GRAY); cvtColor(gray, mat, CV_GRAY2BGR); }
Farbcontroller void OpenCvDemo::controlColor(cv::Mat& mat, int r, int g, int b)
{ float red = (float) r / 100; float green = (float) g / 100; float blue = (float) b / 100; int nr = mat.rows; int nc = mat.cols; if (mat.isContinuous()) { nc = nc * nr; nr = 1; } for (int row = 0; row < nr; ++row) { uchar* pOriginal = mMatOriginal.ptr(row); uchar* pResult = mat.ptr(row); for (int col = 0; col < nc; ++col) { *pResult++ = (uchar) (*pOriginal++ * blue); // B *pResult++ = (uchar) (*pOriginal++ * green); // G *pResult++ = (uchar) (*pOriginal++ * red); // R } } }
Touchfocus void OpenCvDemo::touchFocus(cv::Mat& matOverlay, cv::Mat& matResult, int centerX, int centerY,
int fade, int distance) { int edge = distance + fade; int nr = matResult.rows; int nc = matResult.cols; for (int row = 0; row < nr; ++row) { uchar* pOriginal = mMatOriginal.ptr(row); uchar* pOverlay = matOverlay.ptr(row); uchar* pResult = matResult.ptr(row); for (int col = 0; col < nc; ++col) { float calcDistance = (abs(centerY - row) + abs(centerX - col)); float opacity; if (calcDistance < distance) { opacity = 1; } else if (calcDistance > edge) { opacity = 0; } else { opacity = (fade - (calcDistance - distance)) / fade; } *pResult++ = (uchar) ((*pOverlay++ - *pOriginal) * opacity + *pOriginal++); // B *pResult++ = (uchar) ((*pOverlay++ - *pOriginal) * opacity + *pOriginal++); // G *pResult++ = (uchar) ((*pOverlay++ - *pOriginal) * opacity + *pOriginal++); // R } } }
Zugriff auf Nachbarpixel Möglich über weitere Pointer auf vorhergehende Reihe
und Spalte Sonderbehandlung von erster und letzter Reihe und Spalte Funktioniert, ist aber fehleranfällig und schwer zu warten
Kernel Matrix Zentrum referenziert momentanen Pixel Pixelwerte werden mit Kernelwert
multipliziert und anschließend addiert
Kernel Matrix 96 68 33 48 142 196 146 127
125 96 68 33 48 142 196 146 127 186 0 -1 0 -1 0 -1 0 -1 5 125 ∗ 5 − 68 − 48 − 196 − 127 = 186
Box-Blur void OpenCvDemo::boxBlur(cv::Mat& mat, int size) { float kernelValue =
1.0 / (size * size); cv::Mat kernel(size, size, CV_32F, cv::Scalar(kernelValue)); filter2D(mMatOriginal, mat, mMatOriginal.depth(), kernel); } 1/9 1/9 1/9 1/9 1/9 1/9 1/9 1/9 1/9
void OpenCvDemo::gaussianBlur(cv::Mat& mat, int size) { if (size % 2
== 0) { size++; } // sigma in both directions computed GaussianBlur(mMatOriginal, mat, cv::Size(size, size), 0, 0); } Gaussian-Blur
Auflösung verringern void OpenCvDemo::sizeDown(cv::Mat& mat, int step, bool bad) {
if (bad) { int nr = mat.rows; int nc = mat.cols; for (int row = 0; row < nr; ++row) { uchar* pOriginal = mMatOriginal.ptr(row * step); uchar* pResult = mat.ptr(row); for (int col = 0; col < nc; ++col) { *pResult++ = *pOriginal++; // B *pResult++ = *pOriginal++; // G *pResult++ = *pOriginal++; // R pOriginal += 3 * (step - 1); } } } else { resize(mMatOriginal, mat, cv::Size(mMatOriginal.cols / step, mMatOriginal.rows / step)); } }
Arithmetik void OpenCvDemo::arithmetic(cv::Mat& mat, int factor) { float f =
(float) factor / 10; mat = mMatOriginal + (f * mMatOriginal); }
Unsharp Masking http://en.wikipedia.org/wiki/Unsharp_masking
void OpenCvDemo::sharpen(cv::Mat& mat, int size) { gaussianBlur(mat, size); addWeighted(mMatOriginal, 1.5,
mat, -0.5, 0, mat); } Unsharp Masking
Kantendetektion Gesucht ist der Gradient der Matrix I = ,
= 2 + 2
Sobel -1 0 1 2 1 0 -1 -2 0
-1 -2 -1 0 1 2 1 0 0 Gradient in X-Richtung Gradient in Y-Richtung
Sobel Beispiel 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 255 255 255 255 255 255 255 255 255 255
Sobel Beispiel 0 0 0 0 0 0 -1 -2
-1 0 1 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 1020 1020 1020 1020 1020 1020 1020 1020 1020 1020 0 0 0 0 0
Sobel void OpenCvDemo::sobel(cv::Mat& mat) { cv::Mat originalGray(mMatOriginal.cols, mMatOriginal.rows, CV_8UC1); cvtColor(mMatOriginal,
originalGray, CV_BGR2GRAY); cv::Mat gradX; cv::Mat gradY; cv::Mat absGradX; cv::Mat absGradY; cv::Mat grad; Sobel(originalGray, gradX, CV_16S, 1, 0); Sobel(originalGray, gradY, CV_16S, 0, 1); convertScaleAbs(gradX, absGradX); convertScaleAbs(gradY, absGradY); addWeighted(absGradX, 0.5, absGradY, 0.5, 0, grad); cvtColor(grad, mat, CV_GRAY2BGR); }
Einbindung Android
Anforderungen OpenCV Java API OpenCV C++ API NDK
Warum wir die vorgefertigten Klassen und Java API nicht verwenden
Java API nicht mit allen Plattformen nutzbar Java API ist nur ein Wrapper Flexibilität
Warum wir die vorgefertigten Klassen und Java API nicht verwenden
Eclipse nicht erwünscht Gradle als Build System Gradle Task zum Bauen von C++ Code
repositories { maven { url 'https://raw.github.com/vRallev/mvn-repo/master/' } } dependencies {
compile 'org.opencv:opencv-android:2.4.8' } OpenCV einbinden
Gradle und das NDK Rudimentäre Unterstützung Makefile wird generiert, keine
manuelle Anpassung möglich Automatischer Build mit jni Ordner Umweg über eigenen Build Task https://code.google.com/p/android/issues/detail?id=65241
OpenCV initialisieren static { if (OpenCVLoader.initDebug()) { System.loadLibrary("cv-module"); } else
{ throw new IllegalArgumentException(); } }
ImageView imageView = (ImageView) findViewById(R.id.imageView); Bitmap bitmap = createBitmap(); imageView.setImageBitmap(bitmap);
int[] data = new int[bitmap.getWidth() * bitmap.getHeight]; // übergib Array per JNI und manipuliere Daten bitmap.setPixels(data, 0, width, 0, 0, width, height); Szene laden
ImageView imageView = (ImageView) findViewById(R.id.imageView); Bitmap bitmap = createBitmap(); imageView.setImageBitmap(bitmap);
int[] data = new int[bitmap.getWidth() * bitmap.getHeight]; // übergib Array per JNI und manipuliere Daten bitmap.setPixels(data, 0, width, 0, 0, width, height); Schecht!
Bitmap manipulieren BitmapConverter::BitmapConverter (JNIEnv* env, jobject& bitmap) throw (int) {
AndroidBitmapInfo info; int ret; void* pixels; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); throw ret; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888 !"); throw ret; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); throw ret; } width = info.width; height = info.height; Mat bitmapMat(height, width, CV_8UC4, pixels); mat8UC4 = bitmapMat; Mat matBGR(height, width, CV_8UC3); mat = matBGR; cvtColor(mat8UC4, mat, CV_RGBA2BGR); }
Bitmap manipulieren void BitmapConverter::release (JNIEnv* env, jobject& bitmap) { cvtColor(mat,
mat8UC4, CV_BGR2RGBA); AndroidBitmap_unlockPixels(env, bitmap); }
ImageView imageView = (ImageView) findViewById(R.id.imageView); Bitmap bitmap = createBitmap(); imageView.setImageBitmap(bitmap);
// übergib bitmap per JNI und manipuliere Daten imageView.invalidate(); Bitmap manipulieren
Kamerazugriff Android
Vorgehen Kameraverbindung herstellen SurfaceView oder TextureView Parameter setzen Kamera öffnen
Byte Array als Datenpuffer erstellen PreviewCallback setzen
Vorgehen Zeichnen Daten in Bitmap, die von einer ImageView angezeigt
wird YUV420 muss in RGBA umgewandelt werden
Beispiel void jni(JNIEnv* env, jobject, jbyteArray yuv, jobject bitmap) {
BitmapConverter converterResult (env, bitmap); int width = converterResult.width; int height = converterResult.height; jbyte* _yuv = env->GetByteArrayElements(yuv, 0); Mat myuv = Mat(height + height/2, width, CV_8UC1, (uchar *)_yuv); cvtColor(myuv, converterResult.mat, CV_YUV420sp2BGR); circle(converterResult.mat, Point(width / 2, height / 2), width / 4, Scalar(255, 0, 0), 20); env->ReleaseByteArrayElements(yuv, _yuv, 0); converterResult.release(env, bitmap); }
Best Practices
Best Practices Trennung von Plattform und OpenCV Code Matrizen eignen
sich gut für Parallelisierung Verarbeitung in Workerthreads
Best Practices Bei Analyse erst kleinere Bilder verwenden Bei Typumwandlung
Speicherstrukturen ausnutzen: YUV420sp zu Gray ist günstig BGR zu BGRA ist günstig YUV420sp zu BGR ist teuer
Best Practices Optimierung bei Arithmetik nutzen, falls Funktion zeitkritisch ist
API nutzen, falls Funktionen schon fertig programmiert sind
Ralf Wondratschek
[email protected]