Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Image processing with Android using OpenCV (Ger...
Search
Ralf
September 02, 2014
Programming
330
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
480
Extending kotlin-inject for fun & profit
vrallev
0
1k
Extending kotlin-inject for fun & profit
vrallev
2
1.1k
Managing State Beyond ViewModels and Hilt (Updated)
vrallev
4
1.8k
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.5k
Delegation in Kotlin
vrallev
3
860
Other Decks in Programming
See All in Programming
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.6k
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
490
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
710
phperだってパンチカードでコーディングしたい
o0h
PRO
0
270
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
380
Java 27新機能 / Java 27 new features
kishida
2
220
Kubernetes祭り #2 OP/ED
bells17
0
110
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
370
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
620
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
480
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
960
最近”作らせてる” ローカルAIアプリの話
akit37
0
440
Featured
See All Featured
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
400
Designing for Performance
lara
612
70k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Paper Plane
katiecoart
PRO
4
54k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
580
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
750
Ten Tips & Tricks for a 🌱 transition
stuffmc
1
260
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.2k
The SEO Collaboration Effect
kristinabergwall1
1
590
Code Reviewing Like a Champion
maltzj
529
40k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
The Curious Case for Waylosing
cassininazir
1
560
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]