├── .gitignore
├── libs
└── .gitignore
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── drawable-xxxhdpi
│ └── ic_launcher.png
├── layout
│ ├── bpm.xml
│ ├── app.xml
│ └── config.xml
├── values
│ └── strings.xml
├── menu
│ └── app.xml
└── raw
│ └── lbpcascade_frontalface.xml
├── assets
└── fonts
│ └── ds_digital
│ ├── DS-DIGI.TTF
│ ├── DS-DIGIB.TTF
│ ├── DS-DIGII.TTF
│ ├── DS-DIGIT.TTF
│ └── DIGITAL.TXT
├── jni
├── Application.mk
├── Android.mk
├── pt_chambino_p_pulse_Pulse_Face.h
├── pt_chambino_p_pulse_Pulse.h
├── pt_chambino_p_pulse_Pulse_Face.cpp
└── pt_chambino_p_pulse_Pulse.cpp
├── project.properties
├── ant.properties
├── LICENSE
├── proguard-project.txt
├── src
├── pt
│ └── chambino
│ │ └── p
│ │ └── pulse
│ │ ├── dialog
│ │ ├── BpmDialog.java
│ │ └── ConfigDialog.java
│ │ ├── view
│ │ ├── PulseView.java
│ │ └── BpmView.java
│ │ ├── Pulse.java
│ │ └── App.java
└── org
│ └── opencv
│ └── android
│ ├── MyJavaCameraView.java
│ └── MyCameraBridgeViewBase.java
├── custom_rules.xml
├── AndroidManifest.xml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | gen
4 | local.properties
5 |
--------------------------------------------------------------------------------
/libs/.gitignore:
--------------------------------------------------------------------------------
1 | OpenCV-android-sdk
2 | armeabi-v7a
3 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/assets/fonts/ds_digital/DS-DIGI.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/assets/fonts/ds_digital/DS-DIGI.TTF
--------------------------------------------------------------------------------
/assets/fonts/ds_digital/DS-DIGIB.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/assets/fonts/ds_digital/DS-DIGIB.TTF
--------------------------------------------------------------------------------
/assets/fonts/ds_digital/DS-DIGII.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/assets/fonts/ds_digital/DS-DIGII.TTF
--------------------------------------------------------------------------------
/assets/fonts/ds_digital/DS-DIGIT.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/assets/fonts/ds_digital/DS-DIGIT.TTF
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PChambino/pulse/HEAD/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/jni/Application.mk:
--------------------------------------------------------------------------------
1 | APP_STL := gnustl_static
2 | APP_CPPFLAGS := -frtti -fexceptions -O3
3 | APP_ABI := armeabi-v7a
4 | APP_PLATFORM := android-8
5 |
--------------------------------------------------------------------------------
/res/layout/bpm.xml:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | include libs/OpenCV-android-sdk/sdk/native/jni/OpenCV.mk
6 |
7 | LOCAL_SRC_FILES := pt_chambino_p_pulse_Pulse_Face.cpp
8 | LOCAL_SRC_FILES += pt_chambino_p_pulse_Pulse.cpp
9 | LOCAL_SRC_FILES += Pulse.cpp
10 | LOCAL_SRC_FILES += EvmGdownIIR.cpp
11 | LOCAL_SRC_FILES += ext/opencv.cpp
12 | LOCAL_SRC_FILES += profiler/Profiler.cpp
13 | LOCAL_C_INCLUDES += $(LOCAL_PATH)
14 | LOCAL_LDLIBS += -llog -ldl
15 |
16 | LOCAL_MODULE := pulse
17 |
18 | include $(BUILD_SHARED_LIBRARY)
19 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pulse
4 |
5 |
6 | Record
7 | Switch Camera
8 | Config
9 |
10 |
11 | Face Detection
12 | Magnification
13 | FPS
14 | Done
15 |
16 |
17 | Average BPM
18 |
19 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | android.library.reference.1=libs/OpenCV-android-sdk/sdk/java
14 | # Project target.
15 | target=android-17
16 |
--------------------------------------------------------------------------------
/res/menu/app.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ant.properties:
--------------------------------------------------------------------------------
1 | # This file is used to override default values used by the Ant build system.
2 | #
3 | # This file must be checked into Version Control Systems, as it is
4 | # integral to the build system of your project.
5 |
6 | # This file is only used by the Ant script.
7 |
8 | # You can use this to override default values such as
9 | # 'source.dir' for the location of your java source folder and
10 | # 'out.dir' for the location of your output folder.
11 |
12 | # You can also use it define how the release builds are signed by declaring
13 | # the following properties:
14 | # 'key.store' for the location of your keystore and
15 | # 'key.alias' for the name of the key to use.
16 | # The password will be asked during the build when you use the 'release' target.
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Pedro Boloto Chambino
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/dialog/BpmDialog.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse.dialog;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.app.DialogFragment;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import pt.chambino.p.pulse.App;
10 | import pt.chambino.p.pulse.R;
11 | import pt.chambino.p.pulse.view.BpmView;
12 |
13 | public class BpmDialog extends DialogFragment {
14 |
15 | private BpmView bpmView;
16 | private double bpm;
17 |
18 | @Override
19 | public Dialog onCreateDialog(Bundle savedInstanceState) {
20 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
21 | builder.setTitle(R.string.average_bpm);
22 | builder.setNeutralButton(android.R.string.ok, null);
23 |
24 | View dialogView = getActivity().getLayoutInflater().inflate(R.layout.bpm, null);
25 | builder.setView(dialogView);
26 |
27 | bpmView = ((BpmView)dialogView.findViewById(R.id.bpm));
28 | bpmView.setBpm(bpm);
29 |
30 | return builder.create();
31 | }
32 |
33 | @Override
34 | public void onAttach(Activity activity) {
35 | super.onAttach(activity);
36 | App app = (App)activity;
37 | bpm = app.getRecordedBpmAverage();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/res/layout/app.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
17 |
18 |
23 |
24 |
25 |
26 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/custom_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
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 |
--------------------------------------------------------------------------------
/jni/pt_chambino_p_pulse_Pulse_Face.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class pt_chambino_p_pulse_Pulse_Face */
4 |
5 | #ifndef _Included_pt_chambino_p_pulse_Pulse_Face
6 | #define _Included_pt_chambino_p_pulse_Pulse_Face
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: pt_chambino_p_pulse_Pulse_Face
12 | * Method: _id
13 | * Signature: (J)I
14 | */
15 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1id
16 | (JNIEnv *, jclass, jlong);
17 |
18 | /*
19 | * Class: pt_chambino_p_pulse_Pulse_Face
20 | * Method: _box
21 | * Signature: (JJ)V
22 | */
23 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1box
24 | (JNIEnv *, jclass, jlong, jlong);
25 |
26 | /*
27 | * Class: pt_chambino_p_pulse_Pulse_Face
28 | * Method: _bpm
29 | * Signature: (J)D
30 | */
31 | JNIEXPORT jdouble JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1bpm
32 | (JNIEnv *, jclass, jlong);
33 |
34 | /*
35 | * Class: pt_chambino_p_pulse_Pulse_Face
36 | * Method: _pulse
37 | * Signature: (JJ)V
38 | */
39 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1pulse
40 | (JNIEnv *, jclass, jlong, jlong);
41 |
42 | /*
43 | * Class: pt_chambino_p_pulse_Pulse_Face
44 | * Method: _existsPulse
45 | * Signature: (J)Z
46 | */
47 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1existsPulse
48 | (JNIEnv *, jclass, jlong);
49 |
50 | #ifdef __cplusplus
51 | }
52 | #endif
53 | #endif
54 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/assets/fonts/ds_digital/DIGITAL.TXT:
--------------------------------------------------------------------------------
1 | DS-Font's TrueType Fonts
2 | Font name: DS-Digital (Normal, Bold, Italic, Bold Italic), Version 1.0
3 | Author: Dusit Supasawat
4 | Web Site: http://ds-font.hypermart.net
5 | Contact me: Dusit Supasawat, 325/38 Suksawat32 Ratburana Bangkok Thailand 10140
6 | Email address: dusit@mailcity.com
7 |
8 | Thanks for trying! We hope you really enjoy this my typeface. This font is
9 | distributed as shareware. You can use this font for a long time as you want.
10 | After all, when you think this font can be usefulness for you. You can send
11 | me some money, that would be way cool.
12 |
13 | I'm only asking $20 US shareware fee per this typeface for personal use.
14 | And $45 US is the usual amount per this typeface for commercial use.
15 |
16 | Distribution: You are free to distribute this archive so long as this text
17 | file is distributed with the archive, the font file have not been modified,
18 | and it is understood that the font's copyright remains with the original
19 | author (Dusit Supasawat).
20 |
21 | To register send your payment to:
22 |
23 | Dusit Supasawat
24 | 325/38 Suksawat32 Ratburana
25 | Bangkok Thailand 10140
26 |
27 | And fill out something as this order form, and send it in with your payment.
28 |
29 | Font name:_________________________________________
30 | Your information
31 | Name:______________________________________________
32 | Address:___________________________________________
33 | City, State : _____________________________________
34 | Zip Code:__________________________________________
35 | Country:___________________________________________
36 | E-MAIL address:____________________________________
37 |
38 |
39 | You will receive fonts which you order by Email after registration. These fonts
40 | will be generated for you by specify your name in font information.
--------------------------------------------------------------------------------
/res/layout/config.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
11 |
12 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
36 |
37 |
42 |
43 |
44 |
45 |
50 |
51 |
52 |
55 |
56 |
61 |
62 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | pulse
2 | =====
3 |
4 | ### Android-based implementation of Eulerian Video Magnification for vital signs monitoring
5 |
6 | http://p.chambino.com/dissertation
7 |
8 | Eulerian Video Magnification is a recently presented method capable of
9 | revealing temporal variations in videos that are impossible to see with
10 | the naked eye. Using this method, it is possible to visualize the flow of
11 | blood as it fills the face. From its result, a person’s heart rate is possible
12 | to be extracted.
13 |
14 | This research work was developed at Fraunhofer Portugal and its goal is to
15 | test the feasibility of the implementation of the Eulerian Video Magnification
16 | method on smartphones by developing an Android application for monitoring
17 | vital signs based on the Eulerian Video Magnification method.
18 |
19 | There has been some successful effort on the assessment of vital signs,
20 | such as, heart rate, and breathing rate, in a contact-free way using a
21 | webcamera and even a smartphone. However, since the Eulerian Video
22 | Magnification method was recently proposed, its implementation has not been
23 | tested in smartphones yet.Thus, the Eulerian Video Magnification method
24 | performance for color amplification was optimized in order to execute on an
25 | Android device at a reasonable speed.
26 |
27 | The Android application implemented includes features, such as, detection
28 | of a person’s cardiac pulse, dealing with artifacts’ motion, and real-time
29 | display of the magnified blood flow. Then, the application measurements were
30 | evaluated through tests with several individuals and compared to the ones
31 | detected by the ViTrox application and to the readings of a sphygmomanometer.
32 |
33 |
34 | Dependencies
35 | ============
36 |
37 | * Android SDK (v17)
38 | * Android NDK (r10e)
39 | * OpenCV Android SDK (2.4.11)
40 | * [pulse-cpp]
41 |
42 | [pulse-cpp]: https://github.com/pchambino/pulse-cpp
43 |
44 |
45 | Setup
46 | =====
47 |
48 | cd pulse
49 | android update project -p . -t android-17
50 | # a local.properties file with sdk.dir should have been created
51 | echo ndk.dir=PATH_TO_ANDROID_NDK >> local.properties
52 | echo pulse-cpp.dir=PATH_TO_PULSE_CPP >> local.properties
53 | # copy OpenCV Android SDK to libs directory
54 | android update lib-project -p libs/OpenCV-android-sdk/sdk/java -t android-17
55 | echo android.library=true >> project.properties
56 | ant debug install
57 |
58 |
59 | Attribution
60 | ===========
61 |
62 | [Heart] designed by [Diego Naive] from The Noun Project
63 |
64 | [Heart]: http://thenounproject.com/noun/heart/#icon-No15259
65 | [Diego Naive]: http://thenounproject.com/diegonaive
66 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/view/PulseView.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | public class PulseView extends View {
11 |
12 | public PulseView(Context context) {
13 | super(context);
14 | init();
15 | }
16 |
17 | public PulseView(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | init();
20 | }
21 |
22 | public PulseView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | init();
25 | }
26 |
27 | private double[] pulse;
28 | private int gridSize = 100;
29 | private int gridStep = 5;
30 |
31 | private Paint pulsePaint;
32 | private Paint zeroPaint;
33 | private Paint gridPaint;
34 |
35 | private void init() {
36 | setNoPulse();
37 |
38 | setBackgroundColor(Color.DKGRAY);
39 | pulsePaint = initPulsePaint();
40 | zeroPaint = initZeroPaint();
41 | gridPaint = initGridPaint();
42 | }
43 |
44 | private Paint initPulsePaint() {
45 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
46 | p.setColor(Color.RED);
47 | p.setStrokeWidth(4);
48 | return p;
49 | }
50 |
51 | private Paint initZeroPaint() {
52 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
53 | p.setColor(Color.LTGRAY);
54 | p.setStrokeWidth(4);
55 | return p;
56 | }
57 |
58 | private Paint initGridPaint() {
59 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
60 | p.setColor(Color.LTGRAY);
61 | p.setStrokeWidth(1);
62 | return p;
63 | }
64 |
65 | public double[] getPulse() {
66 | return pulse;
67 | }
68 |
69 | public void setPulse(final double[] pulse) {
70 | this.pulse = pulse;
71 | invalidate();
72 | }
73 |
74 | public void setNoPulse() {
75 | setPulse(new double[0]);
76 | }
77 |
78 | public int getGridSize() {
79 | return gridSize;
80 | }
81 |
82 | public void setGridSize(int gridSize) {
83 | this.gridSize = gridSize;
84 | invalidate();
85 | }
86 |
87 | public int getGridStep() {
88 | return gridStep;
89 | }
90 |
91 | public void setGridStep(int gridStep) {
92 | this.gridStep = gridStep;
93 | invalidate();
94 | }
95 |
96 | @Override
97 | protected void onDraw(Canvas canvas) {
98 | // vertical grid lines
99 | for (int i = 0; i < gridSize; i += gridStep) {
100 | canvas.drawLine(x(i), 0, x(i), getHeight(), gridPaint);
101 | }
102 |
103 | // vertical and horizontal zero lines
104 | canvas.drawLine(2, 0, 2, getHeight(), zeroPaint);
105 | canvas.drawLine(0, y(0), getWidth(), y(0), zeroPaint);
106 |
107 | // pulse signal
108 | for (int i = 1; i < pulse.length; i++) {
109 | canvas.drawLine(x(i-1), y(pulse[i-1]), x(i), y(pulse[i]), pulsePaint);
110 | }
111 | }
112 |
113 | private float x(int x) {
114 | return x * getWidth() / (gridSize - 1);
115 | }
116 |
117 | private float y(double y) {
118 | return (float)(getHeight() / 2. - y * getHeight() / 3.);
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/view/BpmView.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse.view;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ObjectAnimator;
6 | import android.animation.ValueAnimator;
7 | import android.content.Context;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Paint;
11 | import android.graphics.Typeface;
12 | import android.util.AttributeSet;
13 | import android.util.TypedValue;
14 | import android.view.Gravity;
15 | import android.view.animation.AccelerateInterpolator;
16 | import android.widget.TextView;
17 |
18 | public class BpmView extends TextView {
19 |
20 | public BpmView(Context context) {
21 | super(context);
22 | init();
23 | }
24 |
25 | public BpmView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | init();
28 | }
29 |
30 | public BpmView(Context context, AttributeSet attrs, int defStyle) {
31 | super(context, attrs, defStyle);
32 | init();
33 | }
34 |
35 | private double bpm;
36 |
37 | private Paint circlePaint;
38 | private ValueAnimator circlePaintAnimator;
39 |
40 | private void init() {
41 | setNoBpm();
42 |
43 | setTextSize(TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 60f);
44 | setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/ds_digital/DS-DIGIB.TTF"));
45 | setGravity(Gravity.CENTER);
46 |
47 | circlePaint = initCirclePaint();
48 |
49 | circlePaintAnimator = ObjectAnimator.ofInt(circlePaint, "Alpha", 0, 256);
50 | circlePaintAnimator.setInterpolator(new AccelerateInterpolator());
51 | circlePaintAnimator.setDuration(1000);
52 | circlePaintAnimator.setRepeatCount(ValueAnimator.INFINITE);
53 | circlePaintAnimator.setRepeatMode(ValueAnimator.REVERSE);
54 | circlePaintAnimator.addListener(new AnimatorListenerAdapter() {
55 | @Override
56 | public void onAnimationRepeat(Animator animation) {
57 | if (getText() == "-" && circlePaint.getAlpha() == 0) {
58 | animation.cancel();
59 | }
60 | }
61 | });
62 | circlePaintAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
63 | @Override
64 | public void onAnimationUpdate(ValueAnimator animation) {
65 | invalidate();
66 | }
67 | });
68 | }
69 |
70 | private Paint initCirclePaint() {
71 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
72 | p.setColor(Color.RED);
73 | p.setAlpha(0);
74 | p.setStyle(Paint.Style.FILL);
75 | return p;
76 | }
77 |
78 | public double getBpm() {
79 | return bpm;
80 | }
81 |
82 | public void setBpm(double bpm) {
83 | this.bpm = bpm;
84 | long rounded = Math.round(bpm);
85 | if (rounded == 0) {
86 | setText("-");
87 | } else {
88 | setText(String.valueOf(rounded));
89 | }
90 | }
91 |
92 | public void setNoBpm() {
93 | setBpm(0);
94 | }
95 |
96 | @Override
97 | protected void onDraw(Canvas canvas) {
98 | super.onDraw(canvas);
99 | canvas.drawCircle(20, getHeight() / 2f, 10, circlePaint);
100 | if (getText() != "-" && !circlePaintAnimator.isStarted()) {
101 | circlePaintAnimator.start();
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/jni/pt_chambino_p_pulse_Pulse.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class pt_chambino_p_pulse_Pulse */
4 |
5 | #ifndef _Included_pt_chambino_p_pulse_Pulse
6 | #define _Included_pt_chambino_p_pulse_Pulse
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: pt_chambino_p_pulse_Pulse
12 | * Method: _initialize
13 | * Signature: ()J
14 | */
15 | JNIEXPORT jlong JNICALL Java_pt_chambino_p_pulse_Pulse__1initialize
16 | (JNIEnv *, jclass);
17 |
18 | /*
19 | * Class: pt_chambino_p_pulse_Pulse
20 | * Method: _load
21 | * Signature: (JLjava/lang/String;)V
22 | */
23 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1load
24 | (JNIEnv *, jclass, jlong, jstring);
25 |
26 | /*
27 | * Class: pt_chambino_p_pulse_Pulse
28 | * Method: _start
29 | * Signature: (JII)V
30 | */
31 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1start
32 | (JNIEnv *, jclass, jlong, jint, jint);
33 |
34 | /*
35 | * Class: pt_chambino_p_pulse_Pulse
36 | * Method: _onFrame
37 | * Signature: (JJ)V
38 | */
39 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1onFrame
40 | (JNIEnv *, jclass, jlong, jlong);
41 |
42 | /*
43 | * Class: pt_chambino_p_pulse_Pulse
44 | * Method: _facesCount
45 | * Signature: (J)I
46 | */
47 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1facesCount
48 | (JNIEnv *, jclass, jlong);
49 |
50 | /*
51 | * Class: pt_chambino_p_pulse_Pulse
52 | * Method: _face
53 | * Signature: (JI)J
54 | */
55 | JNIEXPORT jlong JNICALL Java_pt_chambino_p_pulse_Pulse__1face
56 | (JNIEnv *, jclass, jlong, jint);
57 |
58 | /*
59 | * Class: pt_chambino_p_pulse_Pulse
60 | * Method: _relativeMinFaceSize
61 | * Signature: (J)D
62 | */
63 | JNIEXPORT jdouble JNICALL Java_pt_chambino_p_pulse_Pulse__1relativeMinFaceSize
64 | (JNIEnv *, jclass, jlong);
65 |
66 | /*
67 | * Class: pt_chambino_p_pulse_Pulse
68 | * Method: _maxSignalSize
69 | * Signature: (J)I
70 | */
71 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1maxSignalSize
72 | (JNIEnv *, jclass, jlong);
73 |
74 | /*
75 | * Class: pt_chambino_p_pulse_Pulse
76 | * Method: _faceDetection
77 | * Signature: (J)Z
78 | */
79 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse__1faceDetection__J
80 | (JNIEnv *, jclass, jlong);
81 |
82 | /*
83 | * Class: pt_chambino_p_pulse_Pulse
84 | * Method: _faceDetection
85 | * Signature: (JZ)V
86 | */
87 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1faceDetection__JZ
88 | (JNIEnv *, jclass, jlong, jboolean);
89 |
90 | /*
91 | * Class: pt_chambino_p_pulse_Pulse
92 | * Method: _magnification
93 | * Signature: (J)Z
94 | */
95 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse__1magnification__J
96 | (JNIEnv *, jclass, jlong);
97 |
98 | /*
99 | * Class: pt_chambino_p_pulse_Pulse
100 | * Method: _magnification
101 | * Signature: (JZ)V
102 | */
103 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1magnification__JZ
104 | (JNIEnv *, jclass, jlong, jboolean);
105 |
106 | /*
107 | * Class: pt_chambino_p_pulse_Pulse
108 | * Method: _magnificationFactor
109 | * Signature: (J)I
110 | */
111 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1magnificationFactor__J
112 | (JNIEnv *, jclass, jlong);
113 |
114 | /*
115 | * Class: pt_chambino_p_pulse_Pulse
116 | * Method: _magnificationFactor
117 | * Signature: (JI)V
118 | */
119 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1magnificationFactor__JI
120 | (JNIEnv *, jclass, jlong, jint);
121 |
122 | /*
123 | * Class: pt_chambino_p_pulse_Pulse
124 | * Method: _destroy
125 | * Signature: (J)V
126 | */
127 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1destroy
128 | (JNIEnv *, jclass, jlong);
129 |
130 | #ifdef __cplusplus
131 | }
132 | #endif
133 | #endif
134 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/dialog/ConfigDialog.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse.dialog;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.app.DialogFragment;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import android.widget.CompoundButton;
10 | import android.widget.SeekBar;
11 | import android.widget.Switch;
12 | import pt.chambino.p.pulse.App;
13 | import pt.chambino.p.pulse.Pulse;
14 | import pt.chambino.p.pulse.R;
15 |
16 | public class ConfigDialog extends DialogFragment {
17 |
18 | private Switch faceDetectionSwitch;
19 | private Switch magnificationSwitch;
20 | private SeekBar magnificationSeekBar;
21 | private Switch fpsSwitch;
22 |
23 | @Override
24 | public Dialog onCreateDialog(Bundle savedInstanceState) {
25 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
26 | builder.setTitle(R.string.config);
27 | builder.setNeutralButton(R.string.done, null);
28 |
29 | View dialogView = getActivity().getLayoutInflater().inflate(R.layout.config, null);
30 | builder.setView(dialogView);
31 |
32 | faceDetectionSwitch = ((Switch)dialogView.findViewById(R.id.face_detection));
33 | faceDetectionSwitch.setOnCheckedChangeListener(new FaceDetectionSwitchConfig());
34 |
35 | magnificationSwitch = ((Switch)dialogView.findViewById(R.id.magnification));
36 | magnificationSwitch.setOnCheckedChangeListener(new MagnificationSwitchConfig());
37 |
38 | magnificationSeekBar = ((SeekBar)dialogView.findViewById(R.id.magnificationFactor));
39 | magnificationSeekBar.setOnSeekBarChangeListener(new MagnificationSeekBarConfig());
40 |
41 | fpsSwitch = ((Switch)dialogView.findViewById(R.id.fps));
42 | fpsSwitch.setOnCheckedChangeListener(new FpsSwitchConfig());
43 |
44 | Pulse pulse = getApp().getPulse();
45 | // on configuration change Pulse may not have loaded yet
46 | if (pulse != null) {
47 | faceDetectionSwitch.setChecked(pulse.hasFaceDetection());
48 | magnificationSwitch.setChecked(pulse.hasMagnification());
49 | magnificationSeekBar.setEnabled(pulse.hasMagnification());
50 | magnificationSeekBar.setProgress(pulse.getMagnificationFactor());
51 | }
52 | fpsSwitch.setChecked(getApp().getCamera().isFpsMeterEnabled());
53 |
54 | return builder.create();
55 | }
56 |
57 | private App getApp() {
58 | return (App)getActivity();
59 | }
60 |
61 | private class FaceDetectionSwitchConfig implements CompoundButton.OnCheckedChangeListener {
62 | @Override
63 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
64 | if (getApp().getPulse() != null) getApp().getPulse().setFaceDetection(isChecked);
65 | }
66 | }
67 |
68 | private class MagnificationSwitchConfig implements CompoundButton.OnCheckedChangeListener {
69 | @Override
70 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
71 | magnificationSeekBar.setEnabled(isChecked);
72 | if (getApp().getPulse() != null) getApp().getPulse().setMagnification(isChecked);
73 | }
74 | }
75 |
76 | private class MagnificationSeekBarConfig implements SeekBar.OnSeekBarChangeListener {
77 | @Override
78 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
79 | if (getApp().getPulse() != null) getApp().getPulse().setMagnificationFactor(progress);
80 | }
81 |
82 | @Override
83 | public void onStartTrackingTouch(SeekBar seekBar) {
84 | }
85 |
86 | @Override
87 | public void onStopTrackingTouch(SeekBar seekBar) {
88 | }
89 | }
90 |
91 | private class FpsSwitchConfig implements CompoundButton.OnCheckedChangeListener {
92 | @Override
93 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
94 | getApp().getCamera().setFpsMeter(isChecked);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/Pulse.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse;
2 |
3 | import org.opencv.core.Mat;
4 | import org.opencv.core.MatOfDouble;
5 | import org.opencv.core.MatOfRect;
6 | import org.opencv.core.Rect;
7 |
8 | public class Pulse {
9 |
10 | public Pulse() {
11 | self = _initialize();
12 | }
13 |
14 | public void load(String filename) {
15 | _load(self, filename);
16 | }
17 |
18 | public void start(int width, int height) {
19 | _start(self, width, height);
20 | }
21 |
22 | public void onFrame(Mat frame) {
23 | _onFrame(self, frame.getNativeObjAddr());
24 | }
25 |
26 | public Face[] getFaces() {
27 | int count = _facesCount(self);
28 | Face[] faces = new Face[count];
29 | for (int i = 0; i < count; i++) {
30 | faces[i] = new Face(_face(self, i));
31 | }
32 | return faces;
33 | }
34 |
35 | public double getRelativeMinFaceSize() {
36 | return _relativeMinFaceSize(self);
37 | }
38 |
39 | public int getMaxSignalSize() {
40 | return _maxSignalSize(self);
41 | }
42 |
43 | public boolean hasFaceDetection() {
44 | return _faceDetection(self);
45 | }
46 |
47 | public void setFaceDetection(boolean m) {
48 | _faceDetection(self, m);
49 | }
50 |
51 | public boolean hasMagnification() {
52 | return _magnification(self);
53 | }
54 |
55 | public void setMagnification(boolean m) {
56 | _magnification(self, m);
57 | }
58 |
59 | public int getMagnificationFactor() {
60 | return _magnificationFactor(self);
61 | }
62 |
63 | public void setMagnificationFactor(int m) {
64 | _magnificationFactor(self, m);
65 | }
66 |
67 | public void release() {
68 | _destroy(self);
69 | self = 0;
70 | }
71 |
72 | @Override
73 | protected void finalize() throws Throwable {
74 | super.finalize();
75 | release();
76 | }
77 |
78 | private long self = 0;
79 | private static native long _initialize();
80 | private static native void _load(long self, String filename);
81 | private static native void _start(long self, int width, int height);
82 | private static native void _onFrame(long self, long frame);
83 | private static native int _facesCount(long self);
84 | private static native long _face(long self, int i);
85 | private static native double _relativeMinFaceSize(long self);
86 | private static native int _maxSignalSize(long self);
87 | private static native boolean _faceDetection(long self);
88 | private static native void _faceDetection(long self, boolean m);
89 | private static native boolean _magnification(long self);
90 | private static native void _magnification(long self, boolean m);
91 | private static native int _magnificationFactor(long self);
92 | private static native void _magnificationFactor(long self, int m);
93 | private static native void _destroy(long self);
94 |
95 | public static class Face {
96 |
97 | private MatOfRect box;
98 | private MatOfDouble pulse;
99 |
100 | private Face(long addr) {
101 | this.self = addr;
102 | }
103 |
104 | public int getId() {
105 | return _id(self);
106 | }
107 |
108 | public Rect getBox() {
109 | if (box == null) box = new MatOfRect();
110 | _box(self, box.getNativeObjAddr());
111 | return box.toArray()[0];
112 | }
113 |
114 | public double getBpm() {
115 | return _bpm(self);
116 | }
117 |
118 | public double[] getPulse() {
119 | if (pulse == null) pulse = new MatOfDouble();
120 | _pulse(self, pulse.getNativeObjAddr());
121 | return pulse.toArray();
122 | }
123 |
124 | public boolean existsPulse() {
125 | return _existsPulse(self);
126 | }
127 |
128 | private long self = 0;
129 | private static native int _id(long self);
130 | private static native void _box(long self, long mat);
131 | private static native double _bpm(long self);
132 | private static native void _pulse(long self, long mat);
133 | private static native boolean _existsPulse(long self);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/jni/pt_chambino_p_pulse_Pulse_Face.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "Pulse.hpp"
6 |
7 | #include
8 | #define LOG_TAG "Pulse::Pulse::Face"
9 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
10 |
11 | using std::vector;
12 | using cv::Mat;
13 | using cv::Mat1d;
14 | using cv::Rect;
15 |
16 | /*
17 | * Class: pt_chambino_p_pulse_Pulse_Face
18 | * Method: _id
19 | * Signature: (J)I
20 | */
21 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1id
22 | (JNIEnv *jenv, jclass, jlong self)
23 | {
24 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1id enter");
25 | jint id = 0;
26 | try
27 | {
28 | if (self)
29 | id = ((Pulse::Face*)self)->id;
30 | }
31 | catch(cv::Exception& e)
32 | {
33 | jclass je = jenv->FindClass("org/opencv/core/CvException");
34 | if(!je) je = jenv->FindClass("java/lang/Exception");
35 | jenv->ThrowNew(je, e.what());
36 | }
37 | catch (...)
38 | {
39 | jclass je = jenv->FindClass("java/lang/Exception");
40 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
41 | }
42 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1id exit");
43 | return id;
44 | }
45 |
46 | /*
47 | * Class: pt_chambino_p_pulse_Pulse_Face
48 | * Method: _box
49 | * Signature: (JJ)V
50 | */
51 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1box
52 | (JNIEnv *jenv, jclass, jlong self, jlong mat)
53 | {
54 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1box enter");
55 | try
56 | {
57 | if (self) {
58 | vector v;
59 | v.push_back(((Pulse::Face*)self)->evm.box);
60 | *((Mat*)mat) = Mat(v, true);
61 | }
62 | }
63 | catch(cv::Exception& e)
64 | {
65 | jclass je = jenv->FindClass("org/opencv/core/CvException");
66 | if(!je) je = jenv->FindClass("java/lang/Exception");
67 | jenv->ThrowNew(je, e.what());
68 | }
69 | catch (...)
70 | {
71 | jclass je = jenv->FindClass("java/lang/Exception");
72 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
73 | }
74 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1box exit");
75 | }
76 |
77 | /*
78 | * Class: pt_chambino_p_pulse_Pulse_Face
79 | * Method: _bpm
80 | * Signature: (J)D
81 | */
82 | JNIEXPORT jdouble JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1bpm
83 | (JNIEnv *jenv, jclass, jlong self)
84 | {
85 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1bpm enter");
86 | jdouble bpm = 0;
87 | try
88 | {
89 | if (self)
90 | bpm = ((Pulse::Face*)self)->bpm;
91 | }
92 | catch(cv::Exception& e)
93 | {
94 | jclass je = jenv->FindClass("org/opencv/core/CvException");
95 | if(!je) je = jenv->FindClass("java/lang/Exception");
96 | jenv->ThrowNew(je, e.what());
97 | }
98 | catch (...)
99 | {
100 | jclass je = jenv->FindClass("java/lang/Exception");
101 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
102 | }
103 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1bpm exit");
104 | return bpm;
105 | }
106 |
107 | /*
108 | * Class: pt_chambino_p_pulse_Pulse_Face
109 | * Method: _pulse
110 | * Signature: (JJ)V
111 | */
112 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1pulse
113 | (JNIEnv *jenv, jclass, jlong self, jlong mat)
114 | {
115 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1pulse enter");
116 | try
117 | {
118 | if (self)
119 | *((Mat*)mat) = ((Pulse::Face*)self)->pulse;
120 | }
121 | catch(cv::Exception& e)
122 | {
123 | jclass je = jenv->FindClass("org/opencv/core/CvException");
124 | if(!je) je = jenv->FindClass("java/lang/Exception");
125 | jenv->ThrowNew(je, e.what());
126 | }
127 | catch (...)
128 | {
129 | jclass je = jenv->FindClass("java/lang/Exception");
130 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
131 | }
132 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1pulse exit");
133 | }
134 |
135 | /*
136 | * Class: pt_chambino_p_pulse_Pulse_Face
137 | * Method: _existsPulse
138 | * Signature: (J)Z
139 | */
140 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse_00024Face__1existsPulse
141 | (JNIEnv *jenv, jclass, jlong self)
142 | {
143 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1existsPulse enter");
144 | jboolean result = false;
145 | try
146 | {
147 | if (self)
148 | result = ((Pulse::Face*)self)->existsPulse;
149 | }
150 | catch(cv::Exception& e)
151 | {
152 | jclass je = jenv->FindClass("org/opencv/core/CvException");
153 | if(!je) je = jenv->FindClass("java/lang/Exception");
154 | jenv->ThrowNew(je, e.what());
155 | }
156 | catch (...)
157 | {
158 | jclass je = jenv->FindClass("java/lang/Exception");
159 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
160 | }
161 | LOGD("Java_pt_chambino_p_pulse_Pulse_00024Face__1existsPulse exit");
162 | return result;
163 | }
164 |
--------------------------------------------------------------------------------
/src/org/opencv/android/MyJavaCameraView.java:
--------------------------------------------------------------------------------
1 | package org.opencv.android;
2 |
3 | import java.util.List;
4 |
5 | import android.content.Context;
6 | import android.content.res.Configuration;
7 | import android.graphics.ImageFormat;
8 | import android.graphics.SurfaceTexture;
9 | import android.hardware.Camera;
10 | import android.hardware.Camera.PreviewCallback;
11 | import android.os.Build;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.view.ViewGroup.LayoutParams;
15 | import org.opencv.core.Core;
16 |
17 | import org.opencv.core.CvType;
18 | import org.opencv.core.Mat;
19 | import org.opencv.core.Size;
20 | import org.opencv.imgproc.Imgproc;
21 |
22 | /**
23 | * This class is an implementation of the Bridge View between OpenCV and Java Camera.
24 | * This class relays on the functionality available in base class and only implements
25 | * required functions:
26 | * connectCamera - opens Java camera and sets the PreviewCallback to be delivered.
27 | * disconnectCamera - closes the camera and stops preview.
28 | * When frame is delivered via callback from Camera - it processed via OpenCV to be
29 | * converted to RGBA32 and then passed to the external callback for modifications if required.
30 | */
31 | public class MyJavaCameraView extends MyCameraBridgeViewBase implements PreviewCallback {
32 |
33 | private static final int MAGIC_TEXTURE_ID = 10;
34 | private static final String TAG = "MyJavaCameraView";
35 |
36 | private byte mBuffer[];
37 | private Mat[] mFrameChain;
38 | private int mChainIdx = 0;
39 | private Thread mThread;
40 | private boolean mStopThread;
41 |
42 | protected Camera mCamera;
43 | protected MyJavaCameraView.JavaCameraFrame mCameraFrame;
44 | private SurfaceTexture mSurfaceTexture;
45 |
46 | public static class JavaCameraSizeAccessor implements MyCameraBridgeViewBase.ListItemAccessor {
47 |
48 | public int getWidth(Object obj) {
49 | Camera.Size size = (Camera.Size) obj;
50 | return size.width;
51 | }
52 |
53 | public int getHeight(Object obj) {
54 | Camera.Size size = (Camera.Size) obj;
55 | return size.height;
56 | }
57 | }
58 |
59 | public MyJavaCameraView(Context context, int cameraId) {
60 | super(context, cameraId);
61 | }
62 |
63 | public MyJavaCameraView(Context context, AttributeSet attrs) {
64 | super(context, attrs);
65 | }
66 |
67 | protected boolean initializeCamera(int width, int height) {
68 | Log.d(TAG, "Initialize java camera");
69 | boolean result = true;
70 | synchronized (this) {
71 | mCamera = null;
72 |
73 | if (mCameraIndex == -1) {
74 | Log.d(TAG, "Trying to open camera with old open()");
75 | try {
76 | mCamera = Camera.open();
77 | }
78 | catch (Exception e){
79 | Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage());
80 | }
81 |
82 | if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
83 | boolean connected = false;
84 | for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) {
85 | Log.d(TAG, "Trying to open camera with new open(" + Integer.valueOf(camIdx) + ")");
86 | try {
87 | mCamera = Camera.open(camIdx);
88 | connected = true;
89 | } catch (RuntimeException e) {
90 | Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage());
91 | }
92 | if (connected) break;
93 | }
94 | }
95 | } else {
96 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
97 | Log.d(TAG, "Trying to open camera with new open(" + Integer.valueOf(mCameraIndex) + ")");
98 | try {
99 | mCamera = Camera.open(mCameraIndex);
100 | } catch (RuntimeException e) {
101 | Log.e(TAG, "Camera #" + mCameraIndex + "failed to open: " + e.getLocalizedMessage());
102 | }
103 | }
104 | }
105 |
106 | if (mCamera == null)
107 | return false;
108 |
109 | /* Now set camera parameters */
110 | try {
111 | Camera.Parameters params = mCamera.getParameters();
112 | Log.d(TAG, "getSupportedPreviewSizes()");
113 | List sizes = params.getSupportedPreviewSizes();
114 |
115 | if (sizes != null) {
116 | for (android.hardware.Camera.Size s : sizes) Log.d(TAG, s.width+"x"+s.height);
117 |
118 | /* Select the size that fits surface considering maximum size allowed */
119 | Size frameSize = calculateCameraFrameSize(sizes, new MyJavaCameraView.JavaCameraSizeAccessor(), width, height);
120 |
121 | params.setPreviewFormat(ImageFormat.NV21);
122 | Log.d(TAG, "Set preview size to " + Integer.valueOf((int)frameSize.width) + "x" + Integer.valueOf((int)frameSize.height));
123 | params.setPreviewSize((int)frameSize.width, (int)frameSize.height);
124 |
125 | List FocusModes = params.getSupportedFocusModes();
126 | if (FocusModes != null && FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
127 | {
128 | params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
129 | }
130 |
131 | if (params.isVideoStabilizationSupported()) {
132 | params.setVideoStabilization(true);
133 | }
134 |
135 | params.setRecordingHint(true);
136 |
137 | mCamera.setParameters(params);
138 | params = mCamera.getParameters();
139 |
140 | mFrameWidth = params.getPreviewSize().width;
141 | mFrameHeight = params.getPreviewSize().height;
142 |
143 | int size = mFrameWidth * mFrameHeight;
144 | size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
145 | mBuffer = new byte[size];
146 |
147 | mCamera.addCallbackBuffer(mBuffer);
148 | mCamera.setPreviewCallbackWithBuffer(this);
149 |
150 | mFrameChain = new Mat[2];
151 | mFrameChain[0] = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
152 | mFrameChain[1] = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
153 |
154 | mCameraFrame = new MyJavaCameraView.JavaCameraFrame(mFrameChain[mChainIdx], mFrameWidth, mFrameHeight);
155 |
156 | if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
157 | mFrameWidth = params.getPreviewSize().height;
158 | mFrameHeight = params.getPreviewSize().width;
159 | }
160 |
161 | AllocateCache();
162 |
163 | if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT)) {
164 | mScale = Math.max(((float)width)/mFrameWidth, ((float)height)/mFrameHeight);
165 | }
166 |
167 | if (mFpsMeter != null) {
168 | mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
169 | }
170 |
171 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
172 | mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID);
173 | mCamera.setPreviewTexture(mSurfaceTexture);
174 | } else
175 | mCamera.setPreviewDisplay(null);
176 |
177 | /* Finally we are ready to start the preview */
178 | Log.d(TAG, "startPreview");
179 | mCamera.startPreview();
180 | }
181 | else
182 | result = false;
183 | } catch (Exception e) {
184 | result = false;
185 | e.printStackTrace();
186 | }
187 | }
188 |
189 | return result;
190 | }
191 |
192 | protected void releaseCamera() {
193 | synchronized (this) {
194 | if (mCamera != null) {
195 | mCamera.stopPreview();
196 | mCamera.release();
197 | }
198 | mCamera = null;
199 | if (mFrameChain != null) {
200 | mFrameChain[0].release();
201 | mFrameChain[1].release();
202 | }
203 | if (mCameraFrame != null)
204 | mCameraFrame.release();
205 | }
206 | }
207 |
208 | @Override
209 | protected boolean connectCamera(int width, int height) {
210 |
211 | /* 1. We need to instantiate camera
212 | * 2. We need to start thread which will be getting frames
213 | */
214 | /* First step - initialize camera connection */
215 | Log.d(TAG, "Connecting to camera");
216 | if (!initializeCamera(width, height))
217 | return false;
218 |
219 | /* now we can start update thread */
220 | Log.d(TAG, "Starting processing thread");
221 | mStopThread = false;
222 | mThread = new Thread(new MyJavaCameraView.CameraWorker());
223 | mThread.start();
224 |
225 | return true;
226 | }
227 |
228 | protected void disconnectCamera() {
229 | /* 1. We need to stop thread which updating the frames
230 | * 2. Stop camera and release it
231 | */
232 | Log.d(TAG, "Disconnecting from camera");
233 | try {
234 | mStopThread = true;
235 | Log.d(TAG, "Notify thread");
236 | synchronized (this) {
237 | this.notify();
238 | }
239 | Log.d(TAG, "Wating for thread");
240 | if (mThread != null)
241 | mThread.join();
242 | } catch (InterruptedException e) {
243 | e.printStackTrace();
244 | } finally {
245 | mThread = null;
246 | }
247 |
248 | /* Now release camera */
249 | releaseCamera();
250 | }
251 |
252 | public void onPreviewFrame(byte[] frame, Camera arg1) {
253 | Log.d(TAG, "Preview Frame received. Frame size: " + frame.length);
254 | synchronized (this) {
255 | mFrameChain[1 - mChainIdx].put(0, 0, frame);
256 | this.notify();
257 | }
258 | if (mCamera != null)
259 | mCamera.addCallbackBuffer(mBuffer);
260 | }
261 |
262 | public class JavaCameraFrame implements MyCameraBridgeViewBase.CvCameraViewFrame {
263 | public Mat gray() {
264 | mGray = mYuvFrameData.submat(0, mHeight, 0, mWidth);
265 | return rotate(mGray);
266 | }
267 |
268 | public Mat rgba() {
269 | Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2BGR_NV12, 4);
270 | return rotate(mRgba);
271 | }
272 |
273 | public Mat rgb() {
274 | Imgproc.cvtColor(mYuvFrameData, mRgb, Imgproc.COLOR_YUV2BGR_NV12);
275 | return rotate(mRgb);
276 | }
277 |
278 | private Mat rotate(Mat frame) {
279 | Camera.CameraInfo info = new Camera.CameraInfo();
280 | Camera.getCameraInfo(mCameraIndex, info);
281 |
282 | // FIXME all this flipping and transposing is very performance expensive
283 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
284 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
285 | Core.flip(frame.t(), frame, -1);
286 | } else {
287 | Core.flip(frame.t(), frame, 1);
288 | }
289 | } else {
290 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
291 | Core.flip(frame, frame, 1);
292 | }
293 | }
294 | return frame;
295 | }
296 |
297 | public JavaCameraFrame(Mat Yuv420sp, int width, int height) {
298 | super();
299 | mWidth = width;
300 | mHeight = height;
301 | mYuvFrameData = Yuv420sp;
302 | mRgba = new Mat();
303 | mRgb = new Mat();
304 | mGray = new Mat();
305 | }
306 |
307 | public void release() {
308 | mRgba.release();
309 | mRgb.release();
310 | mGray.release();
311 | }
312 |
313 | private JavaCameraFrame(MyCameraBridgeViewBase.CvCameraViewFrame obj) {
314 | }
315 |
316 | private Mat mYuvFrameData;
317 | private Mat mRgba;
318 | private Mat mRgb;
319 | private Mat mGray;
320 | private int mWidth;
321 | private int mHeight;
322 | };
323 |
324 | private class CameraWorker implements Runnable {
325 |
326 | public void run() {
327 | do {
328 | synchronized (MyJavaCameraView.this) {
329 | try {
330 | MyJavaCameraView.this.wait();
331 | } catch (InterruptedException e) {
332 | // TODO Auto-generated catch block
333 | e.printStackTrace();
334 | }
335 | }
336 |
337 | if (!mStopThread) {
338 | if (!mFrameChain[mChainIdx].empty())
339 | deliverAndDrawFrame(mCameraFrame);
340 | mChainIdx = 1 - mChainIdx;
341 | }
342 | } while (!mStopThread);
343 | Log.d(TAG, "Finish processing thread");
344 | }
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/jni/pt_chambino_p_pulse_Pulse.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "Pulse.hpp"
6 |
7 | #include
8 | #define LOG_TAG "Pulse::Pulse"
9 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
10 |
11 | using std::string;
12 | using cv::Mat;
13 |
14 | /*
15 | * Class: pt_chambino_p_pulse_Pulse
16 | * Method: _initialize
17 | * Signature: ()J
18 | */
19 | JNIEXPORT jlong JNICALL Java_pt_chambino_p_pulse_Pulse__1initialize
20 | (JNIEnv *jenv, jclass)
21 | {
22 | LOGD("Java_pt_chambino_p_pulse_Pulse__1initialize enter");
23 | jlong result = 0;
24 | try
25 | {
26 | result = (jlong)new Pulse();
27 | }
28 | catch(cv::Exception& e)
29 | {
30 | jclass je = jenv->FindClass("org/opencv/core/CvException");
31 | if(!je) je = jenv->FindClass("java/lang/Exception");
32 | jenv->ThrowNew(je, e.what());
33 | }
34 | catch (...)
35 | {
36 | jclass je = jenv->FindClass("java/lang/Exception");
37 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
38 | }
39 | LOGD("Java_pt_chambino_p_pulse_Pulse__1initialize exit");
40 | return result;
41 | }
42 |
43 | /*
44 | * Class: pt_chambino_p_pulse_Pulse
45 | * Method: _load
46 | * Signature: (JLjava/lang/String;)V
47 | */
48 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1load
49 | (JNIEnv *jenv, jclass, jlong self, jstring jfilename)
50 | {
51 | LOGD("Java_pt_chambino_p_pulse_Pulse__1load enter");
52 | string filename(jenv->GetStringUTFChars(jfilename, NULL));
53 | try
54 | {
55 | if (self)
56 | ((Pulse*)self)->load(filename);
57 | }
58 | catch(cv::Exception& e)
59 | {
60 | jclass je = jenv->FindClass("org/opencv/core/CvException");
61 | if(!je) je = jenv->FindClass("java/lang/Exception");
62 | jenv->ThrowNew(je, e.what());
63 | }
64 | catch (...)
65 | {
66 | jclass je = jenv->FindClass("java/lang/Exception");
67 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
68 | }
69 | LOGD("Java_pt_chambino_p_pulse_Pulse__1load exit");
70 | }
71 |
72 | /*
73 | * Class: pt_chambino_p_pulse_Pulse
74 | * Method: _start
75 | * Signature: (JII)V
76 | */
77 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1start
78 | (JNIEnv *jenv, jclass, jlong self, jint width, jint height)
79 | {
80 | LOGD("Java_pt_chambino_p_pulse_Pulse__1start enter");
81 | try
82 | {
83 | if (self)
84 | ((Pulse*)self)->start(width, height);
85 | }
86 | catch(cv::Exception& e)
87 | {
88 | jclass je = jenv->FindClass("org/opencv/core/CvException");
89 | if(!je) je = jenv->FindClass("java/lang/Exception");
90 | jenv->ThrowNew(je, e.what());
91 | }
92 | catch (...)
93 | {
94 | jclass je = jenv->FindClass("java/lang/Exception");
95 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
96 | }
97 | LOGD("Java_pt_chambino_p_pulse_Pulse__1start exit");
98 | }
99 |
100 | /*
101 | * Class: pt_chambino_p_pulse_Pulse
102 | * Method: _onFrame
103 | * Signature: (JJ)V
104 | */
105 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1onFrame
106 | (JNIEnv *jenv, jclass, jlong self, jlong frame)
107 | {
108 | LOGD("Java_pt_chambino_p_pulse_Pulse__1onFrame enter");
109 | try
110 | {
111 | if (self) {
112 | ((Pulse*)self)->onFrame(*((Mat*)frame));
113 | }
114 | }
115 | catch(cv::Exception& e)
116 | {
117 | jclass je = jenv->FindClass("org/opencv/core/CvException");
118 | if(!je) je = jenv->FindClass("java/lang/Exception");
119 | jenv->ThrowNew(je, e.what());
120 | }
121 | catch (...)
122 | {
123 | jclass je = jenv->FindClass("java/lang/Exception");
124 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
125 | }
126 | LOGD("Java_pt_chambino_p_pulse_Pulse__1onFrame exit");
127 | }
128 |
129 | /*
130 | * Class: pt_chambino_p_pulse_Pulse
131 | * Method: _facesCount
132 | * Signature: (J)I
133 | */
134 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1facesCount
135 | (JNIEnv *jenv, jclass, jlong self)
136 | {
137 | LOGD("Java_pt_chambino_p_pulse_Pulse__1facesCount enter");
138 | try
139 | {
140 | if (self)
141 | return ((Pulse*)self)->faces.size();
142 | }
143 | catch(cv::Exception& e)
144 | {
145 | jclass je = jenv->FindClass("org/opencv/core/CvException");
146 | if(!je) je = jenv->FindClass("java/lang/Exception");
147 | jenv->ThrowNew(je, e.what());
148 | }
149 | catch (...)
150 | {
151 | jclass je = jenv->FindClass("java/lang/Exception");
152 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
153 | }
154 | LOGD("Java_pt_chambino_p_pulse_Pulse__1facesCount exit");
155 | return 0;
156 | }
157 |
158 | /*
159 | * Class: pt_chambino_p_pulse_Pulse
160 | * Method: _face
161 | * Signature: (JI)J
162 | */
163 | JNIEXPORT jlong JNICALL Java_pt_chambino_p_pulse_Pulse__1face
164 | (JNIEnv *jenv, jclass, jlong self, jint i)
165 | {
166 | LOGD("Java_pt_chambino_p_pulse_Pulse__1face enter");
167 | try
168 | {
169 | if (self)
170 | return (jlong)(&((Pulse*)self)->faces.at(i));
171 | }
172 | catch(cv::Exception& e)
173 | {
174 | jclass je = jenv->FindClass("org/opencv/core/CvException");
175 | if(!je) je = jenv->FindClass("java/lang/Exception");
176 | jenv->ThrowNew(je, e.what());
177 | }
178 | catch (...)
179 | {
180 | jclass je = jenv->FindClass("java/lang/Exception");
181 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
182 | }
183 | LOGD("Java_pt_chambino_p_pulse_Pulse__1face exit");
184 | return 0;
185 | }
186 |
187 | /*
188 | * Class: pt_chambino_p_pulse_Pulse
189 | * Method: _relativeMinFaceSize
190 | * Signature: (J)D
191 | */
192 | JNIEXPORT jdouble JNICALL Java_pt_chambino_p_pulse_Pulse__1relativeMinFaceSize
193 | (JNIEnv *jenv, jclass, jlong self)
194 | {
195 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize enter");
196 | jdouble result = 0;
197 | try
198 | {
199 | if (self)
200 | result = ((Pulse*)self)->relativeMinFaceSize;
201 | }
202 | catch(cv::Exception& e)
203 | {
204 | jclass je = jenv->FindClass("org/opencv/core/CvException");
205 | if(!je) je = jenv->FindClass("java/lang/Exception");
206 | jenv->ThrowNew(je, e.what());
207 | }
208 | catch (...)
209 | {
210 | jclass je = jenv->FindClass("java/lang/Exception");
211 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
212 | }
213 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize exit");
214 | return result;
215 | }
216 |
217 | /*
218 | * Class: pt_chambino_p_pulse_Pulse
219 | * Method: _maxSignalSize
220 | * Signature: (J)I
221 | */
222 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1maxSignalSize
223 | (JNIEnv *jenv, jclass, jlong self)
224 | {
225 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize enter");
226 | jint result = 0;
227 | try
228 | {
229 | if (self)
230 | result = ((Pulse*)self)->maxSignalSize;
231 | }
232 | catch(cv::Exception& e)
233 | {
234 | jclass je = jenv->FindClass("org/opencv/core/CvException");
235 | if(!je) je = jenv->FindClass("java/lang/Exception");
236 | jenv->ThrowNew(je, e.what());
237 | }
238 | catch (...)
239 | {
240 | jclass je = jenv->FindClass("java/lang/Exception");
241 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
242 | }
243 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize exit");
244 | return result;
245 | }
246 |
247 | /*
248 | * Class: pt_chambino_p_pulse_Pulse
249 | * Method: _faceDetection
250 | * Signature: (J)Z
251 | */
252 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse__1faceDetection__J
253 | (JNIEnv *jenv, jclass, jlong self)
254 | {
255 | LOGD("Java_pt_chambino_p_pulse_Pulse__1faceDetection__J enter");
256 | jboolean result = false;
257 | try
258 | {
259 | if (self)
260 | result = ((Pulse*)self)->faceDetection.enabled;
261 | }
262 | catch(cv::Exception& e)
263 | {
264 | jclass je = jenv->FindClass("org/opencv/core/CvException");
265 | if(!je) je = jenv->FindClass("java/lang/Exception");
266 | jenv->ThrowNew(je, e.what());
267 | }
268 | catch (...)
269 | {
270 | jclass je = jenv->FindClass("java/lang/Exception");
271 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
272 | }
273 | LOGD("Java_pt_chambino_p_pulse_Pulse__1faceDetection__J exit");
274 | return result;
275 | }
276 |
277 | /*
278 | * Class: pt_chambino_p_pulse_Pulse
279 | * Method: _faceDetection
280 | * Signature: (JZ)V
281 | */
282 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1faceDetection__JZ
283 | (JNIEnv *jenv, jclass, jlong self, jboolean m)
284 | {
285 | LOGD("Java_pt_chambino_p_pulse_Pulse__1faceDetection__JZ enter");
286 | try
287 | {
288 | if (self)
289 | ((Pulse*)self)->faceDetection.enabled = m;
290 | }
291 | catch(cv::Exception& e)
292 | {
293 | jclass je = jenv->FindClass("org/opencv/core/CvException");
294 | if(!je) je = jenv->FindClass("java/lang/Exception");
295 | jenv->ThrowNew(je, e.what());
296 | }
297 | catch (...)
298 | {
299 | jclass je = jenv->FindClass("java/lang/Exception");
300 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
301 | }
302 | LOGD("Java_pt_chambino_p_pulse_Pulse__1faceDetection__JZ exit");
303 | }
304 |
305 | /*
306 | * Class: pt_chambino_p_pulse_Pulse
307 | * Method: _magnification
308 | * Signature: (J)Z
309 | */
310 | JNIEXPORT jboolean JNICALL Java_pt_chambino_p_pulse_Pulse__1magnification__J
311 | (JNIEnv *jenv, jclass, jlong self)
312 | {
313 | LOGD("Java_pt_chambino_p_pulse_Pulse__1magnification__J enter");
314 | jboolean result = false;
315 | try
316 | {
317 | if (self)
318 | result = ((Pulse*)self)->evm.magnify;
319 | }
320 | catch(cv::Exception& e)
321 | {
322 | jclass je = jenv->FindClass("org/opencv/core/CvException");
323 | if(!je) je = jenv->FindClass("java/lang/Exception");
324 | jenv->ThrowNew(je, e.what());
325 | }
326 | catch (...)
327 | {
328 | jclass je = jenv->FindClass("java/lang/Exception");
329 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
330 | }
331 | LOGD("Java_pt_chambino_p_pulse_Pulse__1magnification__J exit");
332 | return result;
333 | }
334 |
335 | /*
336 | * Class: pt_chambino_p_pulse_Pulse
337 | * Method: _magnification
338 | * Signature: (JZ)V
339 | */
340 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1magnification__JZ
341 | (JNIEnv *jenv, jclass, jlong self, jboolean m)
342 | {
343 | LOGD("Java_pt_chambino_p_pulse_Pulse__1magnification__JZ enter");
344 | try
345 | {
346 | if (self)
347 | ((Pulse*)self)->evm.magnify = m;
348 | }
349 | catch(cv::Exception& e)
350 | {
351 | jclass je = jenv->FindClass("org/opencv/core/CvException");
352 | if(!je) je = jenv->FindClass("java/lang/Exception");
353 | jenv->ThrowNew(je, e.what());
354 | }
355 | catch (...)
356 | {
357 | jclass je = jenv->FindClass("java/lang/Exception");
358 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
359 | }
360 | LOGD("Java_pt_chambino_p_pulse_Pulse__1magnification__JZ exit");
361 | }
362 |
363 | /*
364 | * Class: pt_chambino_p_pulse_Pulse
365 | * Method: _magnificationFactor
366 | * Signature: (J)I
367 | */
368 | JNIEXPORT jint JNICALL Java_pt_chambino_p_pulse_Pulse__1magnificationFactor__J
369 | (JNIEnv *jenv, jclass, jlong self)
370 | {
371 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize enter");
372 | jint result = 0;
373 | try
374 | {
375 | if (self)
376 | result = ((Pulse*)self)->evm.alpha;
377 | }
378 | catch(cv::Exception& e)
379 | {
380 | jclass je = jenv->FindClass("org/opencv/core/CvException");
381 | if(!je) je = jenv->FindClass("java/lang/Exception");
382 | jenv->ThrowNew(je, e.what());
383 | }
384 | catch (...)
385 | {
386 | jclass je = jenv->FindClass("java/lang/Exception");
387 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
388 | }
389 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize exit");
390 | return result;
391 | }
392 |
393 | /*
394 | * Class: pt_chambino_p_pulse_Pulse
395 | * Method: _magnificationFactor
396 | * Signature: (JI)V
397 | */
398 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1magnificationFactor__JI
399 | (JNIEnv *jenv, jclass, jlong self, jint m)
400 | {
401 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize enter");
402 | try
403 | {
404 | if (self)
405 | ((Pulse*)self)->evm.alpha = m;
406 | }
407 | catch(cv::Exception& e)
408 | {
409 | jclass je = jenv->FindClass("org/opencv/core/CvException");
410 | if(!je) je = jenv->FindClass("java/lang/Exception");
411 | jenv->ThrowNew(je, e.what());
412 | }
413 | catch (...)
414 | {
415 | jclass je = jenv->FindClass("java/lang/Exception");
416 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
417 | }
418 | LOGD("Java_pt_chambino_p_pulse_Pulse__1maxSignalSize exit");
419 | }
420 |
421 | /*
422 | * Class: pt_chambino_p_pulse_Pulse
423 | * Method: _destroy
424 | * Signature: (J)V
425 | */
426 | JNIEXPORT void JNICALL Java_pt_chambino_p_pulse_Pulse__1destroy
427 | (JNIEnv *jenv, jclass, jlong self)
428 | {
429 | LOGD("Java_pt_chambino_p_pulse_Pulse__1destroy enter");
430 | try
431 | {
432 | if (self)
433 | delete (Pulse*)self;
434 | }
435 | catch(cv::Exception& e)
436 | {
437 | jclass je = jenv->FindClass("org/opencv/core/CvException");
438 | if(!je) je = jenv->FindClass("java/lang/Exception");
439 | jenv->ThrowNew(je, e.what());
440 | }
441 | catch (...)
442 | {
443 | jclass je = jenv->FindClass("java/lang/Exception");
444 | jenv->ThrowNew(je, "Unknown exception in JNI code.");
445 | }
446 | LOGD("Java_pt_chambino_p_pulse_Pulse__1destroy exit");
447 | }
448 |
--------------------------------------------------------------------------------
/src/pt/chambino/p/pulse/App.java:
--------------------------------------------------------------------------------
1 | package pt.chambino.p.pulse;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.Typeface;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 | import android.view.Menu;
13 | import android.view.MenuInflater;
14 | import android.view.MenuItem;
15 | import android.view.WindowManager;
16 | import java.io.File;
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 | import java.io.InputStream;
20 | import java.util.LinkedList;
21 | import java.util.List;
22 | import org.opencv.android.BaseLoaderCallback;
23 | import org.opencv.android.MyCameraBridgeViewBase;
24 | import org.opencv.android.MyCameraBridgeViewBase.CvCameraViewListener;
25 | import org.opencv.android.LoaderCallbackInterface;
26 | import org.opencv.android.OpenCVLoader;
27 | import org.opencv.core.Mat;
28 | import org.opencv.core.Rect;
29 | import org.opencv.highgui.Highgui;
30 | import pt.chambino.p.pulse.Pulse.Face;
31 | import pt.chambino.p.pulse.dialog.BpmDialog;
32 | import pt.chambino.p.pulse.dialog.ConfigDialog;
33 | import pt.chambino.p.pulse.view.BpmView;
34 | import pt.chambino.p.pulse.view.PulseView;
35 |
36 | public class App extends Activity implements CvCameraViewListener {
37 |
38 | private static final String TAG = "Pulse::App";
39 |
40 | private MyCameraBridgeViewBase camera;
41 | private BpmView bpmView;
42 | private PulseView pulseView;
43 | private Pulse pulse;
44 |
45 | private Paint faceBoxPaint;
46 | private Paint faceBoxTextPaint;
47 |
48 | private ConfigDialog configDialog;
49 |
50 | private BaseLoaderCallback loaderCallback = new BaseLoaderCallback(this) {
51 | @Override
52 | public void onManagerConnected(int status) {
53 | switch (status) {
54 |
55 | case LoaderCallbackInterface.SUCCESS:
56 | loaderCallbackSuccess();
57 | break;
58 |
59 | default:
60 | super.onManagerConnected(status);
61 | break;
62 | }
63 | }
64 | };
65 |
66 | private void loaderCallbackSuccess() {
67 | System.loadLibrary("pulse");
68 |
69 | pulse = new Pulse();
70 | pulse.setFaceDetection(initFaceDetection);
71 | pulse.setMagnification(initMagnification);
72 | pulse.setMagnificationFactor(initMagnificationFactor);
73 |
74 | File dir = getDir("cascade", Context.MODE_PRIVATE);
75 | File file = createFileFromResource(dir, R.raw.lbpcascade_frontalface, "xml");
76 | pulse.load(file.getAbsolutePath());
77 | dir.delete();
78 |
79 | pulseView.setGridSize(pulse.getMaxSignalSize());
80 |
81 | camera.enableView();
82 | }
83 |
84 | private File createFileFromResource(File dir, int id, String extension) {
85 | String name = getResources().getResourceEntryName(id) + "." + extension;
86 | InputStream is = getResources().openRawResource(id);
87 | File file = new File(dir, name);
88 |
89 | try {
90 | FileOutputStream os = new FileOutputStream(file);
91 |
92 | byte[] buffer = new byte[4096];
93 | int bytesRead;
94 | while ((bytesRead = is.read(buffer)) != -1) {
95 | os.write(buffer, 0, bytesRead);
96 | }
97 | is.close();
98 | os.close();
99 | } catch (IOException ex) {
100 | Log.e(TAG, "Failed to create file: " + file.getPath(), ex);
101 | }
102 |
103 | return file;
104 | }
105 |
106 | @Override
107 | public void onCreate(Bundle savedInstanceState) {
108 | super.onCreate(savedInstanceState);
109 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
110 |
111 | setContentView(R.layout.app);
112 |
113 | camera = (MyCameraBridgeViewBase) findViewById(R.id.camera);
114 | camera.setCvCameraViewListener(this);
115 | camera.SetCaptureFormat(Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB);
116 | camera.setMaxFrameSize(600, 600);
117 |
118 | bpmView = (BpmView) findViewById(R.id.bpm);
119 | bpmView.setBackgroundColor(Color.DKGRAY);
120 | bpmView.setTextColor(Color.LTGRAY);
121 |
122 | pulseView = (PulseView) findViewById(R.id.pulse);
123 |
124 | faceBoxPaint = initFaceBoxPaint();
125 | faceBoxTextPaint = initFaceBoxTextPaint();
126 | }
127 |
128 | private static final String CAMERA_ID = "camera-id";
129 | private static final String FPS_METER = "fps-meter";
130 | private static final String FACE_DETECTION = "face-detection";
131 | private static final String MAGNIFICATION = "magnification";
132 | private static final String MAGNIFICATION_FACTOR = "magnification-factor";
133 |
134 | private boolean initFaceDetection = true;
135 | private boolean initMagnification = true;
136 | private int initMagnificationFactor = 100;
137 |
138 | @Override
139 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
140 | super.onRestoreInstanceState(savedInstanceState);
141 |
142 | camera.setCameraId(savedInstanceState.getInt(CAMERA_ID));
143 | camera.setFpsMeter(savedInstanceState.getBoolean(FPS_METER));
144 |
145 | initFaceDetection = savedInstanceState.getBoolean(FACE_DETECTION, initFaceDetection);
146 | initMagnification = savedInstanceState.getBoolean(MAGNIFICATION, initMagnification);
147 | initMagnificationFactor = savedInstanceState.getInt(MAGNIFICATION_FACTOR, initMagnificationFactor);
148 | }
149 |
150 | @Override
151 | protected void onSaveInstanceState(Bundle outState) {
152 | super.onSaveInstanceState(outState);
153 |
154 | outState.putInt(CAMERA_ID, camera.getCameraId());
155 | outState.putBoolean(FPS_METER, camera.isFpsMeterEnabled());
156 |
157 | // if OpenCV Manager is not installed, pulse hasn't loaded
158 | if (pulse != null) {
159 | outState.putBoolean(FACE_DETECTION, pulse.hasFaceDetection());
160 | outState.putBoolean(MAGNIFICATION, pulse.hasMagnification());
161 | outState.putInt(MAGNIFICATION_FACTOR, pulse.getMagnificationFactor());
162 | }
163 | }
164 |
165 |
166 | @Override
167 | public void onResume() {
168 | super.onResume();
169 | OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_11, this, loaderCallback);
170 | }
171 |
172 | @Override
173 | public void onPause() {
174 | if (camera != null) {
175 | camera.disableView();
176 | }
177 | bpmView.setNoBpm();
178 | pulseView.setNoPulse();
179 | super.onPause();
180 | }
181 |
182 | @Override
183 | public boolean onCreateOptionsMenu(Menu menu) {
184 | MenuInflater inflater = getMenuInflater();
185 | inflater.inflate(R.menu.app, menu);
186 | return true;
187 | }
188 |
189 | @Override
190 | public boolean onOptionsItemSelected(MenuItem item) {
191 | switch (item.getItemId()) {
192 | case R.id.record:
193 | onRecord(item);
194 | return true;
195 | case R.id.switch_camera:
196 | camera.switchCamera();
197 | return true;
198 | case R.id.config:
199 | if (configDialog == null) configDialog = new ConfigDialog();
200 | configDialog.show(getFragmentManager(), null);
201 | return true;
202 | }
203 | return super.onOptionsItemSelected(item);
204 | }
205 |
206 | private boolean recording = false;
207 | private List recordedBpms;
208 | private BpmDialog bpmDialog;
209 | private double recordedBpmAverage;
210 |
211 | private void onRecord(MenuItem item) {
212 | recording = !recording;
213 | if (recording) {
214 | item.setIcon(android.R.drawable.ic_media_pause);
215 |
216 | if (recordedBpms == null) recordedBpms = new LinkedList();
217 | else recordedBpms.clear();
218 | } else {
219 | item.setIcon(android.R.drawable.ic_media_play);
220 |
221 | recordedBpmAverage = 0;
222 | for (double bpm : recordedBpms) recordedBpmAverage += bpm;
223 | recordedBpmAverage /= recordedBpms.size();
224 |
225 | if (bpmDialog == null) bpmDialog = new BpmDialog();
226 | bpmDialog.show(getFragmentManager(), null);
227 | }
228 | }
229 |
230 | public double getRecordedBpmAverage() {
231 | return recordedBpmAverage;
232 | }
233 |
234 | public Pulse getPulse() {
235 | return pulse;
236 | }
237 |
238 | public MyCameraBridgeViewBase getCamera() {
239 | return camera;
240 | }
241 |
242 | private Rect noFaceRect;
243 |
244 | private Rect initNoFaceRect(int width, int height) {
245 | double r = pulse.getRelativeMinFaceSize();
246 | int x = (int)(width * (1. - r) / 2.);
247 | int y = (int)(height * (1. - r) / 2.);
248 | int w = (int)(width * r);
249 | int h = (int)(height * r);
250 | return new Rect(x, y, w, h);
251 | }
252 |
253 | @Override
254 | public void onCameraViewStarted(int width, int height) {
255 | Log.d(TAG, "onCameraViewStarted("+width+", "+height+")");
256 | pulse.start(width, height);
257 | noFaceRect = initNoFaceRect(width, height);
258 | }
259 |
260 | @Override
261 | public void onCameraViewStopped() {
262 | }
263 |
264 | @Override
265 | public Mat onCameraFrame(Mat frame) {
266 | pulse.onFrame(frame);
267 | return frame;
268 | }
269 |
270 | @Override
271 | public void onCameraFrame(Canvas canvas) {
272 | Face face = getCurrentFace(pulse.getFaces()); // TODO support multiple faces
273 | if (face != null) {
274 | onFace(canvas, face);
275 | } else {
276 | // draw no face box
277 | canvas.drawPath(createFaceBoxPath(noFaceRect), faceBoxPaint);
278 | canvas.drawText("Face here",
279 | canvas.getWidth() / 2f,
280 | canvas.getHeight() / 2f,
281 | faceBoxTextPaint);
282 |
283 | // no faces
284 | runOnUiThread(new Runnable() {
285 | @Override
286 | public void run() {
287 | bpmView.setNoBpm();
288 | pulseView.setNoPulse();
289 | }
290 | });
291 | }
292 | }
293 |
294 | private int currentFaceId = 0;
295 |
296 | private Face getCurrentFace(Face[] faces) {
297 | Face face = null;
298 |
299 | if (currentFaceId > 0) {
300 | face = findFace(faces, currentFaceId);
301 | }
302 |
303 | if (face == null && faces.length > 0) {
304 | face = faces[0];
305 | }
306 |
307 | if (face == null) {
308 | currentFaceId = 0;
309 | } else {
310 | currentFaceId = face.getId();
311 | }
312 |
313 | return face;
314 | }
315 |
316 | private Face findFace(Face[] faces, int id) {
317 | for (Face face : faces) {
318 | if (face.getId() == id) {
319 | return face;
320 | }
321 | }
322 | return null;
323 | }
324 |
325 | private void onFace(Canvas canvas, Face face) {
326 | // grab face box
327 | Rect box = face.getBox();
328 |
329 | // draw face box
330 | canvas.drawPath(createFaceBoxPath(box), faceBoxPaint);
331 |
332 | if (pulse.hasFaceDetection() && !face.existsPulse()) {
333 | // draw hint text
334 | canvas.drawText("Hold still",
335 | box.x + box.width / 2f,
336 | box.y + box.height / 2f - 20,
337 | faceBoxTextPaint);
338 | canvas.drawText("in a",
339 | box.x + box.width / 2f,
340 | box.y + box.height / 2f,
341 | faceBoxTextPaint);
342 | canvas.drawText("bright place",
343 | box.x + box.width / 2f,
344 | box.y + box.height / 2f + 20,
345 | faceBoxTextPaint);
346 | }
347 |
348 | // update views
349 | if (face.existsPulse()) {
350 | final double bpm = face.getBpm();
351 | final double[] signal = face.getPulse();
352 | runOnUiThread(new Runnable() {
353 | @Override
354 | public void run() {
355 | bpmView.setBpm(bpm);
356 | pulseView.setPulse(signal);
357 | }
358 | });
359 | if (recording) {
360 | recordedBpms.add(bpm);
361 | }
362 | } else {
363 | // no pulse
364 | runOnUiThread(new Runnable() {
365 | @Override
366 | public void run() {
367 | bpmView.setNoBpm();
368 | pulseView.setNoPulse();
369 | }
370 | });
371 | }
372 | }
373 |
374 | private Paint initFaceBoxPaint() {
375 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
376 | p.setColor(Color.WHITE);
377 | p.setStyle(Paint.Style.STROKE);
378 | p.setStrokeWidth(4);
379 | p.setStrokeCap(Paint.Cap.ROUND);
380 | p.setStrokeJoin(Paint.Join.ROUND);
381 | p.setShadowLayer(2, 0, 0, Color.BLACK);
382 | return p;
383 | }
384 |
385 | private Paint initFaceBoxTextPaint() {
386 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
387 | p.setColor(Color.WHITE);
388 | p.setShadowLayer(2, 0, 0, Color.DKGRAY);
389 | p.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/ds_digital/DS-DIGIB.TTF"));
390 | p.setTextAlign(Paint.Align.CENTER);
391 | p.setTextSize(20f);
392 | return p;
393 | }
394 |
395 | private Path createFaceBoxPath(Rect box) {
396 | float size = box.width * 0.25f;
397 | Path path = new Path();
398 | // top left
399 | path.moveTo(box.x, box.y + size);
400 | path.lineTo(box.x, box.y);
401 | path.lineTo(box.x + size, box.y);
402 | // top right
403 | path.moveTo(box.x + box.width, box.y + size);
404 | path.lineTo(box.x + box.width, box.y);
405 | path.lineTo(box.x + box.width - size, box.y);
406 | // bottom left
407 | path.moveTo(box.x, box.y + box.height - size);
408 | path.lineTo(box.x, box.y + box.height);
409 | path.lineTo(box.x + size, box.y + box.height);
410 | // bottom right
411 | path.moveTo(box.x + box.width, box.y + box.height - size);
412 | path.lineTo(box.x + box.width, box.y + box.height);
413 | path.lineTo(box.x + box.width - size, box.y + box.height);
414 | return path;
415 | }
416 | }
417 |
--------------------------------------------------------------------------------
/src/org/opencv/android/MyCameraBridgeViewBase.java:
--------------------------------------------------------------------------------
1 | package org.opencv.android;
2 |
3 | import java.util.List;
4 |
5 | import org.opencv.core.Mat;
6 | import org.opencv.core.Size;
7 | import org.opencv.highgui.Highgui;
8 |
9 | import android.app.Activity;
10 | import android.app.AlertDialog;
11 | import android.content.Context;
12 | import android.content.DialogInterface;
13 | import android.content.res.TypedArray;
14 | import android.graphics.Bitmap;
15 | import android.graphics.Canvas;
16 | import android.graphics.Matrix;
17 | import android.util.AttributeSet;
18 | import android.util.Log;
19 | import android.view.SurfaceHolder;
20 | import android.view.SurfaceView;
21 | import static android.view.View.VISIBLE;
22 |
23 | import pt.chambino.p.pulse.R;
24 |
25 | /**
26 | * This is a basic class, implementing the interaction with Camera and OpenCV library.
27 | * The main responsibility of it - is to control when camera can be enabled, process the frame,
28 | * call external listener to make any adjustments to the frame and then draw the resulting
29 | * frame to the screen.
30 | * The clients shall implement CvCameraViewListener.
31 | */
32 | public abstract class MyCameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback {
33 |
34 | private static final String TAG = "MyCameraBridge";
35 | private static final int MAX_UNSPECIFIED = -1;
36 | private static final int STOPPED = 0;
37 | private static final int STARTED = 1;
38 |
39 | private int mState = STOPPED;
40 | private Bitmap mCacheBitmap;
41 | private MyCameraBridgeViewBase.CvCameraViewListener2 mListener;
42 | private boolean mSurfaceExist;
43 | private Object mSyncObject = new Object();
44 |
45 | protected int mFrameWidth;
46 | protected int mFrameHeight;
47 | protected int mMaxHeight;
48 | protected int mMaxWidth;
49 | protected float mScale = 1;
50 | protected int mPreviewFormat = Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA;
51 | protected int mCameraIndex = -1;
52 | protected boolean mEnabled;
53 | protected FpsMeter mFpsMeter = null;
54 |
55 | public MyCameraBridgeViewBase(Context context, int cameraId) {
56 | super(context);
57 | mCameraIndex = cameraId;
58 | getHolder().addCallback(this);
59 | mMaxWidth = MAX_UNSPECIFIED;
60 | mMaxHeight = MAX_UNSPECIFIED;
61 | }
62 |
63 | public MyCameraBridgeViewBase(Context context, AttributeSet attrs) {
64 | super(context, attrs);
65 |
66 | int count = attrs.getAttributeCount();
67 | Log.d(TAG, "Attr count: " + Integer.valueOf(count));
68 |
69 | TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase);
70 | if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false))
71 | enableFpsMeter();
72 |
73 | mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1);
74 |
75 | getHolder().addCallback(this);
76 | mMaxWidth = MAX_UNSPECIFIED;
77 | mMaxHeight = MAX_UNSPECIFIED;
78 | }
79 |
80 | public interface CvCameraViewListener {
81 | /**
82 | * This method is invoked when camera preview has started. After this method is invoked
83 | * the frames will start to be delivered to client via the onCameraFrame() callback.
84 | * @param width - the width of the frames that will be delivered
85 | * @param height - the height of the frames that will be delivered
86 | */
87 | public void onCameraViewStarted(int width, int height);
88 |
89 | /**
90 | * This method is invoked when camera preview has been stopped for some reason.
91 | * No frames will be delivered via onCameraFrame() callback after this method is called.
92 | */
93 | public void onCameraViewStopped();
94 |
95 | /**
96 | * This method is invoked when delivery of the frame needs to be done.
97 | * The returned values - is a modified frame which needs to be displayed on the screen.
98 | * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
99 | */
100 | public Mat onCameraFrame(Mat inputFrame);
101 |
102 | public void onCameraFrame(Canvas canvas);
103 | }
104 |
105 | public interface CvCameraViewListener2 {
106 | /**
107 | * This method is invoked when camera preview has started. After this method is invoked
108 | * the frames will start to be delivered to client via the onCameraFrame() callback.
109 | * @param width - the width of the frames that will be delivered
110 | * @param height - the height of the frames that will be delivered
111 | */
112 | public void onCameraViewStarted(int width, int height);
113 |
114 | /**
115 | * This method is invoked when camera preview has been stopped for some reason.
116 | * No frames will be delivered via onCameraFrame() callback after this method is called.
117 | */
118 | public void onCameraViewStopped();
119 |
120 | /**
121 | * This method is invoked when delivery of the frame needs to be done.
122 | * The returned values - is a modified frame which needs to be displayed on the screen.
123 | * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
124 | */
125 | public Mat onCameraFrame(MyCameraBridgeViewBase.CvCameraViewFrame inputFrame);
126 |
127 | public void onCameraFrame(Canvas canvas);
128 | };
129 |
130 | protected class CvCameraViewListenerAdapter implements MyCameraBridgeViewBase.CvCameraViewListener2 {
131 | public CvCameraViewListenerAdapter(MyCameraBridgeViewBase.CvCameraViewListener oldStypeListener) {
132 | mOldStyleListener = oldStypeListener;
133 | }
134 |
135 | public void onCameraViewStarted(int width, int height) {
136 | mOldStyleListener.onCameraViewStarted(width, height);
137 | }
138 |
139 | public void onCameraViewStopped() {
140 | mOldStyleListener.onCameraViewStopped();
141 | }
142 |
143 | public Mat onCameraFrame(MyCameraBridgeViewBase.CvCameraViewFrame inputFrame) {
144 | Mat result = null;
145 | switch (mPreviewFormat) {
146 | case Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB:
147 | result = mOldStyleListener.onCameraFrame(inputFrame.rgb());
148 | break;
149 | case Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA:
150 | result = mOldStyleListener.onCameraFrame(inputFrame.rgba());
151 | break;
152 | case Highgui.CV_CAP_ANDROID_GREY_FRAME:
153 | result = mOldStyleListener.onCameraFrame(inputFrame.gray());
154 | break;
155 | default:
156 | Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!");
157 | };
158 |
159 | return result;
160 | }
161 |
162 | @Override
163 | public void onCameraFrame(Canvas canvas) {
164 | mOldStyleListener.onCameraFrame(canvas);
165 | }
166 |
167 | public void setFrameFormat(int format) {
168 | mPreviewFormat = format;
169 | }
170 |
171 | private CvCameraViewListenerAdapter() {}
172 |
173 | private int mPreviewFormat = Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA;
174 | private MyCameraBridgeViewBase.CvCameraViewListener mOldStyleListener;
175 | };
176 |
177 | /**
178 | * This class interface is abstract representation of single frame from camera for onCameraFrame callback
179 | * Attention: Do not use objects, that represents this interface out of onCameraFrame callback!
180 | */
181 | public interface CvCameraViewFrame {
182 |
183 | /**
184 | * This method returns RGB Mat with frame
185 | */
186 | public Mat rgb();
187 |
188 | /**
189 | * This method returns RGBA Mat with frame
190 | */
191 | public Mat rgba();
192 |
193 | /**
194 | * This method returns single channel gray scale Mat with frame
195 | */
196 | public Mat gray();
197 | };
198 |
199 | public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
200 | Log.d(TAG, "call surfaceChanged event");
201 | synchronized(mSyncObject) {
202 | if (!mSurfaceExist) {
203 | mSurfaceExist = true;
204 | checkCurrentState();
205 | } else {
206 | /** Surface changed. We need to stop camera and restart with new parameters */
207 | /* Pretend that old surface has been destroyed */
208 | mSurfaceExist = false;
209 | checkCurrentState();
210 | /* Now use new surface. Say we have it now */
211 | mSurfaceExist = true;
212 | checkCurrentState();
213 | }
214 | }
215 | }
216 |
217 | public void surfaceCreated(SurfaceHolder holder) {
218 | /* Do nothing. Wait until surfaceChanged delivered */
219 | }
220 |
221 | public void surfaceDestroyed(SurfaceHolder holder) {
222 | synchronized(mSyncObject) {
223 | mSurfaceExist = false;
224 | checkCurrentState();
225 | }
226 | }
227 |
228 | public void switchCamera() {
229 | setCameraId((mCameraIndex + 1) % 2);
230 | }
231 |
232 | public int getCameraId() {
233 | return mCameraIndex;
234 | }
235 |
236 | public void setCameraId(int cameraId) {
237 | if (mCameraIndex != cameraId) {
238 | mCameraIndex = cameraId;
239 | if (mEnabled) {
240 | disableView();
241 | enableView();
242 | }
243 | }
244 | }
245 |
246 | /**
247 | * This method is provided for clients, so they can enable the camera connection.
248 | * The actual onCameraViewStarted callback will be delivered only after both this method is called and surface is available
249 | */
250 | public void enableView() {
251 | synchronized(mSyncObject) {
252 | mEnabled = true;
253 | checkCurrentState();
254 | }
255 | }
256 |
257 | /**
258 | * This method is provided for clients, so they can disable camera connection and stop
259 | * the delivery of frames even though the surface view itself is not destroyed and still stays on the scren
260 | */
261 | public void disableView() {
262 | synchronized(mSyncObject) {
263 | mEnabled = false;
264 | checkCurrentState();
265 | }
266 | }
267 |
268 | /**
269 | * This method enables label with fps value on the screen
270 | */
271 | public void enableFpsMeter() {
272 | if (mFpsMeter == null) {
273 | mFpsMeter = new FpsMeter();
274 | mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
275 | }
276 | }
277 |
278 | public void disableFpsMeter() {
279 | mFpsMeter = null;
280 | }
281 |
282 | public boolean isFpsMeterEnabled() {
283 | return mFpsMeter != null;
284 | }
285 |
286 | public void setFpsMeter(boolean enable) {
287 | if (enable) enableFpsMeter();
288 | else disableFpsMeter();
289 | }
290 |
291 | /**
292 | *
293 | * @param listener
294 | */
295 |
296 | public void setCvCameraViewListener(MyCameraBridgeViewBase.CvCameraViewListener2 listener) {
297 | mListener = listener;
298 | }
299 |
300 | public void setCvCameraViewListener(MyCameraBridgeViewBase.CvCameraViewListener listener) {
301 | MyCameraBridgeViewBase.CvCameraViewListenerAdapter adapter = new MyCameraBridgeViewBase.CvCameraViewListenerAdapter(listener);
302 | adapter.setFrameFormat(mPreviewFormat);
303 | mListener = adapter;
304 | }
305 |
306 | /**
307 | * This method sets the maximum size that camera frame is allowed to be. When selecting
308 | * size - the biggest size which less or equal the size set will be selected.
309 | * As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
310 | * preview frame will be selected with 176x152 size.
311 | * This method is useful when need to restrict the size of preview frame for some reason (for example for video recording)
312 | * @param maxWidth - the maximum width allowed for camera frame.
313 | * @param maxHeight - the maximum height allowed for camera frame
314 | */
315 | public void setMaxFrameSize(int maxWidth, int maxHeight) {
316 | mMaxWidth = maxWidth;
317 | mMaxHeight = maxHeight;
318 | }
319 |
320 | public void SetCaptureFormat(int format)
321 | {
322 | mPreviewFormat = format;
323 | if (mListener instanceof MyCameraBridgeViewBase.CvCameraViewListenerAdapter) {
324 | MyCameraBridgeViewBase.CvCameraViewListenerAdapter adapter = (MyCameraBridgeViewBase.CvCameraViewListenerAdapter) mListener;
325 | adapter.setFrameFormat(mPreviewFormat);
326 | }
327 | }
328 |
329 | /**
330 | * Called when mSyncObject lock is held
331 | */
332 | private void checkCurrentState() {
333 | int targetState;
334 |
335 | if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) {
336 | targetState = STARTED;
337 | } else {
338 | targetState = STOPPED;
339 | }
340 |
341 | if (targetState != mState) {
342 | /* The state change detected. Need to exit the current state and enter target state */
343 | processExitState(mState);
344 | mState = targetState;
345 | processEnterState(mState);
346 | }
347 | }
348 |
349 | private void processEnterState(int state) {
350 | switch(state) {
351 | case STARTED:
352 | onEnterStartedState();
353 | if (mListener != null) {
354 | mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
355 | }
356 | break;
357 | case STOPPED:
358 | onEnterStoppedState();
359 | if (mListener != null) {
360 | mListener.onCameraViewStopped();
361 | }
362 | break;
363 | };
364 | }
365 |
366 | private void processExitState(int state) {
367 | switch(state) {
368 | case STARTED:
369 | onExitStartedState();
370 | break;
371 | case STOPPED:
372 | onExitStoppedState();
373 | break;
374 | };
375 | }
376 |
377 | private void onEnterStoppedState() {
378 | /* nothing to do */
379 | }
380 |
381 | private void onExitStoppedState() {
382 | /* nothing to do */
383 | }
384 |
385 | // NOTE: The order of bitmap constructor and camera connection is important for android 4.1.x
386 | // Bitmap must be constructed before surface
387 | private void onEnterStartedState() {
388 | /* Connect camera */
389 | if (!connectCamera(getWidth(), getHeight())) {
390 | AlertDialog ad = new AlertDialog.Builder(getContext()).create();
391 | ad.setCancelable(false); // This blocks the 'BACK' button
392 | ad.setMessage("It seems that you device does not support camera (or it is locked). Application will be closed.");
393 | ad.setButton(DialogInterface.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {
394 | public void onClick(DialogInterface dialog, int which) {
395 | dialog.dismiss();
396 | ((Activity) getContext()).finish();
397 | }
398 | });
399 | ad.show();
400 |
401 | }
402 | }
403 |
404 | private void onExitStartedState() {
405 | disconnectCamera();
406 | if (mCacheBitmap != null) {
407 | mCacheBitmap.recycle();
408 | }
409 | }
410 |
411 | /**
412 | * This method shall be called by the subclasses when they have valid
413 | * object and want it to be delivered to external client (via callback) and
414 | * then displayed on the screen.
415 | * @param frame - the current frame to be delivered
416 | */
417 | protected void deliverAndDrawFrame(MyCameraBridgeViewBase.CvCameraViewFrame frame) {
418 | Mat modified;
419 |
420 | if (mListener != null) {
421 | modified = mListener.onCameraFrame(frame);
422 | } else {
423 | modified = frame.rgba();
424 | }
425 |
426 | boolean bmpValid = true;
427 | if (modified != null) {
428 | try {
429 | Utils.matToBitmap(modified, mCacheBitmap);
430 | } catch(Exception e) {
431 | Log.e(TAG, "Mat type: " + modified);
432 | Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
433 | Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
434 | bmpValid = false;
435 | }
436 | }
437 |
438 | if (bmpValid && mCacheBitmap != null) {
439 | if (mListener != null) {
440 | mListener.onCameraFrame(new Canvas(mCacheBitmap));
441 | }
442 | Canvas canvas = getHolder().lockCanvas();
443 | if (canvas != null) {
444 | canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
445 | Log.d(TAG, "mStretch value: " + mScale);
446 |
447 | Matrix matrix = new Matrix();
448 | matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
449 | (canvas.getHeight() - mCacheBitmap.getHeight()) / 2);
450 | matrix.postScale(mScale, mScale, canvas.getWidth() / 2, canvas.getHeight() / 2);
451 | canvas.drawBitmap(mCacheBitmap, matrix, null);
452 |
453 | if (mFpsMeter != null) {
454 | mFpsMeter.measure();
455 | mFpsMeter.draw(canvas, 20, 30);
456 | }
457 | getHolder().unlockCanvasAndPost(canvas);
458 | }
459 | }
460 | }
461 |
462 | /**
463 | * This method is invoked shall perform concrete operation to initialize the camera.
464 | * CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
465 | * initialized with the size of the Camera frames that will be delivered to external processor.
466 | * @param width - the width of this SurfaceView
467 | * @param height - the height of this SurfaceView
468 | */
469 | protected abstract boolean connectCamera(int width, int height);
470 |
471 | /**
472 | * Disconnects and release the particular camera object being connected to this surface view.
473 | * Called when syncObject lock is held
474 | */
475 | protected abstract void disconnectCamera();
476 |
477 | // NOTE: On Android 4.1.x the function must be called before SurfaceTextre constructor!
478 | protected void AllocateCache()
479 | {
480 | mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
481 | }
482 |
483 | public interface ListItemAccessor {
484 | public int getWidth(Object obj);
485 | public int getHeight(Object obj);
486 | };
487 |
488 | /**
489 | * This helper method can be called by subclasses to select camera preview size.
490 | * It goes over the list of the supported preview sizes and selects the maximum one which
491 | * fits both values set via setMaxFrameSize() and surface frame allocated for this view
492 | * @param supportedSizes
493 | * @param surfaceWidth
494 | * @param surfaceHeight
495 | * @return optimal frame size
496 | */
497 | protected Size calculateCameraFrameSize(List> supportedSizes, MyCameraBridgeViewBase.ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
498 | int calcWidth = 0;
499 | int calcHeight = 0;
500 |
501 | int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth;
502 | int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight;
503 |
504 | for (Object size : supportedSizes) {
505 | int width = accessor.getWidth(size);
506 | int height = accessor.getHeight(size);
507 |
508 | if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
509 | if (width >= calcWidth && height >= calcHeight) {
510 | calcWidth = (int) width;
511 | calcHeight = (int) height;
512 | }
513 | }
514 | }
515 |
516 | return new Size(calcWidth, calcHeight);
517 | }
518 | }
519 |
--------------------------------------------------------------------------------
/res/raw/lbpcascade_frontalface.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | BOOST
9 | LBP
10 | 24
11 | 24
12 |
13 | GAB
14 | 0.9950000047683716
15 | 0.5000000000000000
16 | 0.9500000000000000
17 | 1
18 | 100
19 |
20 | 256
21 | 20
22 |
23 |
24 | <_>
25 | 3
26 | -0.7520892024040222
27 |
28 |
29 | <_>
30 |
31 | 0 -1 46 -67130709 -21569 -1426120013 -1275125205 -21585
32 | -16385 587145899 -24005
33 |
34 | -0.6543210148811340 0.8888888955116272
35 |
36 | <_>
37 |
38 | 0 -1 13 -163512766 -769593758 -10027009 -262145 -514457854
39 | -193593353 -524289 -1
40 |
41 | -0.7739216089248657 0.7278633713722229
42 |
43 | <_>
44 |
45 | 0 -1 2 -363936790 -893203669 -1337948010 -136907894
46 | 1088782736 -134217726 -741544961 -1590337
47 |
48 | -0.7068563103675842 0.6761534214019775
49 |
50 | <_>
51 | 4
52 | -0.4872078299522400
53 |
54 |
55 | <_>
56 |
57 | 0 -1 84 2147483647 1946124287 -536870913 2147450879
58 | 738132490 1061101567 243204619 2147446655
59 |
60 | -0.8083735704421997 0.7685696482658386
61 |
62 | <_>
63 |
64 | 0 -1 21 2147483647 263176079 1879048191 254749487 1879048191
65 | -134252545 -268435457 801111999
66 |
67 | -0.7698410153388977 0.6592915654182434
68 |
69 | <_>
70 |
71 | 0 -1 106 -98110272 1610939566 -285484400 -850010381
72 | -189334372 -1671954433 -571026695 -262145
73 |
74 | -0.7506558895111084 0.5444605946540833
75 |
76 | <_>
77 |
78 | 0 -1 48 -798690576 -131075 1095771153 -237144073 -65569 -1
79 | -216727745 -69206049
80 |
81 | -0.7775990366935730 0.5465461611747742
82 |
83 | <_>
84 | 4
85 | -1.1592328548431396
86 |
87 |
88 | <_>
89 |
90 | 0 -1 47 -21585 -20549 -100818262 -738254174 -20561 -36865
91 | -151016790 -134238549
92 |
93 | -0.5601882934570313 0.7743113040924072
94 |
95 | <_>
96 |
97 | 0 -1 12 -286003217 183435247 -268994614 -421330945
98 | -402686081 1090387966 -286785545 -402653185
99 |
100 | -0.6124526262283325 0.6978127956390381
101 |
102 | <_>
103 |
104 | 0 -1 26 -50347012 970882927 -50463492 -1253377 -134218251
105 | -50364513 -33619992 -172490753
106 |
107 | -0.6114496588706970 0.6537628173828125
108 |
109 | <_>
110 |
111 | 0 -1 8 -273 -135266321 1877977738 -2088243418 -134217987
112 | 2146926575 -18910642 1095231247
113 |
114 | -0.6854077577590942 0.5403239130973816
115 |
116 | <_>
117 | 5
118 | -0.7562355995178223
119 |
120 |
121 | <_>
122 |
123 | 0 -1 96 -1273 1870659519 -20971602 -67633153 -134250731
124 | 2004875127 -250 -150995969
125 |
126 | -0.4051094949245453 0.7584033608436585
127 |
128 | <_>
129 |
130 | 0 -1 33 -868162224 -76810262 -4262145 -257 1465211989
131 | -268959873 -2656269 -524289
132 |
133 | -0.7388162612915039 0.5340843200683594
134 |
135 | <_>
136 |
137 | 0 -1 57 -12817 -49 -541103378 -152950 -38993 -20481 -1153876
138 | -72478976
139 |
140 | -0.6582943797111511 0.5339496731758118
141 |
142 | <_>
143 |
144 | 0 -1 125 -269484161 -452984961 -319816180 -1594032130 -2111
145 | -990117891 -488975296 -520947741
146 |
147 | -0.5981323719024658 0.5323504805564880
148 |
149 | <_>
150 |
151 | 0 -1 53 557787431 670265215 -1342193665 -1075892225
152 | 1998528318 1056964607 -33570977 -1
153 |
154 | -0.6498787999153137 0.4913350641727448
155 |
156 | <_>
157 | 5
158 | -0.8085358142852783
159 |
160 |
161 | <_>
162 |
163 | 0 -1 60 -536873708 880195381 -16842788 -20971521 -176687276
164 | -168427659 -16777260 -33554626
165 |
166 | -0.5278195738792419 0.6946372389793396
167 |
168 | <_>
169 |
170 | 0 -1 7 -1 -62981529 -1090591130 805330978 -8388827 -41945787
171 | -39577 -531118985
172 |
173 | -0.5206505060195923 0.6329920291900635
174 |
175 | <_>
176 |
177 | 0 -1 98 -725287348 1347747543 -852489 -16809993 1489881036
178 | -167903241 -1 -1
179 |
180 | -0.7516061067581177 0.4232024252414703
181 |
182 | <_>
183 |
184 | 0 -1 44 -32777 1006582562 -65 935312171 -8388609 -1078198273
185 | -1 733886267
186 |
187 | -0.7639313936233521 0.4123568832874298
188 |
189 | <_>
190 |
191 | 0 -1 24 -85474705 2138828511 -1036436754 817625855
192 | 1123369029 -58796809 -1013468481 -194513409
193 |
194 | -0.5123769044876099 0.5791834592819214
195 |
196 | <_>
197 | 5
198 | -0.5549971461296082
199 |
200 |
201 | <_>
202 |
203 | 0 -1 42 -17409 -20481 -268457797 -134239493 -17473 -1 -21829
204 | -21846
205 |
206 | -0.3763174116611481 0.7298233509063721
207 |
208 | <_>
209 |
210 | 0 -1 6 -805310737 -2098262358 -269504725 682502698
211 | 2147483519 1740574719 -1090519233 -268472385
212 |
213 | -0.5352765917778015 0.5659480094909668
214 |
215 | <_>
216 |
217 | 0 -1 61 -67109678 -6145 -8 -87884584 -20481 -1073762305
218 | -50856216 -16849696
219 |
220 | -0.5678374171257019 0.4961479902267456
221 |
222 | <_>
223 |
224 | 0 -1 123 -138428633 1002418167 -1359008245 -1908670465
225 | -1346685918 910098423 -1359010520 -1346371657
226 |
227 | -0.5706262588500977 0.4572288393974304
228 |
229 | <_>
230 |
231 | 0 -1 9 -89138513 -4196353 1256531674 -1330665426 1216308261
232 | -36190633 33498198 -151796633
233 |
234 | -0.5344601869583130 0.4672054052352905
235 |
236 | <_>
237 | 5
238 | -0.8776460289955139
239 |
240 |
241 | <_>
242 |
243 | 0 -1 105 1073769576 206601725 -34013449 -33554433 -789514004
244 | -101384321 -690225153 -264193
245 |
246 | -0.7700348496437073 0.5943940877914429
247 |
248 | <_>
249 |
250 | 0 -1 30 -1432340997 -823623681 -49153 -34291724 -269484035
251 | -1342767105 -1078198273 -1277955
252 |
253 | -0.5043668746948242 0.6151274442672730
254 |
255 | <_>
256 |
257 | 0 -1 35 -1067385040 -195758209 -436748425 -134217731
258 | -50855988 -129 -1 -1
259 |
260 | -0.6808040738105774 0.4667325913906097
261 |
262 | <_>
263 |
264 | 0 -1 119 832534325 -34111555 -26050561 -423659521 -268468364
265 | 2105014143 -2114244 -17367185
266 |
267 | -0.4927591383457184 0.5401885509490967
268 |
269 | <_>
270 |
271 | 0 -1 82 -1089439888 -1080524865 2143059967 -1114121
272 | -1140949004 -3 -2361356 -739516
273 |
274 | -0.6445107460021973 0.4227822124958038
275 |
276 | <_>
277 | 6
278 | -1.1139287948608398
279 |
280 |
281 | <_>
282 |
283 | 0 -1 52 -1074071553 -1074003969 -1 -1280135430 -5324817 -1
284 | -335548482 582134442
285 |
286 | -0.5307556986808777 0.6258179545402527
287 |
288 | <_>
289 |
290 | 0 -1 99 -706937396 -705364068 -540016724 -570495027
291 | -570630659 -587857963 -33628164 -35848193
292 |
293 | -0.5227634310722351 0.5049746036529541
294 |
295 | <_>
296 |
297 | 0 -1 18 -2035630093 42119158 -268503053 -1671444 261017599
298 | 1325432815 1954394111 -805306449
299 |
300 | -0.4983572661876679 0.5106441378593445
301 |
302 | <_>
303 |
304 | 0 -1 111 -282529488 -1558073088 1426018736 -170526448
305 | -546832487 -5113037 -34243375 -570427929
306 |
307 | -0.4990860521793366 0.5060507059097290
308 |
309 | <_>
310 |
311 | 0 -1 92 1016332500 -606301707 915094269 -1080086049
312 | -1837027144 -1361600280 2147318747 1067975613
313 |
314 | -0.5695009231567383 0.4460467398166657
315 |
316 | <_>
317 |
318 | 0 -1 51 -656420166 -15413034 -141599534 -603435836
319 | 1505950458 -787556946 -79823438 -1326199134
320 |
321 | -0.6590405106544495 0.3616424500942230
322 |
323 | <_>
324 | 7
325 | -0.8243625760078430
326 |
327 |
328 | <_>
329 |
330 | 0 -1 28 -901591776 -201916417 -262 -67371009 -143312112
331 | -524289 -41943178 -1
332 |
333 | -0.4972776770591736 0.6027074456214905
334 |
335 | <_>
336 |
337 | 0 -1 112 -4507851 -411340929 -268437513 -67502145 -17350859
338 | -32901 -71344315 -29377
339 |
340 | -0.4383158981800079 0.5966237187385559
341 |
342 | <_>
343 |
344 | 0 -1 69 -75894785 -117379438 -239063587 -12538500 1485072126
345 | 2076233213 2123118847 801906927
346 |
347 | -0.6386105418205261 0.3977999985218048
348 |
349 | <_>
350 |
351 | 0 -1 19 -823480413 786628589 -16876049 -1364262914 242165211
352 | 1315930109 -696268833 -455082829
353 |
354 | -0.5512794256210327 0.4282079637050629
355 |
356 | <_>
357 |
358 | 0 -1 73 -521411968 6746762 -1396236286 -2038436114
359 | -185612509 57669627 -143132877 -1041235973
360 |
361 | -0.6418755054473877 0.3549866080284119
362 |
363 | <_>
364 |
365 | 0 -1 126 -478153869 1076028979 -1645895615 1365298272
366 | -557859073 -339771473 1442574528 -1058802061
367 |
368 | -0.4841901361942291 0.4668019413948059
369 |
370 | <_>
371 |
372 | 0 -1 45 -246350404 -1650402048 -1610612745 -788400696
373 | 1467604861 -2787397 1476263935 -4481349
374 |
375 | -0.5855734348297119 0.3879135847091675
376 |
377 | <_>
378 | 7
379 | -1.2237116098403931
380 |
381 |
382 | <_>
383 |
384 | 0 -1 114 -24819 1572863935 -16809993 -67108865 2146778388
385 | 1433927541 -268608444 -34865205
386 |
387 | -0.2518476545810700 0.7088654041290283
388 |
389 | <_>
390 |
391 | 0 -1 97 -1841359 -134271049 -32769 -5767369 -1116675 -2185
392 | -8231 -33603327
393 |
394 | -0.4303432404994965 0.5283288359642029
395 |
396 | <_>
397 |
398 | 0 -1 25 -1359507589 -1360593090 -1073778729 -269553812
399 | -809512977 1744707583 -41959433 -134758978
400 |
401 | -0.4259553551673889 0.5440809130668640
402 |
403 | <_>
404 |
405 | 0 -1 34 729753407 -134270989 -1140907329 -235200777
406 | 658456383 2147467263 -1140900929 -16385
407 |
408 | -0.5605589151382446 0.4220733344554901
409 |
410 | <_>
411 |
412 | 0 -1 134 -310380553 -420675595 -193005472 -353568129
413 | 1205338070 -990380036 887604324 -420544526
414 |
415 | -0.5192656517028809 0.4399855434894562
416 |
417 | <_>
418 |
419 | 0 -1 16 -1427119361 1978920959 -287119734 -487068946
420 | 114759245 -540578051 -707510259 -671660453
421 |
422 | -0.5013077259063721 0.4570254683494568
423 |
424 | <_>
425 |
426 | 0 -1 74 -738463762 -889949281 -328301948 -121832450
427 | -1142658284 -1863576559 2146417353 -263185
428 |
429 | -0.4631414115428925 0.4790246188640595
430 |
431 | <_>
432 | 7
433 | -0.5544230937957764
434 |
435 |
436 | <_>
437 |
438 | 0 -1 113 -76228780 -65538 -1 -67174401 -148007 -33 -221796
439 | -272842924
440 |
441 | -0.3949716091156006 0.6082032322883606
442 |
443 | <_>
444 |
445 | 0 -1 110 369147696 -1625232112 2138570036 -1189900 790708019
446 | -1212613127 799948719 -4456483
447 |
448 | -0.4855885505676270 0.4785369932651520
449 |
450 | <_>
451 |
452 | 0 -1 37 784215839 -290015241 536832799 -402984963
453 | -1342414991 -838864897 -176769 -268456129
454 |
455 | -0.4620285332202911 0.4989669024944305
456 |
457 | <_>
458 |
459 | 0 -1 41 -486418688 -171915327 -340294900 -21938 -519766032
460 | -772751172 -73096060 -585322623
461 |
462 | -0.6420643329620361 0.3624351918697357
463 |
464 | <_>
465 |
466 | 0 -1 117 -33554953 -475332625 -1423463824 -2077230421
467 | -4849669 -2080505925 -219032928 -1071915349
468 |
469 | -0.4820112884044647 0.4632140696048737
470 |
471 | <_>
472 |
473 | 0 -1 65 -834130468 -134217476 -1349314083 -1073803559
474 | -619913764 -1449131844 -1386890321 -1979118423
475 |
476 | -0.4465552568435669 0.5061788558959961
477 |
478 | <_>
479 |
480 | 0 -1 56 -285249779 1912569855 -16530 -1731022870 -1161904146
481 | -1342177297 -268439634 -1464078708
482 |
483 | -0.5190586447715759 0.4441480338573456
484 |
485 | <_>
486 | 7
487 | -0.7161560654640198
488 |
489 |
490 | <_>
491 |
492 | 0 -1 20 1246232575 1078001186 -10027057 60102 -277348353
493 | -43646987 -1210581153 1195769615
494 |
495 | -0.4323809444904327 0.5663768053054810
496 |
497 | <_>
498 |
499 | 0 -1 15 -778583572 -612921106 -578775890 -4036478
500 | -1946580497 -1164766570 -1986687009 -12103599
501 |
502 | -0.4588732719421387 0.4547033011913300
503 |
504 | <_>
505 |
506 | 0 -1 129 -1073759445 2013231743 -1363169553 -1082459201
507 | -1414286549 868185983 -1356133589 -1077936257
508 |
509 | -0.5218553543090820 0.4111092388629913
510 |
511 | <_>
512 |
513 | 0 -1 102 -84148365 -2093417722 -1204850272 564290299
514 | -67121221 -1342177350 -1309195902 -776734797
515 |
516 | -0.4920000731945038 0.4326725304126740
517 |
518 | <_>
519 |
520 | 0 -1 88 -25694458 67104495 -290216278 -168563037 2083877442
521 | 1702788383 -144191964 -234882162
522 |
523 | -0.4494568109512329 0.4448510706424713
524 |
525 | <_>
526 |
527 | 0 -1 59 -857980836 904682741 -1612267521 232279415
528 | 1550862252 -574825221 -357380888 -4579409
529 |
530 | -0.5180826783180237 0.3888972699642181
531 |
532 | <_>
533 |
534 | 0 -1 27 -98549440 -137838400 494928389 -246013630 939541351
535 | -1196072350 -620603549 2137216273
536 |
537 | -0.6081240773200989 0.3333222270011902
538 |
539 | <_>
540 | 8
541 | -0.6743940711021423
542 |
543 |
544 | <_>
545 |
546 | 0 -1 29 -150995201 2071191945 -1302151626 536934335
547 | -1059008937 914128709 1147328110 -268369925
548 |
549 | -0.1790193915367127 0.6605972051620483
550 |
551 | <_>
552 |
553 | 0 -1 128 -134509479 1610575703 -1342177289 1861484541
554 | -1107833788 1577058173 -333558568 -136319041
555 |
556 | -0.3681024610996246 0.5139749646186829
557 |
558 | <_>
559 |
560 | 0 -1 70 -1 1060154476 -1090984524 -630918524 -539492875
561 | 779616255 -839568424 -321
562 |
563 | -0.3217232525348663 0.6171553134918213
564 |
565 | <_>
566 |
567 | 0 -1 4 -269562385 -285029906 -791084350 -17923776 235286671
568 | 1275504943 1344390399 -966276889
569 |
570 | -0.4373284578323364 0.4358185231685638
571 |
572 | <_>
573 |
574 | 0 -1 76 17825984 -747628419 595427229 1474759671 575672208
575 | -1684005538 872217086 -1155858277
576 |
577 | -0.4404836893081665 0.4601220190525055
578 |
579 | <_>
580 |
581 | 0 -1 124 -336593039 1873735591 -822231622 -355795238
582 | -470820869 -1997537409 -1057132384 -1015285005
583 |
584 | -0.4294152259826660 0.4452161788940430
585 |
586 | <_>
587 |
588 | 0 -1 54 -834212130 -593694721 -322142257 -364892500
589 | -951029539 -302125121 -1615106053 -79249765
590 |
591 | -0.3973052501678467 0.4854526817798615
592 |
593 | <_>
594 |
595 | 0 -1 95 1342144479 2147431935 -33554561 -47873 -855685912 -1
596 | 1988052447 536827383
597 |
598 | -0.7054683566093445 0.2697997391223908
599 |
600 | <_>
601 | 9
602 | -1.2042298316955566
603 |
604 |
605 | <_>
606 |
607 | 0 -1 39 1431368960 -183437936 -537002499 -137497097
608 | 1560590321 -84611081 -2097193 -513
609 |
610 | -0.5905947685241699 0.5101932883262634
611 |
612 | <_>
613 |
614 | 0 -1 120 -1645259691 2105491231 2130706431 1458995007
615 | -8567536 -42483883 -33780003 -21004417
616 |
617 | -0.4449204802513123 0.4490709304809570
618 |
619 | <_>
620 |
621 | 0 -1 89 -612381022 -505806938 -362027516 -452985106
622 | 275854917 1920431639 -12600561 -134221825
623 |
624 | -0.4693818688392639 0.4061094820499420
625 |
626 | <_>
627 |
628 | 0 -1 14 -805573153 -161 -554172679 -530519488 -16779441
629 | 2000682871 -33604275 -150997129
630 |
631 | -0.3600351214408875 0.5056326985359192
632 |
633 | <_>
634 |
635 | 0 -1 67 6192 435166195 1467449341 2046691505 -1608493775
636 | -4755729 -1083162625 -71365637
637 |
638 | -0.4459891915321350 0.4132415652275085
639 |
640 | <_>
641 |
642 | 0 -1 86 -41689215 -3281034 1853357967 -420712635 -415924289
643 | -270209208 -1088293113 -825311232
644 |
645 | -0.4466069042682648 0.4135067760944367
646 |
647 | <_>
648 |
649 | 0 -1 80 -117391116 -42203396 2080374461 -188709 -542008165
650 | -356831940 -1091125345 -1073796897
651 |
652 | -0.3394956290721893 0.5658645033836365
653 |
654 | <_>
655 |
656 | 0 -1 75 -276830049 1378714472 -1342181951 757272098
657 | 1073740607 -282199241 -415761549 170896931
658 |
659 | -0.5346512198448181 0.3584479391574860
660 |
661 | <_>
662 |
663 | 0 -1 55 -796075825 -123166849 2113667055 -217530421
664 | -1107432194 -16385 -806359809 -391188771
665 |
666 | -0.4379335641860962 0.4123645126819611
667 |
668 | <_>
669 | 10
670 | -0.8402050137519836
671 |
672 |
673 | <_>
674 |
675 | 0 -1 71 -890246622 15525883 -487690486 47116238 -1212319899
676 | -1291847681 -68159890 -469829921
677 |
678 | -0.2670986354351044 0.6014143228530884
679 |
680 | <_>
681 |
682 | 0 -1 31 -1361180685 -1898008841 -1090588811 -285410071
683 | -1074016265 -840443905 2147221487 -262145
684 |
685 | -0.4149844348430634 0.4670888185501099
686 |
687 | <_>
688 |
689 | 0 -1 40 1426190596 1899364271 2142731795 -142607505
690 | -508232452 -21563393 -41960001 -65
691 |
692 | -0.4985891580581665 0.3719584941864014
693 |
694 | <_>
695 |
696 | 0 -1 109 -201337965 10543906 -236498096 -746195597
697 | 1974565825 -15204415 921907633 -190058309
698 |
699 | -0.4568729996681213 0.3965812027454376
700 |
701 | <_>
702 |
703 | 0 -1 130 -595026732 -656401928 -268649235 -571490699
704 | -440600392 -133131 -358810952 -2004088646
705 |
706 | -0.4770836830139160 0.3862601518630981
707 |
708 | <_>
709 |
710 | 0 -1 66 941674740 -1107882114 1332789109 -67691015
711 | -1360463693 -1556612430 -609108546 733546933
712 |
713 | -0.4877715110778809 0.3778986334800720
714 |
715 | <_>
716 |
717 | 0 -1 49 -17114945 -240061474 1552871558 -82775604 -932393844
718 | -1308544889 -532635478 -99042357
719 |
720 | -0.3721654713153839 0.4994400143623352
721 |
722 | <_>
723 |
724 | 0 -1 133 -655906006 1405502603 -939205164 1884929228
725 | -498859222 559417357 -1928559445 -286264385
726 |
727 | -0.3934195041656494 0.4769641458988190
728 |
729 | <_>
730 |
731 | 0 -1 0 -335837777 1860677295 -90 -1946186226 931096183
732 | 251612987 2013265917 -671232197
733 |
734 | -0.4323300719261169 0.4342164099216461
735 |
736 | <_>
737 |
738 | 0 -1 103 37769424 -137772680 374692301 2002666345 -536176194
739 | -1644484728 807009019 1069089930
740 |
741 | -0.4993278682231903 0.3665378093719482
742 |
743 | <_>
744 | 9
745 | -1.1974394321441650
746 |
747 |
748 | <_>
749 |
750 | 0 -1 43 -5505 2147462911 2143265466 -4511070 -16450 -257
751 | -201348440 -71333206
752 |
753 | -0.3310225307941437 0.5624626278877258
754 |
755 | <_>
756 |
757 | 0 -1 90 -136842268 -499330741 2015250980 -87107126
758 | -641665744 -788524639 -1147864792 -134892563
759 |
760 | -0.5266560912132263 0.3704403042793274
761 |
762 | <_>
763 |
764 | 0 -1 104 -146800880 -1780368555 2111170033 -140904684
765 | -16777551 -1946681885 -1646463595 -839131947
766 |
767 | -0.4171888828277588 0.4540435671806335
768 |
769 | <_>
770 |
771 | 0 -1 85 -832054034 -981663763 -301990281 -578814081
772 | -932319000 -1997406723 -33555201 -69206017
773 |
774 | -0.4556705355644226 0.3704262077808380
775 |
776 | <_>
777 |
778 | 0 -1 24 -118492417 -1209026825 1119023838 -1334313353
779 | 1112948738 -297319313 1378887291 -139469193
780 |
781 | -0.4182529747486115 0.4267231225967407
782 |
783 | <_>
784 |
785 | 0 -1 78 -1714382628 -2353704 -112094959 -549613092
786 | -1567058760 -1718550464 -342315012 -1074972227
787 |
788 | -0.3625369668006897 0.4684656262397766
789 |
790 | <_>
791 |
792 | 0 -1 5 -85219702 316836394 -33279 1904970288 2117267315
793 | -260901769 -621461759 -88607770
794 |
795 | -0.4742925167083740 0.3689507246017456
796 |
797 | <_>
798 |
799 | 0 -1 11 -294654041 -353603585 -1641159686 -50331921
800 | -2080899877 1145569279 -143132713 -152044037
801 |
802 | -0.3666271567344666 0.4580127298831940
803 |
804 | <_>
805 |
806 | 0 -1 32 1887453658 -638545712 -1877976819 -34320972
807 | -1071067983 -661345416 -583338277 1060190561
808 |
809 | -0.4567637443542481 0.3894708156585693
810 |
811 | <_>
812 | 9
813 | -0.5733128190040588
814 |
815 |
816 | <_>
817 |
818 | 0 -1 122 -994063296 1088745462 -318837116 -319881377
819 | 1102566613 1165490103 -121679694 -134744129
820 |
821 | -0.4055117964744568 0.5487945079803467
822 |
823 | <_>
824 |
825 | 0 -1 68 -285233233 -538992907 1811935199 -369234005 -529
826 | -20593 -20505 -1561401854
827 |
828 | -0.3787897229194641 0.4532003402709961
829 |
830 | <_>
831 |
832 | 0 -1 58 -1335245632 1968917183 1940861695 536816369
833 | -1226071367 -570908176 457026619 1000020667
834 |
835 | -0.4258328974246979 0.4202791750431061
836 |
837 | <_>
838 |
839 | 0 -1 94 -1360318719 -1979797897 -50435249 -18646473
840 | -608879292 -805306691 -269304244 -17840167
841 |
842 | -0.4561023116111755 0.4002747833728790
843 |
844 | <_>
845 |
846 | 0 -1 87 2062765935 -16449 -1275080721 -16406 45764335
847 | -1090552065 -772846337 -570464322
848 |
849 | -0.4314672648906708 0.4086346626281738
850 |
851 | <_>
852 |
853 | 0 -1 127 -536896021 1080817663 -738234288 -965478709
854 | -2082767969 1290855887 1993822934 -990381609
855 |
856 | -0.4174543321132660 0.4249868988990784
857 |
858 | <_>
859 |
860 | 0 -1 3 -818943025 168730891 -293610428 -79249354 669224671
861 | 621166734 1086506807 1473768907
862 |
863 | -0.4321364760398865 0.4090838730335236
864 |
865 | <_>
866 |
867 | 0 -1 79 -68895696 -67107736 -1414315879 -841676168
868 | -619843344 -1180610531 -1081990469 1043203389
869 |
870 | -0.5018386244773865 0.3702533841133118
871 |
872 | <_>
873 |
874 | 0 -1 116 -54002134 -543485719 -2124882422 -1437445858
875 | -115617074 -1195787391 -1096024366 -2140472445
876 |
877 | -0.5037505626678467 0.3564981222152710
878 |
879 | <_>
880 | 9
881 | -0.4892596900463104
882 |
883 |
884 | <_>
885 |
886 | 0 -1 132 -67113211 2003808111 1862135111 846461923 -2752
887 | 2002237273 -273154752 1937223539
888 |
889 | -0.2448196411132813 0.5689709186553955
890 |
891 | <_>
892 |
893 | 0 -1 62 1179423888 -78064940 -611839555 -539167899
894 | -1289358360 -1650810108 -892540499 -1432827684
895 |
896 | -0.4633283913135529 0.3587929606437683
897 |
898 | <_>
899 |
900 | 0 -1 23 -285212705 -78450761 -656212031 -264050110 -27787425
901 | -1334349961 -547662981 -135796924
902 |
903 | -0.3731099069118500 0.4290455579757690
904 |
905 | <_>
906 |
907 | 0 -1 77 341863476 403702016 -550588417 1600194541
908 | -1080690735 951127993 -1388580949 -1153717473
909 |
910 | -0.3658909499645233 0.4556473195552826
911 |
912 | <_>
913 |
914 | 0 -1 22 -586880702 -204831512 -100644596 -39319550
915 | -1191150794 705692513 457203315 -75806957
916 |
917 | -0.5214384198188782 0.3221037387847900
918 |
919 | <_>
920 |
921 | 0 -1 72 -416546870 545911370 -673716192 -775559454
922 | -264113598 139424 -183369982 -204474641
923 |
924 | -0.4289036989212036 0.4004956185817719
925 |
926 | <_>
927 |
928 | 0 -1 50 -1026505020 -589692154 -1740499937 -1563770497
929 | 1348491006 -60710713 -1109853489 -633909413
930 |
931 | -0.4621542394161224 0.3832748532295227
932 |
933 | <_>
934 |
935 | 0 -1 108 -1448872304 -477895040 -1778390608 -772418127
936 | -1789923416 -1612057181 -805306693 -1415842113
937 |
938 | -0.3711548447608948 0.4612701535224915
939 |
940 | <_>
941 |
942 | 0 -1 92 407905424 -582449988 52654751 -1294472 -285103725
943 | -74633006 1871559083 1057955850
944 |
945 | -0.5180652141571045 0.3205870389938355
946 |
947 | <_>
948 | 10
949 | -0.5911940932273865
950 |
951 |
952 | <_>
953 |
954 | 0 -1 81 4112 -1259563825 -846671428 -100902460 1838164148
955 | -74153752 -90653988 -1074263896
956 |
957 | -0.2592592537403107 0.5873016119003296
958 |
959 | <_>
960 |
961 | 0 -1 1 -285216785 -823206977 -1085589 -1081346 1207959293
962 | 1157103471 2097133565 -2097169
963 |
964 | -0.3801195919513702 0.4718827307224274
965 |
966 | <_>
967 |
968 | 0 -1 121 -12465 -536875169 2147478367 2130706303 -37765492
969 | -866124467 -318782328 -1392509185
970 |
971 | -0.3509117066860199 0.5094807147979736
972 |
973 | <_>
974 |
975 | 0 -1 38 2147449663 -20741 -16794757 1945873146 -16710 -1
976 | -8406341 -67663041
977 |
978 | -0.4068757295608521 0.4130136370658875
979 |
980 | <_>
981 |
982 | 0 -1 17 -155191713 866117231 1651407483 548272812 -479201468
983 | -447742449 1354229504 -261884429
984 |
985 | -0.4557141065597534 0.3539792001247406
986 |
987 | <_>
988 |
989 | 0 -1 100 -225319378 -251682065 -492783986 -792341777
990 | -1287261695 1393643841 -11274182 -213909521
991 |
992 | -0.4117803275585175 0.4118592441082001
993 |
994 | <_>
995 |
996 | 0 -1 63 -382220122 -2002072729 -51404800 -371201558
997 | -923011069 -2135301457 -2066104743 -1042557441
998 |
999 | -0.4008397758007050 0.4034757018089294
1000 |
1001 | <_>
1002 |
1003 | 0 -1 101 -627353764 -48295149 1581203952 -436258614
1004 | -105268268 -1435893445 -638126888 -1061107126
1005 |
1006 | -0.5694189667701721 0.2964762747287750
1007 |
1008 | <_>
1009 |
1010 | 0 -1 118 -8399181 1058107691 -621022752 -251003468 -12582915
1011 | -574619739 -994397789 -1648362021
1012 |
1013 | -0.3195341229438782 0.5294018983840942
1014 |
1015 | <_>
1016 |
1017 | 0 -1 92 -348343812 -1078389516 1717960437 364735981
1018 | -1783841602 -4883137 -457572354 -1076950384
1019 |
1020 | -0.3365339040756226 0.5067458748817444
1021 |
1022 | <_>
1023 | 10
1024 | -0.7612916231155396
1025 |
1026 |
1027 | <_>
1028 |
1029 | 0 -1 10 -1976661318 -287957604 -1659497122 -782068 43591089
1030 | -453637880 1435470000 -1077438561
1031 |
1032 | -0.4204545319080353 0.5165745615959168
1033 |
1034 | <_>
1035 |
1036 | 0 -1 131 -67110925 14874979 -142633168 -1338923040
1037 | 2046713291 -2067933195 1473503712 -789579837
1038 |
1039 | -0.3762553930282593 0.4075302779674530
1040 |
1041 | <_>
1042 |
1043 | 0 -1 83 -272814301 -1577073 -1118685 -305156120 -1052289
1044 | -1073813756 -538971154 -355523038
1045 |
1046 | -0.4253497421741486 0.3728055357933044
1047 |
1048 | <_>
1049 |
1050 | 0 -1 135 -2233 -214486242 -538514758 573747007 -159390971
1051 | 1994225489 -973738098 -203424005
1052 |
1053 | -0.3601998090744019 0.4563256204128265
1054 |
1055 | <_>
1056 |
1057 | 0 -1 115 -261031688 -1330369299 -641860609 1029570301
1058 | -1306461192 -1196149518 -1529767778 683139823
1059 |
1060 | -0.4034293889999390 0.4160816967487335
1061 |
1062 | <_>
1063 |
1064 | 0 -1 64 -572993608 -34042628 -417865 -111109 -1433365268
1065 | -19869715 -1920939864 -1279457063
1066 |
1067 | -0.3620899617671967 0.4594142735004425
1068 |
1069 | <_>
1070 |
1071 | 0 -1 36 -626275097 -615256993 1651946018 805366393
1072 | 2016559730 -430780849 -799868165 -16580645
1073 |
1074 | -0.3903816640377045 0.4381459355354309
1075 |
1076 | <_>
1077 |
1078 | 0 -1 93 1354797300 -1090957603 1976418270 -1342502178
1079 | -1851873892 -1194637077 -1153521668 -1108399474
1080 |
1081 | -0.3591445386409760 0.4624078869819641
1082 |
1083 | <_>
1084 |
1085 | 0 -1 91 68157712 1211368313 -304759523 1063017136 798797750
1086 | -275513546 648167355 -1145357350
1087 |
1088 | -0.4297670423984528 0.4023293554782867
1089 |
1090 | <_>
1091 |
1092 | 0 -1 107 -546318240 -1628569602 -163577944 -537002306
1093 | -545456389 -1325465645 -380446736 -1058473386
1094 |
1095 | -0.5727006793022156 0.2995934784412384
1096 |
1097 | <_>
1098 |
1099 | 0 0 3 5
1100 | <_>
1101 |
1102 | 0 0 4 2
1103 | <_>
1104 |
1105 | 0 0 6 3
1106 | <_>
1107 |
1108 | 0 1 2 3
1109 | <_>
1110 |
1111 | 0 1 3 3
1112 | <_>
1113 |
1114 | 0 1 3 7
1115 | <_>
1116 |
1117 | 0 4 3 3
1118 | <_>
1119 |
1120 | 0 11 3 4
1121 | <_>
1122 |
1123 | 0 12 8 4
1124 | <_>
1125 |
1126 | 0 14 4 3
1127 | <_>
1128 |
1129 | 1 0 5 3
1130 | <_>
1131 |
1132 | 1 1 2 2
1133 | <_>
1134 |
1135 | 1 3 3 1
1136 | <_>
1137 |
1138 | 1 7 4 4
1139 | <_>
1140 |
1141 | 1 12 2 2
1142 | <_>
1143 |
1144 | 1 13 4 1
1145 | <_>
1146 |
1147 | 1 14 4 3
1148 | <_>
1149 |
1150 | 1 17 3 2
1151 | <_>
1152 |
1153 | 2 0 2 3
1154 | <_>
1155 |
1156 | 2 1 2 2
1157 | <_>
1158 |
1159 | 2 2 4 6
1160 | <_>
1161 |
1162 | 2 3 4 4
1163 | <_>
1164 |
1165 | 2 7 2 1
1166 | <_>
1167 |
1168 | 2 11 2 3
1169 | <_>
1170 |
1171 | 2 17 3 2
1172 | <_>
1173 |
1174 | 3 0 2 2
1175 | <_>
1176 |
1177 | 3 1 7 3
1178 | <_>
1179 |
1180 | 3 7 2 1
1181 | <_>
1182 |
1183 | 3 7 2 4
1184 | <_>
1185 |
1186 | 3 18 2 2
1187 | <_>
1188 |
1189 | 4 0 2 3
1190 | <_>
1191 |
1192 | 4 3 2 1
1193 | <_>
1194 |
1195 | 4 6 2 1
1196 | <_>
1197 |
1198 | 4 6 2 5
1199 | <_>
1200 |
1201 | 4 7 5 2
1202 | <_>
1203 |
1204 | 4 8 4 3
1205 | <_>
1206 |
1207 | 4 18 2 2
1208 | <_>
1209 |
1210 | 5 0 2 2
1211 | <_>
1212 |
1213 | 5 3 4 4
1214 | <_>
1215 |
1216 | 5 6 2 5
1217 | <_>
1218 |
1219 | 5 9 2 2
1220 | <_>
1221 |
1222 | 5 10 2 2
1223 | <_>
1224 |
1225 | 6 3 4 4
1226 | <_>
1227 |
1228 | 6 4 4 3
1229 | <_>
1230 |
1231 | 6 5 2 3
1232 | <_>
1233 |
1234 | 6 5 2 5
1235 | <_>
1236 |
1237 | 6 5 4 3
1238 | <_>
1239 |
1240 | 6 6 4 2
1241 | <_>
1242 |
1243 | 6 6 4 4
1244 | <_>
1245 |
1246 | 6 18 1 2
1247 | <_>
1248 |
1249 | 6 21 2 1
1250 | <_>
1251 |
1252 | 7 0 3 7
1253 | <_>
1254 |
1255 | 7 4 2 3
1256 | <_>
1257 |
1258 | 7 9 5 1
1259 | <_>
1260 |
1261 | 7 21 2 1
1262 | <_>
1263 |
1264 | 8 0 1 4
1265 | <_>
1266 |
1267 | 8 5 2 2
1268 | <_>
1269 |
1270 | 8 5 3 2
1271 | <_>
1272 |
1273 | 8 17 3 1
1274 | <_>
1275 |
1276 | 8 18 1 2
1277 | <_>
1278 |
1279 | 9 0 5 3
1280 | <_>
1281 |
1282 | 9 2 2 6
1283 | <_>
1284 |
1285 | 9 5 1 1
1286 | <_>
1287 |
1288 | 9 11 1 1
1289 | <_>
1290 |
1291 | 9 16 1 1
1292 | <_>
1293 |
1294 | 9 16 2 1
1295 | <_>
1296 |
1297 | 9 17 1 1
1298 | <_>
1299 |
1300 | 9 18 1 1
1301 | <_>
1302 |
1303 | 10 5 1 2
1304 | <_>
1305 |
1306 | 10 5 3 3
1307 | <_>
1308 |
1309 | 10 7 1 5
1310 | <_>
1311 |
1312 | 10 8 1 1
1313 | <_>
1314 |
1315 | 10 9 1 1
1316 | <_>
1317 |
1318 | 10 10 1 1
1319 | <_>
1320 |
1321 | 10 10 1 2
1322 | <_>
1323 |
1324 | 10 14 3 3
1325 | <_>
1326 |
1327 | 10 15 1 1
1328 | <_>
1329 |
1330 | 10 15 2 1
1331 | <_>
1332 |
1333 | 10 16 1 1
1334 | <_>
1335 |
1336 | 10 16 2 1
1337 | <_>
1338 |
1339 | 10 17 1 1
1340 | <_>
1341 |
1342 | 10 21 1 1
1343 | <_>
1344 |
1345 | 11 3 2 2
1346 | <_>
1347 |
1348 | 11 5 1 2
1349 | <_>
1350 |
1351 | 11 5 3 3
1352 | <_>
1353 |
1354 | 11 5 4 6
1355 | <_>
1356 |
1357 | 11 6 1 1
1358 | <_>
1359 |
1360 | 11 7 2 2
1361 | <_>
1362 |
1363 | 11 8 1 2
1364 | <_>
1365 |
1366 | 11 10 1 1
1367 | <_>
1368 |
1369 | 11 10 1 2
1370 | <_>
1371 |
1372 | 11 15 1 1
1373 | <_>
1374 |
1375 | 11 17 1 1
1376 | <_>
1377 |
1378 | 11 18 1 1
1379 | <_>
1380 |
1381 | 12 0 2 2
1382 | <_>
1383 |
1384 | 12 1 2 5
1385 | <_>
1386 |
1387 | 12 2 4 1
1388 | <_>
1389 |
1390 | 12 3 1 3
1391 | <_>
1392 |
1393 | 12 7 3 4
1394 | <_>
1395 |
1396 | 12 10 3 2
1397 | <_>
1398 |
1399 | 12 11 1 1
1400 | <_>
1401 |
1402 | 12 12 3 2
1403 | <_>
1404 |
1405 | 12 14 4 3
1406 | <_>
1407 |
1408 | 12 17 1 1
1409 | <_>
1410 |
1411 | 12 21 2 1
1412 | <_>
1413 |
1414 | 13 6 2 5
1415 | <_>
1416 |
1417 | 13 7 3 5
1418 | <_>
1419 |
1420 | 13 11 3 2
1421 | <_>
1422 |
1423 | 13 17 2 2
1424 | <_>
1425 |
1426 | 13 17 3 2
1427 | <_>
1428 |
1429 | 13 18 1 2
1430 | <_>
1431 |
1432 | 13 18 2 2
1433 | <_>
1434 |
1435 | 14 0 2 2
1436 | <_>
1437 |
1438 | 14 1 1 3
1439 | <_>
1440 |
1441 | 14 2 3 2
1442 | <_>
1443 |
1444 | 14 7 2 1
1445 | <_>
1446 |
1447 | 14 13 2 1
1448 | <_>
1449 |
1450 | 14 13 3 3
1451 | <_>
1452 |
1453 | 14 17 2 2
1454 | <_>
1455 |
1456 | 15 0 2 2
1457 | <_>
1458 |
1459 | 15 0 2 3
1460 | <_>
1461 |
1462 | 15 4 3 2
1463 | <_>
1464 |
1465 | 15 4 3 6
1466 | <_>
1467 |
1468 | 15 6 3 2
1469 | <_>
1470 |
1471 | 15 11 3 4
1472 | <_>
1473 |
1474 | 15 13 3 2
1475 | <_>
1476 |
1477 | 15 17 2 2
1478 | <_>
1479 |
1480 | 15 17 3 2
1481 | <_>
1482 |
1483 | 16 1 2 3
1484 | <_>
1485 |
1486 | 16 3 2 4
1487 | <_>
1488 |
1489 | 16 6 1 1
1490 | <_>
1491 |
1492 | 16 16 2 2
1493 | <_>
1494 |
1495 | 17 1 2 2
1496 | <_>
1497 |
1498 | 17 1 2 5
1499 | <_>
1500 |
1501 | 17 12 2 2
1502 | <_>
1503 |
1504 | 18 0 2 2
1505 |
1506 |
--------------------------------------------------------------------------------