百度空间 | 百度首页 
 
查看文章
 
[原创]flash人脸抓取技术,半成品
2008-12-14 16:26
这几天,通过晨会,知道同事正在做一个贺卡的项目。
需要用户选择一张照片,然后将头像放大,缩小并移动到一个64×64的范围。
这些功能都通过javascript实现,然后上传到服务端,然后在flash端,将用户上传的头像,放置在一个头部容器中,这样,卡片中人物的头像就会随着预先设定的舞蹈运动。

但是通过指定64×64的方格而选定的头像是没有办法将背景去除的,我想到以前我做.NET项目的时候,用到的一个被称为木刻的算法,会将图像极端化,处 理成只有黑和白的两种颜色。而在人像摄影中,脸部的边缘部分会因为阴影的原因产生一个比较大的色差,最后使用木刻算法会有一个明显的边缘。那么我是否可以 利用这种算法,将人脸的轮廓取出呢?

带着这个问题,我开始了我的测试。

首先说说木刻算法:由于24位图片的储存规格实际是一个宽×长的矩阵,其中每个点储存的是一个24位颜色值,可以转化为红(Red),绿(Green),蓝(Blue)三个颜色的分量。
每个分量是个8bit值,即能保存0~255(2的8次方)种值。
而木刻算法就是通过找到R,G,B三个分量中最大值和最小值,然后得到(最大值+最小值)/2之后的中间值(还有一种算法是直接找到R,G,B三个分量的平均值)。
此中间值大于我们预先定义的一个阀值,则让颜色值为0xffffff(即白色),否则让颜色值为0(即黑色)。
以前我是用C#写的,现在需要先修改成AS3的代码:

        private function WoodCarving(bmpData:BitmapData):BitmapData
        {
            var newData:BitmapData = new BitmapData(bmpData.width,bmpData.height,false);
            for(var y:int = 0;y<bmpData.height;y++)
            {
                for(var x:int = 0;x<bmpData.width;x++)
                {
                    //取得颜色值
                    var color:uint = bmpData.getPixel(x,y);
                    //取得RGB分量
                    var r:uint     = color>>16;
                    var g:uint     = color>>8 & 0xff;
                    var b:uint    = color & 0xff;
                   
                    var min:uint = r;
                    var max:uint = r;
                   
                    if (g < min)
                        min = g;
                    if (b < min)
                        min = b;
                    if (g > max)
                        max = g;
                    if (b > max)
                        max = b;
                    //获取中间值
                    var gr:uint = (int)((min + max) / 2);
                    if (gr < _value)
                        gr = 0;
                    else
                        gr = 0xffffff;
                    newData.setPixel(x,y,gr);
                }
            }   
            return newData;
        }

这可以将

转换为


然后我的思路是建立一个3*3的矩阵(除去中间点的8个点)。
var arr:Array             = [[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];
让检测点为中心点,探测每一个黑色(颜色值为0)的像素周围的8个点中,如果白色点超过2个以上,我将该点视为边缘点。
bmpData为木刻效果之后的图像数据:
var shapeData:BitmapData = new BitmapData(bmpData.width,bmpData.height,false,0xffffff);
var xPixel:Array = [];
for(var y:int = 0;y<bmpData.height;y++)
            {
                for(var x:int = 0;x<bmpData.width;x++)
                {
                    var tt:uint = bmpData.getPixel(x,y);
                    if( tt == 0 )
                    {
                        var blackCount:uint = 0;
                        var whiteCount:uint = 0;
                        for each( var zz:Array in arr )
                        {
                            if( bmpData.getPixel(x + zz[0],y+zz[1]) == 0 )
                            {
                                blackCount++;
                            }
                            else
                            {
                                whiteCount++;
                            }
                        }
                        var dis:uint = blackCount > whiteCount ? (blackCount - whiteCount) : (whiteCount - blackCount);
                        if( whiteCount > 2 )
                        {
                            xPixel.push(x);
                            shapeData.setPixel(x,y,0xff);
                        }
                    }
                }
            }
我将数据保存在shapeData这个新的bitmapData中。
将x坐标保存到xPixel中。

先将可能的轮廓点找到:
得到这个图像:


创建左数组var leftPixel:Array = [];
以及右数组var rightPixel:Array = [];

先找到图像的中心:
xPixel.sort(Array.NUMERIC);
var middleX:Number = (middleX[0] + middleX[openPixel.length-1])/2;
以保证leftPixel数组中保存的都是在middleX左边的坐标,而rightPixel数组中保存的都是在middleX右边的坐标。

获取leftPixel和rightPixel的数据信息:

            var lastPoint:Object;
            for(y = 0;y<shapeData.height;y++)
            {
                if(lastPoint )
                {
                    if( lastPoint.x > middleX )
                    rightPixel.push(lastPoint);
                }
                var count:uint = 0;
                for( x=0;x<shapeData.width;x++)
                {
                    if( shapeData.getPixel(x,y) == 0xff )
                    {
                        if( !count )
                        {
                            if( x < middleX )
                            leftPixel.push({x:x,y:y});
                        }
                        lastPoint = {x:x,y:y};
                        count++;
                    }
                }
            }

最终使用画线的方法将需要做遮罩的区域绘制成黑色。
            var shape:Shape = new Shape();
            shape.graphics.beginFill(0,1);
            shape.graphics.lineStyle(1,1);
            for( var txx:int = 0;txx<rightPixel.length;txx++)
            {
                if( txx==0)
                {
                    shape.graphics.moveTo(rightPixel[txx].x,rightPixel[txx].y);
                }
                else
                {
                    shape.graphics.lineTo(rightPixel[txx].x,rightPixel[txx].y);
                }
            }
           
            for( txx = leftPixel.length-1;txx>=0;txx--)
            {
                shape.graphics.lineTo(leftPixel[txx].x,leftPixel[txx].y);
            }
            shape.graphics.endFill();
并将其作为图像的遮罩:
最终效果:
我的方法在最终绘制的时候还存在一定问题,按我想象应该是可以采取顺时针找点的方式,但是我始终没有研究出确实的算法,所以人脸抓取只能算个半成品吧。

头像为公司一帅哥Eric,单身,收入稳定,有意者请于本人联系

类别:flex/flash | 添加到搜藏 | 浏览() | 评论 (8)
 
最近读者:
 
网友评论:
1
2008-12-15 20:17 | 回复
这篇文章真是多功能啊……还带交友的……
 
2
2008-12-15 20:18 | 回复
以后山寨数码相机的人脸识别算法就叫给你了!
 
3
2008-12-16 08:06 | 回复
哈哈,这里是婚姻介绍所吗??
 
4
2008-12-19 16:10 | 回复
O(∩_∩)O哈哈~
 
5
2008-12-21 20:15 | 回复
哇 学习一下 看看能不能看懂...
 
6
2008-12-23 12:25 | 回复
令人敬畏啊……Merry Xmas……
 
7
2008-12-30 20:32 | 回复
呵呵,很厉害的小伙嘛…… ^_^
 
8
2009-07-16 15:06 | 回复
很强的技术,以前在facebook里面见过这种功能,但一直不知道是什么技术,今天看了,深有感触,高。。。
 
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2009 Baidu