1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
|
/***
* @author: Ajinkya Dhaigude
*/
package com.example.ajinkya.cvisionapp;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.JavaCameraView;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfInt4;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.core.TermCriteria;
import org.opencv.imgproc.Imgproc;
import org.opencv.video.Video;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {
private JavaCameraView javaCameraView; // Objet caméra
private Mat ycc; // Objet Mat permettant de stocker une matrice par exemple, channel ou multi channel d'une image
private int camDim[] = {320, 240}; // could be better // Dimension de la caméra
private float offsetFactX, offsetFactY;
private float scaleFactX, scaleFactY; // Valeur des coordonées x et y de l'échelle
private boolean handDetected = false; // Etat de la détection de la main
private Scalar handColor; // Couleur de la main (pour la détection)
private Scalar minHSV; // Valeur des pixels min du cube (derived from Vec<_Tp, 4>, Scalar_ and Scalar can be used just as typical 4-element vectors)
private Scalar maxHSV; // Valeur des pixels max du cube (derived from Vec<_Tp, 4>, Scalar_ and Scalar can be used just as typical 4-element vectors)
private Mat frame, frame2;
private Point palmCenter; // Objet Point contenant le centre de la main
private List<Point> fingers; // List contenant l'ensemble des points pour reconnaitre les doights
private TermCriteria termCriteria;
private List<Rect> allRoi; // List de Rect
private List<Mat> allRoiHist; // List de Mat
private MatOfFloat ranges;
private MatOfInt channels;
private Mat dstBackProject;
private MatOfPoint palmContour;
private MatOfPoint hullPoints;
private MatOfInt hull;
private Mat hierarchy;
private Mat touchedMat;
private MatOfInt4 convexityDefects;
private Mat nonZero;
private Mat nonZeroRow;
private List<MatOfPoint> contours; // List de MAtOfPoint représentant le contour de la main
private GLRenderer myGLRenderer; // Objet OpenGL GLRenderer
private int speedTime = 0; // Temp de rapidité
private int speedFingers = 0; // Temp de rapidité des doights
// Vérifier que OpenCV charge les fonctions/méthodes natives
static {
if (!OpenCVLoader.initDebug())
Log.e("init", "noo");
else
Log.e("init", "yess");
}
// Méthode permettant de verifier que OpenCV s'est correctement lancer et d'activer la caméra
private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i("START!!!", "OpenCV loaded successfully");
javaCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // On garde la fenetre éveillé
View decorView = getWindow().getDecorView(); // Le DecorView est la vue qui tient le fond de la fenetre
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; // On met l'app en pleine écran
decorView.setSystemUiVisibility(uiOptions); // Fonction requise pour le pleine écran (personnalisation de la vue)
setContentView(R.layout.activity_main);
javaCameraView = (JavaCameraView) findViewById(R.id.java_surface_view); // On récupère une référence vers la caméra
javaCameraView.setVisibility(SurfaceView.VISIBLE); // On affiche ce que la caméra filme
javaCameraView.setCvCameraViewListener(this); // On implémente un listenner sur la caméra
javaCameraView.setMaxFrameSize(camDim[0], camDim[1]); // On dimensionne le cadre de la fenetre de la caméra
GLSurfaceView myGLView = new GLSurfaceView(this); // Une SurfaceView est utilisé& pour une surface dédié à l'affichage d'un rendu OpenGL
myGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Configure les couleurs RGB de la surfaceView
myGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT); // Permet de rendre les pixels translucides (invisbles)
myGLRenderer = new GLRenderer(); // Nouvel objet GLRenderer
myGLView.setRenderer(myGLRenderer); // On applique cet objet à la surfaceView
addContentView(myGLView, new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT)); // on applique le paramètre wrapContent au layout de cette surfaceview
myGLView.setZOrderMediaOverlay(true); // Contrôler si la surface de la vue de surface est placé sur le dessus de sa fenêtre.
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onResume(){
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this, baseLoaderCallback);
}
public void onDestroy() {
super.onDestroy();
if (javaCameraView != null)
javaCameraView.disableView();
}
// Fonction permettant d'initialiser et de configurer les paramètres de la caméra
@Override
public void onCameraViewStarted(int width, int height) {
setScaleFactors(width, height); // On appliquer les valeurs de largeur et hauteur de l'échelle de la fenêtre
myGLRenderer.setVidDim(camDim[0], camDim[1]); // On applique les dimensions à la caméra
ycc = new Mat(height, width, CvType.CV_8UC3); // On affecte les dimensions des channels d'une image
handColor = new Scalar(255); // Représent la couleur de la main
minHSV = new Scalar(3); // On affecte la valeur minimum d'un pixel
maxHSV = new Scalar(3); // On affecte la valeur maximum d'un pixel
frame = new Mat(); // n-dimensional dense numerical single-channel or multi-channel array. | can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms
termCriteria = new TermCriteria(TermCriteria.COUNT | TermCriteria.EPS, 10, 1); // utilisé pour les algorythme itératif
allRoi = new ArrayList<>(); // Tableau de List
allRoiHist = new ArrayList<>(); // Tableau de List
ranges = new MatOfFloat(0, 180); //MatOfInt, MatOfFloat est des classes qui sont héritées de Mat et a 1 canal de type et de taille définie 1xN. Il est analogue de std :: vector <int>, std :: vector <deux>, etc dans le code C ++.
channels = new MatOfInt(0); // MatOfInt, MatOfFloat est des classes qui sont héritées de Mat et a 1 canal de type et de taille définie 1xN. Il est analogue de std :: vector <int>, std :: vector <deux>, etc dans le code C ++.
dstBackProject = new Mat(); // n-dimensional dense numerical single-channel or multi-channel array. | can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms
palmContour = new MatOfPoint(); // Tableau Vector de point correspondant au contour de la main
hullPoints = new MatOfPoint(); // Tableau Vector de point correspondant au point de chaque doight
hull = new MatOfInt(); // MatOfInt, MatOfFloat est des classes qui sont héritées de Mat et a 1 canal de type et de taille définie 1xN. Il est analogue de std :: vector <int>, std :: vector <deux>, etc dans le code C ++.
hierarchy = new Mat();
touchedMat = new Mat();
convexityDefects = new MatOfInt4();
nonZero = new Mat();
frame2 = new Mat();
nonZeroRow = new Mat();
contours = new ArrayList<>();
palmCenter = new Point(-1, -1); // Point de coordonnées x et y correspondant au centre de la main
}
// Méthode permettant d'appliquer des fonctions à l'image de la caméra et de renvoyer l'image modifié à la caméra
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
// On remplie la matrice des channels de l'image avec les couleurs RGBA
ycc = inputFrame.rgba();
// Imgproc.GaussianBlur(ycc, ycc, new Size(9, 9), 0);
// Imgproc.threshold(ycc, ycc, 70, 255, Imgproc.THRESH_BINARY_INV+Imgproc.THRESH_OTSU);
// Si la main est détectée
if (handDetected) {
//return frame;
frame = ycc.clone();
// for(int i=0; i<allRoi.size(); i++){
// Rect roi = motionTrack(frame, allRoi.get(i), allRoiHist.get(i)); // change to frame
// allRoi.set(i, roi);
// Imgproc.rectangle(ycc, roi.tl(), roi.br(), new Scalar(255, 0, 255), 3);
// }
/* Imgproc = traitement d'image.
Les fonctions et structures de ce module ont trait aux transformations d'images, au filtrage, à la détection de contours, de points d'intérêt
*/
Imgproc.GaussianBlur(frame, frame, new Size(9, 9), 5); // Applique un effet de flau gaussian à l'image
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGB2HSV_FULL); // Convertit une image d'un espace colorimétrique à un autre.
/* core = les fonctionnalités de base. Cette bibliothèque permet de manipuler les structures de base, réaliser des opérations sur des matrices, dessiner sur des images, sauvegarder et charger des données dans des fichiers XML
*/
Core.inRange(frame, minHSV, maxHSV, frame); // Vérifie si les éléments d'un tableau sont liés avec les éléments d'un autre tableau
// Point palm = getDistanceTransformCenter(frame);
// myGLRenderer.setPos(palm.x, palm.y);
// new: maybe move to function
// frame = ycc.clone();
// double avgHSV[] = getAvgHSV(frame, (int)palm.x, (int)palm.y, frame.rows(), frame.cols());
// assignHSV(avgHSV);
//TODO: check if needs to be released
contours = getAllContours(frame);
int indexOfPalmContour = getPalmContour(contours);
// Conditionnnel permettant de savoir si on affiche le cube ou pas en fonction de la détection du contour de la main ou pas
if(indexOfPalmContour < 0)
myGLRenderer.setRenderCube(false);
else{
Point palm = getDistanceTransformCenter(frame);
// Imgproc.circle(ycc, palm, 6, new Scalar(25, 120, 255));
myGLRenderer.setPos(palm.x, palm.y);
Rect roi = Imgproc.boundingRect(contours.get(indexOfPalmContour));
myGLRenderer.setCubeSize(getEuclDistance(palm, roi.tl()));
// palmContour = contours.get(indexOfPalmContour);
// List<Point> listHullPoints = getConvexHullPoints(palmContour);
// //Log.e("hull size ------>", hullPoints.size()+"");
// contours.clear();
// Point hullArray[] = new Point[listHullPoints.size()];
// listHullPoints.toArray(hullArray);
// hullPoints = new MatOfPoint(hullArray);
// contours.add(hullPoints);
// contours.add(palmContour);
// // draw convex hull
// Imgproc.drawContours(ycc, contours, 0, new Scalar(0, 255, 0));
// Imgproc.drawContours(ycc, contours, 1, new Scalar(0, 0, 255));
//
//
//
// // new: maybe move to function
//// frame = ycc.clone();
//// getAvgHSV(frame);
//
//
// Imgproc.rectangle(ycc, roi.tl(), roi.br(), new Scalar(0, 0, 255), 1);
// Log.e("mop", contours.get(indexOfPalmContour).dims() + " " + contours.get(0).get(0, 0).length);
//
// Imgproc.convexityDefects(palmContour, hull, convexityDefects);
// List<Integer> defectIndices = getDefects(convexityDefects.toList());
// Point palmContourPoints[] = palmContour.toArray();
//// Log.e("defects -->", defectIndices.size()+"");
//
// List<Point> defectPoints = new ArrayList<>();
// for(int i=0; i<defectIndices.size(); i+=4) {
// Imgproc.circle(ycc, palmContourPoints[defectIndices.get(i + 2)], 6, new Scalar(255, 105, 185));
// defectPoints.add(palmContourPoints[defectIndices.get(i + 2)]);
// }
// Point defectArray[] = new Point[defectPoints.size()];
// defectPoints.toArray(defectArray);
// hullPoints = new MatOfPoint(defectArray);
// contours.add(0, hullPoints);
// Imgproc.drawContours(ycc, contours, 0, new Scalar(254, 10, 0));
List<Point> hullPoints = getConvexHullPoints(contours.get(indexOfPalmContour)); //1 //indexOfPalmContour
fingers = getFingersTips(hullPoints, frame.rows());
Collections.reverse(fingers);
int fSize = fingers.size();
if(fSize != speedFingers){
speedFingers = fSize;
speedTime = 0;
}
else if(fSize != 5)
speedTime++;
if(speedTime > 8)
myGLRenderer.setCubeRotation(fSize);
Log.e("speed", speedTime+" "+fSize);
// for(int i=0; i<fingers.size(); i++) {
// Log.e("in f", getEuclDistance(fingers.get(i), palmCenter)+"");
// Imgproc.circle(ycc, fingers.get(i), 6, new Scalar(255, 0, 0));
// Imgproc.putText(ycc, fingers.get(i).x+" "+fingers.get(i).y, fingers.get(i), 1, 0.7, new Scalar(25, 0, 250) );
// }
}
// convex defects and moment mass
// Imgproc.convexityDefects(maxContour, hull, convexityDefects);
// List<Integer> defectIndices = convexityDefects.toList();
//
// Moments mont = Imgproc.moments(contours.get(0));
// int x = (int) (mont.get_m10() / mont.get_m00());
// int y = (int) (mont.get_m01() / mont.get_m00());
// Point palm = new Point(x, y);
// Imgproc.circle(ycc, palm, 6, new Scalar(25, 120, 255));
// ycc = getDistanceTransformCenter(ycc);
// contours.add(new MatOfPoint());
// contours.get(contours.size()-1).fromList(hullPoints);
// Imgproc.drawContours(frame, tempContours, tempIndex, new Scalar(0, 255, 0));
//
// // Bounded Rectangle
// Rect roi = Imgproc.boundingRect(contours.get(indexOfMaxContour));
// Imgproc.rectangle(frame, roi.tl(), roi.br(), new Scalar(0, 0, 255), 3);
// Log.e("mop", contours.get(0).dims() + " " + contours.get(0).get(0, 0).length);
//
// // Rotated Rectangle
// RotatedRect box = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(indexOfMaxContour).toArray()));
// Point corners[] = new Point[4];
// box.points(corners);
// for(int i=0; i<4; ++i) {
// Imgproc.line(frame, corners[i], corners[(i + 1) % 4], new Scalar(255, 0, 0));
// }
// return frame;
// }
return ycc;
}
return ycc;
}
// Fonction pour rafraichir la vue de la caméra lorsque la main n'est plus détecté
@Override
public void onCameraViewStopped() {
frame.release();
ycc.release();
ranges.release();
channels.release();
dstBackProject.release();
palmContour.release();
hullPoints.release();
hull.release();
hierarchy.release();
touchedMat.release();
convexityDefects.release();
nonZero.release();
frame2.release();
nonZeroRow.release();
while (allRoiHist.size() > 0)
allRoiHist.get(0).release();
while (contours.size() > 0)
contours.get(0).release();
}
// Méthode permettant d'agir sur pression de l'écran par l'utilisateur
@Override
public boolean onTouchEvent(MotionEvent event) {
if(! handDetected){
// clone and blur touched frame
frame = ycc.clone();
Imgproc.GaussianBlur(frame, frame, new Size(9, 9), 5);
// calc x, y coords coz resolution is scaled on device display
int x = Math.round((event.getX() - offsetFactX) * scaleFactX) ;
int y = Math.round((event.getY() - offsetFactY) * scaleFactY);
int rows = frame.rows();
int cols = frame.cols();
if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) return false;
palmCenter.x = x;
palmCenter.y = y;
// int xx = (int)((0 / scaleFactX)+ offsetFactX);
// int yy = (int)((0 / scaleFactY)+ offsetFactY);
//
// Log.e("1st", xx+" "+yy);
//
// xx = (int)((320 / scaleFactX)+ offsetFactX);
// yy = (int)((240 / scaleFactY)+ offsetFactY);
//
// Log.e("2nd", xx+" "+yy);
// get average HSV values of a square patch around the touched pixel
// and store them in global variables
getAvgHSV(frame);
// do below stuff in real time
// // to get palm center: do better coz image patch was in HSV earlier
// Needs work. Maybe find mean of all points
// Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGB2HSV_FULL);
// Core.inRange(frame, minHSV, maxHSV, frame);
//
//
//
////// basePoint = getDistanceTransformCenter(ycc);
//// basePoint = new Point(x, y);
////
// List<MatOfPoint> contours = getAllContours(frame);
// int indexOfMaxContour = getIndexOfMaxContour(contours);
// List<Point> hullPoints = getConvexHullPoints(contours.get(indexOfMaxContour));
// fingers = getFingersTips(hullPoints, rows);
// Collections.reverse(fingers); // thumb is 0
//
//
//
// //************test***********
//// //Imgproc.drawContours(frame, contours, indexOfMaxContour, new Scalar(255, 0, 0));
//// frame = ycc.clone();
//// Point hullArray[] = new Point[hullPoints.size()];
//// hullPoints.toArray(hullArray);
//// contours.add(new MatOfPoint(hullArray));
//// //contours.get(contours.size()-1).fromList(hullPoints);
//// Imgproc.drawContours(frame, contours, contours.size()-1, new Scalar(0, 255, 0));
////
////
//
//
//
//
// for(int i=0; i<fingers.size(); i++){
// allRoi.add(new Rect());
// allRoiHist.add(new Mat());
// assignRoiHist(fingers.get(i), ycc, allRoi.get(i), allRoiHist.get(i));
// }
handDetected = true;
}
return false;
}
// Méthode permettant de renvoyer la liste des defauts
protected List<Integer> getDefects(List<Integer> defectIndicesOld){
int thresh = 800;
int prevDepth = 0;
List<Integer> defectIndices = new ArrayList<Integer>();
for(int i = 0; i<defectIndicesOld.size(); i+=4) {
int curDepth = defectIndicesOld.get(i+3);
// Log.e("depth", i+" "+curDepth);
// if (curDepth < prevDepth)
// defectIndices.addAll(defectIndicesOld.subList(i-4, i));
// prevDepth = curDepth;
if(curDepth > thresh)
defectIndices.addAll(defectIndicesOld.subList(i, i+4));
}
return defectIndices;
}
// Méthode permettant de gerer la fenetre et le cube ensemble
protected Rect motionTrack(Mat frame, Rect roi, Mat roiHist){
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGB2HSV_FULL);
List<Mat> tempList = new ArrayList<>();
tempList.add(frame);
Imgproc.calcBackProject(tempList, channels, allRoiHist.get(0), dstBackProject, ranges, 1);
Video.meanShift(dstBackProject, roi, termCriteria);
return roi;
}
// Fonction permettant de placer le rectangle et de calculer ses coordonnées x, y pour le centrer
protected void assignRoiHist(Point point, Mat frame, Rect roi, Mat roiHist){
int halfSide = 10; // valeur de la moitié d'un coté
roi.x = ((int) point.x - halfSide > 0)? (int) point.x - halfSide : 0;
roi.y = ((int) point.y - halfSide > 0)? (int) point.y - halfSide : 0;
roi.width = (2 * halfSide < frame.width())? 2 * halfSide : frame.width();
roi.height = (2 * halfSide < frame.height())? 2 * halfSide : frame.height();
Log.e("roi", roi.x+" "+roi.y+" "+roi.width+" "+roi.height);
Mat submat = frame.submat(roi);
Mat mask = new Mat();
MatOfInt histSize = new MatOfInt(180);
Imgproc.cvtColor(submat, submat, Imgproc.COLOR_RGB2HSV_FULL);
Core.inRange(submat, minHSV, maxHSV, mask);
List<Mat> tempMatList = new ArrayList();
tempMatList.add(submat);
Imgproc.calcHist(tempMatList, channels, mask, roiHist, histSize, ranges);
Core.normalize(roiHist, roiHist, 0, 255, Core.NORM_MINMAX);
submat.release();
mask.release();
histSize.release();
}
// Méthode permettant de manipuler dynamiquement l'échelle du cube en fonction de la distance avec le centre de la fentre
protected Point getDistanceTransformCenter(Mat frame){
Imgproc.distanceTransform(frame, frame, Imgproc.CV_DIST_L2, 3);
frame.convertTo(frame, CvType.CV_8UC1);
Core.normalize(frame, frame, 0, 255, Core.NORM_MINMAX);
Imgproc.threshold(frame, frame, 254, 255, Imgproc.THRESH_TOZERO);
Core.findNonZero(frame, nonZero);
// are you kidding me
int sumx = 0, sumy = 0;
for(int i=0; i<nonZero.rows(); i++) {
sumx += nonZero.get(i, 0)[0];
sumy += nonZero.get(i, 0)[1];
}
sumx /= nonZero.rows();
sumy /= nonZero.rows();
return new Point(sumx, sumy);
}
// Méthode permettant de stocker et renvoyer une liste de point situé sur les doigts pour les détecter
protected List<Point> getFingersTips(List<Point> hullPoints, int rows){
// group into clusters and find distance between each cluster. distance should approx be same
double betwFingersThresh = 80;
double distFromCenterThresh = 80;
double thresh = 80;
List<Point> fingerTips = new ArrayList<>();
for(int i=0; i<hullPoints.size(); i++){
Point point = hullPoints.get(i);
if(rows - point.y < thresh){ //betwFingersThresh // lies very near frame edge hence arm
// || getEuclDistance(point, palmCenter) < distFromCenterThresh) {
// Log.e("dist", getEuclDistance(point, palmCenter)+"");
continue;
}
if(fingerTips.size() == 0){
fingerTips.add(point);
continue;
}
Point prev = fingerTips.get(fingerTips.size() - 1);
double euclDist = getEuclDistance(prev, point);
if(getEuclDistance(prev, point) > thresh/2 &&
getEuclDistance(palmCenter, point) > thresh) {
// Log.e("be f", euclDist+"");
fingerTips.add(point);
}
if(fingerTips.size() == 5) // prevent detection of point after thumb
break;
}
return fingerTips;
}
// Méthode permettant de calculer une distance moyenne entre les doights de la main
protected double getEuclDistance(Point one, Point two){
return Math.sqrt(Math.pow((two.x - one.x), 2)
+ Math.pow((two.y - one.y), 2));
}
// Méthode permettant de renvoyer les différents points qui définisse le contour de la main
protected List<Point> getConvexHullPoints(MatOfPoint contour){
Imgproc.convexHull(contour, hull);
List<Point> hullPoints = new ArrayList<>();
for(int j=0; j < hull.toList().size(); j++){
hullPoints.add(contour.toList().get(hull.toList().get(j)));
}
return hullPoints;
}
// Méthode permettant de renvoyer les différents points qui définisse le contour de la main
protected int getPalmContour(List<MatOfPoint> contours){
Rect roi;
int indexOfMaxContour = -1;
// int currentMax = 0;
for (int i = 0; i < contours.size(); i++) {
roi = Imgproc.boundingRect(contours.get(i));
if(roi.contains(palmCenter))
return i;
// if (contours.get(i).dims() > currentMax)
// indexOfMaxContour = i;
}
return indexOfMaxContour;
}
// Méthode permettant de renvoyer les différents points qui définisse le contour de la main
protected List<MatOfPoint> getAllContours(Mat frame){
frame2 = frame.clone();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(frame2, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
return contours;
}
// Méthode permettant d'afficher le cube au touché
protected void getAvgHSV(Mat frame){
// consider square patch around touched pixel
int x = (int) palmCenter.x;
int y = (int) palmCenter.y;
int rows = frame.rows();
int cols = frame.cols();
Rect touchedSquare = new Rect();
int squareSide = 20;
touchedSquare.x = (x > squareSide) ? x - squareSide : 0;
touchedSquare.y = (y > squareSide) ? y - squareSide : 0;
touchedSquare.width = (x + squareSide < cols) ?
x + squareSide - touchedSquare.x : cols - touchedSquare.x;
touchedSquare.height = (y + squareSide < rows) ?
y + squareSide - touchedSquare.y : rows - touchedSquare.y;
touchedMat = frame.submat(touchedSquare);
// convert patch to HSV and get average values
Imgproc.cvtColor(touchedMat, touchedMat, Imgproc.COLOR_RGB2HSV_FULL);
Scalar sumHSV = Core.sumElems(touchedMat);
int total = touchedSquare.width * touchedSquare.height;
double avgHSV[] = {sumHSV.val[0] / total, sumHSV.val[1] / total, sumHSV.val[2] / total};
assignHSV(avgHSV);
}
// Fonction permettant de calculer les dimensions du rectangle
protected void assignHSV(double avgHSV[]){
minHSV.val[0] = (avgHSV[0] > 10) ? avgHSV[0] - 10 : 0;
maxHSV.val[0] = (avgHSV[0] < 245) ? avgHSV[0] + 10 : 255;
minHSV.val[1] = (avgHSV[1] > 130) ? avgHSV[1] - 100 : 30;
maxHSV.val[1] = (avgHSV[1] < 155) ? avgHSV[1] + 100 : 255;
minHSV.val[2] = (avgHSV[2] > 130) ? avgHSV[2] - 100 : 30;
maxHSV.val[2] = (avgHSV[2] < 155) ? avgHSV[2] + 100 : 255;
Log.e("HSV", avgHSV[0]+", "+avgHSV[1]+", "+avgHSV[2]);
Log.e("HSV", minHSV.val[0]+", "+minHSV.val[1]+", "+minHSV.val[2]);
Log.e("HSV", maxHSV.val[0]+", "+maxHSV.val[1]+", "+maxHSV.val[2]);
}
protected Mat downSample(Mat ycc, int n){
// TODO: erode then dilate
for (int i=0; i<n; i++)
Imgproc.pyrDown(ycc, ycc);
return ycc;
}
// Méthode permettant de calculer et d'appliquer l'échelle du cube
protected void setScaleFactors(int vidWidth, int vidHeight){
float deviceWidth = javaCameraView.getWidth();
float deviceHeight = javaCameraView.getHeight();
if(deviceHeight - vidHeight < deviceWidth - vidWidth){
float temp = vidWidth * deviceHeight / vidHeight;
offsetFactY = 0;
offsetFactX = (deviceWidth - temp) / 2;
scaleFactY = vidHeight / deviceHeight;
scaleFactX = vidWidth / temp;
}
else{
float temp = vidHeight * deviceWidth / vidWidth;
offsetFactX= 0;
offsetFactY = (deviceHeight - temp) / 2;
scaleFactX = vidWidth / deviceWidth;
scaleFactY = vidHeight / temp;
}
}
} |
Partager