移动开发最新在Android上使用OpenCV创建和优化自定义模糊_android opencv,android面试题2024基础

总结

这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码
因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

        innerMask.setTo(Scalar.all(0.0))

        //thickness set to -1 for inner fill
        Imgproc.rectangle(
            innerMask,
            Rect(startX,startY,endX,endY),
            Scalar.all(255.0),
            -1
        )

        val outerMask = Mat()
        Core.bitwise_not(innerMask, outerMask)

        val blurredImage = Mat()
        Imgproc.blur(imageSrc, blurredImage, Size(64.0, 64.0))

        //original image minus cutout
        val originalCut = Mat()
        Core.bitwise_and(imageSrc, outerMask, originalCut)

        //blurred image minus cutout
        val blurredCut = Mat()
        Core.bitwise_and(blurredImage, innerMask, blurredCut)

        val merge = Mat()
        Core.bitwise_or(originalCut, blurredCut, merge)

        val copy = Bitmap.createBitmap(merge.width(), merge.height(), Bitmap.Config.ARGB_8888)
        Utils.matToBitmap(merge, copy)
        copy
    }
}

![](https://img-blog.csdnimg.cn/d21811ac8f914a31a35202bb6bbc2a1b.png#pic_center)



val innerMask = Mat.zeros(imageSrc.size(), imageSrc.type())
innerMask.setTo(Scalar.all(0.0))


这里我们创建一个空图像,像素值设置为0,这意味着这是一个黑色图像



Imgproc.rectangle(innerMask, Rect(startX,startY,endX,endY), Scalar.all(255.0), -1)


上面的代码意味着我们正在创建的黑色图像中绘制一个矩形。起点和终点在rect对象中提供。这个区域充满了一个白色的蒙版Scalar.all(255.0)厚度为-1。根据文档,负厚度值可以填充形状,而不是绘制形状的笔划。


这里有一个黑色的蒙版。我们需要这个蒙版来执行一些位操作,并将我们的模糊部分嵌入到这个白色蒙版中,而原始图像的其余部分嵌入到黑色区域中。  
 ![](https://img-blog.csdnimg.cn/9ea461f0dbbb4f16802e95baa7e11388.png#pic_center)



val outerMask = Mat()
Core.bitwise_not(innerMask, outerMask)


从黑白图像中,我们试图反转这些片段,使黑色区域变为白色,白色区域变为黑色。它遵循使用非门的二进制运算符的基本原理。因此,当我们有一个像1110011的值,并且这里应用了一个not gate,那么1变成0,反之亦然。幸运的是,OpenCV有一个允许执行这些操作形式的函数。  
 对于外部遮罩,我们将尝试使用另一个按位运算符将原始图像像素值复制到此变量,and。这也遵循了将两个二进制数组合在一起时的按位运算的原理,即只有当所有涉及的值都非零时,才有一个真值,否则我们将得到一个假值。这样,原始图像的彩色部分保持不变,而黑色区域根据需要保持为零。



val originalCut = Mat()
Core.bitwise_and(imageSrc, outerMask, originalCut)


![在这里插入图片描述](https://img-blog.csdnimg.cn/ac825ddfa5434120ab1817a52099af3c.png#pic_center)  
 为了得到一个模糊不清的图像:



val blurredCut = Mat()
Core.bitwise_and(blurredImage, innerMask, blurredCut)


![](https://img-blog.csdnimg.cn/d9db203acad6448db0466450f1e64b66.png#pic_center)  
 最后,我们将原始切割和模糊切割相结合,得到代表两幅图像融合的单一图像。黑色像素将与模糊像素相加以产生模糊像素,从而替换两个图像之间的所有黑色像素。只要参与算术运算的一个值是非零,我们就有一个真实值。下面是它的实现方式。



val merge = Mat()
Core.bitwise_or(originalCut, blurredCut, merge)


现在merge代表了我们真正的模糊区域结果,我们正在尝试实现,就像我们使用submat一样。以下是您应该得到的结果:  
 ![](https://img-blog.csdnimg.cn/bb677e8f4d9f45999255f58fcfd66e87.png#pic_center)  
 如果我们想用同样的方法实现圆模糊,我们所要做的就是替换Imgproc.rectangle()具有Imgproc.circle()并传入绘制圆所需的参数。例如:



val circleMask = Mat()
Imgproc.circle(circleMask, org.opencv.core.Point(rowEnd/2.0, rowEnd/2.0), rowEnd/2, Scalar(1.0))


看看前面的图像,你会注意到在模糊的图像边界和原始图像的其余部分之间有一个锐利的边缘。如果我们想避免这种情况,使模糊区域看起来与原始图像混合,应该怎么做呢?如下图所示:  
 ![](https://img-blog.csdnimg.cn/4e37feafce4842849d615ae270ba1885.png#pic_center)


我们要做的事情叫做alpha混合,这是一个在背景图像上覆盖透明前景图像的过程。在图像的每个像素处,我们需要使用alpha遮罩将前景图像与背景图像结合起来。前景图像为模糊图像,背景图像为原始图像,alpha掩模由模糊黑白掩模表示。  
 以下是我们在kotlin的实现:



private fun alphaBlend(imageSrc:Mat, blurredImage: Mat, blurredMask: Mat): Mat{
val destination = Mat(imageSrc.size(), imageSrc.type())

    for (col in 0 until destination.cols()){
        for (row in 0 until destination.rows()){
            val pixel = imageSrc.get(row, col)
            val blurredPixel = blurredImage.get(row, col)
            val blurVal = blurredMask.get(row, col)[0] / 255

            val newPixelValue = pixel.mapIndexed { index, value ->
                (blurVal * blurredPixel[index]) + ((1.0f - blurVal) * value)
            }.toDoubleArray()

            destination.put(row, col, *newPixelValue)
        }
    }
    return destination
}

我们从模糊掩模中得到每一个像素,除以255,得到一个介于0和1之间的值,然后用每个模糊的像素乘以这个值,用1.0减去相同的值,再乘以原始图像。我们最终获得新的像素值并将其分配给新的图像变量。原始和模糊图像的透明度将为0,从而产生相同的像素值,但是该值随着接近模糊图像的边缘而变化,从而导致沿着模糊遮罩边缘的透明模糊效果。  
 我们现在更新blurImage()函数匹配此混合函数:



private suspend fun blurImage(bitmap: Bitmap):Bitmap {
return withContext(Dispatchers.IO) {
val imageSrc = Mat()

        Utils.bitmapToMat(bitmap, imageSrc)

        val innerMask = Mat.zeros(imageSrc.size(), imageSrc.type())

        //thickness set to -1 for inner fill
        Imgproc.circle(
            innerMask,
            Point(imageSrc.width() / 2.0, imageSrc.height() / 1.5),
            600,
            Scalar.all(255.0),
            -1
        )

        val blurredImage = Mat(imageSrc.size(), imageSrc.type())
        Imgproc.blur(imageSrc, blurredImage, Size(64.0, 64.0))

        val blurredMask = Mat(innerMask.size(), innerMask.type())
        Imgproc.blur(innerMask, blurredMask, Size(64.0, 64.0))

        val merge = alphaBlend(imageSrc, blurredImage, blurredMask)

        val copy = Bitmap.createBitmap(merge.width(), merge.height(), Bitmap.Config.ARGB_8888)
        Utils.matToBitmap(merge, copy)
        copy
    }
}

![](https://img-blog.csdnimg.cn/296ac2f1bdcd40c88602541298716a02.png#pic_center)  
 一个新的问题出现了!!!  
 有一点你会注意到,我们的模糊过程变得非常缓慢,需要超过15秒才能达到模糊效果。这是因为两个原因;


我们循环遍历二维网格中的每个像素,这意味着对于1080x1080的图像,我们实际上在图像中迭代1166400次,这是相当多的!  
 我们使用get和设置方法,这是相当复杂的操作。


### 摆脱混乱的方法


在对优化这一过程的各种方法进行了大量研究之后,我终于找到了一个解决问题的方法邮递它提供了一些关于OpenCV的见解。


从这篇文章中,我认为优化整个过程的最佳方法是将像素转换为像IntArray/FloatArray这样的原语,然后对这个对象执行操作,将处理过的像素重新分配回image对象。get/set操作将比way更快,而且还可以允许每个像素的单个交互,因为图像现在被表示为一维数组。


首先,我想强调一些一开始对我不起作用的方法。我将像素分配给ByteArray,并对这些像素执行正常的alpha混合操作,但由于某些原因,我无法获得所需的结果。我是这样做的:



private fun alphaBlend(imageSrc:Mat, blurredImage: Mat, blurredMask: Mat): Mat{

    val total = imageSrc.width() * imageSrc.height()
    val channels = imageSrc.channels()
    val size = total * channels

    val array = ByteArray(size)
    val array1 = ByteArray(size)
    val array2 = ByteArray(size)

    val array3 = ByteArray(size)

    val destination = Mat(imageSrc.size(), imageSrc.type())

    imageSrc.get(0,0, array)
    blurredImage.get(0,0, array1)
    blurredMask.get(0,0, array2)

    for (index in 0 until size){
        val pixel = array[index]
        val blurredPixel = array1[index]
        val blurVal = (array2[index]) / 255.0f

        val newValue = ((blurVal * blurredPixel) + ((1.0f - blurVal) * pixel)).toInt().toByte()
        array3[index] = newValue
    }
    destination.put(0,0, array3)
    return destination
}

然后我花了好几天时间弄清楚为什么这不起作用,并决定选择其他有效的原语。我没有使用ByteArray,而是选择了FloatArray因为alpha掩码值应该是0.0到1.0之间的十进制值。在执行此操作之后,我遇到了一些错误。  
 经过几个小时的调试之后,我意识到图像的数据类型应该与分配给它们的数组原语相匹配,这是由于OpenCV在后台实现的某些条件。  
 所以在将像素分配给FloatArray原语之前,我将图像转换为float数组所需的数据类型,如下所示:



imageSrc.convertTo(imageSrc, CV_32F)
blurredImage.convertTo(blurredImage, CV_32F)
blurredMask.convertTo(blurredMask, CV_32F)


上述方法解决了这个错误,只是这种转换形式产生了另一个错误。解决方案是通过以下方法将目标mat对象转换为这两个对象之一:



destination.convertTo(destination, CV_8UC3)


以下是使用alpha混合优化图像模糊的完整代码实现:



private fun alphaBlend(imageSrc:Mat, blurredImage: Mat, blurredMask: Mat): Mat{

    val total = imageSrc.width() * imageSrc.height()
    val channels = imageSrc.channels()
    val size = total * channels

    val array = FloatArray(size)
    val array1 = FloatArray(size)
    val array2 = FloatArray(size)

    val array3 = FloatArray(size)
    imageSrc.convertTo(imageSrc, CV_32F)
    blurredImage.convertTo(blurredImage, CV_32F)
    blurredMask.convertTo(blurredMask, CV_32F)

    val destination = Mat(imageSrc.size(), imageSrc.type())

    imageSrc.get(0,0, array)
    blurredImage.get(0,0, array1)
    blurredMask.get(0,0, array2)

    for (index in 0 until size){
        val pixel = array[index]
        val blurredPixel = array1[index]
        val blurVal = (array2[index]) / 255.0f

        val newValue = ((blurVal * blurredPixel) + ((1.0f - blurVal) * pixel))
        array3[index] = newValue
    }
    destination.put(0,0, array3)
    destination.convertTo(destination, CV_8UC3)
    return destination
}

private suspend fun blurImage(bitmap: Bitmap):Bitmap {
    return withContext(Dispatchers.IO) {
        val imageSrc = Mat()

        Utils.bitmapToMat(bitmap, imageSrc)

        val innerMask = Mat.zeros(imageSrc.size(), imageSrc.type())

        //thickness set to -1 for inner fill
        Imgproc.circle(
            innerMask,
            Point(imageSrc.width() / 2.0, imageSrc.height() / 1.5),
            600,
            Scalar.all(255.0),

最后

上面这些公司都是时下最受欢迎的互联网大厂,他们的职级、薪资、福利也都讲的差不多了,相信大家都是有梦想和野心的人,心里多少应该都有些想法。

也相信很多人也都在为即将到来的金九银十做准备,也有不少人的目标都是这些公司。

我这边有不少朋友都在这些厂工作,其中也有很多人担任过面试官,上面的资料也差不多都是从朋友那边打探来的。除了上面的信息,我这边还有这些大厂近年来的面试真题及解析,以及一些朋友出于兴趣和热爱一起整理的Android时下热门知识点的学习资料

部分文件:


网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

-BrxZiri0-1715457437688)]
[外链图片转存中…(img-fxpSTfms-1715457437689)]
[外链图片转存中…(img-TrB7cvqG-1715457437689)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值