mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Wrote a benchmark activity for Android which reads images recursively from the SD card, decodes each 10 times, and prints the average time to decode in milliseconds. The UI is extremely minimal, as the results are written to the log, but this is a good start towards measuring our performance under Dalvik on a platform without floating point hardware. This should run fine on the emulator but I haven't tested it yet.
git-svn-id: https://zxing.googlecode.com/svn/trunk@640 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
f83e68d7bc
commit
de419268b8
|
@ -28,6 +28,8 @@
|
|||
<activity android:name="CameraTestActivity"
|
||||
android:screenOrientation="landscape">
|
||||
</activity>
|
||||
<activity android:name="BenchmarkActivity"
|
||||
android:label="@string/benchmark_name"/>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
</manifest>
|
||||
|
|
35
androidtest/res/layout/benchmark.xml
Executable file
35
androidtest/res/layout/benchmark.xml
Executable file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2008 ZXing authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="10px">
|
||||
<TableRow>
|
||||
<Button android:id="@+id/benchmark_run"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/benchmark_run"/>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TextView android:id="@+id/benchmark_help"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="false"
|
||||
android:text="@string/benchmark_help"/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
|
@ -19,6 +19,16 @@
|
|||
android:layout_height="fill_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="10px">
|
||||
<TableRow>
|
||||
<Button android:id="@+id/test_camera"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/test_camera"/>
|
||||
<Button android:id="@+id/run_benchmark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/benchmark_run"/>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<Button android:id="@+id/scan_product"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -30,10 +40,10 @@
|
|||
android:text="@string/scan_qr_code"/>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<Button android:id="@+id/test_camera"
|
||||
<Button android:id="@+id/scan_anything"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/test_camera"/>
|
||||
android:text="@string/scan_anything"/>
|
||||
<Button android:id="@+id/search_book_contents"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Messages IDs -->
|
||||
<item type="id" name="auto_focus"/>
|
||||
<item type="id" name="benchmark_done"/>
|
||||
<item type="id" name="quit"/>
|
||||
<item type="id" name="save"/>
|
||||
<item type="id" name="save_succeeded"/>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
-->
|
||||
<resources>
|
||||
<string name="app_name">ZXing Test</string>
|
||||
<string name="scan_anything">Scan anything</string>
|
||||
<string name="scan_product">Scan product</string>
|
||||
<string name="scan_qr_code">Scan QR Code</string>
|
||||
<string name="search_book_contents">Search Book Contents</string>
|
||||
|
@ -33,4 +34,9 @@
|
|||
<string name="status_message">Press the shutter button to save images to the SD card for testing purposes. Press DPAD_CENTER to trigger autofocus.</string>
|
||||
<string name="save_succeeded">Save succeeded</string>
|
||||
<string name="save_failed">Save failed - is the SD card installed?</string>
|
||||
|
||||
<string name="benchmark_name">ZXing Benchmark</string>
|
||||
<string name="benchmark_help">Place images in /sdcard/zxingbenchmark, then check \"adb logcat\" for results. Turn on Airplane Mode first for more reliable results.</string>
|
||||
<string name="benchmark_run">Run benchmark</string>
|
||||
<string name="benchmark_running">Benchmark running...\u2026</string>
|
||||
</resources>
|
||||
|
|
96
androidtest/src/com/google/zxing/client/androidtest/BenchmarkActivity.java
Executable file
96
androidtest/src/com/google/zxing/client/androidtest/BenchmarkActivity.java
Executable file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.androidtest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
public class BenchmarkActivity extends Activity {
|
||||
|
||||
private static final String PATH = "/sdcard/zxingbenchmark";
|
||||
private static final String TAG = "ZXingBenchmark";
|
||||
|
||||
private Button mRunBenchmarkButton;
|
||||
private TextView mTextView;
|
||||
private BenchmarkThread mBenchmarkThread;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.benchmark);
|
||||
|
||||
mRunBenchmarkButton = (Button) findViewById(R.id.benchmark_run);
|
||||
mRunBenchmarkButton.setOnClickListener(mRunBenchmark);
|
||||
mTextView = (TextView) findViewById(R.id.benchmark_help);
|
||||
|
||||
mBenchmarkThread = null;
|
||||
}
|
||||
|
||||
public Button.OnClickListener mRunBenchmark = new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (mBenchmarkThread == null) {
|
||||
mRunBenchmarkButton.setEnabled(false);
|
||||
mTextView.setText(R.string.benchmark_running);
|
||||
mBenchmarkThread = new BenchmarkThread(BenchmarkActivity.this, PATH);
|
||||
mBenchmarkThread.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public Handler mHandler = new Handler() {
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case R.id.benchmark_done:
|
||||
handleBenchmarkDone(message);
|
||||
mBenchmarkThread = null;
|
||||
mRunBenchmarkButton.setEnabled(true);
|
||||
mTextView.setText(R.string.benchmark_help);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void handleBenchmarkDone(Message message) {
|
||||
Vector<BenchmarkItem> items = (Vector<BenchmarkItem>) message.obj;
|
||||
int count = 0;
|
||||
for (int x = 0; x < items.size(); x++) {
|
||||
BenchmarkItem item = items.get(x);
|
||||
if (item != null) {
|
||||
BarcodeFormat format = item.getFormat();
|
||||
Log.v(TAG, (item.getDecoded() ? "DECODED: " : "FAILED: ") + item.getPath());
|
||||
Log.v(TAG, " Ran " + item.getCount() + " tests on " +
|
||||
(format != null ? format.toString() : "unknown") + " format with an average of " +
|
||||
item.getAverageMilliseconds() + " ms");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
Log.v(TAG, "TOTAL: Decoded " + count + " images");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.androidtest;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
public class BenchmarkItem {
|
||||
|
||||
private String mPath;
|
||||
private int[] mTimes;
|
||||
private int mPosition;
|
||||
private boolean mDecoded;
|
||||
private BarcodeFormat mFormat;
|
||||
|
||||
public BenchmarkItem(String path, int runs) {
|
||||
mPath = path;
|
||||
mTimes = new int[runs];
|
||||
mPosition = 0;
|
||||
mDecoded = false;
|
||||
mFormat = null;
|
||||
}
|
||||
|
||||
// I'm storing these separately instead of as a running total so I can add features like
|
||||
// calculating the min and max later, or ignoring outliers.
|
||||
public void addResult(int milliseconds) {
|
||||
mTimes[mPosition] = milliseconds;
|
||||
mPosition++;
|
||||
}
|
||||
|
||||
public void setDecoded(boolean decoded) {
|
||||
mDecoded = decoded;
|
||||
}
|
||||
|
||||
public void setFormat(BarcodeFormat format) {
|
||||
mFormat = format;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public int getAverageMilliseconds() {
|
||||
int size = mTimes.length;
|
||||
int total = 0;
|
||||
for (int x = 0; x < size; x++) {
|
||||
total += mTimes[x];
|
||||
}
|
||||
if (size > 0) {
|
||||
return total / size;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mTimes.length;
|
||||
}
|
||||
|
||||
public boolean getDecoded() {
|
||||
return mDecoded;
|
||||
}
|
||||
|
||||
public BarcodeFormat getFormat() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
}
|
102
androidtest/src/com/google/zxing/client/androidtest/BenchmarkThread.java
Executable file
102
androidtest/src/com/google/zxing/client/androidtest/BenchmarkThread.java
Executable file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.androidtest;
|
||||
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Date;
|
||||
import java.util.Vector;
|
||||
|
||||
final class BenchmarkThread extends Thread {
|
||||
|
||||
private static final String TAG = "BenchmarkThread";
|
||||
private static final int RUNS = 10;
|
||||
|
||||
private BenchmarkActivity mActivity;
|
||||
private String mPath;
|
||||
private MultiFormatReader mMultiFormatReader;
|
||||
|
||||
BenchmarkThread(BenchmarkActivity activity, String path) {
|
||||
mActivity = activity;
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mMultiFormatReader = new MultiFormatReader();
|
||||
mMultiFormatReader.setHints(null);
|
||||
|
||||
Vector<BenchmarkItem> items = new Vector<BenchmarkItem>();
|
||||
walkTree(mPath, items);
|
||||
Message message = Message.obtain(mActivity.mHandler, R.id.benchmark_done);
|
||||
message.obj = items;
|
||||
message.sendToTarget();
|
||||
}
|
||||
|
||||
// Recurse to allow subdirectories
|
||||
private void walkTree(String path, Vector<BenchmarkItem> items) {
|
||||
File file = new File(path);
|
||||
if (file.isDirectory()) {
|
||||
String[] files = file.list();
|
||||
for (int x = 0; x < files.length; x++) {
|
||||
walkTree(file.getAbsolutePath() + "/" + files[x], items);
|
||||
}
|
||||
} else {
|
||||
BenchmarkItem item = decode(path);
|
||||
if (item != null) {
|
||||
items.addElement(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BenchmarkItem decode(String path) {
|
||||
RGBMonochromeBitmapSource source = null;
|
||||
try {
|
||||
source = new RGBMonochromeBitmapSource(path);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
BenchmarkItem item = new BenchmarkItem(path, RUNS);
|
||||
for (int x = 0; x < RUNS; x++) {
|
||||
Date startDate = new Date();
|
||||
boolean success;
|
||||
Result result = null;
|
||||
try {
|
||||
result = mMultiFormatReader.decodeWithState(source);
|
||||
success = true;
|
||||
} catch (ReaderException e) {
|
||||
success = false;
|
||||
}
|
||||
Date endDate = new Date();
|
||||
if (x == 0) {
|
||||
item.setDecoded(success);
|
||||
item.setFormat(result != null ? result.getBarcodeFormat() : null);
|
||||
}
|
||||
item.addResult((int) (endDate.getTime() - startDate.getTime()));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.androidtest;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import com.google.zxing.common.BaseMonochromeBitmapSource;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
public class RGBMonochromeBitmapSource extends BaseMonochromeBitmapSource {
|
||||
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private byte[] mLuminances;
|
||||
|
||||
public RGBMonochromeBitmapSource(String path) throws FileNotFoundException {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(path);
|
||||
if (bitmap == null) {
|
||||
throw new FileNotFoundException("Couldn't open " + path);
|
||||
}
|
||||
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
int[] pixels = new int[width * height];
|
||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array up
|
||||
// front, which is the same as the Y channel of the YUVMonochromeBitmapSource in the real app.
|
||||
mLuminances = new byte[width * height];
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * height;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = pixels[offset + x];
|
||||
int r = (pixel >> 16) & 0xff;
|
||||
int g = (pixel >> 8) & 0xff;
|
||||
int b = pixel & 0xff;
|
||||
if (r == g && g == b) {
|
||||
// Image is already greyscale, so pick any channel
|
||||
mLuminances[offset + x] = (byte) r;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green
|
||||
mLuminances[offset + x] = (byte) ((r + g + g + b) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
public int getLuminance(int x, int y) {
|
||||
return mLuminances[y * mWidth + x] & 0xff;
|
||||
}
|
||||
|
||||
public void cacheRowForLuminance(int y) {
|
||||
|
||||
}
|
||||
|
||||
public void cacheColumnForLuminance(int x) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -32,14 +32,20 @@ public class ZXingTestActivity extends Activity {
|
|||
|
||||
setContentView(R.layout.test);
|
||||
|
||||
Button test_camera = (Button) findViewById(R.id.test_camera);
|
||||
test_camera.setOnClickListener(mTestCamera);
|
||||
|
||||
Button run_benchmark = (Button) findViewById(R.id.run_benchmark);
|
||||
run_benchmark.setOnClickListener(mRunBenchmark);
|
||||
|
||||
Button scan_product = (Button) findViewById(R.id.scan_product);
|
||||
scan_product.setOnClickListener(mScanProduct);
|
||||
|
||||
Button scan_qr_code = (Button) findViewById(R.id.scan_qr_code);
|
||||
scan_qr_code.setOnClickListener(mScanQRCode);
|
||||
|
||||
Button test_camera = (Button) findViewById(R.id.test_camera);
|
||||
test_camera.setOnClickListener(mTestCamera);
|
||||
Button scan_anything = (Button) findViewById(R.id.scan_anything);
|
||||
scan_anything.setOnClickListener(mScanAnything);
|
||||
|
||||
Button search_book_contents = (Button) findViewById(R.id.search_book_contents);
|
||||
search_book_contents.setOnClickListener(mSearchBookContents);
|
||||
|
@ -66,6 +72,22 @@ public class ZXingTestActivity extends Activity {
|
|||
encode_bad_data.setOnClickListener(mEncodeBadData);
|
||||
}
|
||||
|
||||
public Button.OnClickListener mTestCamera = new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(ZXingTestActivity.this, CameraTestActivity.class.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
};
|
||||
|
||||
public Button.OnClickListener mRunBenchmark = new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(ZXingTestActivity.this, BenchmarkActivity.class.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
};
|
||||
|
||||
public Button.OnClickListener mScanProduct = new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
|
||||
|
@ -82,11 +104,10 @@ public class ZXingTestActivity extends Activity {
|
|||
}
|
||||
};
|
||||
|
||||
public Button.OnClickListener mTestCamera = new Button.OnClickListener() {
|
||||
public Button.OnClickListener mScanAnything = new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(ZXingTestActivity.this, CameraTestActivity.class.getName());
|
||||
startActivity(intent);
|
||||
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue