Android Camera图像处理学习

学习资料

十分感谢GcsSloop,直接去看他的博客学习,图文并茂


Android中有2个Cameraandroid.hardware.Cameraandroid.graphics.Camera,看包名就可以区分出两个Camera的作用。hardware硬件,是摄像头;graphics,图像,用于图形处理。android.hardware.Camera5.0后被废弃,由android.hardware.Camera2代替,具体使用可以看看Android Camera2 拍照入门学习 : )


1. android.graphics.Camera <p>

A camera instance can be used to compute 3D transformations and generate a matrix that can be applied, for instance, on a Canvas.

Camera对象可以用来计算3D变换,并将计算结果封装进一个Matrix矩阵,之后便进行应用(ps:我这都啥破翻译)

Camera可以用在画布上 。计算过程进行了封装,内部有一个Matrix

只有一个空的构造方法Camera()

Camera既然可以用来做3D三维变换,坐标系就和之前学习遇到的二维坐标系不同,是左手坐标系统,Y轴在三维坐标系中是向上的,Z轴是朝向屏幕里面的

3维左手坐标系

Z轴是朝里面的,屏幕像一个窗口,我们看到的是窗口外面的物体投射到窗口上的二维镜像。Camera实际上就像我们的眼睛,眼睛看到的是物体投射到窗口上的图形,其实这里就有3个要素,一是物体,二是窗子,三是眼睛,也就是物体,屏幕和Camera。最终呈现在用户面前的是屏幕上的图形。影响物体投射到屏幕上的效果,可以移动物体(Matrix),也可以移动眼睛(setLocation()方法)

以上从麻花儿wt的android camera 3d特效 详解与进阶摘抄


在一个虚拟的3D的立体空间中,由于我们无法直接用眼睛去观察这一个空间,所以要借助摄像机采集信息,制成2D影像供我们观察。简单来说,摄像机就是我们观察虚拟3D空间的眼睛。<p>
Android 上面观察View的摄像机默认位置在屏幕左上角,而且是距屏幕有一段距离的,假设灰色部分是手机屏幕,白色是上面的一个View,摄像机位置看起来大致就是下面这样子的(为了更好的展示摄像机的位置,做了一个空间转换效果的动图)。

Camera位置

Camera默认距离为-8172像素,在坐标系中(0,0,-576)

以上从GcsSloop 安卓自定义View进阶-Matrix Camera摘抄


对于一个像素点的矩阵来说

一个像素点矩阵

1CameraZ轴的默认值,比1大,代表相对于默认位置,将屏幕进行拉远。远小近大,值越大就越远,投射到Camera上的图像也就也小


2. 方法 <p>

Camera中方法并不算多

原始图像

原始图像只是将Bitmap宽高缩小为1/2后,绘制在了画布上


2.1 translate(float x, float y, float z) <p>

方法的名字和参数都比较容易理解,这个方法可以理解为移动了物体,但z这个值,会影响x,y的值。

例如mCamera.translate(100,0,100)x的值为100,但最终显示效果,图像右移的距离并没有100。z值导致图像缩放,移动距离也进行了缩放

Y轴是向上的,mCamera.translate(0,30,0)mMatrix.translate(0,-30)效果是一样的


2.2 rotate(float x, float y, float z) <p>

这个方法是rotateX(float x),rotateY(float y),rotateZ(float z)的综合

以默认点左上角为中心绕值不为0的轴顺时针进行旋转

例如rotate(30,0,0),就是以左上角为中心,绕X轴顺时针旋转30°
x的值为角度,0°到360°


简单使用:

    /**
     * 初始化
     */
    private void init() {
        mCamera = new Camera();
        mMatrix = new Matrix();
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mCamera.save();
        mCamera.rotate(180,0,0);
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        mMatrix.postTranslate(0,500);
        canvas.drawBitmap(mBitmap,mMatrix, null);
    }
以控件左上角为旋转中心,绕x轴旋转180°

mMatrix.postTranslate(0,500),矩阵后乘,由于图片默认以左上角为旋转中心,绕着x轴旋转180°,图片已经超出了屏幕,使用后乘平移,将图片再移动到屏幕范围内。不可以setTanslate(),因为set方法会将原先的矩阵的值清空,成为初始矩阵

其他同理


2.3 setLocation(float x, float y, float z) <p>

设置相机的位置,默认为-8

相机在默认位置,绕Y轴旋转30°

简单修改代码,修改相机的位置

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mCamera.save();
        mCamera.setLocation(0,0,-30);//设置相机位置
        mCamera.rotate(0,30,0);
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        mMatrix.postTranslate(0,500);
        canvas.drawBitmap(mBitmap,mMatrix, null);
    }
修改相机位置后

Y轴方向是向屏幕里的,所以-30-8要离物体远,这样旋转的最终效果就会减轻,根据生活经验,手机拍照时,手机距离拍摄物的远近,物体进行同样角度的改变,照片的效果并不完全一样。但Z轴的值并不会影响平移操作


3.结合动画,形成简单的3D效果 <p>

代码是从GcsSloop 安卓自定义View进阶-Matrix Camera学到,里面涉及到的优化原理讲的很好

矩阵值的影响

动画代码:

public class Rotate3DAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private final float mDepthZ;
    private final boolean mReverse;
    private Camera mCamera;
    float scale = 1;    // 像素密度

    /**
     * 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
     *  @param context     <------- 添加上下文,为获取像素密度准备
     * @param fromDegrees 起始时角度
     * @param toDegrees   结束时角度
     * @param centerX     旋转中心x坐标
     * @param centerY     旋转中心y坐标
     * @param depthZ      最远到达的z轴坐标
     * @param reverse     true 表示由从0到depthZ,false相反
     */
    public Rotate3DAnimation(Context context, float fromDegrees, float toDegrees,
                             float centerX, float centerY, float depthZ, boolean reverse) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;
        mReverse = reverse;
        // 像素密度
        scale = context.getResources().getDisplayMetrics().density;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;
        final Matrix matrix = t.getMatrix();
        camera.save();

        // 调节深度
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }

        // 绕y轴旋转
        camera.rotateY(degrees);

        camera.getMatrix(matrix);
        camera.restore();

        // 修正失真,主要修改 MPERSP_0 和 MPERSP_1
        float[] mValues = new float[9];
        matrix.getValues(mValues);              //获取数值
        mValues[6] = mValues[6]/scale;          //数值修正
        mValues[7] = mValues[7]/scale;          //数值修正
        matrix.setValues(mValues);              //重新赋值
        // 调节中心点
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

Activity代码:

public class CameraGActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_g);
        initView();
    }

    private void initView() {
        ImageView iv = (ImageView) findViewById(R.id.iv_camera_g_activity);
        iv.setImageResource(R.drawable.test);
        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 计算中心点(这里是使用view的中心作为旋转的中心点)
                final float centerX = v.getWidth() / 2.0f;
                final float centerY = v.getHeight() / 2.0f;

                //括号内参数分别为(上下文,开始角度,结束角度,x轴中心点,y轴中心点,深度,是否扭曲)
                final Rotate3DAnimation rotation = new Rotate3DAnimation(CameraGActivity.this, 0, 360, centerX, centerY, 0f, true);
                rotation.setDuration(3000);
                rotation.setFillAfter(true);
                rotation.setInterpolator(new LinearInterpolator());
                v.startAnimation(rotation);
            }
        });
    }
}

围绕图片中心旋转

优化就是修正失真,主要修改 MPERSP_0MPERSP_1


4. 最后

本篇内容的学习,是最近学习过程中,效率最低的,Camera与物体,屏幕三者的关系没有搞清楚,一开始搜到的学习博客,感觉有错误,质量不是很高。

强烈推荐学习GcsSloop的系列博客,感觉质量都很高,真正的图文并茂

本人很菜,写的很水,有错误请指出

共勉 : )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 本文行文目录:一、Camera与Matrix初步认识二、Camera与Matrix旋转效果拆分介绍三、Camera...
    zhangke3016阅读 17,131评论 18 136
  • 穿梭在人群中不为找人 站在汽车站旁不为等车 每次出发毫无目的 也无方向 牵手路过的情侣 荷尔蒙也不曾嫉妒 闻到飘来...
    嘿一只想飞的狗阅读 285评论 7 5
  • 昨日又和老爸去钓鱼了,心情很复杂。纠结复杂的源头,一方面是因为钓鱼过程中的所见,另一方面或许是心里又蹦哒出来朋友传...
    beyound20阅读 233评论 0 0
  • 13.自动加载类 __autoload()函数 很多开发者写面向对象的应用程序时,对每个类的定义建立一个 PHP ...
    PHPer_阅读 291评论 0 0