De curând am avut de realizat pentru o materie de la master un proiect/ o aplicație care să exemplifice tehnici de a detecta fețe umane. Și cum Verzișorul (a se citi Android OS) știu că dispune de o clasă care permite detectarea fețelor am decis să realizez o astfel de aplicație care să îmi aducă o notă mare ( aveam 2 variante posibile: să iau 10 sau să iau 10 ). 🙂
Așadar am început minunatul proces de implementare, dar înainte de a scrie cod a trebuit să ma documentez în legătură cu această temă. Iată ce am aflat:
- clasa FaceDetector utilizează ochii ca și reper de identificare a feței, așa că prieteni jos ochelarii de soare dacă vreți ca Verzișorul să nu se supere pe voi. 😀
- bitmap-urile folosite trebuie să fie utilizate în formatul RGB565. A funcționat și utilizând constructorul default, dar recomand totuși RGB565, just in case (să nu spuneți apoi că nu v-am avertizat).
- referitor la câte fețe pot fi detectate…pot răspunde cu: multe. Vom testa pentru 5.
- clasa FaceDetector.Face nu poate fi extinsă.
Acum fiind informați să trecem la fapte! Am realizat o aplicație care să permită preluarea de imagini fie din galeria proprie a telefonului, fie să obținem una nouă cu ajutorul camerei. Fețele identificate vor fi încadrate într-un pătrat verde.
Pasul 1: Design-ul
detectlayout.xm
</pre> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/white"> <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/image_view" android:layout_weight="1.0" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:layout_width="150dip" android:layout_height="50dip" android:onClick="btnRestart_onClick" android:background="@drawable/style_button" android:text="Take a new photo" android:layout_alignParentLeft="true"> </Button> <Button android:layout_width="150dip" android:layout_height="50dip" android:layout_alignParentRight="true" android:background="@drawable/style_button" android:onClick="btnDetectFace_onClick" android:text="@string/detect_face"> </Button> </RelativeLayout> </LinearLayout> <pre>
Pasul 2: Codul Java
Secretul reușitei acestei aplicații este determinat de o metodă, și anume detectFaces() din unica activitate existentă FaceDetectAndroidActivity :
</pre> private void detectFaces() { if(null != cameraBitmap) { int width = cameraBitmap.getWidth(); int height = cameraBitmap.getHeight(); FaceDetector detector = new FaceDetector(width, height, FaceDetectAndroidActivity.MAX_FACES); Face[] faces = new Face[FaceDetectAndroidActivity.MAX_FACES]; Bitmap bitmap565 = Bitmap.createBitmap(width, height, Config.RGB_565); Paint ditherPaint = new Paint(); Paint drawPaint = new Paint(); ditherPaint.setDither(true); drawPaint.setColor(Color.RED); drawPaint.setStyle(Paint.Style.STROKE); drawPaint.setStrokeWidth(2); Canvas canvas = new Canvas(); canvas.setBitmap(bitmap565); canvas.drawBitmap(cameraBitmap, 0, 0, ditherPaint); int facesFound = detector.findFaces(bitmap565, faces); PointF midPoint = new PointF(); float eyeDistance = 0.0f; float confidence = 0.0f; ArrayList<String> listConfidence = new ArrayList<String>(); ArrayList<String> listEyeDistances = new ArrayList<String>(); ArrayList<String> listPoints = new ArrayList<String>(); if(facesFound > 0) { for(int index=0; index<facesFound; ++index) { faces[index].getMidPoint(midPoint); eyeDistance = faces[index].eyesDistance(); confidence = faces[index].confidence(); Log.i("FaceDetector", "Confidence: " + confidence + ", Eye distance: " + eyeDistance + ", Mid Point: (" + midPoint.x + ", " + midPoint.y + ")"); listConfidence.add(String.format("%.3f", confidence) + ""); listEyeDistances.add(String.format("%.3f", eyeDistance) + ""); listPoints.add("(" + String.format("%.3f", midPoint.x) + ", " +String.format("%.3f", midPoint.y) + ")\n"); // Draw a small rectangle frame around the eye canvas.drawRect((int)midPoint.x - eyeDistance , (int)midPoint.y - eyeDistance , (int)midPoint.x + eyeDistance, (int)midPoint.y + eyeDistance, drawPaint); } } else { AlertDialog alertDialog = new AlertDialog.Builder(this).create(); alertDialog.setTitle("Alert"); alertDialog.setMessage("No faces found!"); alertDialog.setButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which){} }); alertDialog.setIcon(R.drawable.ic_app); alertDialog.show(); } String filepath = Environment.getExternalStorageDirectory() + "/facedetect" + System.currentTimeMillis() + ".jpg"; Log.e("file path", filepath); try { FileOutputStream fos = new FileOutputStream(filepath); bitmap565.compress(CompressFormat.JPEG, 90, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } ImageView imageView = (ImageView)findViewById(R.id.image_view); imageView.setImageBitmap(bitmap565); DetailsDialog detailsDialog = new DetailsDialog(this, this, facesFound + " faces", listConfidence.toString(), listEyeDistances.toString(), listPoints); detailsDialog.show(); } } <pre>