zxing横屏改为竖屏识别,多次扫描,以及存在的摄像拉伸的问题

mikel阅读(1013)

摘自http://www.cnblogs.com/moka/archive/2013/05/24/3096937.html

按照以上做法,可以实现。

使用简化版可以免去许多不必要的代码,方便学习研究,更好定位核心功能。

如果你调试成功后,就可以着手修改将其变为竖屏识别了。

第1步:

AndroidManifest中将CaptureActivity的screenOrientation属性做如下修改:

android:screenOrientation="portrait"

 

第2步:

我们要把摄像头预览景调为竖向

CameraConfigurationManager类中的setDesiredCameraParameters()方法中添加如下代码:

// 使摄像头旋转90度
    setDisplayOrientation(camera, 90);

然后在CameraConfigurationManager类的最后添加setDisplayOrientation()方法:

/*改变照相机成像的方向的方法*/
  protected void setDisplayOrientation(Camera camera, int angle) {
      Method downPolymorphic = null;        
      try {
        downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
        if (downPolymorphic != null)     
              downPolymorphic.invoke(camera, new Object[]{angle});        
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

  }

 

最后在CameraConfigurationManager中的initFromCameraParameters()方法的Log.d(TAG, “Screen resolution: ” + screenResolution);句后面添加如下代码,这段代码是为了解决摄像头竖过来后图像拉伸的问题:

//为竖屏添加
    Point screenResolutionForCamera = new Point();
    screenResolutionForCamera.x = screenResolution.x;
    screenResolutionForCamera.y = screenResolution.y;
    if (screenResolution.x < screenResolution.y) {
        screenResolutionForCamera.x = screenResolution.y;
        screenResolutionForCamera.y = screenResolution.x;
    }
    // 下句第二参数要根据竖屏修改
    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);

 

第3步:

CameranManager类中getFramingRectInPreview()方法将:

// 下面为横屏模式
      rect.left = rect.left * cameraResolution.x / screenResolution.x;
      rect.right = rect.right * cameraResolution.x / screenResolution.x;
      rect.top = rect.top * cameraResolution.y / screenResolution.y;
      rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

替换为:

// 下面为竖屏模式
      rect.left = rect.left * cameraResolution.y / screenResolution.x;      
      rect.right = rect.right * cameraResolution.y / screenResolution.x;      
      rect.top = rect.top * cameraResolution.x / screenResolution.y;      
      rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

 

第4步:

PlanarYUVLuminanceSource类中的getRow()方法为识别条形码部分,

getMatrix()方法为识别二维码部分

renderCroppedGreyscaleBitmap()方法为生成获取的码图部分

将getRow()中的:

int offset = (y + top) * dataWidth + left;

getMatrix()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

renderCroppedGreyscaleBitmap()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

这些语句中dataWidth全部替换为dataHeight

同时将PlanarYUVLuminanceSource构造方法中:

if (left + width > dataWidth || top + height > dataHeight) {
      throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
    }

dataWidth与dateHeight中互换位置即可。

此时,你的程序竖屏识别码图应该没有任何问题了。至于取景框的样式,大家可以在自定义的ViewfinderView中修改成自己喜欢的样式。

——————————————————————————————

测试,可以竖屏,但是取景还是得横着来。

http://blog.csdn.net/sunmanzth/article/details/6860157  转摘这篇

解决方法:

1.在DecodeHandler.java中,修改decode方法
PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);

byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height – y – 1] = data[x + y * width];
}
int tmp = width; // Here we are swapping, that’s the difference to #11
width = height;
height = tmp;

PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);

2.在CameraManager.java中,注释代码:
// rect.left = rect.left * cameraResolution.x / screenResolution.x;
// rect.right = rect.right * cameraResolution.x / screenResolution.x;
// rect.top = rect.top * cameraResolution.y / screenResolution.y;
// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
修改为
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

3.在CameraConfigurationManager.java中,在setDesiredCameraParameters方法中添加一句
camera.setDisplayOrientation(90);

4.在AndroidManifest.xml中,把Activity的属性Android:screenOrientation=”landscape”
改为
android:screenOrientation=”portrait”
编译运行即可!

参考:

http://code.google.com/p/zxing/issues/detail?id=178#c46

代码:

https://github.com/pplante/zxing-android

————————

按照下篇的解决方案,成功解决问题。

实现zxing多次扫描问题:

private void continuePreview()

{
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
initCamera(surfaceHolder);
if (handler != null)
handler.restartPreviewAndDecode();
}

CaptureActivityHandler中restartPreviewAndDecode属性由private 设置成public

——————————————————————————————

扫描二维码图片时候,正方形的二维码图片,显示会发现变拉长、拉伸。

找着的解决途径如下:

http://www.apkbus.com/android-94078-1-1.html

更改CameraConfigurationManager.java文件

在 Log.d(TAG, “Screen resolution: ” + screenResolution);这句之后增加
Point screenResolutionForCamera = new Point();
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
// preview size is always something like 480*320, other 320*480
if (screenResolution.x < screenResolution.y) {
screenResolutionForCamera.x = screenResolution.y;
screenResolutionForCamera.y = screenResolution.x;
}
再 更改cameraResolution = getCameraResolution(parameters, screenResolution);为cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);

PS:有时间一定要读zxing源码的。

Android总结篇系列:Activity启动模式(lauchMode)

mikel阅读(702)

本来想针对Activity中的启动模式写篇文章的,后来网上发现有人已经总结的相当好了,在此直接引用过来,并加上自己的一些理解,在此感谢原作者。

文章地址:

http://blog.csdn.net/liuhe688/article/details/6754323

————————————————————————————————-

launchMode在多个Activity跳转的过程中扮演着重要的角色,它可 以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下 task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。

Activity一共有以下四种launchMode:

1.standard

2.singleTop

3.singleTask

4.singleInstance

我们可以在AndroidManifest.xml配置<activity>的Android:launchMode属性为以上四种之一即可。

下面我们结合实例一一介绍这四种lanchMode:

1.standard

standard模式是默认的启动模式,不用为<activity>配置android:launchMode属性即可,当然也可以指定值为standard。

我们将会一个Activity,命名为FirstActivity,来演示一下标准的启动模式。FirstActivity代码如下:

复制代码
 1 package com.scott.launchmode;
 2 
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.os.Bundle;
 6 import android.view.View;
 7 import android.widget.Button;
 8 import android.widget.TextView;
 9 
10 public class FirstActivity extends Activity {
11     @Override
12     public void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.first);
15         TextView textView = (TextView) findViewById(R.id.textView);
16         textView.setText(this.toString());
17         Button button = (Button) findViewById(R.id.button);
18         button.setOnClickListener(new View.OnClickListener() {
19             @Override
20             public void onClick(View v) {
21                 Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
22                 startActivity(intent);
23             }
24         });
25     }
26 }
复制代码

我们FirstActivity界面中的TextView用于显示当前Activity实例的序列号,Button用于跳转到下一个FirstActivity界面。

然后我们连续点击几次按钮,将会出现下面的现象:

我们注意到都是FirstActivity的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个FristActivity。standard模式的原理如下图所示:

如图所示,每次跳转系统都会在task中生成一个新的FirstActivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的FirstActivity实例。

这就是standard启动模式,不管有没有已存在的实例,都生成新的实例。

简单点理解:standard启动模式Activity栈从栈底到栈顶顺序为A1 -> B -> C -> A2…。(其中A、B、C等都表示不同的Activity实例,A1、A2则表示属于具有同一Activity类的不同实例)

 

2.singleTop

我们在上面的基础上为<activity>指定属性android:launchMode=”singleTop”,系统就会按照singleTop启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象:

我们看到这个结果跟standard有所不同,三个序列号是相同的,也就是说使用的都是同一个FirstActivity实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个Activity实例。singleTop模式的原理如下图所示:

正如上图所示,跳转时系统会先在栈结构中寻找是否有一个FirstActivity实例正位于栈顶,如果有则不再生成新的,而是直接使用。也许朋友们会有疑问,我只看到栈内只有一个Activity,如果是多个Activity怎么办,如果不是在栈顶会如何?我们接下来再通过一个示例来证实一下大家的疑问。

我们再新建一个Activity命名为SecondActivity,如下:

复制代码
 1 package com.scott.launchmode;
 2 
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.os.Bundle;
 6 import android.view.View;
 7 import android.widget.Button;
 8 import android.widget.TextView;
 9 
10 public class SecondActivity extends Activity {
11     @Override
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.second);
15         TextView textView = (TextView) findViewById(R.id.textView);
16         textView.setText(this.toString());
17         Button button = (Button) findViewById(R.id.button);
18         button.setOnClickListener(new View.OnClickListener() {
19             @Override
20             public void onClick(View v) {
21                 Intent intent = new Intent(SecondActivity.this, FirstActivity.class);
22                 startActivity(intent);                
23             }
24         });
25     }
26 }
复制代码

然后将之前的FirstActivity跳转代码改为:

1 Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
2 startActivity(intent);

是的,FirstActivity会跳转到SecondActivity,SecondActivity又会跳转到FirstActivity。演示结果如下:

我们看到,两个FirstActivity的序列号是不同的,证明从SecondActivity跳转到FirstActivity时生成了新的FirstActivity实例。原理图如下:

我们看到,当从SecondActivity跳转到FirstActivity时,系统发现存在有FirstActivity实例,但不是位于栈顶,于是重新生成一个实例。

这就是singleTop启动模式,如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例。

简单点理解,singleTop即表示当前Activity栈中“栈顶唯一”,Activity跳转顺序或standard模式下栈结构如果为:A -> B -> C -> D1 -> D2,则singleTop启动模式为:A -> B -> C -> D1(此时回调D1的onNewIntent()..)。

 

3.singleTask

在上面的基础上我们修改FirstActivity的属性android:launchMode=”singleTask”。演示的结果如下:

我们注意到,在上面的过程中,FirstActivity的序列号是不变 的,SecondActivity的序列号却不是唯一的,说明从SecondActivity跳转到FirstActivity时,没有生成新的实例,但 是从FirstActivity跳转到SecondActivity时生成了新的实例。singleTask模式的原理图如下图所示:

在图中的下半部分是SecondActivity跳转到 FirstActivity后的栈结构变化的结果,我们注意到,SecondActivity消失了,没错,在这个跳转过程中系统发现有存在的 FirstActivity实例,于是不再生成新的实例,而是将FirstActivity之上的Activity实例统统出栈,将 FirstActivity变为栈顶对象,显示到幕前。也许朋友们有疑问,如果将SecondActivity也设置为singleTask模式,那么 SecondActivity实例是不是可以唯一呢?在我们这个示例中是不可能的,因为每次从SecondActivity跳转到 FirstActivity时,SecondActivity实例都被迫出栈,下次等FirstActivity跳转到SecondActivity时, 找不到存在的SecondActivity实例,于是必须生成新的实例。但是如果我们有ThirdActivity,让SecondActivity和 ThirdActivity互相跳转,那么SecondActivity实例就可以保证唯一。

这就是singleTask模式,如果发现所在Activity栈中有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前。

简单点理解,singleTask表示当前Activity栈中“实例唯一”,Activity跳转顺序或standard模式下栈结构如果为:A -> B1 -> C -> D -> B2,则singleTask启动模式为:A -> B1(此时回调onNewIntent()..)

 

4.singleInstance

这种启动模式比较特殊,因为它会启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。

我们修改FirstActivity的 launchMode=”standard”,SecondActivity的launchMode=”singleInstance”,由于涉及到了多 个栈结构,我们需要在每个Activity中显示当前栈结构的id,所以我们为每个Activity添加如下代码:

1 TextView taskIdView = (TextView) findViewById(R.id.taskIdView);
2 taskIdView.setText("current task id: " + this.getTaskId());

然后我们再演示一下这个流程:

我们发现这两个Activity实例分别被放置在不同的栈结构中,关于singleInstance的原理图如下:

我们看到从FirstActivity跳转到SecondActivity时,重 新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在 SecondActivity中再次跳转到FirstActivity,这个时候系统会在原始栈结构中生成一个FirstActivity实例,然后回退 两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到FirstActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。

此 处的解释不是很赞同,第一次按Back键首先是在当前Activity栈中将栈顶元素出栈,然后显示当前Activity栈中下一个Activity栈, 这个没什么解释的,然后按下Back键,不是回到手机桌面,而是回到另一个Activity栈中的SecondActivityInstance,我认为 原因在于在于“最近栈”,只要此栈位于上次Home操作之后,就会先显示它。

如果我们修改FirstActivity的launchMode值为singleTop、singleTask、singleInstance中的任意一个,流程将会如图所示:

singleInstance启动模式可能是最复杂的一种模式,为了帮助大家理 解,我举一个例子,假如我们有一个share应用,其中的ShareActivity是入口Activity,也是可供其他应用调用的Activity, 我们把这个Activity的启动模式设置为singleInstance,然后在其他应用中调用。我们编辑ShareActivity的配置:

复制代码
<activity android:name=".ShareActivity" android:launchMode="singleInstance">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SINGLE_INSTANCE_SHARE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
复制代码

然后我们在其他应用中这样启动该Activity:

1 Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");
2 startActivity(intent);

当我们打开 ShareActivity后再按后退键回到原来界面时,ShareActivity做为一个独立的个体存在,如果这时我们打开share应用,无需创建 新的ShareActivity实例即可看到结果,因为系统会自动查找,存在则直接利用。大家可以在ShareActivity中打印一下taskId, 看看效果。关于这个过程,原理图如下:

 

原作者此处的解释可能有点让人误解。当我们打开ShareActivity后再按后退键回到原来界面时,ShareActivity做为一个独立的个体存在,此 处不应该是按Back键,而是Home键,因为一旦按下了Back键,ShrareActivityInstance自然就销毁了,也就不存在所谓的“无 须重新创建了”。按下Home键后,接下来打开app MainActivity,在另一个Activity栈中app MainActivity入栈,此时startActivity到ShareActivity,无需创建新的ShareActivity实例即可看到结 果,因为系统会自动查找,存在则直接利用。此时第一次按下Back,ShareActivity Instance出栈,此时这个栈中没有其他Activity了,自然是回到了app MainActivity所在的栈并显示app MainActivity,接下来按Back键,此时app MainActivity所在的栈也没有其他Activity了,同时又不包含任何其他的“最近栈”,自然是回到了手机桌面。注:此处理解的“最近栈”是 以Home键或桌面状态为间隔区分。

简 单点理解,singleInstance所标识的Activity,当被启动时,系统会首先判断系统其他栈中是否已经存在此Activity实例,有则直 接使用,并且其所在的Activity栈理论上只有它一个Activity元素。所以启动它的Activity与它并不在一个task中,所以才需要特别 注意Back的问题。一般表示为:task1 A -> task2 B。

singleInstance表示该Activity在系统范围内“实例唯一”。由此我们发现,singInstance和singleTask主要区别在与系统范围内的“实例唯一”还是当前Activity栈“实例唯一”。

[转载]Applying Low Pass Filter to Android Sensor’s Readings | raw engineering

mikel阅读(960)

来源: [转载]Applying Low Pass Filter to Android Sensor’s Readings | raw engineering

This is a continuation of the AugmentedRealityView project which was released previously on GitHub. You can find the code referenced in this project there, as well.

Overview of Android Sensors

The Android sensor framework lets you access many types of sensors. Two very basic types are:

  1. Hardware Sensors.
  2. Software Sensors.

Hardware sensors are physical components built into a handset or tablet device. They derive their data by directly measuring specific environmental properties, such as acceleration, geomagnetic field strength, or angular change.

For example: Sensor.TYPE_ACCELEROMETER", Sensor.TYPE_MAGNETIC_FIELD

Software sensors are not physical devices, although they mimic hardware-based sensors. Software-based sensors derive their data from one or more of the hardware-based sensors and are sometimes called virtual sensors or synthetic sensors.

For example: Sensor.TYPE_ORIENTATION, Sensor.TYPE_ROTATION_VECTOR

Best Practices for Accessing and Using Sensors

  1. Unregister sensor listeners.
  2. Don’t test your code on the emulator.
  3. Don’t block the onSensorChanged() method.
  4. Avoid using deprecated methods or sensor types.
  5. Verify sensors before you use them.
  6. Choose sensor delays carefully.
  7. Filter the values received in onSensorChanged(). Allow only those that are needed.

After we register the Sensors, the sensor readings get notified in SensorEventListener‘s onSensorChanged() method. However, the rate of change in sensor values is so high that if we map these small changes a.k.a ‘Noise’ the values jump within a large range of values.

We can also specify the SensorManager‘s delay properties from one of these:

  1. SENSOR_DELAY_FASTEST
  2. SENSOR_DELAY_GAME
  3. SENSOR_DELAY_UI
  4. SENSOR_DELAY_NORMAL

This, however, is only a peek into the system. Events may be received faster or slower than the specified rate, but usually events are received faster.

Moral of the story is:

Allow only those values which are useful and discard the unnecessary noise.

The solution for this is to apply a Low-Pass Filter on these values.

A Small Glimpse of Low Pass Filter

A low-pass filter passes low-frequency signals/values and attenuates (reduces the amplitude of) signals/values with frequencies higher than the cutoff frequency.

Take an example of simple signal with values ranging from 0 to 1.
Due to an external source (environmental factors such as jerks or vibrations), a considerable amount of noise is added to these signals. These high frequency signals (noise) cause the readings to hop between considerable high and low values.

Programmatically Apply Low Pass Filter

A device’s sensor readings contribute noise data due to high sensitivity of its hardware to various factors. For gaming purposes, these highly sensitive values are a boon, but for application hat need smooth readings, these hopping values are a mess.

Lets look at AugmentedRealityView on GitHub, where we have to point markers on Camera SurfaceView.

The high sensitivity causes the markers to change positions randomly due to noise.
A Low-Pass Filter concept comes to rescue, because we can omit those high frequencies in the input signal, applying a suitable threshold to the filter output reading to plot the markers.

With this implementation the markers won’t hop randomly because we have removed the unwanted high reading values.

Here is the algorithm implementation:

for i from 1 to n  
y[i] := y[i-1] + α * (x[i] - y[i-1])

Here, α is the cut-off/threshold.

Lets implement it in Android:

lowPass(float[] input, float[] output)

The above method filters the input values and applies LPF and outputs the filtered signals. static final float ALPHA = 0.25f; // if ALPHA = 1 OR 0, no filter applies.

protected float[] lowPass( float[] input, float[] output ) {
    if ( output == null ) return input;     
    for ( int i=0; i<input.length; i++ ) {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}

Low-Pass Filter is finally applied to sensor values in onSensorChanged(SensorEvent event) as follows:

<pre><code class="java"><span class="annotation">@Override</span>
<span class="keyword">public</span> <span class="keyword">void</span> onSensorChanged(SensorEvent evt) {
    <span class="keyword">if</span> (evt.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
        gravSensorVals = lowPass(evt.values.clone(), gravSensorVals);
    } <span class="keyword">else</span> <span class="keyword">if</span> (evt.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
        magSensorVals = lowPass(evt.values.clone(), magSensorVals);
    }
    <span class="keyword">if</span> (gravSensorVals != <span class="keyword">null</span> &amp;&amp; magSensorVals != <span class="keyword">null</span>) {
        SensorManager.getRotationMatrix(RTmp, I, gravSensorVals, magSensorVals);
        <span class="keyword">int</span> rotation = Compatibility.getRotation(<span class="keyword">this</span>);
        <span class="keyword">if</span> (rotation == <span class="number">1</span>) {
            SensorManager.remapCoordinateSystem(RTmp, SensorManager.AXIS_X, SensorManager.AXIS_MINUS_Z, Rot);
        } <span class="keyword">else</span> {
            SensorManager.remapCoordinateSystem(RTmp, SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_Z, Rot);
        }
        SensorManager.getOrientation(Rot, results);
        UIARView.azimuth = (<span class="keyword">float</span>)(((results[<span class="number">0</span>]*<span class="number">180</span>)/Math.PI)+<span class="number">180</span>);
        UIARView.pitch = (<span class="keyword">float</span>)(((results[<span class="number">1</span>]*<span class="number">180</span>/Math.PI))+<span class="number">90</span>);
        UIARView.roll = (<span class="keyword">float</span>)(((results[<span class="number">2</span>]*<span class="number">180</span>/Math.PI)));
        radarMarkerView.postInvalidate();
    }
}

Here i have applied low pass filter for Sensor.TYPE_ACCELEROMETER and Sensor.TYPE_MAGNETIC_FIELD.

All code in this post and more can be found on GitHub.


[转载]Android 自定义camera - peterZ.D - 博客园

mikel阅读(767)

来源: [转载]Android 自定义camera – peterZ.D – 博客园

根据官方教程,翻译而来。

创建一个Camera App:

& 一般步骤

  • 检查并访问Camera  —— 写代码检查是否存在cameras并请求使用
  • 创建一个Preview类 —— 创建一个preview类,继承自SurfaceView 并实现SurfaceHolder 接口。这个类是用来预览在拍照时的影像
  • 创建一个Preview布局 —— 接下去创建一个跟Preview类对应的布局文件,里面可以放一些你想要的交互控件
  • 为Capture(拍照)设置监听(Listener) —— 为你的交互控件设置监听,比如按下一个Button
  • 拍照并保存文件 —— 为拍照或录像写代码,并保存到输出流(output)
  • 释放Camera —— 在使用完后,必须要合理地释放,以供其他应用使用。

注意:要及时地释放Camera资源,通过调用Camera.release() 来实现。否则其他应用,包括你自己的应用,如果想要使用Camera,都会被关闭。

 

* 检查(detecting)并访问(access)Camera:

如果应用没有在Manifest文件里声明要使用Camera硬件,那就应该在runtime时检查Camera是否可用。通过PackageManager.hasSystemFeature()检查。整体代码如下:


/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}

可能会有多个摄像头,在API9之后,可以通过Camera.getNumberOfCameras()得到有几个可用的摄像头。

* 访问Cameras:

确定有设备之后,要通过得到一个Camera实例来访问它。(除非使用intent 来访问Camera),通过Camera.open()方法,可以得到主摄像头。代码如下:


/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}

注意:要用try/catch Camera.open()这个方法,防止其他应用在使用Camera,而导致本应用被系统关闭。在API Level 9以上,可以通过Camera.open(int)来打开特定的摄像头。

* 检查Camera特征

可以通过Camera.getParameters()方法来得到更多的关于它的功能。在API Level 9及以上版本中,使用Camera.getCameraInfo()来确定这个摄像头是前面的还是后面的,以及这个图像的朝向(orientation)。

 

* 创建一个预览类(preview class)

下面的代码演示了如何创建一个基本的可以包含在一个View 布局里的预览图,这个类实现了SurfaceHolder.Callback接口,来捕获创建/删除这个view的回调事件,这些事件是被安排Camera预览所需要的。


/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;

public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;

// 把这个监听添加进去,这样可以监听到创建和销毁的事件了
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}

public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.

if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}

// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}

// set preview size and make any resize, rotate or
// reformatting changes here

// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();

} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}

如果自己想指定大小,在surfaceChanged()方法里可以写。设置之前,必须使用从getSupportedPreviewSizes()得到的值,不要在setPreviewSize()里使用合意值。注:supportedPreviewSize()是在camera.getParameters()后再得到的。

* 把预览图放在一个布局里

一个Preview类,要放在一个布局里,这个布局还要包括其他用来控制拍照等行为的交互界面。这部分展示如何为预览图构建一个布局以及 Activity。在这个例子里,FrameLayout元素用来包含预览图。使用这种布局,可以把多余的信息或控制覆盖到活动的Camera预览图上。


&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
&gt;
&lt;FrameLayout
android:id="@+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/&gt;

&lt;Button
android:id="@+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/&gt;
&lt;/LinearLayout&gt;

在多数的设备上,默认的朝向是横向的。这个例子的布局指定一个水平的布局,下面的代码固定应用的朝向是横向的。在Manifest里如下指定,就可以简单地让应用保持横向。


&lt;activity android:name=".CameraActivity"
android:label="@string/app_name"

android:screenOrientation="landscape"&gt;
&lt;!-- configure this activity to use landscape orientation --&gt;

&lt;intent-filter&gt;
&lt;action android:name="android.intent.action.MAIN" /&gt;
&lt;category android:name="android.intent.category.LAUNCHER" /&gt;
&lt;/intent-filter&gt;
&lt;/activity&gt;

note:Preview没有必要一定是Landscape(横向)模式。从API Level 8开始,可以使用setDiaplayOrientation()方法来设置预览图的旋转。如果要改变朝向,在surfaceChanged()方法里, 先用Camera.stopPreview()方法stop这个Preview,改变朝向,然后再用Camera.startPreview()来开始 Preview。

在你的CameraActivity中,把Preview类加入到这个Framelayout中。你的CameraActivity必须要保证在停止或关闭时释放这个camera。下面代码说明一切。


public class CameraActivity extends Activity {

private Camera mCamera;
private CameraPreview mPreview;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Create an instance of Camera
mCamera = getCameraInstance();

// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
}

* 拍照

一旦设置好Preview,就可以使用它去拍照了。在应用代码里,必须要为你的交互界面设置拍照的响应。

为了得到图,要使用Camera.takePicture()方法。这个方法带了三个参数,用来接收来自Camera的数据。为了接收JPEG 格式的数据,必须要实现Camera.PictureCallback接口,来接收图像数据,并写入到一个文件。下例展示了一个基本的实现,用来保存来自 Camera的图片。


private PictureCallback mPicture = new PictureCallback() {

@Override
public void onPictureTaken(byte[] data, Camera camera) {

File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " +
e.getMessage());
return;
}

try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};

“getOutputMediaFile(MEDIA_TYPE_IMAGE)”这个方法及这个常量会在之后“保存文件”一节中说到

调用Camera.takePicture()方法来触发拍照事件。


// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
);

* 释放Camera

当停止使用Camera时要及时把它释放掉。在Activity.onPause()方法中也要释放。通过Camera.release()方法来释放。代码如下:


public class CameraActivity extends Activity {
private Camera mCamera;
private SurfaceView mPreview;
private MediaRecorder mMediaRecorder;

...

@Override
protected void onPause() {
super.onPause();
releaseMediaRecorder();       // if you are using MediaRecorder, release it first
releaseCamera();              // release the camera immediately on pause event
}

private void releaseMediaRecorder(){
if (mMediaRecorder != null) {
mMediaRecorder.reset();   // clear recorder configuration
mMediaRecorder.release(); // release the recorder object
mMediaRecorder = null;
mCamera.lock();           // lock camera for later use
}
}

private void releaseCamera(){
if (mCamera != null){
mCamera.release();        // release the camera for other applications
mCamera = null;
}
}
}

* 保存文件

Media文件应该被保存到设备的外部存储目录(SD Card),以此来节约系统空间。有很多可以存放文件的地方,但作为开发者,有两个标准的目录可以使用:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES这 个方法返回一个标准的,分享的,并被推荐的目录,用来存放图片和Video。如果被用户卸载了,文件也会存在。为了防止与用户已存在的文件冲突,你应该再 创建一个子目录用来存放自己应用的图片。如下面的例子。这个方法在API Level 8以上可以使用,更早的设备,可以查看其他方法。
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES,这个方法返回一个标准的用来存放你的应用的图片和Video的地方。如果应用被卸载,这里的文件也会被卸载。其他应用也可以操作这里的文件。

如下代码展示了如何创建一个File或者一个Uri,用来保存文件。适用于通过Intent或者自己构建的应用。


public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.

File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.

// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}

// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}

return mediaFile;
}

* Camera Features

可以设置很多的特性,比如图片格式,闪光模式,焦点,还有其他。本节列出几个常用的特性,简单介绍如何使用它们。很多的特性可以通过访问Camera.Parameters对象来得到。但还是有一些重要的特性需要更多的设置。概括为以下几个部分:

  • 计量和焦点区域
  • 面部识别
  • 定时摄影

关于如何使用这些由Camera.Parameters对象控制的特性,可以查看”使用Camera特性”一节。从API Level1到14,有很多的特性,便并不是所有的都可以被设备使用,使用前要先检查一下。

& 检测特性是否可用

要明确使用哪些特性,以及是哪个版本的,之后可以在代码里检查设备硬件是否支持这个特性,如果不行的话,要合理地处理。

可以通过得到一个Camera Parameters对象还检查特性是否可用,以及相应的方法。下例演示如何得到一个Camera.Parameters对象,并检查是否支持自动对焦功能:


// get Camera parameters
Camera.Parameters params = mCamera.getParameters();

List&lt;String&gt; focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// Autofocus mode is supported
}

有些功能是要在Manifest里进行声明,比如闪光和自动对焦。可以在Manifest里的“Features Reference”中查到。

& 使用特性

从camera中getParameters();然后进行setXXX();之后再setParameters()进Camera。

重要:有些参数的设置,可能需要先stop preview,变换preview size ,然后再重启preview。从4.0开始之后就不用再重启preview了。

上文提到的三个部分,需要写一些更多的代码,下面会说到。

& Metering和对焦区域

跟其他的调用方法差不多

& 面部识别

大多数包括人的照片里,脸部很重要,拍照时应该被焦点或者白平衡。4.0提供了API来确定脸部,并利用面部识别来捕捉照片。

注意:当使用面部识别时,setWhiteBalance(String),setFocusAreas(List)以及setMeteringAreas(List)就没有用了。

使用这个通常需要几个步骤:

    • 检查这个功能是是否被设备支持
    • 创建一个面部监测的监听
    • 把这个监听添加到Camera对象里
    • 在preview之后开始面部监测

面部识别不被所有设备支持,通过调用getMaxNumDetectedFaces()来检查是否可以使用。在下面的startFAceDetection()例子中,展示一个这个检测。

为了能被提醒并且响应面部识别的监测,需要对面部检测加一个监听。创建一个实现了Camera.FaceDetectionListener接口的监听。如下代码所示:


class MyFaceDetectionListener implements Camera.FaceDetectionListener {

@Override
public void onFaceDetection(Face[] faces, Camera camera) {
if (faces.length &gt; 0){
Log.d("FaceDetection", "face detected: "+ faces.length +
" Face 1 Location X: " + faces[0].rect.centerX() +
"Y: " + faces[0].rect.centerY() );
}
}
}

创建完之后,把它加入到Camera对象里。

<pre>mCamera.setFaceDetectionListener(new MyFaceDetectionListener()

应用应该在每次开始(或重启)Camera Preview时开启这个监听方法。创建一个用来开启面部识别的方法,如下所示:

public void startFaceDetection(){
    // Try starting Face Detection
    Camera.Parameters params = mCamera.getParameters();

    // start face detection only *after* preview has started
    if (params.getMaxNumDetectedFaces() &gt; 0){
        // camera supports face detection, so can start it:
        mCamera.startFaceDetection();
    }
}

  必须在每次打开(或重启)preview时,开启面部监测。把上面这个方法添加到你的Preview类里的surfaceCreated()和surfaceChanged()方法中。如下所示:

public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // start face detection feature

    } catch (IOException e) {
        Log.d(TAG, "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    if (mHolder.getSurface() == null){
        // preview surface does not exist
        Log.d(TAG, "mHolder.getSurface() == null");
        return;
    }

    try {
        mCamera.stopPreview();

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: " + e.getMessage());
    }

    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();

        startFaceDetection(); // re-start face detection feature

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

 注意:记住要在调用startPreview()之后调用这个方法。不要尝试在mainActivity 的onCreate()方法里启动面部识别。

Android解决:INSTALL_FAILED_MEDIA_UNAVAILABLE

mikel阅读(1073)

问题描述:

使用eclipse编译程序,在Android手机上运行,报错如下:

Installation error: INSTALL_FAILED_MEDIA_UNAVAILABLE

Please check logcat output for more details.

Launch canceled!

解决方案:

使用adb shell命令让手机自己选择安装在哪里。命令如下:

adb shell

pm setInstallLocation 0
注:不同的setInstallLocation说明:

pm setInstallLocation 0 由App自行决定软件能否安装在SD卡

pm setInstallLocation 1 强制全部App安装在ROM内

pm setInstallLocation 2 强制全部App安装在SD卡

操作步骤:

1、win+R快捷键打开运行窗口(或开始开始菜单,直接在搜索程序或文件框中)直接输入cmd命令

(win7 cmd切换目录不了的可参考win7系统cmd命令切换到指定文件夹目录

2、cd 命令直接更改目录到platform-tools目录,比如我下载的SDK文件存放的目录为:

G:\Feitianxinhong\Android\adt-bundle-windows-x86\sdk\platform-tools

3、输入adb shell命令

4、输入pm setInstallLocation 0 命令

Android解决:INSTALL_FAILED_MEDIA_UNAVAILABLE - 飞天心宏 - 飞天心宏的博客

 

完了以后,再运行程序,测试正常。

测试结果如下图所示:

Android解决:INSTALL_FAILED_MEDIA_UNAVAILABLE - 飞天心宏 - 飞天心宏的博客

 

 

Android上使用OpenCV处理图像

mikel阅读(936)

        本文会介绍在Android项目中使用OpenCV的一种方法,并会给出两个demo。

文中涉及开发环境在《Windows下Android开发环境的配置》的基础之上进行搭建。


http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.4.8/下载OpenCV-2.4.8-Android-sdk.zip。假定解压到目录D:\workspace\opencv-android-sdk。

打开Eclipse,点击 File->Import->General->Existing Projects into Workspace,找到D:\workspace\opencv-android-sdk,选择导入OpenCV Library,也可以再导入一两个sample,但最好不要全部导入,因为如果设置了自动编译,会降低每次打开Eclipse的速度。

导入完成后,右击OpenCV Library,点击Properties->Android,选择合适的Build Project Target,即Android的API版本。

可以尝试运行一下OpenCV sample项目,检查一下设置是否正确。

仿照文章《Windows下Android开发环境的配置》的步骤创建一个新的Android项目。假定包名为net.johnhany.grayprocessjni。所需添加的文件及代码如下所示:

GrayProcess.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package net.johnhany.grayprocessjni;
import org.opencv.android.BaseLoaderCallback; 
import org.opencv.android.LoaderCallbackInterface; 
import org.opencv.android.OpenCVLoader;
import android.os.Bundle;
import android.app.Activity; 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.Config;       
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView; 
  
public class GrayProcess extends Activity implements OnClickListener{ 
      
    private Button btnProc; 
    private ImageView imageView; 
    private Bitmap bmp; 
      
     private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) { 
        @Override
        public void onManagerConnected(int status) { 
            switch (status) { 
                case LoaderCallbackInterface.SUCCESS:{ 
                    System.loadLibrary("image_proc"); 
                } break
                default:{ 
                    super.onManagerConnected(status); 
                } break
            
        
    }; 
      
    @Override
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_gray_process); 
        btnProc = (Button) findViewById(R.id.btn_gray_process); 
        imageView = (ImageView) findViewById(R.id.image_view); 
         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.testpic1); 
        imageView.setImageBitmap(bmp); 
        btnProc.setOnClickListener(this); 
    
  
    @Override
    public void onClick(View v) { 
           
        int w = bmp.getWidth(); 
        int h = bmp.getHeight(); 
        int[] pixels = new int[w*h];      
        bmp.getPixels(pixels, 0, w, 0, 0, w, h); 
        int[] resultInt = ImageProc.grayProc(pixels, w, h); 
        Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888); 
        resultImg.setPixels(resultInt, 0, w, 0, 0, w, h); 
        imageView.setImageBitmap(resultImg);     
    
      
    @Override
    public void onResume(){ 
        super.onResume(); 
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_8, this, mLoaderCallback); 
    

ImageProcess.java

1
2
3
4
5
package net.johnhany.grayprocessjni; 
  
public class ImageProc { 
    public static native int[] grayProc(int[] pixels, int w, int h); 
}

activity_gray_process.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:opencv="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
      
    <Button
        android:id="@+id/btn_gray_process"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/str_proc"/> 
      
    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/str_proc"/> 
  
</LinearLayout>

res\values\strings.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">GrayProcessJNI</string>
    <string name="action_settings">Settings</string>
    <string name="str_proc">gray process</string
    <string name="str_desc">image description</string
</resources>

把一张图片拷贝到tes\drawable-hdpi,假设图片名称为testpic1。

testpic1
image-526

在项目目录里新建一个名为“jni”的文件夹,里面添加如下3个文件:

Android.mk

1
2
3
4
5
6
7
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include D:\workspace\opencv-android-sdk\sdk\native\jni\OpenCV.mk
LOCAL_SRC_FILES  := ImageProc.cpp
LOCAL_MODULE     := image_proc
include $(BUILD_SHARED_LIBRARY)

这个文件负责把cpp或c文件编译成可以被Android程序调用的.so库.

Application.mk

1
2
3
4
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-15

ImageProc.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <ImageProc.h>
#include <opencv2/core/core.hpp>
#include <string>
#include <vector>
using namespace cv;
using namespace std;
JNIEXPORT jintArray JNICALL Java_net_johnhany_grayprocessjni_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){
    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, false);
    if(cbuf == NULL){
        return 0;
    }
    Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
    uchar* ptr = imgData.ptr(0);
    for(int i = 0; i < w*h; i ++){
        int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
        ptr[4*i+1] = grayScale;
        ptr[4*i+2] = grayScale;
        ptr[4*i+0] = grayScale;
    }
    int size=w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, cbuf);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}

这个cpp文件负责调用OpenCV函数进行图像处理。

项目结构大致如图:

jni-00
image-527

打开cmd.exe,输入:

1
2
3
cd D:\workspace\GrayProcessJni\bin\classes
D:
javah net.johnhany.grayprocessjni.ImageProc

此时classes文件夹内会多出一个net_johnhany_grayprocessjni_ImageProc.h文件,把它拷贝到jni文件夹内,并把名字改为ImageProc.h。

h-file
image-528

在Eclipse内点击Window->Preferences->C/C++->Build->Environment,增加一个环境变量:

变量名
NDKROOT D:\android-ndk

ndkroot
image-529

右击项目名称,点击New->Other->C/C++->Convert to a C/C++ Project (Adds C/C++ Nature)。

jni-01
image-530

选择C++ Project,工具链选择Makefile的Other Toolchain(由于我的项目已经转换过,所以没有显示在项目名称列表里)。

jni-02
image-531

转换完成后会提示是否切换到C/C++视图,选择不切换。

此时在代码文件内会有大量的错误,这是由于还没有包含进所需的C++库文件。

右击项目名称,点击Refresh。

再右击项目名称,点击Properties->Android,选择合适的Android API版本,点击Library中的Add,选择OpenCV Library。

jni-03
image-532

点击C/C++ Build,把Builder Settings中的Build Command改为:

${NDKROOT}/ndk-build.cmd

jni-04
image-533

Behaviour的设置如图:

jni-05
image-534

点击C/C++ General->Paths and Symbols->Includes,在GNU C++中添加如下路径:

${NDKROOT}/platforms/android-15/arch-arm/usr/include

${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include

${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include

${ProjDirPath}/../OpenCV-2.4.8-android-sdk/sdk/native/jni/include

jni-06
image-535

这里的路径最好用NDKROOT或ProjDirPath的相对路径表示,如果用带有盘符的绝对路径可能会由于编译工具和Windows系统的表示方式不同而造成无法编译(如Cygwin)。


还要在安卓设备上安装OpenCV Manager。如果使用的是模拟器,打开cmd.exe,输入:

1
2
cd D:\android-sdk\platform-tools
adb install D:\workspace\opencv-android-sdk\apk\OpenCV_2.4.8_Manager_2.16_armv7a-neon.apk

如果使用的是手机,把D:\workspace\opencv-android-sdk\apk目录下的OpenCV_2.4.8_Manager_2.16_armv7a-neon.apk拷贝到手机中,在手机中手动安装。


点击Run,会自动完成编译、打开模拟器、安装、启动运行等步骤;或者在编译之后把bin文件夹内的apk文件拷贝到手机里,手动安装,运行。

运行效果如下,点击按钮,图片变成灰色:

gray-process-1
image-536

gray-process-2
image-537

这里有另一个使用OpenCV的例子,支持的图像处理方法更多,可以切换图像,还可以用手指选择要作处理的区域:

https://github.com/johnhany/AndroidProj/tree/master/ImageProcess

OpenCV for Android入门

mikel阅读(1142)

        在上一篇转载的文章(http://blog.csdn.net/liudekuan/article/details/8569687)中,已经对OpenCV在Android环境的搭建进行了比较详细的说明,但文中所用版本为OpenCV2.3.1,与目前最新版OpenCV-2.4.3.2-Android-sdk稍有差异。本文将在新版基础上进行OpenCV4Android入门级说明。

 

1.环境搭建

进行android开发所需要的环境一般为:eclipse + android sdk + ADT,而OpenCV的开发由于需要编写本地代码(C/C++),因此还需要安装以下工具:NDK,Cygwin,CDT。网上都有大量详细的安装讲 解,本文只描述下其中关键步骤。

1.1 NDK的安装

(1) NDK下载后解压到固定目录即可,无需安装。本文解压到D盘根目录下,其路径为:D:\android-ndk-r8d;

(2) 添加环境变量,将其安装路径添加到系统path变量中,并添加系统变量NDKROOT:D:\android-ndk-r8d。

1.2 Cygwin的安装

(1) 安装包当然可以选择全部,只是如此以来则比较耗时。你也可以只安装开发NDK用得着的包:autoconf2.1、automake1.10、 binutils、gcc-core、gcc-g++、gcc4-core、gcc4-g++、gdb、pcre、pcre-devel、gawk、 make;

(2) 将安装路径添加系统变量path中;

(3) 为了方便的在命令行下调用Android NDK,找到”C:\cygwin\home\(你的用户名)”这个目录,打开文件”.bash_profile”,在文件的最下面加上下面两行内容:

NDK=/cygdrive/f/android-ndk-r6b-windows/android-ndk-r6b

export NDK

1.3 CDT的安装

打开http://www.eclipse.org/cdt/downloads.php,找到对应的repository地址,注意这个地址对应的Eclipse版本要与第二步中你下载的版本一致。接着,打开Eclipse软件Help->Install New Software菜单安装即可。

 

2.OpenCV4Android

2.1 下载

进入官网(http://opencv.org/)下载OpenCV4Android并解压,其目录结构如下:

图1 OpenCV-2.4.3.2-android-sdk目录结构

        其中,sdk目录即是我们开发opencv所需要的类库;samples目录中存放着若干opencv应用示例(包括人脸检测等),可为我们进行 android下的opencv开发提供参考;doc目录为opencv类库的使用说明及api文档等;而apk目录则存放着对应于各内核版本的 OpenCV_2.4.3.2_Manager_2.4应用安装包。此应用用来管理手机设备中的opencv类库,在运行opencv应用之前,必须确保 手机中已经安装了OpenCV_2.4.3.2_Manager_2.4_*.apk,否则opencv应用将会因为无法加载opencv类库而无法运 行。

2.2 将SDK引入工作空间

(1) 选择一个路径,新建文件夹做为工作空间(我在E盘根目录下新建workspace目录来做为工作空间);

(2) 将OpenCV-2.4.3.2-android-sdk中的sdk目录copy至工作空间,并将其更名为OpenCV-SDK(是否更改名称无所谓,这是我个人习惯而已);

(3) 以新建的目录为工作空间,打开eclipse;

(4) 将OpenCV-SDK引入到工作空间中,如下图所示:

图2

图 3

图4

 

图5

3 开发实例

在经过上述的环境配置之后,就可以进行opencv开发了。如http://blog.csdn.net/liudekuan/article/details/8569687所述,android中opencv的开发有两种方式:直接调用opencv中的java api;利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库。本文分别利用这两种方式实现图像的灰度处理操作。

3.1 工程一:通过调用OpenCV提供的java api实现灰度处理

3.1.1 创建工程

(1) 打开eclipse,创建android应用工程GrayProcess;

(2) 将测试图像lena.jpg添加到资源目录res/drawable-hdpi中;

(3) 在Package Explorer中选择项目GrayProcess,单击右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择 Android,然后点击右下方的Add按钮,选择OpenCV Library 2.4.3并点击OK,操作完成后,会将OpenCV类库添加到GrayProcess的Android Dependencies中,如下图所示:

图 6

图7

图8

3.1.2 工程代码

(1) 字符串资源文件:strings.xml

  1. <resources>
  2.     <string name=“app_name”>GrayProcess</string>
  3.     <string name=“hello_world”>Hello world!</string>
  4.     <string name=“menu_settings”>Settings</string>
  5.     <string name=“title_activity_main”>MainActivity</string>
  6.     <string name=“str_proc”>gray process</string>
  7.     <string name=“str_desc”>image description</string>
  8. </resources>

(2) 布局文件:main.xml

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=“http://schemas.android.com/tools”
  3.     android:orientation=“vertical”
  4.     android:layout_width=“match_parent”
  5.     android:layout_height=“match_parent” >
  6.     <Button
  7.         android:id=“@+id/btn_gray_process”
  8.         android:layout_width=“fill_parent”
  9.         android:layout_height=“wrap_content”
  10.         android:text=“@string/str_proc”/>
  11.     <ImageView
  12.         android:id=“@+id/image_view”
  13.         android:layout_width=“wrap_content”
  14.         android:layout_height=“wrap_content”
  15.         android:contentDescription=“@string/str_proc”/>
  16. </LinearLayout>

(3) MainActivity.java

  1. package com.iron.grayprocess;
  2. import org.opencv.android.BaseLoaderCallback;
  3. import org.opencv.android.LoaderCallbackInterface;
  4. import org.opencv.android.OpenCVLoader;
  5. import org.opencv.android.Utils;
  6. import org.opencv.core.Mat;
  7. import org.opencv.imgproc.Imgproc;
  8. import android.os.Bundle;
  9. import android.app.Activity;
  10. import android.graphics.Bitmap;
  11. import android.graphics.BitmapFactory;
  12. import android.graphics.Bitmap.Config;
  13. import android.view.View;
  14. import android.view.View.OnClickListener;
  15. import android.widget.Button;
  16. import android.widget.ImageView;
  17. public class MainActivity extends Activity implements OnClickListener{
  18.     private Button btnProc;
  19.     private ImageView imageView;
  20.     private Bitmap bmp;
  21.     //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
  22.     private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
  23.         @Override
  24.         public void onManagerConnected(int status) {
  25.             switch (status) {
  26.                 case LoaderCallbackInterface.SUCCESS:{
  27.                 } break;
  28.                 default:{
  29.                     super.onManagerConnected(status);
  30.                 } break;
  31.             }
  32.         }
  33.     };
  34.     @Override
  35.     public void onCreate(Bundle savedInstanceState) {
  36.         super.onCreate(savedInstanceState);
  37.         setContentView(R.layout.main);
  38.         btnProc = (Button) findViewById(R.id.btn_gray_process);
  39.         imageView = (ImageView) findViewById(R.id.image_view);
  40.         //将lena图像加载程序中并进行显示
  41.         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
  42.         imageView.setImageBitmap(bmp);
  43.         btnProc.setOnClickListener(this);
  44.     }
  45.     @Override
  46.     public void onClick(View v) {
  47.         Mat rgbMat = new Mat();
  48.         Mat grayMat = new Mat();
  49.         //获取lena彩色图像所对应的像素数据
  50.         Utils.bitmapToMat(bmp, rgbMat);
  51.         //将彩色图像数据转换为灰度图像数据并存储到grayMat中
  52.         Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);
  53.         //创建一个灰度图像
  54.         Bitmap grayBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.RGB_565);
  55.         //将矩阵grayMat转换为灰度图像
  56.         Utils.matToBitmap(grayMat, grayBmp);
  57.         imageView.setImageBitmap(grayBmp);
  58.     }
  59.     @Override
  60.     public void onResume(){
  61.         super.onResume();
  62.         //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
  63.         //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
  64.         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
  65.     }
  66. }

 

3.1.3 运行结果

       

图9

3.2 工程二:利用JNI编写C++ OpenCV代码实现灰度处理

3.2.1 创建工程

        步骤如工程一,创建新工程GrayProcess2,将lena.jpg添加到资源文件,并按3.1.1所示将opencv类库添加到工程中。

3.2.2 编写上层代码(java)

        (1) res/values/strings.xml

  1. <resources>
  2.     <string name=“app_name”>GrayProcess2</string>
  3.     <string name=“hello_world”>Hello world!</string>
  4.     <string name=“menu_settings”>Settings</string>
  5.     <string name=“title_activity_main”>GrayProcess2</string>
  6.     <string name=“str_proc”>gray process</string>
  7.     <string name=“str_desc”>image description</string>
  8. </resources>

        (2) res/layout/main.xml

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=“http://schemas.android.com/tools”
  3.     android:orientation=“vertical”
  4.     android:layout_width=“match_parent”
  5.     android:layout_height=“match_parent” >
  6.     <Button
  7.         android:id=“@+id/btn_gray_process”
  8.         android:layout_width=“fill_parent”
  9.         android:layout_height=“wrap_content”
  10.         android:text=“@string/str_proc”/>
  11.     <ImageView
  12.         android:id=“@+id/image_view”
  13.         android:layout_width=“wrap_content”
  14.         android:layout_height=“wrap_content”
  15.         android:contentDescription=“@string/str_proc”/>
  16. </LinearLayout>

        (3)MainActivity.java

  1. package com.iron.grayprocess2;
  2. import org.opencv.android.BaseLoaderCallback;
  3. import org.opencv.android.LoaderCallbackInterface;
  4. import org.opencv.android.OpenCVLoader;
  5. import android.os.Bundle;
  6. import android.app.Activity;
  7. import android.graphics.Bitmap;
  8. import android.graphics.BitmapFactory;
  9. import android.graphics.Bitmap.Config;
  10. import android.view.View;
  11. import android.view.View.OnClickListener;
  12. import android.widget.Button;
  13. import android.widget.ImageView;
  14. public class MainActivity extends Activity implements OnClickListener{
  15.     private Button btnProc;
  16.     private ImageView imageView;
  17.     private Bitmap bmp;
  18.     //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
  19.      private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
  20.         @Override
  21.         public void onManagerConnected(int status) {
  22.             switch (status) {
  23.                 case LoaderCallbackInterface.SUCCESS:{
  24.                     System.loadLibrary(“image_proc”);
  25.                 } break;
  26.                 default:{
  27.                     super.onManagerConnected(status);
  28.                 } break;
  29.             }
  30.         }
  31.     };
  32.     @Override
  33.     public void onCreate(Bundle savedInstanceState) {
  34.         super.onCreate(savedInstanceState);
  35.         setContentView(R.layout.main);
  36.         btnProc = (Button) findViewById(R.id.btn_gray_process);
  37.         imageView = (ImageView) findViewById(R.id.image_view);
  38.         //将lena图像加载程序中并进行显示
  39.          bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
  40.         imageView.setImageBitmap(bmp);
  41.         btnProc.setOnClickListener(this);
  42.     }
  43.     @Override
  44.     public void onClick(View v) {
  45.         int w = bmp.getWidth();
  46.         int h = bmp.getHeight();
  47.         int[] pixels = new int[w*h];
  48.         bmp.getPixels(pixels, 0, w, 0, 0, w, h);
  49.         int[] resultInt = ImageProc.grayProc(pixels, w, h);
  50.         Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
  51.         resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
  52.         imageView.setImageBitmap(resultImg);
  53.     }
  54.     @Override
  55.     public void onResume(){
  56.         super.onResume();
  57.         //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
  58.         //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
  59.         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
  60.     }
  61. }

        代码第28行:System.loadLibrary(“image_proc”)用来当OpenCV类库初始化完成后加载类库image_proc。此类库由我们来生成,用于完成图像灰度处理的操作,此部分将在3.2.3中说明。

         (4) ImageProc.java

  1. package com.iron.grayprocess2;
  2. public class ImageProc {
  3.     public static native int[] grayProc(int[] pixels, int w, int h);
  4. }

ImageProc.java中只定义了方法grayProc,关键字native表明,此方法的实现由本地代码(C/C++)来完成。

3.2.3 编写JNI及C相关代码

        在项目中新建目录jni,在jni目录中分别添加文件Android.mk,Application.mk,ImageProc.h,ImageProc.cpp,这四个文件的内容分别如下所示。

        (1) Android.mk

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. include ../OpenCV-SDK/native/jni/OpenCV.mk
  4. LOCAL_SRC_FILES  := ImageProc.cpp
  5. LOCAL_MODULE     := image_proc
  6. include $(BUILD_SHARED_LIBRARY)

代码说明:

第一行:指明当前编译路径;

第二行:清空变量;

第三行:将OpenCV类库中的编译文件包含进来,如此一来在我们的工程中即可使用OpenCV类库;

第四行:指定需要编译的C++源文件;

第五行:指定编译生成的类库名称;

第六行:调用命令将源文件编译为静态库。

注:第三行指定的路径很关键,当opencv类库与工程路径相关位置发生改变时,此路径也要随之改变。

        (2) Application.mk(配置文件)

  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti -fexceptions
  3. APP_ABI := armeabi-v7a
  4. APP_PLATFORM := android-8

        (3) ImageProc.h

  1. #include <jni.h>
  2. extern “C” {
  3. JNIEXPORT jintArray JNICALL Java_com_iron_grayprocess2_ImageProc_grayProc
  4.   (JNIEnv *, jclass, jintArray, jint, jint);
  5. }

(4) ImageProc.cpp

  1. #include <ImageProc.h>
  2. #include <opencv2/core/core.hpp>
  3. #include <string>
  4. #include <vector>
  5. using namespace cv;
  6. using namespace std;
  7. JNIEXPORT jintArray JNICALL Java_com_iron_grayprocess2_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){
  8.     jint *cbuf;
  9.     cbuf = env->GetIntArrayElements(buf, false);
  10.     if(cbuf == NULL){
  11.         return 0;
  12.     }
  13.     Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
  14.     uchar* ptr = imgData.ptr(0);
  15.     for(int i = 0; i < w*h; i ++){
  16.         //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
  17.         //对于一个int四字节,其彩色值存储方式为:BGRA
  18.         int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
  19.         ptr[4*i+1] = grayScale;
  20.         ptr[4*i+2] = grayScale;
  21.         ptr[4*i+0] = grayScale;
  22.     }
  23.     int size=w * h;
  24.     jintArray result = env->NewIntArray(size);
  25.     env->SetIntArrayRegion(result, 0, size, cbuf);
  26.     env->ReleaseIntArrayElements(buf, cbuf, 0);
  27.     return result;
  28. }

说明:

  •         ImageProc.h头文件可以通过jdk提供的工具javah来生成,具体使用说明请参考相关文档,本文为演示自己编写了此文件;
  •         JNI中的定义的函数遵循一定的命名规则:Java_包名_类名_方法名,具体参考JNI编程相关知识;
  •         ImageProc.cpp中利用彩色值转换为灰度值的计算公式,将lena.jpg图像的每个像素转换为灰度值,并返回给应用层。需要注意的是对于ARGB_8888类型的图像而言,其每一个像素值在int型数据中的存储序列为BGRA。

3.2.4 运行

        由于程序中涉及到了JNI编程,因此需要用cygwin对其中的C/C++代码进行编译。打开cygwin,进入到工程的根目录中执行命令:$NDK /ndk-build完成相关编译;之后在eclipse中刷新工程GrayProcess2,运行即可。编译及运行结果分别如下所示。

图10 使用cygwin对C代码进行编译

 

图11 程序运行结果图对比

版权声明:本文为博主原创文章,未经博主允许不得转载。

OpenCV-2.4.6-android-sdk 人脸识别demo搭建

mikel阅读(659)

最近项目需要研究下人脸识别,在领导推荐下准备研究OpenCV

一,上官网了解下 基本知识

http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/android_dev_intro.html#android-dev-intro

 

到此、如果你已经安装了jdk/ndk/sdk/adt/cdt并且配置了 windows的环境变量Path最后面添加ndk的根目录,请接着看。

验证:cmd命令行收入 ndk-build有明确的提示。

二,下载最新的opencv-Androidhttp://opencv.org/

 

三,下载完成如下图

解压到和你的sdk同一个目录:

如:我的sdk目录放在D盘如下图所示。

 

四,导入opencv的lib项目库

注意:不要导入自己的工作目录、等下使用ndk编译的时候会牵扯到一个路径的问题

 

五,导入face-detection的代码 File->new Project->other->Android project from Existing code

 

六,修改jni/Android.mk的路径 ../../sdk/native/jni/OpenCV.mk 为../../../sdk/native/jni/OpenCV.mk

七,编译jni的lib文件

回到eclipse刷新下项目会发现多了一个libs的文件,这里就是上面的ndk-build编译生成的so文件。

八,右键->run Application->Android

手机运行效果如下图:期间会不断的闪烁蓝色的方框进行人脸的捕捉!

 

ok,至此 人脸识别的demo就运行起来的,下面还需要进一步研究!

其他的人脸识别技术:http://blog.jobbole.com/45936/

树莓派(raspberry)启用root账户

mikel阅读(830)

树莓派(raspberry)启用root账户

今天玩树莓派,需要安装些东西,老是sudo感觉不方便于是想直接用root。咦···?默认不是空密码?试下密码raspberry。还不对。那是神马??

论他找了下,原来之前用centos习惯了,错觉,错觉!!

 

树莓派使用的linux是debian系统,所以树莓派启用root和debian是相同的。

debian里root账户默认没有密码,但账户锁定。

当需要root权限时,由默认账户经由sudo执行,Raspberry pi 系统中的Raspbian

默认用户是pi 密码为raspberry

重新开启root账号,可由pi用户登录后,在命令行下执行

sudo passwd root

执行此命令后系统会提示输入两遍的root密码,输入你想设的密码即可,然后在执行

sudo passwd --unlock root

这样就可以解锁root账户了。

好了,搞定!

[转载]Android-opencv之CVCamera | 增强视觉 | 计算机视觉 增强现实

mikel阅读(771)

hellogv再次发力,献出opencv2.2Android教程 全文照抄如下。 Android-opencv是opencv在android手机上的移植版,而CVCamera是这个移植版的一个sample。本文主要介绍android-opencv的安装和使用。 android-openc

来源: [转载]Android-opencv之CVCamera | 增强视觉 | 计算机视觉 增强现实

hellogv再次发力,献出opencv2.2android教程

全文照抄如下。

android-opencv是opencv在android手机上的移植版,而CVCamera是这个移植版的一个sample。本文主要介绍android-opencv的安装和使用。

opencv基于C++,因此android-opencv也必须依赖NDK(android-ndk-r4-crystax)来编译。PS:关于android-ndk-r4-crystax和CYGWIN的安装和使用,本文不再唠叨,详见http://blog.csdn.net/hellogv/archive/2010/12/23/6094127.aspx

安装步骤具体如下:

  1. 确保在系统Path中包含了D:\cygwin\bin;D:\cygwin\android-ndk-r4-crystax;(存放目录自己决定,Path中必须包含cygwin的bin和android-ndk-r4-crystax的路径)
  2. 再拷贝android-ndk-r4-crystax到\cygwin\home\GV\android-ndk-r4-crystax,编译android-opencv时需要,编译成功之后可以删除这份拷贝。
  3. 运行cygwin,来到opencv目录下,输入sh build.sh进行编译,编译成功的话会在\opencv\android\libs\生成armeabi和armeabi-v7a两个文件夹,里面都包含libandroid-opencv.so。

PS:编译的时候提示缺少文件的话,从网上搜索下载。

  • android-opencv的使用
    1:打开eclipse ,Import Opencv这个工程,工程位于\opencv\android\ 。PS:如果也提示缺少文件,也需要从网上搜索下载
    2:Opencv 这个工程编译通过之后,就可以Export它,选择JAVA的JAR file,导出时去掉[obj]和[libs]这两个文件夹,AndroidManifest.xml和default.properties,输出文件 名为Opencv.jar,Export
    设置如下图
    3:下载swigwin-1.3.39,在系统path中加入D:\cygwin\swigwin-1.3.39(存放位置自定),重启
    4:打开cygwin,去到\samples\CVCamera\,输入sh build.sh,开始编译CVCamera的JNI,成功编译之后会生成libcvcamera.so
    5:把 \opencv\android\libs 复制到\samples\CVCamera\,因为CVCamera同时需要libandroid-opencv.so和libcvcamera.so
    6:Import CVCamera这个工程,加入Opencv.jar 这个Libraries
    7:编译CVCamera这个工程,生成CVCamera.apk(有6.06MB大小)