Android + OpenCV: Part 9 — Display Custom Meter

Homan Huang
4 min readMay 24, 2020

--

Have you seen the FPS meter on the OpenCV camera view? You can insert your own meter. In this part, I will show how to program your own meter.

_ _ _ _ _ _ _ _ — == Menu == — _ _ _ _ _ _ _ _

😄1. Understand the Path of FpsMeter
📷2. FpsMeter Class
🔁3. DIY Meter: RotationMeter
📶4. RotationMeter to CameraBridgeViewBase
📲5. Test the Result

😄1. Understand the Path of FpsMeter

Fps meter is in the android folder. Let’s take a look at the file structure in {OpenCV SDK}/sdk/java/src/org/opencv/android. You can easily edit in Android Studio.

How does it load? Here is its path.

The JavaCamera2View or JavaCameraView share extends CameraBridgeViewBase. You will find the Fps meter defined in CameraBridgeViewBase.

It reads the attribute set file, attrs.xml, in the constructor.

protected FpsMeter mFpsMeter = null;
...
public CameraBridgeViewBase(Context context, AttributeSet attrs) {
...

TypedArray styledAttrs = getContext()
.obtainStyledAttributes(
attrs, R.styleable.CameraBridgeViewBase);
if (styledAttrs.getBoolean(
R.styleable.CameraBridgeViewBase_show_fps, false))
enableFpsMeter();
else
disableFpsMeter();

...
}

The CPU found the setting of FpsMeter and switch it to ON/OFF.

/**
* This method enables label with fps value on the screen
*/
public void enableFpsMeter() {
if (mFpsMeter == null) {
mFpsMeter = new FpsMeter();
mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
}
}

public void disableFpsMeter() {
mFpsMeter = null;
}

And draw Fps data in deliverAndDrawFrame() function.

protected void deliverAndDrawFrame(CvCameraViewFrame frame) { 
...

if (bmpValid && mCacheBitmap != null) {
...

if (mFpsMeter != null) {
mFpsMeter.measure();
mFpsMeter.draw(canvas, 20, 30);
}
...
}
}
}

📷2. FpsMeter Class

Let’s dive into FpsMeter.java. It has functioned as:

  1. init(): Initial variables.
  2. measure(): Calculate the fps after first 20 frames.
  3. setResolution(): Set screen height and width.
  4. draw(): Paint on canvas.

That’s it. It’s easy to understand.

🔁3. DIY Meter: RotationMeter

I want to insert a RotationMeter. It is simple. Let’ s copy the FpsMeter to RotationMeter. Next, I remove some junk variables.

public class RotationMeter {
private static final String TAG = "Rotation: ";

private String mRotation;
Paint mPaint;
int mWidth = 0;
int mHeight = 0;

Set init(), I change the color to YELLOW.

public void init() {
mRotation = "Rotation: 0";

mPaint = new Paint();
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(20);
}

Create updateMeter(),

public void updateMeter(int iRotation) {
mRotation = TAG + iRotation;
}

I remove the measure() and setResolution(). Next, let’s draw(),

public void draw(Canvas canvas, float offsetx, float offsety) {
canvas.drawText(mRotation, offsetx, offsety, mPaint);
}

📶4. RotationMeter to CameraBridgeViewBase

To insert our new meter, you need to edit the attrs.xml at res/value of OpenCV module.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name = "CameraBridgeViewBase" >
<attr name="show_fps" format="boolean"/>
<attr name="show_rotation" format="boolean"/>
...
</declare-styleable>
</resources>

Next, you need to edit your layout file, such as activity_main.xml.

<org.opencv.android.JavaCamera2View
...
app:show_fps="true"
app:show_rotation="true"
... />

In CameraBridgeViewBase.java, We need a listener for the change of orientation.

protected RotationMeter mRotationMeter = null;private int rotation = 0;
public static boolean isBetween(int x, int lower, int upper) {
return lower <= x && x <= upper;
}
private void detectRotation(Context context) {
//screen rotation
OrientationEventListener orientationEventListener =
new OrientationEventListener(
context, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int orientation) {
if (isBetween(orientation, 45, 134)) {
rotation = 270;
} else if (isBetween(orientation, 135, 224)) {
rotation = 180;
} else if (isBetween(orientation, 225, 314)) {
rotation = 90;
} else {
rotation = 0;
}
}
};
if (orientationEventListener.canDetectOrientation()) {
orientationEventListener.enable();
} else {
orientationEventListener.disable();
}
}

Let’s enable the setting.

public CameraBridgeViewBase(Context context, AttributeSet attrs) {
super(context, attrs);
detectRotation(context);
...
if (styledAttrs.getBoolean(
R.styleable.CameraBridgeViewBase_show_fps, false))
enableFpsMeter();
else
disableFpsMeter();

if (styledAttrs.getBoolean(
R.styleable.CameraBridgeViewBase_show_rotation, false))
enableRotationMeter();
else
disableRotationMeter();

and,

/**
* This method enables label with rotation value on the screen
*/
public void enableRotationMeter() {
if (mRotationMeter == null) {
mRotationMeter = new RotationMeter();
mRotationMeter.init();
mRotationMeter.updateMeter(rotation);
}
}

public void disableRotationMeter() {
mRotationMeter = null;
}

Finally, let’s draw value on the screen.

protected void deliverAndDrawFrame(CvCameraViewFrame frame) { 
...

if (bmpValid && mCacheBitmap != null) {
...
if (canvas != null) {
...

if (mFpsMeter != null) {...}

if (mRotationMeter != null) {
mRotationMeter.updateMeter(rotation);
mRotationMeter.draw(canvas, 150, 30);
}
...
}
}
}

📲5. Test the Result

Now, let’s connect the phone and run the app.

Great, they are all matched.

--

--

Homan Huang
Homan Huang

Written by Homan Huang

Computer Science BS from SFSU. I studied and worked on Android system since 2017. If you are interesting in my past works, please go to my LinkedIn.

No responses yet