前篇:
好早就装了开发环境,真正着手还是这两天,非常的生疏,虽然有SDK文档,那么多蚊子一般的字,实在没心思慢慢研究。这不想调用摄像头,原以为很容易就能搞定的,累计花了大概有一天的时间才只能保证不出错……至于效果嘛,难说啊!
先看API-examples里有调用 摄像头的例子,在模拟器上虽然看不出什么效果,毕竟还是能执行的,就是一个方块在黑白相间的背景上移动呗。
就这么一个Google提供的范例,传到我的HTC G2上也能一执行就报错,我对Google的尊敬之情顿时减少了0.0001%啊……(当然有可能是G2不够标准,但毕竟其他的软件都是能用的,看来是有不少健壮代码了啊)。联机调试看了一下,出错的这一行(android-7里的):
parameters.setPreviewSize(w, h);
查一下,摄像头不是所有随便的(w, h)都能够认识的,所以呢,我们有了下面这样的增强版:
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
|
List<Size>
mSupportedPreviewSizes;
mSupportedPreviewSizes
= mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize
= getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
private
Size getOptimalPreviewSize(List<Size> sizes, int
w, int
h) {
final
double
ASPECT_TOLERANCE = 0.1 ;
double
targetRatio = ( double )
w / h;
if
(sizes == null )
return
null ;
Size
optimalSize = null ;
double
minDiff = Double.MAX_VALUE;
int
targetHeight = h;
for
(Size size : sizes) {
double
ratio = ( double )
size.width / size.height;
if
(Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue ;
if
(Math.abs(size.height - targetHeight) < minDiff) {
optimalSize
= size;
minDiff
= Math.abs(size.height - targetHeight);
}
}
if
(optimalSize == null )
{
minDiff
= Double.MAX_VALUE;
for
(Size size : sizes) {
if
(Math.abs(size.height - targetHeight) < minDiff) {
optimalSize
= size;
minDiff
= Math.abs(size.height - targetHeight);
}
}
}
return
optimalSize;
}
|
后来的Sample里有了这段代码,看起来强大了不少。然而非常不幸的,首先getSupportedPreviewSizes()这个函数在2.1之后才有,我一开始是打算用1.6开发的……好吧我改,这个先不说,自己的手机已经刷到2.1了,这个函数的返回值居然是null?!如果确实想老版本上也用的话,怎么办??
有鉴于有软件可以达成,所以肯定是有方法的!得这么写:
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
|
public
class
SupportedSizesReflect {
private
static
Method Parameters_getSupportedPreviewSizes = null ;
private
static
Method Parameters_getSupportedPictureSizes = null ;
static
{
initCompatibility();
};
private
static
void
initCompatibility() {
try
{
Parameters_getSupportedPreviewSizes
= Camera.Parameters. class .getMethod(
"getSupportedPreviewSizes" ,
new
Class[] {});
Parameters_getSupportedPictureSizes
= Camera.Parameters. class .getMethod(
"getSupportedPictureSizes" ,
new
Class[] {});
}
catch
(NoSuchMethodException nsme) {
nsme.printStackTrace();
Parameters_getSupportedPreviewSizes
= Parameters_getSupportedPictureSizes = null ;
}
}
/**
*
Android 2.1之后有效
*
@param p
*
@return Android1.x返回null
*/
public
static
List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
return
getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
}
public
static
List<Size> getSupportedPictureSizes(Camera.Parameters p){
return
getSupportedSizes(p, Parameters_getSupportedPictureSizes);
}
@SuppressWarnings ( "unchecked" )
private
static
List<Size> getSupportedSizes(Camera.Parameters p, Method method){
try
{
if
(method != null )
{
return
(List<Size>) method.invoke(p);
}
else
{
return
null ;
}
}
catch
(InvocationTargetException ite) {
Throwable
cause = ite.getCause();
if
(cause instanceof
RuntimeException) {
throw
(RuntimeException) cause;
}
else
if
(cause instanceof
Error) {
throw
(Error) cause;
}
else
{
throw
new
RuntimeException(ite);
}
}
catch
(IllegalAccessException ie) {
return
null ;
}
}
}
|
啊啊~,リフレクションなんか、大嫌い……然后还要用类似这样的方法调用~
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
|
@Override
public
void
surfaceChanged(SurfaceHolder holder, int
format, int
width, int
height) {
Camera.Parameters
params = camera.getParameters();
List<Size>
supportedPictureSizes
=
SupportedSizesReflect.getSupportedPictureSizes(params);
List<Size>
supportedPreviewSizes
=
SupportedSizesReflect.getSupportedPreviewSizes(params);
if
( supportedPictureSizes != null
&&
supportedPreviewSizes
!= null
&&
supportedPictureSizes.size()
> 0
&&
supportedPreviewSizes.size()
> 0 )
{
pictureSize
= supportedPictureSizes.get( 0 );
int
maxSize = 1280 ;
if (maxSize
> 0 ){
for (Size
size : supportedPictureSizes){
if (maxSize
>= Math.max(size.width,size.height)){
pictureSize
= size;
break ;
}
}
}
WindowManager
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display
display = windowManager.getDefaultDisplay();
DisplayMetrics
displayMetrics = new
DisplayMetrics();
display.getMetrics(displayMetrics);
previewSize
= getOptimalPreviewSize(
supportedPreviewSizes,
display.getWidth(),
display.getHeight());
params.setPictureSize(pictureSize.width,
pictureSize.height);
params.setPreviewSize(previewSize.width,
previewSize.height);
}
this .camera.setParameters(params);
try
{
this .camera.setPreviewDisplay(holder);
}
catch
(IOException e) {
e.printStackTrace();
}
this .camera.startPreview();
}
|
死机无数次之后总结出来的啊,发现程序写的一个不好强制结束了,摄像头都无法再次启用了,kill都不行,只能重新启动手机才好。重启一次还那么慢,谁知道有比较适合G2的row?
哦还有一个,预览画面90°的,2.X后可以用parameters.set(“rotation”, “90″),之前的话得写成parameters.set(“orientation”, “portrait”)。但是据说不是所有的机器都可以的……
续篇:
上次讲的是摄像头的初始化,如果觉得这么就万事OK的话,那就大错特错了。接下来的东西让人感到更加头痛。
在我的这个应用里,不需要把拍下来的图片存储,只需要把预览的图片数据处理一下就好,很自然的我只是用了onPreviewFrame调用,考虑处理传递进来的data数据流就是了。
网上很多帖子都说,然后用BitmapFactory的decodeByteArray()函数来解析图片就行了,我试了一下,发现这真是彻头彻尾的谎言,data字节流默认是YCbCr_420_SP(虽然可以改,但其他的格式未必兼容),decodeByteArray()压根儿不认!SDK2.2之后,似乎提供了一个YuvImage的类来转一下(那Google一开始提供这个借口是做什么的?),难道就要把老机给抛弃了么??万万不能啊(穷人最理解穷人们了)!
好在这个世界总是不缺少好人和牛人的,有人提供了这么一段转换的代码:
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
|
static
public
void
decodeYUV420SP( int []
rgb, byte []
yuv420sp, int
width, int
height) {
final
int
frameSize = width * height;
for
( int
j = 0 ,
yp = 0 ;
j < height; j++) {
int
uvp = frameSize + (j >> 1 )
* width, u = 0 ,
v = 0 ;
for
( int
i = 0 ;
i < width; i++, yp++) {
int
y = ( 0xff
& (( int )
yuv420sp[yp])) - 16 ;
if
(y < 0 )
y = 0 ;
if
((i & 1 )
== 0 )
{
v
= ( 0xff
& yuv420sp[uvp++]) - 128 ;
u
= ( 0xff
& yuv420sp[uvp++]) - 128 ;
}
int
y1192 = 1192
* y;
int
r = (y1192 + 1634
* v);
int
g = (y1192 - 833
* v - 400
* u);
int
b = (y1192 + 2066
* u);
if
(r < 0 )
r = 0 ;
else
if
(r > 262143 )
r = 262143 ;
if
(g < 0 )
g = 0 ;
else
if
(g > 262143 )
g = 262143 ;
if
(b < 0 )
b = 0 ;
else
if
(b > 262143 )
b = 262143 ;
rgb[yp]
= 0xff000000
| ((r << 6 )
& 0xff0000 )
| ((g >> 2 )
& 0xff00 )
| ((b >> 10 )
& 0xff );
}
}
}
|
我不是很清楚这里面的原理,但是它能在我这里工作,暂时可以了……然后你才可以吧处理完的rgb[]传给decodeByteArray()。
顺便好心的把使用SDK2.2之后的也贴上吧,万一有用呢……
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
void
onPreviewFrame( byte []
data, Camera arg1) {
FileOutputStream
outStream = null ;
try
{
YuvImage
yuvimage = new
YuvImage(data,ImageFormat.NV21,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height, null );
ByteArrayOutputStream
baos = new
ByteArrayOutputStream();
yuvimage.compressToJpeg( new
Rect( 0 , 0 ,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height),
80 ,
baos);
outStream
= new
FileOutputStream(String.format( "/sdcard/%d.jpg" ,
System.currentTimeMillis()));
outStream.write(baos.toByteArray());
outStream.close();
Log.d(TAG,
"onPreviewFrame
- wrote bytes: "
+ data.length);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
}
Preview. this .invalidate();
}
|
哦,得到的图像旋转了90°(似乎有的机型设置一下setRotation(90)可以搞定,但还是那句话,不通用啊,况且这个是2.1之后的API)。手动转一下吧……
1
2
3
4
5
6
|
Matrix
matrix = new
Matrix();
matrix.postRotate( 90 );
Bitmap
bmp = Bitmap.createBitmap(rgb, 0 ,
w, w, h, Bitmap.Config.ARGB_4444);
Bitmap
nbmp = Bitmap.createBitmap(bmp,
0 ,
0 ,
bmp.getWidth(), bmp.getHeight(), matrix, true );
|
终于正常了~~~
考虑到需要做识别,自然得先把它转成灰度图像,经典心理公式Gray = R*0.299 + G*0.587 + B*0.114出场了,但是手机的计算速度不那么快,这样的浮点运算还是尽量避免吧~ 于是考虑Gray = (R*299 + G*587 + B*114 + 500) / 1000或者Gray = (R*30 + G*59 + B*11 + 50) / 100。但是除法总是还是不够快,用移位吧……Gray
= (R*19595 + G*38469 + B*7472) >> 16,稍微小一点,用Gray = (R*38 + G*75 + B*15) >> 7也足够了。
经过一番努力学习,把写就的代码兴致勃勃的在手机上跑了一下,虽然不够快结果出来了,想想也是大负荷运算啊,自我安慰客户应该可以有这样的耐心吧。
就在这个时候,我突然想起一件很重要的事情!
我需要的是灰度图,也就是亮度风量,而最开始的YUV,不就是亮度色度饱和度么?!那么Y分类不就是我需要的灰度值吗!!我在做什么,辛辛苦苦转成RGB,再转成亮度,吃饱了撑着不是。想到这里我立刻用头撞墙九九一百八十一次,一悼念我那白白死去的脑细胞的在天之灵。立刻重写,删除大量代码,快多了,效果也好~~ 鄙视一下两小时前的自己!
另外,今天去看变形金刚3了,还不错吧,人好多,坐前排脖子都抬酸了……
转自:http://eyehere.net/2011/android-camera-2/
分享到:
相关推荐
在光缆巡检终端设计中,Android系统提供了一个稳定的平台,支持丰富的硬件接口,如GPS、摄像头、传感器等,便于集成巡检所需的各种功能。 二、客户端应用开发 在Android平台上开发光缆巡检应用,主要涉及以下几个...
以下是对该系统设计与实现的关键知识点的详细阐述。 1. **Android平台开发**:Android是一个开源的操作系统,广泛应用于移动设备,如智能手机和平板电脑。它提供了丰富的API和工具,便于开发者构建各种应用程序。在...
1. 图像处理:二维码扫描的第一步是捕获图像,这需要使用到Android的Camera API或者CameraX库来获取手机摄像头的实时预览流。图像捕获后,会进行灰度化、二值化等预处理操作,以便后续的二维码识别。 2. 图像分析:...
"YZxing-master"通过摄像头捕获图像,然后使用图像处理算法对图像进行预处理,如灰度化、二值化,以增强二维码的识别特征。接着,采用模板匹配或角点检测等方法定位二维码,最后运用错误纠正算法确保即使在部分遮挡...
- **定位与解析**:使用开源库如ZXing(Zebra Crossing)或Android自带的`CameraSource`,进行二维码定位,然后解析码内的数据。 - **错误检测与纠正**:利用二维码的错误校验机制,如 Reed-Solomon 编码,来修复...
6. **多平台兼容**:考虑到智能手机的多样性和操作系统差异,该软件可能有Android和iOS版本,确保大部分用户都能使用。 7. **安全性**:对于涉及敏感信息的二维码,软件应具备安全机制,防止数据泄露,保护用户隐私...
在实际应用中,为了提升扫描速度和准确性,开发者需要注意优化摄像头参数,如曝光、对焦,以及对图像进行实时处理,如灰度化、二值化等。 9. **安全性考量**: 生成二维码时,应考虑安全因素,避免包含敏感信息。...
了解电视机的保修期限、维修点位置及数量等信息,对于日后可能出现的维修和保养非常重要。如果售后服务不佳,考虑更换其他品牌也是一种明智的选择。 在电视机的性能方面,屏幕分辨率是液晶电视的一项重要参数。...
下面将详细阐述这些关键组件的相关知识点。 曲面玻璃板材: 曲面玻璃在电信设备中主要用作显示屏,提供用户交互界面。其独特之处在于其非平面的设计,能够提供更佳的视觉体验和触感。曲面设计可以减少边缘的反射,...