汪建军的博客

休迅飞凫,飘忽若神,凌波微步,罗袜生尘。

Android developer, sometimes thinking, sometimes try it.


Android下图片清晰度识别

还是公司项目的一个需求:判断用户拍的照片是否清晰,不清晰就不让提交。

核心思想:使用opencv的拉普拉斯算法检测图片的边缘数,越模糊的图片边缘数越少。

刚接触的小伙伴可以看一下这篇启发性文章

关于OpenCV

OpenCV是一款开源的跨平台的计算机视觉库,提供了图像处理与计算机视觉多种算法。由C/C++编写,通过jni可以轻松在Android开发上使用。
opencv3-Android-sdk的下载地址
gradle:compile 'org.opencv:OpenCV-Android:3.1.0'
OpenCV想要深入比较难,需要许多专业的知识储备,有兴趣的同学可以参考这里
opencv有两个比较重要的类MatImgproc。这里我们只需要了解到Mat是图像矩阵化后的信息,下面出现的CV_8U这些类型表示通道阵列的数据类型,具体可以查看这篇文章。Imgproc包含了多种图像处理的算法。

先贴代码:

public static boolean isBlurByOpenCV(Bitmap image) {
    System.out.println("image.w=" + image.getWidth() + ",image.h=" + image.getHeight());
    int l = CvType.CV_8UC1; //8-bit grey scale image
    Mat matImage = new Mat();
    Utils.bitmapToMat(image, matImage);
    Mat matImageGrey = new Mat();
    Imgproc.cvtColor(matImage, matImageGrey, Imgproc.COLOR_BGR2GRAY); // 图像灰度化

    Bitmap destImage;
    destImage = Bitmap.createBitmap(image);
    Mat dst2 = new Mat();
    Utils.bitmapToMat(destImage, dst2);
    Mat laplacianImage = new Mat();
    dst2.convertTo(laplacianImage, l);
    Imgproc.Laplacian(matImageGrey, laplacianImage, CvType.CV_8U); // 拉普拉斯变换
    Mat laplacianImage8bit = new Mat();
    laplacianImage.convertTo(laplacianImage8bit, l);

    Bitmap bmp = Bitmap.createBitmap(laplacianImage8bit.cols(), laplacianImage8bit.rows(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(laplacianImage8bit, bmp);
    int[] pixels = new int[bmp.getHeight() * bmp.getWidth()];
    bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight()); // bmp为轮廓图

    int maxLap = -16777216; // 16m
    for (int pixel : pixels) {
        if (pixel > maxLap)
            maxLap = pixel;
    }
    int userOffset = -3881250; // 界线(严格性)降低一点
    int soglia = -6118750 + userOffset; // -6118750为广泛使用的经验值
    System.out.println("maxLap=" + maxLap);
    if (maxLap <= soglia) {
        System.out.println("这是一张模糊图片");
    }
    System.out.println("==============================================\n");
    soglia += 6118750 + userOffset;
    maxLap += 6118750 + userOffset;

    LogUtil.log("opencvanswers..result:image.w=" + image.getWidth() + ", image.h=" + image.getHeight()
            + "\nmaxLap= " + maxLap + "(清晰范围:0~" + (6118750 + userOffset) + ")"
            + "\n" + Html.fromHtml("<font color='#eb5151'><b>" + (maxLap <= soglia ? "模糊" : "清晰") + "</b></font>"));
    return maxLap <= soglia;
}

public static boolean isBlurByOpenCV(String picFilePath) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inDither = true;
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    // 通过path得到一个不超过2000*2000的Bitmap
    Bitmap image = decodeSampledBitmapFromFile(picFilePath, options, 2000, 2000);
    return isBlurByOpenCV(image);
}

public static Bitmap decodeSampledBitmapFromFile(String imgPath, BitmapFactory.Options options, int reqWidth, int reqHeight) {
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imgPath, options);
    // inSampleSize为缩放比例,举例:options.inSampleSize = 2表示缩小为原来的1/2,3则是1/3,以此类推
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(imgPath, options);
}

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
    while ((height / inSampleSize) > reqHeight || (width / inSampleSize) > reqWidth) {
        inSampleSize *= 2;
    }
    System.out.println("inSampleSize=" + inSampleSize);
    return inSampleSize;
}

说明:

1、isBlurByOpenCV重载了两种传参方式,直接传一个bitmap以及传一个图片文件地址(可以对该地址下的图片进行不超过自定义长宽的缩放)。
2、导入opencv3的sdk到你的Android工程,如果不想改动他的底层C/C++实现的源码,只需要拷贝sdk\native\libs、sdk\java\src、sdk\java\res\values\attrs.xml到你的工程。如果你只需要一些基础功能,libs下各个架构只留一个libopencv_java3.so就够了。
3、别忘了在调用的类里添加

    static {
        System.loadLibrary("opencv_java3");
    }

以上,禁止商业用途转载,其他转载请告知,谢谢!

最近的文章

Android下实现选取最大矩形-OpenCV与Card.io库

还是公司的项目需求:用户拍照提供银行卡照片,需要自动截取出卡片。当用户对准卡片拍照,那么照片中卡片基本上是在中间而且是整个图片中最大的矩形,那么问题转换为如何截取图片中最大的矩形。 这里提供两种解决途…

杂技继续阅读
更早的文章

Android实现图片相似度

最近公司有一个需求,就是希望能判断用户提交的照片是否是身份证的正面或者反面。可以通过预设一张拍摄清晰的身份证正面或者反面,来对比是否相似,那么问题就转化为如何计算两张图片相似度。找到一篇阮一峰老师当年…

举个例子继续阅读
comments powered by Disqus