[转载]在树莓派下安装.NET环境 | 树莓派实验室

mikel阅读(956)

树莓派(Raspberry Pi)中文资讯站,提供丰富的树莓派教程和DIY资讯。

来源: [转载]在树莓派下安装.NET环境 | 树莓派实验室

由于树莓派的Raspbian衍生自Debian,所以Mono什么的非常好装。但是官方源中的Mono在Hard Float的Raspbian下是没法完整支持.NET程序的,比如没法在LXDE上运行Winform,但是装Soft Float的话又感觉很亏,所以我们要在Hard Float的Raspbian下装支持Hard Float的.NET环境。

一、安装支持Hard Float的Mono
Raspberry的论坛上有好心人编译好了Raspberry能用的支持Hard Float的Mono,所以我们分别敲入下列代码安装即可,而且压缩包内都是按目录分好的,直接解压缩到“/”下即可,Mono将安装在“/usr/local/”下。

1
2
3
4
5
cd ~
wget https://www.dropbox.com/s/sask17flot3zqlg/mono_2_11_4_armv6hf_binary.tgz
cd /
sudo tar zxf ~/mono_2_11_4_armv6hf_binary.tgz
sudo ldconfig

不过众所周知的,Dropbox在天朝的下载速度实在是太慢了,我拖下来然后把它共享在百度云上,有需要的可以从国内下载直接解压缩就好了:http://pan.baidu.com/s/1pEds1

如果要是之前已经安装过Mono的话,需要先将其完全删除,然后再进行安装。删除可以使用以下命令:

1
2
sudo apt-get remove mono-complete mono-runtime mono-common cli-common libmono0
sudo apt-get remove --purge mono-runtime libmono-2.0 libmono-profiler mono-devel monodoc-browser

装完后可以输入mono -V看下,有了hardfp-abi好高兴!(图中下图是官方源中的Mono)
20131102215759446-0

二、安装Gtk#
装支持Hard Float的Mono就是为了这玩意,由于安装的Mono不是从官方源中安装的,所以从这步开始的很多组件都只能自己下载源码进行编译安装了。

首先安装必须的组件

1
sudo apt-get install automake libtool libgdiplus libpango1.0-dev libatk1.0-dev libgtk2.0-dev libglade2-dev

然后下载Gtk#并安装

1
2
3
4
5
6
7
cd ~
wget http://ftp.gnome.org/pub/gnome/sources/gtk-sharp/2.12/gtk-sharp-2.12.10.tar.gz
tar zxf gtk-sharp-2.12.10.tar.gz
cd gtk-sharp-2.12.10/
./configure
make
sudo make install

不过貌似gnome的网站的速度也挺慢,传送门:http://pan.baidu.com/s/1koy2W

装完后去LXDE下就能打开Winform的程序了。不过如果locale设置的不是zh-cn的话,即使安装了中文字体,界面中的字仍然是方块,如下图。
20131102215759313-0

比较简单的方法是修改字体的映射,我们可以修改“/etc/fonts/conf.d/49-sansserif.conf”,将最后中的sans-serif修改为喜欢的中文字体即可,比如“WenQuanyi Micro Hei”,如下图。
20131102215759292-0

三、安装xsp
在Linux下除了能跑Winform的程序外,比较有用的应该是跑Web应用,首先我们需要安装xsp。

1
2
3
4
5
6
7
cd ~
sudo wget http://download.mono-project.com/sources/xsp/xsp-2.10.tar.bz2
sudo tar jxf xsp-2.10.tar.bz2
cd xsp-2.10
./configure
make
sudo make install

四、安装mod_mono
如果喜欢使用apache2的话可以使用apache2的组件mod_mono来使apache2支持Mono,当然是用其他的Web服务器也是可以的。

1
2
3
4
5
6
7
8
cd ~
sudo wget http://download.mono-project.com/sources/mod_mono/mod_mono-2.10.tar.bz2
sudo tar jxf mod_mono-2.10.tar.bz2
sudo apt-get install apache2-threaded-dev
cd mod_mono-2.10
./configure
make
sudo make install

不过由于Mono和Xsp并不是安装在“/usr/”下,而是安装在“/usr/local/”下,所以我们在配置的时候特别需要注意。比如可以使 用MonoAutoApplication,即修改“/etc/apache2/mod_mono.conf”,在最后添加如下两句:

1
2
MonoServerPath "/usr/local/bin/mod-mono-server2"
MonoAutoApplication enabled

然后将mod_mono.conf复制到“/etc/apache2/mods-enabled”下,然后重启apache2即可。当然比较传统的方式是将mod_mono.conf移动到mods-available下,然后再在mods-enabled下创建个链接。

相关链接
Mono (C#) 2.11.4 hard-float for Raspberry Pi (EXPERIMENTAL):http://www.raspberrypi.org/phpBB3/viewtopic.php?t=37174
在Linux(Ubuntu/openSUSE/CentOS)下配置ASP.NET(Apache + Mono):http://www.cnblogs.com/mayswind/p/3189724.html

via

本文来自:树莓派实验室
链接地址:http://shumeipai.nxez.com/2013/11/02/raspberry-pi-install-the-dotnet-environment.html

[转载]用树莓派DIY便携式警报器 | 树莓派实验室

mikel阅读(1237)

树莓派(Raspberry Pi)中文资讯站,提供丰富的树莓派教程和DIY资讯。

来源: [转载]用树莓派DIY便携式警报器 | 树莓派实验室

我一直在寻找一些能给自己孩子带来教益的瞬间。当我五岁的儿子来寻求我的帮助,别再让他的弟弟偷偷溜进他的房间的时候,我突然灵光一现,发现这是教他关于输入、输出和其他一些编程知识的绝佳时机。而且让他在解决自己实际问题中学习这些也会相对容易。

20150614215022111-0

我本来可以用很多不同的方法来建立一个简单的警报系统,但是我想要让这个系统是一体化的并且在功能上不止能满足我儿子最初的原始需求。选择树莓派作 为控制器是因为它很容易连接到网络、能播放MP3格式的文件、可以和像摄像机这样的USB外围设备进行交互,而且它具有GPIO(通用的输入输出),这样 就可以把它和按钮、传感器、灯这样的简单电子元件连接起来。

这个警报系统的源代码非常简单,你可以在我的GitHub上 查看。为了利用Linux系统下多线程的优势,我把任务分解了到两个Python脚本中。一个Python脚本(keypadd.py)负责监视小键盘上 的有效编码。系统启动时被设置为”disarmed”状态。一旦检测到arm/disarm代码,系统状态就会因为一个在”armed.txt”文件中设 置的字节发生翻转而改变。

20150614215022200-0

第二个Python脚本(alarmd.py)利用树莓派的GPIO来监视PIR(无源红外线传感器)。如果检测到动作发生,脚本接着检查”armed.txt”文件判断系统是否产生警报。如果检测到动作的时候产生了警报,警报就会响起。

20150614215022683-0

我建立这个警报系统来满足我自己的需求。下面的内容会一步步地指导你建立一个类似这样的系统。当建立系统的时候,一定要根据自己的需求来定制,比如可以增加激光绊线或者蜂窝式无线电等。

步骤 1:搭建树莓派

  • 尽管从头开始搭建树莓派超出了这个项目的范围,但是别担心,一些帮助还是会有的。你可以在网上找到一些教程,但是我建议你读一下Matt Richardson和Shawn Wallace合著的《Getting Started With Raspbery Pi》。这本书不仅仅在PiLarm这个项目上会对你有所帮助,更会帮助你去发掘树莓派的各种功能,这样你才可能会把树莓派的功能结合到自己全新的、有趣的创造上面。20150614215022292-0
  • 通过那些有关树莓派的指导,将你的树莓派连接到互联网。这样就可以从网上下载在接下来的几步中所需要的库文件和代码,树莓派也就能通过推特和电子邮件来发送检测到的入侵者的图片了。

步骤2:安装GPIO

这个库文件允许你控制树莓派的GPIO来使用PIR和键盘作为输入,旋转灯作为输出。在终端中使用下面三条命令来安装:
安装GPIO库

1
2
3
sudo apt-get update
sudo apt-get install python-dev
sudo apt-get install python-rpi.gpio

 步骤3:安装fswebcam

  • 如果你在使用第三方摄像头,比如Playstation Eye,那么fswebcam可以让你用它拍摄静态图片。如果你使用的是树莓派自带的摄像头,请参考树莓派官方的安装说明,不需要安装fswebcaam。可以用下面的命令来安装fswebcam:

安装fswebcam

1
sudo apt-get install fswebcam
  • 你可以用这个命令手动拍摄照片:fswebcam -r 640×480 -d /dev/video0 testpictire.jpg。然后使用 “ls” 命令查看拍摄的照片是否出现在当前工作目录中。

步骤4:安装mgp123

  • mgp123是脚本”alarmd.py”用来播放”System armed(系统警报)”、”Motion detected.Please enter passcode(检测到物体移动,请输入密码)”等声音文件的命令行音频播放器。安装命令如下:

安装mpg123

1
sudo apt-get install mpg123
  • 现在开始添加我创建的实现PiLarm功能的代码。它位于:https://github.com/BabyWrassler/PiLarm
  • 为了把代码放到没有头部没有显示器,只能通过互联网使用SSH进入)的树莓派中,你可以使用git。树莓派并不自带git,但是你可以通过命令安装:

安装git

1
sudo apt-get install git-core
  • 然后使用”clone”命令将代码下载到你的树莓派:

下载PiLarm代码

1
git clone https://github.com/BabyWrassler/PiLarm.git
  • 现在,你有了一个叫作PiLarm的目录,它包含PiLarm项目的python文件和音频文件。在/etc/rc.local文件中含有”exit 0″的行之前添加下面两行设置alarmd.py和keypadd.py为开机启动。

设置开机启动脚本

1
2
python /home/pi/Alarm/keypadd.py &
python /home/pi/Alarm/alarmd.py &
  • 请自己熟悉代码,将代码和以上的流程图比较,弄清楚代码中的语句是怎样运行的。
  • keypadd.py文件的第96行包含了用于给系统警报和解除警报的密码(1912)。第97 行的是关机密码(5764),是用来正常关闭树莓派使它可以拔下电源。
  • 如果你使用的是树莓派的摄像头组件,请返回去参考树莓派的官方指导查看你需要用哪些命令来替换alarmd.py中与fswebcam相关的命令。

步骤6:设置Twitter

  • https://dev.twitter.com/apps/new页面中新建一个”App”来获得Twitter的API接口密钥。
  • 在”Access Level”中,选择访问权限的等级设置为”Read and Write”。这样之后你会获得”Consumer Key”, “Consumer Secret”, “Access Token”, “Access Secret”的值,而这些都是需要在创建TweetPony API对象的函数中插入的(插入位置在alarmd.py文件的第10行,默认内容为 “api = tweetpony.API(consumer_key = “abcd”, consumer_secret = “efgh”, access_token = “ijkl”, access_token_secret = “mnop”)。

步骤7:焊封面板套件Perma-Proto

20150614215022464-0

  • Adafruit公司的面板套件Perma-Proto非常棒,因为它自带有适合树莓派上通用输入输出(GPIO)的连接器。树莓派上的引脚通过连接器固定在板子上,这让连接其余的部件变得非常简单。
  • 将树莓派的3.3V电源接到Perma-Proto的正极上,树莓派的地线接到Perma-Proto的接地端。接下来的问题就是熟练、小心的布线,来把所有部件连接起来。我使用公引脚和母引脚作为连接器连接无源红外线传感器之类的器件,但你也可以把它们直接焊接起来。
  • 注意音频放大器是在Perma-Proto板上的,从Perma-Proto的电源端获得电源,它并没有通过GPIO接头连接到树莓派上。音频线单独连接到放大器上,扬声器连接到放大器的螺丝接线端上。
  • 键盘的连接线很短,因此在焊封前请一定设计好你的布局。

步骤8:完整的系统测试

  • 现在你一定很想把这些都放进一个盒子里完成这个项目了,但是在你首先还得测试各个组件是否工作正常,因为一旦把它们放到一个狭小的空间中封装起来,出错的时候再解决故障就变得麻烦多了。
  • 如果你的旋转灯出问题了,可能的一个原因是极性连接错误(polarity issue)。我在面包板上测试了自己的系统,手动把TIP120三极管接到电源或地端看看是否能解决问题。
  • 如果是代码有问题,可能是一些组件连接到了错误的引脚,也可能是你忘记了设置一些常量的值,比如在第六步中要设置Twitter的一些键值。解决 代码的错误,你可以使用Python的”print”命令来打印一些变量的内容到控制台,或者在代码中将一些事件标记为已执行。为了查看 alarmd.py或者keypadd.py的控制台输出,你需要通过SSH进入到树莓派中通过命令”sudo python keypadd.py”或者”sudo python alarmd.py”自己运行这些脚本。

步骤9:安装外壳

20150614215022750-0

  • 大多数警报系统都被安装成永久固定不变的,但是我设计的这套系统是便携式的,这样可以很容易地扩展功能和重新作为其他用途。最后我选择了能把所有多西都装进去的最小的盒子。
  • 我建立第一个PiLarm系统的时候树莓派的摄像头组件还不能使用,但是这些功能马上就要来了。如果你使用这个组件,弄清楚你要怎样加载它。如果 你只是直接在外壳上钻一个孔把镜头露出来,你可能会使摄像头的视角范围变小。最好要么把摄像头安装在一个转轴上,要么把整个装置放置在一个三角架上来确保 摄像头的视角。
  • 如果你使用的是USB摄像头,直接用双面胶把它粘到外壳外面就行了,最好把露在外面的线孔都盖住。
  • 外壳倒过来用效果更好,所以盖子的位置其实是底部。
  • 在放置组件之前,要在外壳上为树莓派的螺母柱、Perma-Proto板的螺母柱、为扬声器预留的网格孔、LED灯、PIR、USB插孔、摄像头和旋转灯的连接线钻孔。可以不这样,但你肯定不想在某个连接器上掉下了钻孔留下的碎屑或者不小心钻透了某个很昂贵的器件。
  • 键盘的连接线需要钻一排相互挨着的1/8″(10.3mm)的孔,用美工刀切掉相互之间的材料,做成一个槽。
  • 双面胶会把摄像头和旋转灯固定到合适的位置。键盘可以用粘合剂。扬声器被螺栓固定到外壳上,Perma-Proto板和树莓派使用电路板的螺母固定到外壳上。
  • 选择放置电路板的位置时,确保会为连线留下足够的空间。不要把连接线扭在一起或者在连接器上拉得过紧。连接线应该有适当的弯曲,连接器不应该承受任何拉力。PS3摄像头上的连接线很长,你可以把它整齐地盘绕成圈以免带来麻烦。

步骤10:和他人分享,激发他人的兴趣

恭喜你终于完成了!给我们发来你已经完成的系统的照片或者视频,让我们知道你是怎么个性化设计方案来满足自己的不同需求的。把你的杰作展示给那些你认为会因此产生灵感而成为创作者的人!

本文来自:树莓派实验室
链接地址:http://shumeipai.nxez.com/2015/06/14/diy-portable-alarm-with-raspberry-pi.html

笔记本上树莓派远程连接设置教程

mikel阅读(1170)

1.硬件连接:

刚开始以为笔记本有HDMI接口就能接树莓派,线买回来一查才知道笔记本的HDMI只能输出不能输入,用HDMI线是不能连接笔记本和树莓派的,不过可以利用HDMI转VGA的转换头连接笔记本的VGA接口,不过用了一个VGA转接头连接独立显示器, 还是不能显示,于是想到笔记本远程连接。

拿到树莓派,是没有系统的,将内存卡放入读卡器中,插入电脑中,格式化,选择TF32的格式,再下载树莓派系统到电脑中,这是我现在用的,直接给链接:链接:http://pan.baidu.com/s/1mg7e5nU 密码:i1c2,这是系统,下载好到电脑中,不要动!再下载这个软件,烧录系统,同样的,给链接:链接:http://pan.baidu.com/s/1qW6Wxsg 密码:ukya。这是个压缩包,解压,打开这个文件。、

f3b5efc451da81cbd8b5d1115066d0160b2431f2.jpg

7bf6e62a2834349b58933359cbea15ce34d3beb4.jpg8f139586c9177f3ea4a5a1d172cf3bc79d3d56b0.jpg

系统就这样写好了,把内存卡插到树莓派上,接通电源,用一根网线一端接在树莓派上,另一端接在路由器的一个端口上,注意不要插在WLAN接口!电脑你可以用无线网或者有线接在同一个路由器下

2.查找树莓派的IP地址:

这时候就要用到一个很有用的软件,要安装下,比用CMD查找ip地址方便多了,链接:链接:http://pan.baidu.com/s/1i3yxicd 密码:zdxl,安装好后打开点击scan,等两分钟就能看到所有链接这个路由器的IP地址,这个软件是贴吧里另一个精品贴里的大神给的,我觉得很好用我 就盗来了。这个软件的好处是他能识别出树莓派的ip地址并且有名称raspberry pi这个地址就是树莓派的ip地址,记住。

3.利用Putty连接树莓派,然后开启远程桌面

然后,在用这个软件putty,链接:http://pan.baidu.com/s/1jGmjQce 密码:osoe,下载运行,
e0d25963f6246b6040e5eca8e9f81a4c530fa25a.jpg
这时要输入用户名和密码,先输入pi,回车,在输入raspberry再回车,就能进入树莓派执行命令界面,第一次运行系统他会提醒先运行sudo raspi-config,这个可以先运行下,不用更改任何设置,在退出,然后,在路由器有网络的情况下输入: sudo apt-get install xrdp
下载好后就行了。

4.Win7下开启远程桌面连接树莓派

退出putty。win7在桌面右击计算机,属性,

选择“远程设置”
c9f0e60735fae6cd9e7df3bd0db30f2440a70f78

把“允许远程协助连接这台计算机”打钩,下面的“允许运行任意版本远程桌面的计算机连接(较不安全)”选中,其他的不用管它。
8adc07fae6cd7b8978a5625f0d2442a7db330e78
,这里是盗用百度经验里的,电脑远程桌面设置,不懂得可以再百度。

欢迎来到小码哥的博客 博客搬家啦http://t.cn/RvFZs2c 让树莓派说出自己的IP地址

mikel阅读(664)

让树莓派说出自己的IP地址

当亲爱的树莓派没有显示器时如何控制它?对,就是ssh,但是ssh需要IP地址啊,树莓派的IP地址是多少?这个问题问的好,目前大约有这样几种解决方案:、

  • 获取到IP地址后将地址发到邮箱:前提是树莓派能上网
  • 通过串口连接树莓派后查看IP地址:需要USB转串口设备,还要连线,小白用户表示鸭梨山大
  • 从路由器上查看:如果没有路由器或者没法登上路由器咋办?
  • 固定IP地址:假设换到别的网络,网段不一样咋办?地址冲突咋办?
  • 通过某些工具扫描整个网段:你确定能扫出来吗?
  • 一个一个尝试:如果是A类IP岂不泪奔?

所以嘛,上面的解决方案是有局限性的!有木有更好的方案?小码哥告诉你,有!

原理很简单,当树莓派获取到IP地址之后,让它自己说出自己的IP地址就是了。

获取IP地址比较容易,ifconfig之类的命令大家也应该听说过。关键是“说”!

其实这个问题也挺简单的,去网上下载0~9的数字发音,然后对应着获取的IP地址,依次按顺序读出来就行啦。 树莓派里面是有播放器的,omxplayer就是一个。其他的播放器比如mpg123也可以的。

你需要做的,就是让树莓派开机自动执行这个程序,然后找个耳机插到树莓派的音频孔里面(表告诉我你木有耳机), 竖起你的耳朵,仔细听就是了~~~

为了方便大家安装,一键脚本已经写好了,大家只要在树莓派上执行这个命令就好了:

curl "https://raw.github.com/ma6174/speak_raspi_ip/master/setup.sh" | bash

项目主页:https://github.com/ma6174/speak_raspi_ip

Android自动聚焦、摄像头拍照、缩放至标准大小的完整实现

mikel阅读(656)

为什么把自动聚焦放在前面呢?因为前文http://blog.csdn.net/yanzi1225627/article/details/7738736已经实现了拍照功能,且网上拍照的源码例子很多,自动聚焦很多人写的很简单,但结果发现不中。我这里就总结下,我的自动聚焦的实现。

手机华为U9200,

Android4.0.3,

预览的参数:myParameters.setPreviewSize(1280, 720)

图片参数:myParameters.setPictureSize(2048, 1152); //1280, 720

图片最终尺寸:宽600 * 高800

(关于setPreviewSize和setPictureSize的问题可以参照我以前的文章http://blog.csdn.net/yanzi1225627/article/details/7738736

 

参照http://www.cnblogs.com/liuan/archive/2012/01/10/2318300.html, 上面的思路是弄了一个定时器来进行自动聚焦,但发现根本不中,一按拍照程序就挂掉。而且由于定时器一直在重复循环,相机就在那聚焦来聚焦去,也就是一下清 晰一下又模糊了。后来我把程序改成,当拍照后把定时器关闭掉发现还是不中。经反复实验,终于成功。南无阿弥陀佛,本着我为人人,人人为我。将源码贴在此 处:

第一,布局文件。里面有一个surfaceview和三个按钮,分别是预览、拍照、保存。

 

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <LinearLayout xmlns:Android=“http://schemas.android.com/apk/res/android”
  3.     android:layout_width=“fill_parent”
  4.     android:layout_height=“fill_parent”
  5.     android:orientation=“vertical” >
  6. <SurfaceView
  7.     android:id=“@+id/mySurfaceView”
  8.     android:layout_width=“fill_parent”
  9.     android:background=“#D1EEEE”
  10.     android:layout_height=“800px”
  11.     android:gravity=“center” />
  12. <LinearLayout
  13.     android:id=“@+id/LinearLayout01”
  14.     android:layout_width=“wrap_content”
  15.     android:layout_height=“wrap_content”
  16.     android:layout_gravity=“center_horizontal”
  17.     android:paddingTop=“20dip”
  18.     android:orientation=“horizontal” >
  19.     <Button
  20.         android:id=“@+id/btnPreview”
  21.         android:layout_width=“wrap_content”
  22.         android:layout_height=“wrap_content”
  23.         android:text=“预览” />
  24.     <Button
  25.         android:id=“@+id/btnPhoto”
  26.         android:layout_width=“wrap_content”
  27.         android:layout_height=“wrap_content”
  28.         android:text=“拍照” />
  29.     <Button
  30.         android:id=“@+id/btnSave”
  31.         android:layout_width=“wrap_content”
  32.         android:layout_height=“wrap_content”
  33.         android:text=“保存” />
  34. </LinearLayout>
  35. </LinearLayout>

第2,源程序:

  1. package yan.guoqi.testphoto;
  2. import java.io.BufferedOutputStream;
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.util.Timer;
  7. import java.util.TimerTask;
  8. import android.app.Activity;
  9. import android.app.AlertDialog;
  10. import android.app.AlertDialog.Builder;
  11. import android.content.DialogInterface;
  12. import android.graphics.Bitmap;
  13. import android.graphics.BitmapFactory;
  14. import android.graphics.PixelFormat;
  15. import android.hardware.Camera;
  16. import android.hardware.Camera.PictureCallback;
  17. import android.hardware.Camera.ShutterCallback;
  18. import android.os.Bundle;
  19. import android.util.Log;
  20. import android.view.SurfaceHolder;
  21. import android.view.SurfaceView;
  22. import android.view.View;
  23. import android.view.View.OnClickListener;
  24. import android.view.Window;
  25. import android.view.WindowManager;
  26. import android.widget.Button;
  27. import android.widget.Toast;
  28. public class TestPhotoActivity extends Activity implements SurfaceHolder.Callback{
  29.     /** Called when the activity is first created. */
  30.     private static final  String TAG = “yan:”;
  31.     SurfaceView mySurfaceView = null;
  32.     SurfaceHolder mySurfaceHolder = null;
  33.     Button btnPreview = null;
  34.     Button btnPhoto = null;
  35.     Button btnSave = null;
  36.     Camera myCamera = null;
  37.     Camera.Parameters myParameters;
  38.     boolean isView = false;
  39.     Bitmap bm;
  40.     String savePath = “/mnt/sdcard/testPhoto/”;
  41.     int cntSave = 0;
  42.     private Camera.AutoFocusCallback mAutoFocusCallback;
  43.     //private Camera.PreviewCallback mPreviewCallback;
  44.     private Timer mTimer;
  45.     private TimerTask mTimerTask;
  46.     @Override
  47.     public void onCreate(Bundle savedInstanceState) {
  48.         super.onCreate(savedInstanceState);
  49.         //璁剧疆鍏ㄥ睆鏃犳爣棰?       requestWindowFeature(Window.FEATURE_NO_TITLE);
  50.         int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
  51.         Window myWindow = this.getWindow();
  52.         myWindow.setFlags(flag, flag);
  53.         setContentView(R.layout.main); //璁剧疆甯冨眬
  54.         mySurfaceView = (SurfaceView)findViewById(R.id.mySurfaceView);
  55.         mySurfaceView.setZOrderOnTop(true);
  56.         mySurfaceHolder = mySurfaceView.getHolder();
  57.         mySurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
  58.         btnPreview = (Button)findViewById(R.id.btnPreview);
  59.         btnPhoto = (Button)findViewById(R.id.btnPhoto);
  60.         btnSave = (Button)findViewById(R.id.btnSave);
  61.         if(!isFolderExist(savePath)) //濡傛灉鍒涘缓鏂囦欢澶瑰け璐?       {
  62.             AlertDialog.Builder alertDialog = new Builder(TestPhotoActivity.this);
  63.             alertDialog.setTitle(“閿欒”);
  64.             alertDialog.setMessage(“鍥剧墖淇濆瓨鏂囦欢澶瑰垱寤哄け璐ワ紒”);
  65.             alertDialog.setPositiveButton(“纭畾”new DialogInterface.OnClickListener() {
  66.                 public void onClick(DialogInterface dialog, int which) {
  67.                     // TODO Auto-generated method stub
  68.                     dialog.dismiss();
  69.                     TestPhotoActivity.this.finish();
  70.                 }
  71.             });
  72.             alertDialog.show();
  73.         }
  74.         else
  75.             Toast.makeText(TestPhotoActivity.this,
  76.                     “鎮ㄧ殑鐓х墖灏嗕繚瀛樺湪锛? + savePath,
  77.                     Toast.LENGTH_SHORT).show();
  78.         mySurfaceHolder.addCallback(this);
  79.         mySurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  80.         mAutoFocusCallback = new Camera.AutoFocusCallback() {
  81.             public void onAutoFocus(boolean success, Camera camera) {
  82.                 // TODO Auto-generated method stub
  83.                 if(success){
  84.                     myCamera.setOneShotPreviewCallback(null);
  85.                     Toast.makeText(TestPhotoActivity.this,
  86.                             “鑷姩鑱氱劍鎴愬姛锛?,
  87.                             Toast.LENGTH_SHORT).show();
  88.                 }
  89.             }
  90.         };
  91.         mTimer = new Timer();
  92.         mTimerTask = new CameraTimerTask();
  93.         //mTimer.schedule(mTimerTask, 0, 500);
  94.         btnPreview.setOnClickListener(new BtnListener());
  95.         btnPhoto.setOnClickListener(new BtnListener());
  96.         btnSave.setOnClickListener(new BtnListener());
  97.     }
  98.     ShutterCallback myShutterCallback = new ShutterCallback() {
  99.         public void onShutter() {
  100.             // TODO Auto-generated method stub
  101.         }
  102.     };
  103.     PictureCallback myRawCallback = new PictureCallback() {
  104.         public void onPictureTaken(byte[] data, Camera camera) {
  105.             // TODO Auto-generated method stub
  106.         }
  107.     };
  108.     PictureCallback myjpegCalback = new PictureCallback() {
  109.         public void onPictureTaken(byte[] data, Camera camera) {
  110.             // TODO Auto-generated method stub
  111.             Log.i(TAG, “onPictureTaken……..”);
  112.             bm = BitmapFactory.decodeByteArray(data, 0, data.length);
  113.             isView = false;
  114.             myCamera.stopPreview();
  115.             myCamera.release();
  116.             myCamera = null;
  117.             isView = false;
  118.         }
  119.     };
  120.     class BtnListener implements OnClickListener{
  121.         public void onClick(View v) {
  122.             // TODO Auto-generated method stub
  123.             switch(v.getId()){
  124.             case R.id.btnPreview:
  125.                 Toast.makeText(TestPhotoActivity.this,
  126.                         “鎮ㄦ寜浜嗛瑙堟寜閽?,
  127.                         Toast.LENGTH_SHORT).show();
  128.                 initCamera();
  129.                 break;
  130.             case R.id.btnPhoto:
  131.                 if(isView && myCamera!=null){
  132.                     myCamera.takePicture(myShutterCallback, myRawCallback, myjpegCalback);
  133.                 }
  134.                 else
  135.                     Toast.makeText(TestPhotoActivity.this, “璇峰厛棰勮鐒跺悗鎷嶇収锛?, Toast.LENGTH_SHORT).show();
  136.                 break;
  137.             case R.id.btnSave:
  138.                   if(bm == null)
  139.                   {
  140.                       Toast.makeText(TestPhotoActivity.this“璇锋媿鎽勬垚鍔熷悗鍐嶄繚瀛橈紒锛侊紒”, Toast.LENGTH_SHORT).show();
  141.                       return;
  142.                   }
  143.                 int b =0, c=1;
  144.                 File fTest = new File(savePath + b + c + “.JPG”);
  145.                 while(fTest.exists()){
  146.                     if(c==9){
  147.                         b++;
  148.                         c = 0;
  149.                     }
  150.                     else
  151.                         c++;
  152.                     if(b==9){
  153.                         b = 0;
  154.                         Toast.makeText(TestPhotoActivity.this, “姝ゅ織鎰胯€呮牱鏈暟鐩凡瓒呰繃100锛?,
  155.                                 Toast.LENGTH_SHORT).show();
  156.                     }
  157.                     fTest = new File(savePath + b + c + “.JPG”);
  158.                 }
  159.                 try {
  160.                     FileOutputStream fout = new FileOutputStream(fTest);
  161.                     BufferedOutputStream bos = new BufferedOutputStream(fout);
  162.                     Bitmap mBitmap = Bitmap.createScaledBitmap(bm, 600, 800, false);
  163.                     mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
  164.                     //bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
  165.                     bos.flush();
  166.                     bos.close();
  167.                     cntSave++;
  168.                     Toast.makeText(TestPhotoActivity.this“鎮ㄦ媿鐨勭”+cntSave+“寮爌icture淇濆瓨鎴愬姛锛佺紪鍙凤細”+ b + c,
  169.                             Toast.LENGTH_SHORT).show();
  170.                 }  catch (IOException e) {
  171.                     // TODO Auto-generated catch block
  172.                     e.printStackTrace();
  173.                     Toast.makeText(TestPhotoActivity.this,
  174.                             “淇濆瓨澶辫触”,
  175.                             Toast.LENGTH_SHORT).show();
  176.                 }
  177.                 break;
  178.                 default:
  179.             }
  180.         }
  181.     }
  182.     public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
  183.         // TODO Auto-generated method stub
  184.     }
  185.     public void surfaceCreated(SurfaceHolder arg0) {
  186.         // TODO Auto-generated method stub
  187.     }
  188.     public void surfaceDestroyed(SurfaceHolder arg0) {
  189.         // TODO Auto-generated method stub
  190.     }
  191.     //鍒濆鍖栨憚鍍忓ご
  192.     public void initCamera()
  193.     {
  194.         if(myCamera == null && !isView)
  195.         {
  196.             myCamera = Camera.open();
  197.             Log.i(TAG, “camera.open”);
  198.         }
  199.         if(myCamera != null && !isView) {
  200.             try {
  201.                     myParameters = myCamera.getParameters();
  202.                     myParameters.setPictureFormat(PixelFormat.JPEG);
  203.                     myParameters.setPreviewSize(1280, 720);
  204.                     //myParameters.setFocusMode(“auto”);
  205.                     myParameters.setPictureSize(2048, 1152); //1280, 720
  206.                     myParameters.set(“rotation”, 90);
  207.                     myCamera.setDisplayOrientation(90);
  208.                     myCamera.setParameters(myParameters);
  209.                     myCamera.setPreviewDisplay(mySurfaceHolder);
  210.                     myCamera.startPreview();
  211.                     isView = true;
  212.                     myCamera.autoFocus(mAutoFocusCallback);
  213.             } catch (Exception e) {
  214.                 // TODO: handle exception
  215.                 e.printStackTrace();
  216.                 Toast.makeText(TestPhotoActivity.this, “鍒濆鍖栫浉鏈洪敊璇?,
  217.                         Toast.LENGTH_SHORT).show();
  218.             }
  219.         }
  220.     }
  221.     class CameraTimerTask extends TimerTask{
  222.         @Override
  223.         public void run() {
  224.             // TODO Auto-generated method stub
  225.             if(myCamera != null)
  226.             {
  227.                 myCamera.autoFocus(mAutoFocusCallback);
  228.             }
  229.         }
  230.     }
  231.     //鍒ゆ柇鏂囦欢澶规槸鍚﹀瓨鍦紝濡傛灉涓嶅瓨鍦ㄥ垯鍒涘缓涓€涓?       public boolean isFolderExist(String folderPath){
  232.             boolean result = false;
  233.             File f = new File(folderPath);
  234.             if(!f.exists()){
  235.                 if(f.mkdir()){
  236.                     result = true;
  237.                 }
  238.                 else
  239.                     result = false;
  240.             }
  241.             else
  242.                 result = true;
  243.             return result;
  244.         }
  245. }

自动聚焦部分,核心的代码有,构造函数里的

private Camera.AutoFocusCallback mAutoFocusCallback;
mAutoFocusCallback = new Camera.AutoFocusCallback() {

public void onAutoFocus(boolean success, Camera camera) {
// TODO Auto-generated method stub
if(success){
myCamera.setOneShotPreviewCallback(null);
Toast.makeText(TestPhotoActivity.this,
“自动聚焦成功” , Toast.LENGTH_SHORT).show();
}

}
};
和initCamera里的myCamera.autoFocus(mAutoFocusCallback);调用!

 

第三,AndroidManifest文件,红线部分是添加的对摄像头操作、sd存储的权限许可。

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android
package=”yan.guoqi.testphoto”
android:versionCode=”1″
android:versionName=”1.0″ >
<uses-sdk android:minSdkVersion=”15″ />
<!– 鍦╯d鍗′腑鍒涘缓鍜屽垹闄ゆ枃浠剁殑鏉冮檺 –>
    <uses-permission android:name=”android.permission.MOUNT_UNMOUNT_FILESYSTEMS” />
<!– 鍦╯d鍗′腑鍐欏叆鏁版嵁鐨勬潈闄?–>
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
<!– 浣跨敤鎷嶇収鐨勬潈闄?–>
<uses-permission android:name=”android.permission.CAMERA” />

    <uses-feature android:name=”android.hardware.camera” />

<application
android:icon=”@drawable/ic_launcher”
android:label=”@string/app_name” >
<activity
android:name=”.TestPhotoActivity”
android:label=”@string/app_name” >
<intent-filter>
<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>

</manifest>

 
上两张效果图,来看下摄像头相同的previewSize、pictureSize下,自动对焦前后的差别:

效果差别不是一般的大啊!

1,上面的源程序里mTimer、mTimerTask等这些是我第一种思路,用定时器来控制聚焦,结果是失败的。碰到这些,大家略过去就行了。

2,这里控制聚焦的核心实现时,在initCamera函数里,执行myCamera.autoFocus(mAutoFocusCallback); 自动聚焦这句话!在构造函数里定义了一个mAutoFocusCallback的回调函数变量。还可以在按下拍照按钮后判断当前是否自动聚焦成功,如果聚 焦成功再拍照!mAutoFocusCallback里的if(success)就是标示自动聚焦成功

3,网上有很多,只是简单的在构造函数里写一句myCamera.setFocus(null),我真怀疑,这不是坑爹么?反正我这样是没弄成功。

4,程序里的乱码部分是文字,由于从linux下拷出来就成乱码了,大家不用太关心,都是些提示性的话。

5,另外还要交代一下,自动聚焦这句话一定要在摄像头正常预览的时候调用,否则是没有意义的,程序也会挂掉。

6,我核心的参考文章:

http://www.cnblogs.com/liuan/archive/2012/01/10/2318300.html

http://www.cnblogs.com/skyseraph/archive/2012/03/26/2418665.html

向他们表示感谢!

遗留的问题:

我第一次按下预览按钮后,会进 行自动聚焦,并提示自动聚焦成功。如果此时执行拍照–保存–再次预览,则自动聚焦是成功的。 如果我预览后,调整了和拍摄物的距离这时想再次自动聚焦,即连续两次按下预览按钮,怎么第二次就不自动聚焦了呢???这是不是和android摄像头内置 的属性有关系?

源码下载链接:http://download.csdn.net/detail/yanzi1225627/4538626

———————-作者yanzi1225627 转载请注明 2012.8.31

android 使用两个surfaceview 在摄像机画面上绘图

mikel阅读(887)

使用双surface,将第一个设置为透明背景,在摄像机上绘制图像,纠结搞了一天。

其中参考了http://blog.csdn.net/yanzi1225627/article/details/7934710 的思路和代码,但是,始终会出现 canvas=holder.lockcanvas();返回的canvas为null的情况。加入线程后解决问题。

主Activity:


public class MainActivity extends Activity {

private SVDraw surfaceDraw = null;
private SurfaceView surfaceView = null;
private SurfaceHolder holder1 = null;
private Canvas canvas = null;
private Camera cam = null;
private boolean previewRunning = true;

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

//绘制surface1
&amp;nbsp;               surfaceView = (SurfaceView) findViewById(R.id.surface1);
holder1 = surfaceView.getHolder();
holder1.addCallback(new MySurfaceViewCallback());
holder1.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder1.setFixedSize(500, 350);
holder1.setFormat(PixelFormat.TRANSPARENT);

canvas = holder1.lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.TRANSPARENT);
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(Color.RED);
p.setStyle(Style.STROKE);
// canvas.drawPoint(100.0f, 100.0f, p);
canvas.drawLine(0, 110, 500, 110, p);
}

// 绘制surface2  其中使用线程
surfaceDraw = (SVDraw) findViewById(R.id.mDraw);
surfaceDraw.setVisibility(View.VISIBLE);
surfaceDraw.drawLine();
}

// =============================create surface 1=================================================
private class MySurfaceViewCallback implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}

@SuppressWarnings("deprecation")
@Override
public void surfaceCreated(SurfaceHolder holder) {
cam = Camera.open(); // 取得第一个摄像头
Parameters param = cam.getParameters();
// param.setPreviewSize(display.getWidth(), display.getHeight()) ;
param.setPreviewFrameRate(5); // 一秒5帧
param.setPictureFormat(PixelFormat.JPEG); // 图片形式
param.set("jpen-quality", 80);
cam.setParameters(param);
cam.setDisplayOrientation(90); // 纠正摄像头自动旋转,纠正角度,如果引用,则摄像角度偏差90度

try {
cam.setPreviewDisplay(holder);
} catch (IOException e) {
}

cam.startPreview(); // 进行预览
previewRunning = true; // 已经开始预览
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (cam != null) {
if (previewRunning) {
cam.stopPreview(); // 停止预览
previewRunning = false;
}
cam.release();
}
}
}
}

/*只不过一个是继承的view一个是surfaceview,将AttributeSetattrs加上。只要处理好谁是顶层的view谁设成透明,预览视频的surfaceview设成底层,在且要在xml属性文件里设成visible就可以了*/
public class SVDraw extends SurfaceView implements SurfaceHolder.Callback{

protected SurfaceHolder sh;
private int mWidth;
private int mHeight;
private MyThread thread;

public SVDraw(Context context, AttributeSet attrs) {
super(context, attrs);
sh = this.getHolder();
sh.addCallback(this);
sh.setFormat(PixelFormat.TRANSPARENT);
setZOrderOnTop(true);
}

public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) {
mWidth = w;
mHeight = h;
}

public void surfaceCreated(SurfaceHolder arg0) {

}

public void surfaceDestroyed(SurfaceHolder arg0) {

}
void clearDraw()
{
Canvas canvas = sh.lockCanvas();
canvas.drawColor(Color.BLUE);
sh.unlockCanvasAndPost(canvas);
}
public void drawLine()
{
//预览视频的时候绘制图像
/*Canvas canvas = sh.lockCanvas();
canvas.drawColor(Color.TRANSPARENT);
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(Color.RED);
p.setStyle(Style.STROKE);
//canvas.drawPoint(100.0f, 100.0f, p);
canvas.drawLine(0,110, 500, 110, p);
canvas.drawCircle(110, 110, 10.0f, p);
sh.unlockCanvasAndPost(canvas);*/

thread = new MyThread(sh);
thread.setRun(true);
thread.start();
}
}

// 绘制线程
public class MyThread extends Thread {
private SurfaceHolder holder;
private boolean run;

public MyThread(SurfaceHolder holder) {
this.holder = holder;
run = true;
}

@Override
public void run() {
int counter = 0;
Canvas canvas = null;
while (run) {
// 具体绘制工作
try {
// 获取Canvas对象,并锁定之
canvas = holder.lockCanvas();
// 设定Canvas对象的背景颜色
canvas.drawColor(Color.TRANSPARENT);

// 创建画笔
Paint p = new Paint();
// 设置画笔颜色
p.setColor(Color.RED);
// 设置文字大小
p.setTextSize(30);

// 创建一个Rect对象rect
Rect rect = new Rect(100, 50, 380, 330);
// 在canvas上绘制rect
canvas.drawRect(rect, p);
// 在canvas上显示时间
canvas.drawText("Interval = " + (counter++) + " seconds.", 100, 410, p);
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
// 解除锁定,并提交修改内容
holder.unlockCanvasAndPost(canvas);
}
}
}
}

public boolean isRun() {
return run;
}

public void setRun(boolean run) {
this.run = run;
}
}

&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test02"
android:versionCode="1"
android:versionName="1.0" &gt;

&lt;uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" /&gt;

&lt;application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" &gt;
&lt;activity
android:name="com.example.test02.MainActivity"
android:label="@string/app_name" &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;
&lt;/application&gt;

&lt;!--z&gt;
&amp;nbsp;&lt;uses-feature android:name="android.hardware.camera" /&gt;
&lt;uses-feature android:name="android.hardware.camera.autofocus" /&gt;
&lt;uses-permission android:name="android.permission.CAMERA" /&gt;
&lt;uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /&gt;
&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
&lt;/manifest&gt;

&nbsp;

Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整实现(原理:底层SurfaceView+上层绘制ImageView)

mikel阅读(932)

【后注:】下载代码的注意,我的手机是4.3寸的屏,华为U9200.如果不能运行的请修改参数。看前文的第四条。Y的,省的说我传的代码不能用发火 

 

最近一直在审视以前做过的东西,关于Android摄像头预览,预览界面上呈现矩形框,在前文(

Android开发 摄像头SurfaceView预览 背景带矩形框 实现(原理:双surfaceview,顶层画矩形框,底层预览视频)

)—-http://blog.csdn.net/yanzi1225627/article/details/7934710已 经实现。最近发现上层绘制矩形框,用surfaceview有点大材小用了。SurfaceView绘制动画更合适,只绘制个矩形框用ImageView 足够了。但有些时候必须要用SurfaceView来实现。比如360手机安全卫士扫描二维码的实现应该就是通过上下两层SurfaceView实现的 (见下图)。上层SurfaceView用于显示那个可以旋转的扫描示意框,底层SurfaceView预览摄像头视频。

废话不说了,稍候几天我会仿照上面360这个扫描二维码的界面做一个工程(结合PreviewCallback),公开出来。这次先谈用底层 surfaceView+上层ImageView实现只拍摄矩形框中的图像。新建一个类继承ImageView,源码如下:

  1. package yan.guoqi.rectphoto;
  2. import Android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.Color;
  5. import android.graphics.Paint;
  6. import android.graphics.Paint.Style;
  7. import android.graphics.Rect;
  8. import android.util.AttributeSet;
  9. import android.widget.ImageView;
  10. public class DrawImageView extends ImageView{
  11.     public DrawImageView(Context context, AttributeSet attrs) {
  12.         super(context, attrs);
  13.         // TODO Auto-generated constructor stub
  14.     }
  15.     Paint paint = new Paint();
  16.     {
  17.         paint.setAntiAlias(true);
  18.         paint.setColor(Color.RED);
  19.         paint.setStyle(Style.STROKE);
  20.         paint.setStrokeWidth(2.5f);//设置线宽
  21.         paint.setAlpha(100);
  22.     };
  23.     @Override
  24.     protected void onDraw(Canvas canvas) {
  25.         // TODO Auto-generated method stub
  26.         super.onDraw(canvas);
  27.         canvas.drawRect(new Rect(100200400500), paint);//绘制矩形
  28.     }
  29. }

布局文件里与前文http://blog.csdn.net/yanzi1225627/article/details/8577756这里一样,只是在帧布局里加一个上面自定义的DrawImageView,整个布局文件示下:

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=“http://schemas.android.com/tools”
  3.     android:layout_width=“fill_parent”
  4.     android:layout_height=“fill_parent”
  5.     android:orientation=“vertical” >
  6.     <TextView
  7.         android:layout_width=“wrap_content”
  8.         android:layout_height=“wrap_content”
  9.         android:text=“@string/BestWish”
  10.         tools:context=“.RectPhoto” />
  11.     <FrameLayout
  12.         android:layout_width=“wrap_content”
  13.         android:layout_height=“wrap_content” >
  14.         <SurfaceView
  15.             android:id=“@+id/previewSV”
  16.             android:layout_width=“fill_parent”
  17.             android:layout_height=“800px” />
  18.         <yan.guoqi.rectphoto.DrawImageView
  19.              android:id=“@+id/drawIV”
  20.              android:layout_width=“fill_parent”
  21.              android:layout_height=“800px”
  22.             />
  23.     </FrameLayout>
  24.     <ImageButton
  25.         android:id=“@+id/photoImgBtn”
  26.         android:layout_width=“wrap_content”
  27.         android:layout_height=“wrap_content”
  28.         android:background=“@drawable/photo_img_btn”
  29.         android:layout_gravity=“center” />
  30. </LinearLayout>

在主程序文件里,onCreate()函数里设置底层SurfaceView为底层且透明(如果不设也可以,默认就是如此):

mPreviewSV.setZOrderOnTop(false);

mySurfaceHolder.setFormat(PixelFormat.TRANSPARENT);//translucent半透明 transparent透明

在主UI线程里的onCreate()函数里添加代码:

//绘制矩形的ImageView
mDrawIV = (yan.guoqi.rectphoto.DrawImageView)findViewById(R.id.drawIV);
mDrawIV.onDraw(new Canvas());

看上面的DrawImageView的函数里的onDraw,画的矩形是Rect(100, 200, 400, 500)。在onPictureTaken(byte[] data, Camera camera)函数里,先将图片旋转90度,大小成为宽×高(960×1280)。由于预览surfaceview的大小是宽×高(540×800),所 以在onPictureTaken函数里将960×1280的图片缩放到540×800, 缩放相同大小后就可以用矩阵的坐标直接截取子图了。核心函数就是这两句:

//将960×1280缩放到540×800
            Bitmap sizeBitmap = Bitmap.createScaledBitmap(rotaBitmap, 540, 800, true);
Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, 100, 200, 300, 300);//截取

注意这个截取的函数参数和矩阵的坐标关系,分别是x轴 y轴起始坐标及 x轴宽度 y轴宽度。截取出来的图片大小应该是300×300. onPictureTaken()函数的源码如下:

  1. public void onPictureTaken(byte[] data, Camera camera) {
  2.             // TODO Auto-generated method stub
  3.             Log.i(tag, “myJpegCallback:onPictureTaken…”);
  4.             if(null != data){
  5.                 mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据,将其解析成位图
  6.                 myCamera.stopPreview();
  7.                 isPreview = false;
  8.             }
  9.             //设置FOCUS_MODE_CONTINUOUS_VIDEO)之后,myParam.set(“rotation”, 90)失效。图片竟然不能旋转了,故这里要旋转下
  10.             Matrix matrix = new Matrix();
  11.             matrix.postRotate((float)90.0);
  12.             Bitmap rotaBitmap = Bitmap.createBitmap(mBitmap, 00, mBitmap.getWidth(), mBitmap.getHeight(), matrix, false);
  13.             //旋转后rotaBitmap是960×1280.预览surfaview的大小是540×800
  14.             //将960×1280缩放到540×800
  15.             Bitmap sizeBitmap = Bitmap.createScaledBitmap(rotaBitmap, 540800true);
  16.             Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap, 100200300300);//截取
  17.             //保存图片到sdcard
  18.             if(null != rectBitmap)
  19.             {
  20.                 saveJpeg(rectBitmap);
  21.             }
  22.             //再次进入预览
  23.             myCamera.startPreview();
  24.             isPreview = true;
  25.         }

涉及到的其他函数如saveJpeg()参见前文:

2013新春奉送:Android摄像头开发完美demo—(循环聚焦,缩放大小,旋转picture,查询支持的picturesize, ImageButton按键效果)————

http://blog.csdn.net/yanzi1225627/article/details/8577756   重复的东西我就不发了。

效果图如下所示:

点击拍照,查看保存后的图片如下:

反思:

1,SurfaceView为啥 无论translucent半透明还是 transparent透明基本没啥区别?而且surfaceview的setAlpha函数不能用。

2,在这里surfaceview一定要在底层(默认如此),如果设成顶层会看不到红色矩形框。可以自己测试下。

3, 最纠结的一点,第一副图片里的360扫描二维码的界面,底层的预览surfaceview是半透明的,底色是灰色的,只有中间的扫描矩形框是透明的,亮 色。这一块究竟是怎么实现的??下午实验了n种方法愣是无济于事。我擦。。。如果有高人,希望能不吝指点下。 不过说实话,人家已经设计出来的产品界面看着就是好,不得不服阿。以后要多多模仿钻研这些成型产品的设计。

源码下载:http://download.csdn.net/detail/yanzi1225627/5063105

欢迎android爱好者加群248217350,备注:yanzi

—————————————- 本文系原创,转载请注明作者:yanzi1225627

Android OpenGL Camera Preview Issue

mikel阅读(866)

来源: Android OpenGL Camera Preview Issue

‘m working on an Android camera app that modifies the camera feed and displays it live on the screen. I have it working and doing what I want perfectly on my DROID RAZR MAXX running 4.3, and it works perfect on other phones, but unfortunately I have ran into a problem on several phones and am unable to track down the issue.

I’ve attached a screenshot showing what the issue is.

It’s very hard to tell what the green “artifacts” are, but it almost looks like it’s blocks from the camera feed from when it first turned on. The colors flicker, but the shapes inside the blocks don’t really change.

I’ve stripped out everything that isn’t needed and cleaned up the code as best I can, but I honestly have zero clue as to why this is happening, especially since it seems to work on some phones fine, while other phones it doesn’t.

If I need to give more information just comment and I will add it!

CameraActivity.java

<pre><code>public class CameraActivity extends Activity
{
    private MyGLSurfaceView glSurfaceView;
    private MyCamera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        mCamera = new MyCamera();

        glSurfaceView = new MyGLSurfaceView(this, mCamera);

        setContentView(glSurfaceView);
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        mCamera.stop();
    }
}

MyCamera.java

<pre><code>public class MyCamera
{
    private final static String LOG_TAG = "MyCamera";

    private Camera mCamera;
    private Parameters mCameraParams;
    private Boolean running = false;

    void start(SurfaceTexture surface)
    {
        Log.v(LOG_TAG, "Starting Camera");

        mCamera = Camera.open(0);
        mCameraParams = mCamera.getParameters();
        Log.v(LOG_TAG, mCameraParams.getPreviewSize().width + " x " + mCameraParams.getPreviewSize().height);

        try {
            mCamera.setPreviewTexture(surface);
            mCamera.startPreview();
            running = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    void stop()
    {
        if (running) {
            Log.v(LOG_TAG, "Stopping Camera");
            mCamera.stopPreview();
            mCamera.release();
            running = false;
        }
    }
}

MyGLSurfaceView.java

<pre><code>class MyGLSurfaceView extends GLSurfaceView implements Renderer
{
    private final static String LOG_TAG = "MyGLSurfaceView";
    private MyCamera mCamera;
    private SurfaceTexture mSurface;
    private DirectVideo mDirectVideo;

    public MyGLSurfaceView(Context context, MyCamera camera)
    {
        super(context);

        mCamera = camera;
        setEGLContextClientVersion(2);

        setRenderer(this);
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        float[] mtx = new float[16];
        mSurface.updateTexImage();
        mSurface.getTransformMatrix(mtx);

        mDirectVideo.draw();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        Log.v(LOG_TAG, "Surface Changed");
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        Log.v(LOG_TAG, "Surface Created");
        int texture = createTexture();
        mDirectVideo = new DirectVideo(texture);
        mSurface = new SurfaceTexture(texture);
        mCamera.start(mSurface);
    }

    private int createTexture()
    {
        int[] textures = new int[1];

        // generate one texture pointer and bind it as an external texture.
        GLES20.glGenTextures(1, textures, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);

        // No mip-mapping with camera source.
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        // Clamp to edge is only option.
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

        return textures[0];
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

DirectVideo.java

<pre><code>public class DirectVideo
{
    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
            "attribute vec2 inputTextureCoordinate;" +
            "varying vec2 textureCoordinate;" +
            "void main()" +
            "{"+
                "gl_Position = vPosition;"+
                "textureCoordinate = inputTextureCoordinate;" +
            "}";

    private final String fragmentShaderCode =
            "#extension GL_OES_EGL_image_external : require\n"+
            "precision mediump float;" +
            "varying vec2 textureCoordinate;\n" +
            "uniform samplerExternalOES s_texture;\n" +
            "void main() {" +
            "  gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
            "}";

    private FloatBuffer vertexBuffer, textureVerticesBuffer;
    private ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mTextureCoordHandle;

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    // number of coordinates per vertex in this array
    private static final int COORDS_PER_VERTEX = 2;

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    static float squareCoords[] = {
       -1.0f,  1.0f,
       -1.0f, -1.0f,
        1.0f, -1.0f,
        1.0f,  1.0f,
    };

    static float textureVertices[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        1.0f, 0.0f,
        0.0f, 0.0f,
    };

    private int texture;

    public DirectVideo(int texture)
    {
        this.texture = texture;

        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
        bb2.order(ByteOrder.nativeOrder());
        textureVerticesBuffer = bb2.asFloatBuffer();
        textureVerticesBuffer.put(textureVertices);
        textureVerticesBuffer.position(0);

        int vertexShader    = MyGLSurfaceView.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader  = MyGLSurfaceView.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // creates OpenGL ES program executables
    }

    public void draw()
    {
        GLES20.glUseProgram(mProgram);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the &lt;insert shape here&gt; coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

        mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
        GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
        GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
    }
}

In the onDrawFrame method you get a transformation matrix, but you don’t use it. This matrix should be used to transform texture coordinates. For details see the documentation of the SurfaceTexture class.

Here is the fix:

  1. pass the matrix to the the draw method:
    @Override
    public void onDrawFrame(GL10 gl)
    {
        float[] mtx = new float[16];
        mSurface.updateTexImage();
        mSurface.getTransformMatrix(mtx);    
    
        mDirectVideo.draw(mtx);
    }
    
  2. Add the following method to the DirectVideo class:
     private float[] transformTextureCoordinates( float[] coords, float[] matrix)
     {          
        float[] result = new float[ coords.length ];        
        float[] vt = new float[4];      
    
        for ( int i = 0 ; i < coords.length ; i += 2 ) {
            float[] v = { coords[i], coords[i+1], 0 , 1  };
            Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
            result[i] = vt[0];
            result[i+1] = vt[1];
        }
        return result;
     }
    
  3. In the draw method Convert the textureVertices list before adding to the buffer ( you should do this conversion at every paint since the matrix can change):
    textureVerticesBuffer.clear();
    textureVerticesBuffer.put( transformTextureCoordinates( textureVertices, mtx ));
    textureVerticesBuffer.position(0);
    

An alternative solution is if you pass the matrix to the shader.


 



[转载]Android开发学习总结(六)—— APK反编译 - 孤傲苍狼 - 博客园

mikel阅读(1191)

来源: [转载]Android开发学习总结(六)—— APK反编译 – 孤傲苍狼 – 博客园

  学习和开发Android应用有一段时间了,今天写一篇博客总结一下Android 的apk文件反编译。我们知道,Android应用开发完成之后,我们最终都会将应用打包成一个apk文件,然后让用户通过手机或者平板电脑下载下来进行 安装。正常情况下,Android应用打包成apk之后,就无法再看到开发这个应用时使用的资源文件以及代码了。但是我们通过网上提供了一些工具,还是可 以将apk进行反编译的,apk反编译之后,我们就可以看到开发这个应用使用的资源文件(图片)、layout、样式、相关的实现代码等,apk反编译也 算是Android开发中一个比较实用的技巧吧,当我们对别人开发好的应用感兴趣时,我们就可以通过这种技术手段将别人打包好的apk进行反编译,继而可 以看到我们感兴趣的内容,(注:反编译不是让各位开发者去对一个应用破解搞重装什么的,主要目的是为了促进开发者学习,借鉴好的代码,提升自我开发水平。)下面就来说说如何将一个apk反编译出来。

一、准备必要工具

工欲善其事,必先利其器,首先我们要下载好反编译apk时需要的相关工具

1.1、使用工具

  1. apktool (资源文件获取)
  2. dex2jar(源码文件获取)
  3. jd-gui  (源码查看)

1.2、工具介绍

  apktool  

         作用:资源文件获取,可以提取出图片文件和布局文件进行使用查看

  dex2jar

         作用:将apk反编译成java源码(classes.dex转化成jar文件)

  jd-gui

         作用:查看APK中classes.dex转化成出的jar文件,即源码文件

1.3工具下载

apktool下载地址:https://bitbucket.org/iBotPeaches/apktool/downloads

下载好之后得到一个如下图所示的jar文件

dex2jar下载地址:http://sourceforge.net/projects/dex2jar/files/

下载完成之后,得到一个如下图所示的压缩包

jd-gui下载地址:http://jd.benow.ca/

下载完成之后,得到一个如下图所示的压缩包:

到此,需要使用到的3个相关工具都下载好了,在这里说明一下jd-gui的下载,我从官方网站上点击下载时会经常出现如下图所示的问题

但是多试几次又可以下载了,所以如果有遇到这个问题的朋友们不妨多试几次,或者从别的地方下载jd-gui,jd-gui算是做Java开发的 一个必备工具了,用它来将class反编译成java源代码是非常方便的,网上搜索一下一般都可以下载到,只不过版本不一定是最新的。

二、Apk反编译流程

为了方便进行反编译,我们将上述下载好的3个工具统一放到一个文件夹中,例如:

然后将【dex2jar-2.0.zip】和【jd-gui-windows-1.3.0.zip】分别解压到当前文件夹,如下图所示:

2.1、使用apktool反编译apk得到图片、XML配置、语言资源等文件

进入CMD命令行,如下:

切换到上述的3个工具的所在目录,如:E:\AndroidDevelopTool\Android反编译工具包

接下来我们要做的就是运行apktool_2.0.1.jar这个jar文件来将apk文件进行反编译,在java中,运行可执行jar包的命令是:

java -jar jar包名.jar

使用如下的命令运行apktool_2.0.1.jar反编译MMTS-release-1.0.2.apk

java -jar apktool_2.0.1.jar d -f E:\AndroidDevelopTool\Android反编译工具包\测试apk\MMTS-release-1.0.2.apk -o MMTS

这个命令是启动apktool_2.0.1.jar将位于【E:\AndroidDevelopTool\Android反编译工具包\测试 apk\】目录下的”MMTS-release-1.0.2.apk”这个apk反编译,然后将反编译生成的文件存放到当前目录 (apktool_2.0.1.jar所在的目录,也就是”E:\AndroidDevelopTool\Android反编译工具包”目录)下的一个 【MMTS】文件夹中。这个文件夹的名字是可以随便取的,喜欢叫啥都行。

执行反编译操作:

反编译成功后,会在当前目录(E:\AndroidDevelopTool\Android反编译工具包)下生成一个MMTS文件夹,打开MMTS里面就有反编译后生成的文件,如下图所示:

打开MMTS文件夹,就可以看到反编译后的生成的文件,如下图所示:

生成的文件和文件夹当中,我们关心的是【res】文件夹中和AndroidManifest.xml文件,打开res文件夹,里面就有我们想要看到的东西了,如下图所示:

想查看哪个xml文件就使用文本编辑器打开看看吧,反正全部都可以看到了。以上就是使用apktool这个工具将一个apk反编译得到图片、XML配置、语言资源等文件的过程。

2.2、使用dex2jar反编译apk得到Java源代码

将要反编译的APK后缀名改为.rar或者 .zip,并解压,得到其中的classes.dex文件(它就是java文件编译再通过dx工具打包而成的),如下图所示:

将获取到的classes.dex放到之前解压出来的工具【dex2jar-2.0】文件夹内,如下图所示:

在命令行下定位到dex2jar.bat所在目录,输入”d2j-dex2jar classes.dex“,效果如下:

命令执行完成之后,在当前目录下就可以看到生成的Jar文件了,如下图所示:

反编译classes.dex得到classes-dex2jar.jar文件之后,就可以使用【jd-gui】工具将class文件反编译成java源代码了

使用jd-gui打开classes-dex2jar.jar就可以看到源代码了,如下图所示:

JD-GUI虽然可以将class反编译成java源代码,但是对于一些被混淆过的class,反编译的效果就不是那么理想了,被混淆过的class反编译后的效果图(类文件名称以及里面的方法名称都会以a,b,c….之类的样式命名):

以上步骤是我亲自实践过之后一步步整理出来的,对照着做应该不会有太大问题。

三、Apk反编译注意问题

3.1、apktool版本太旧导致反编译失败的问题

之前用过了apktool这个工具的一些旧版本,发现总是反编译不成功,在执行反编译时会出现如下错误:

Exception in thread “main” brut.androlib.AndrolibException: Could not decode arsc file

这个问题是因为apktool版本过低导致,而解决这个问题的办法就是使用最新版本的apktool就可以了,最新版本的下载地址:https://bitbucket.org/iBotPeaches/apktool/downloads

3.2、apktool执行反编译命令出现”Input file was not found or was not readable”的问题

这个问题是因为apktool升级到2.0以上时,使用方式已经替换,格式为:apktool d [-s] -f <apkPath> -o <folderPath>

好了,关于反编译apk的内容就这么多了。
最后,给大家分享我下载好的那三个相关工具,下载地址:http://pan.baidu.com/s/1jGKSQyU

[转载]IOS 通过摄像头读取每一帧的图片,并且做人脸识别(swift) - qg - 博客园

mikel阅读(1093)

来源: [转载]IOS 通过摄像头读取每一帧的图片,并且做人脸识别(swift) – qg – 博客园

最近帮别人做一个项目,主要是使用摄像头做人脸识别

github地址:https://github.com/qugang/AVCaptureVideoTemplate

要使用IOS的摄像头,需要使用AVFoundation 库,库里面的东西我就不介绍。

启动摄像头需要使用AVCaptureSession 类。

然后得到摄像头传输的每一帧数据,需要使用AVCaptureVideoDataOutputSampleBufferDelegate 委托。

首先在viewDidLoad 里添加找摄像头设备的代码,找到摄像头设备以后,开启摄像头

复制代码
        captureSession.sessionPreset = AVCaptureSessionPresetLow
        let devices = AVCaptureDevice.devices()
        for device in devices {
            if (device.hasMediaType(AVMediaTypeVideo)) {
                if (device.position == AVCaptureDevicePosition.Front) {
                    captureDevice = device as?AVCaptureDevice
                    if captureDevice != nil {
                        println("Capture Device found")
                        beginSession()
                    }
                }
            }
        }
复制代码

beginSession,开启摄像头:

复制代码
func beginSession() {
        var err : NSError? = nil
        captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))
        let output = AVCaptureVideoDataOutput()
        
        let cameraQueue = dispatch_queue_create("cameraQueue", DISPATCH_QUEUE_SERIAL)
        output.setSampleBufferDelegate(self, queue: cameraQueue)
        output.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_32BGRA]
        captureSession.addOutput(output)
        
        
        if err != nil {
            println("error: \(err?.localizedDescription)")
        }
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer?.videoGravity = "AVLayerVideoGravityResizeAspect"
        previewLayer?.frame = self.view.bounds
        self.view.layer.addSublayer(previewLayer)
        
        captureSession.startRunning()
    }
复制代码

开启以后,实现captureOutput 方法:

复制代码
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
        
        if(self.isStart)
        {
            let resultImage = sampleBufferToImage(sampleBuffer)
            
            let context = CIContext(options:[kCIContextUseSoftwareRenderer:true])
            let detecotr = CIDetector(ofType:CIDetectorTypeFace,  context:context, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh])
            
            
            
            
            let ciImage = CIImage(image: resultImage)
            
            let results:NSArray = detecotr.featuresInImage(ciImage,options: ["CIDetectorImageOrientation" : 6])
            
            for r in results {
                let face:CIFaceFeature = r as! CIFaceFeature;
                let faceImage = UIImage(CGImage: context.createCGImage(ciImage, fromRect: face.bounds),scale: 1.0, orientation: .Right)
                
                NSLog("Face found at (%f,%f) of dimensions %fx%f", face.bounds.origin.x, face.bounds.origin.y,pickUIImager.frame.origin.x, pickUIImager.frame.origin.y)
                
                dispatch_async(dispatch_get_main_queue()) {
                    if (self.isStart)
                    {
                        self.dismissViewControllerAnimated(true, completion: nil)
                        self.didReceiveMemoryWarning()
                        
                        self.callBack!(face: faceImage!)
                    }
                    self.isStart = false
                }
            }
        }
    }
复制代码

在每一帧图片上使用CIDetector 得到人脸,CIDetector 还可以得到眨眼,与微笑的人脸,如果要详细使用去官方查看API

上面就是关键代码,设置了有2秒的延迟,2秒之后开始人脸检测。

全部代码:

复制代码
//
//  ViewController.swift
//  AVSessionTest
//
//  Created by qugang on 15/7/8.
//  Copyright (c) 2015年 qugang. All rights reserved.
//

import UIKit
import AVFoundation

class AVCaptireVideoPicController: UIViewController,AVCaptureVideoDataOutputSampleBufferDelegate {
    
    var callBack :((face: UIImage) ->())?
    let captureSession = AVCaptureSession()
    var captureDevice : AVCaptureDevice?
    var previewLayer : AVCaptureVideoPreviewLayer?
    var pickUIImager : UIImageView = UIImageView(image: UIImage(named: "pick_bg"))
    var line : UIImageView = UIImageView(image: UIImage(named: "line"))
    var timer : NSTimer!
    var upOrdown = true
    var isStart = false
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        captureSession.sessionPreset = AVCaptureSessionPresetLow
        let devices = AVCaptureDevice.devices()
        for device in devices {
            if (device.hasMediaType(AVMediaTypeVideo)) {
                if (device.position == AVCaptureDevicePosition.Front) {
                    captureDevice = device as?AVCaptureDevice
                    if captureDevice != nil {
                        println("Capture Device found")
                        beginSession()
                    }
                }
            }
        }
        pickUIImager.frame = CGRect(x: self.view.bounds.width / 2 - 100, y: self.view.bounds.height / 2 - 100,width: 200,height: 200)
        line.frame = CGRect(x: self.view.bounds.width / 2 - 100, y: self.view.bounds.height / 2 - 100, width: 200, height: 2)
        self.view.addSubview(pickUIImager)
        self.view.addSubview(line)
        timer =  NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "animationSate", userInfo: nil, repeats: true)
        
        NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "isStartTrue", userInfo: nil, repeats: false)
    }
    
    func isStartTrue(){
        self.isStart = true
    }
    
    override func didReceiveMemoryWarning(){
        super.didReceiveMemoryWarning()
        captureSession.stopRunning()
        
    }
    
    
    
    
    func animationSate(){
        if upOrdown {
            if (line.frame.origin.y >= pickUIImager.frame.origin.y + 200)
            {
                upOrdown = false
            }
            else
            {
                line.frame.origin.y += 2
            }
        } else {
            if (line.frame.origin.y <= pickUIImager.frame.origin.y)
            {
                upOrdown = true
            }
            else
            {
                line.frame.origin.y -= 2
            }
        }
    }
    
    
    
    
    
    
    func beginSession() {
        var err : NSError? = nil
        captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))
        let output = AVCaptureVideoDataOutput()
        
        let cameraQueue = dispatch_queue_create("cameraQueue", DISPATCH_QUEUE_SERIAL)
        output.setSampleBufferDelegate(self, queue: cameraQueue)
        output.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_32BGRA]
        captureSession.addOutput(output)
        
        
        if err != nil {
            println("error: \(err?.localizedDescription)")
        }
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer?.videoGravity = "AVLayerVideoGravityResizeAspect"
        previewLayer?.frame = self.view.bounds
        self.view.layer.addSublayer(previewLayer)
        
        captureSession.startRunning()
    }
    func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
        
        if(self.isStart)
        {
            let resultImage = sampleBufferToImage(sampleBuffer)
            
            let context = CIContext(options:[kCIContextUseSoftwareRenderer:true])
            let detecotr = CIDetector(ofType:CIDetectorTypeFace,  context:context, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh])
            
            
            
            
            let ciImage = CIImage(image: resultImage)
            
            let results:NSArray = detecotr.featuresInImage(ciImage,options: ["CIDetectorImageOrientation" : 6])
            
            for r in results {
                let face:CIFaceFeature = r as! CIFaceFeature;
                let faceImage = UIImage(CGImage: context.createCGImage(ciImage, fromRect: face.bounds),scale: 1.0, orientation: .Right)
                
                NSLog("Face found at (%f,%f) of dimensions %fx%f", face.bounds.origin.x, face.bounds.origin.y,pickUIImager.frame.origin.x, pickUIImager.frame.origin.y)
                
                dispatch_async(dispatch_get_main_queue()) {
                    if (self.isStart)
                    {
                        self.dismissViewControllerAnimated(true, completion: nil)
                        self.didReceiveMemoryWarning()
                        
                        self.callBack!(face: faceImage!)
                    }
                    self.isStart = false
                }
            }
        }
    }
    private func sampleBufferToImage(sampleBuffer: CMSampleBuffer!) -> UIImage {
        let imageBuffer: CVImageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)
        CVPixelBufferLockBaseAddress(imageBuffer, 0)
        let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)
        
        let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
        let width = CVPixelBufferGetWidth(imageBuffer)
        let height = CVPixelBufferGetHeight(imageBuffer)
        
        let colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()
        
        let bitsPerCompornent = 8
        var bitmapInfo = CGBitmapInfo((CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue) as UInt32)
        
        
        let newContext = CGBitmapContextCreate(baseAddress, width, height, bitsPerCompornent, bytesPerRow, colorSpace, bitmapInfo) as CGContextRef
        
        let imageRef: CGImageRef = CGBitmapContextCreateImage(newContext)
        let resultImage = UIImage(CGImage: imageRef, scale: 1.0, orientation: UIImageOrientation.Right)!
        
        return resultImage
    }
    
    func imageResize (imageObj:UIImage, sizeChange:CGSize)-> UIImage{
        
        let hasAlpha = false
        let scale: CGFloat = 0.0 
        
        UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
        imageObj.drawInRect(CGRect(origin: CGPointZero, size: sizeChange))
        
        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        return scaledImage
    }
}
复制代码