Android OpenCV应用篇二:图片处理

上篇我们成功搭建了OpenCV的Android开发环境,并简单介绍了一下OpenCV的相关应用:

Android OpenCV应用篇一:环境搭建,高斯差分

接下来我们继续探索OpenCV在Android上对应用。


在进行图像处理的一些高级操作时候,我们需要对图片进行预处理,以过滤一些不必要对信息,缩短计算时间,接下来我们就来介绍一些基本的图片处理操作

  • 灰度
  • 模糊
  • 降噪
  • 锐化
  • 腐蚀和膨胀
  • 阈值化、自适应阈值
  • 直方图均衡

OpenCV中图像对存储

开始之前,我们先来了解一些OpenCV中是如何保存操作图片对象对。对于Android系统API相信大家都知道系统有提供Bitmap,Drawable来临时保存图片对象供我们使用操作,相对都,OpenCV也有提供相关对工具类:Mat
Mat对象保存来图片对行数(高度)、列数(宽度)、通道(颜色通道)、图片数据等相关信息,并封装来一些图片等操作方法(后续会有用到再做介绍)。

灰度图像单通道表示 图像RGB三通道表示
上面两幅图简单介绍来一下**Mat**是如何表示衣服图像的,OK,简单了解这些后,我们开始进行图片的处理工作。

利用OpenCV处理图片

一、灰度处理

在进行许多复杂的图像处理之前,我们都需要将图像转换成灰度单通道。
在没有使用OpenCV之前,我们在Android上操作Bitmap是如何得到一张RGBA图片的灰度图像的:
Android转化为bitmap类得到灰度图像,并对其数据做如下操作:

    A通道保持不变,然后逐像素计算:X = 0.3×R+0.59×G+0.11×B,并使这个像素的值新R,G,B值为X,即:

    new_R = X, new_G = X, new_B = X

    例如:原来一个像素是4个byte,分别为ARGB,现在这个像素应该为AXXX

没错,我们通过重新计算每个像素点的颜色值,然后重新绘图得到,得到的图片的通道仍然与原图保持一致,归根揭底还是一个RGBA的图片,只是颜色值为gray而已。

OpenCV给我们提供来更为彻底的处理函数:

Imgproc.cvtColor(Mat src, Mat dst, ind code)
  • src为要处理的图片
  • dst为处理后输出图片
  • code为转换模型
    例如我们要将一个RGBA的图片转换成GRAY,如下处理
Imgproc.cvtColor(src, src, Imgproc.COLOR_RGBA2GRAY)

得到的是一个单通道的GRAY图像,效果如下:

灰度图像单通道表示

二、线性滤波

线性滤波最常见对一种用途是降噪,噪声是图像中亮度或色彩信息对随机变化,我们用模糊操作来减少图像中对噪声。

2.1、高斯模糊

高斯模糊是比较常用的模糊算法,在许多图片美化工具中都用经常用到。具体的高斯模糊函数以及原理我们这里不做介绍(高数实在是全部换回去了,无力研究),感兴趣的可以自己去找资料研究一下。
OpenCV为我们提供了内置函数用来在应用中执行高斯模糊:GaussianBlur

Imgproc.GaussianBlur(
	Mat src, // 要处理的图像
	Mat dst, // 输出图像
	Size kSize, // 高斯内核大小
	double sigmax, // 高斯函数在x方向上的标准偏差
	double sigmay  // 高斯函数在y方向上的标准偏差
)

例如我们对图片进行一次高斯模糊处理:

// 高斯模糊
Imgproc.GaussianBlur(src, src, Size(3.0, 3.0), 0.0,0.0)

效果图:

2.2、中值模糊

噪声在图片是是一种比较常见对现象,尤其是椒盐噪声,该噪声是疏密分布与图片中对黑色白色像素点。我们可以利用中值滤波去除这一类噪声。

OpenCV给我们提供了medianBlur内置函数来进行中值滤波

medianBlur(Mat src, Mat dst, int ksize) 

例:

// 中值滤波
Imgproc.medianBlur(src, src, 3)

效果图:

2.3、均值模糊

均值模糊是最简单一中模糊处理方式
在OpenCV中,我们使用内置函数:blur来进行处理

blur(Mat src, Mat dst, Size ksize)

例:

// 均值模糊
Imgproc.blur(src, src, Size(8.0, 8.0))

效果图:

2.4、锐化

锐化可以看作是一种线性滤波操作,具体原理性质的概念不做赘述。通常我们在处理沙滩、毛发之类的图片时会经常用到锐化的操作,可以给人一中更有质感的感觉。
话不多说,OpenCV 中的内置函数:filter2D 可以帮助我们实现这一效果

filter2D(Mat src, Mat dst, int ddepth, Mat kernel)

例:

//锐化处理,做卷积
val kernel = Mat(3, 3, CvType.CV_16SC1)
kernel.put(
      0, 0,
      0.0, -1.0, 0.0,
      -1.0, 5.0, -1.0,
      0.0, -1.0, 0.0
    )
Imgproc.filter2D(src, src, src.depth(), kernel)

效果图:

三 阈值化

阈值化是一种将我们想要在图像中分析的区域分割出来的方法,基本原理是把每个像素值跟我们预设的值进行比较,再根据比较结果进行像素调整。

在OpenCV中,提供来5中阈值化操作:

通过内置函数:threshold 来处理

threshold(Mat src, Mat dst, double thresh, double maxval, int type)

当然,图像受关照条件等的影响,我们只定义一个全局的阈值比不是好的选择,为了克服这个限制,我们要试图根据邻像素为任意像素计算阈值:自适应阈值
OpenCV提供给我们自适应阈值的操作:adaptiveThreshold

adaptiveThreshold(
	Mat src, 
	Mat dst, 
	double maxValue, 
	int adaptiveMethod, 
	int thresholdType, 
	int blockSize, 
	double C
  ) 
  • adaptiveMethod : 自适应方法,ADAPTIVE_THRESH_MEAN_C (阈值是领域像素的均值),ADAPTIVE_THRESH_GAUSSIAN_C(阈值是领域像素的加权和,权重来自高斯核)
  • thresholdType :阈值类型,API中说明:Thresholding type that must be either #THRESH_BINARY or #THRESH_BINARY_INV
  • blockSize :领域的大小
  • C :从每个像素计算得到的像素值或加权值减去的常量

例:

				// 自适应阈值
                Imgproc.cvtColor(src, src, Imgproc.COLOR_RGBA2GRAY)
                Imgproc.adaptiveThreshold(
                    src,
                    src,
                    255.0,
                    Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
                    Imgproc.THRESH_BINARY,
                    3,
                    0.0
                )
                Imgproc.cvtColor(src, src, Imgproc.COLOR_GRAY2RGBA)

效果:

四 腐蚀和膨胀

形态学运算是一类根据图像特征和结构元素进行图像处理的操作,大多针对二值或灰度图像。

膨胀是一种将图像中亮区域扩张的方法,相反的,腐蚀是一种将图像中暗区域扩张的方法。
我们利用OpenCV 通过的 erode方法进行腐蚀处理,用dilate进行膨胀处理

 dilate(Mat src, Mat dst, Mat kernel)
 
 erode(Mat src, Mat dst, Mat kernel) 

例:

// 膨胀
val dilateKernel = Imgproc.getStructuringElement(
	Imgproc.MORPH_RECT, 
	Size(3.0, 3.0)
)
Imgproc.dilate(src, src, dilateKernel)


// 腐蚀
val erodeKernel = Imgproc.getStructuringElement(
	Imgproc.MORPH_ELLIPSE, 
	Size(5.0, 5.0)
)
Imgproc.erode(src, src, erodeKernel)

效果图(膨胀左,腐蚀右):

五 均衡

直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度。
我利用:equalizeHist函数来处理(注:该函数只能处理单通道图像)

equalizeHist(Mat src, Mat dst)

5.1、单通道直方图均衡处理

如我们将一个图片均衡处理:

  • 先将图片转换成但通道
  • 均衡化
    例:
Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY)
Imgproc.equalizeHist(src, src)

效果图:

5.2、多通道直方图均衡处理

那么进行直方图均衡处理得到一张彩色但图片呢?

  • 分离通道
  • 直方图均衡处理每个通道
  • 合并通道
    例:
val mats = ArrayList<Mat>()
// 通道分解
Core.split(src, mats)
mats.forEach {
   // 直方图均衡每个通道
   Imgproc.equalizeHist(it, it)
 }
// 通道合并
Core.merge(mats, src)

效果图:



附上处理的关键代码:

import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import com.hankang.imagepick.PictureSelector
import com.hankang.opencv.R
import org.opencv.android.Utils
import org.opencv.core.Core
import org.opencv.core.CvType
import org.opencv.core.Mat
import org.opencv.core.Size
import org.opencv.imgproc.Imgproc
import kotlin.collections.ArrayList

class ProcessActivity : AppCompatActivity() {
    private val PICTURE_REQUEST_CODE = 0x01
    lateinit var imageSource: ImageView
    lateinit var imageResult: ImageView
    lateinit var imageBitmap: Bitmap

    private val BLUR = 0x01
    private val GUSSIAN_BlUR = 0x02
    private val MEDIAN_BlUR = 0x03
    private val SHARPEN = 0x04
    private val DILATE = 0x05
    private val ERODE = 0x06
    private val CVT = 0x07
    private val GRAY = 0x08
    private val GRAYJH = 0x09
    private val COLORJH = 0x10


    init {
        System.loadLibrary("opencv_java3")

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_process)

        imageSource = findViewById(R.id.image_1)
        imageResult = findViewById(R.id.image_2)
        findViewById<Button>(R.id.main_button).setOnClickListener {
            PictureSelector
                .create(this, PICTURE_REQUEST_CODE)
                .selectPicture(true, 200, 200, 1, 1)
        }

        findViewById<Button>(R.id.main_button1).setOnClickListener {
            processImage(BLUR)
        }

        findViewById<Button>(R.id.main_button2).setOnClickListener {
            processImage(GUSSIAN_BlUR)
        }

        findViewById<Button>(R.id.main_button3).setOnClickListener {
            processImage(MEDIAN_BlUR)
        }

        findViewById<Button>(R.id.main_button4).setOnClickListener {
            processImage(SHARPEN)
        }

        findViewById<Button>(R.id.main_button5).setOnClickListener {
            processImage(DILATE)
        }

        findViewById<Button>(R.id.main_button6).setOnClickListener {
            processImage(ERODE)
        }
        findViewById<Button>(R.id.main_button7).setOnClickListener {
            processImage(CVT)
        }
        findViewById<Button>(R.id.main_button8).setOnClickListener {
            processImage(GRAY)
        }
        findViewById<Button>(R.id.main_button9).setOnClickListener {
            processImage(GRAYJH)
        }
        findViewById<Button>(R.id.main_button10).setOnClickListener {
            processImage(COLORJH)
        }

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode != Activity.RESULT_OK) {
            return
        }
        when (requestCode) {
            PICTURE_REQUEST_CODE -> {
                data?.apply {
                    val path = getStringExtra(PictureSelector.PICTURE_PATH)
                    imageBitmap = BitmapFactory.decodeFile(path)
                    imageSource.setImageBitmap(imageBitmap)
                }
            }
        }
    }

    private fun processImage(model: Int) {

        val src = Mat(imageBitmap.height, imageBitmap.width, CvType.CV_8UC4)
        Utils.bitmapToMat(imageBitmap, src)

        when (model) {
            BLUR -> {
                // 均值模糊
                Imgproc.blur(src, src, Size(8.0, 8.0))
            }
            GUSSIAN_BlUR -> {
                // 高斯模糊
                Imgproc.GaussianBlur(src, src, Size(11.0, 11.0), 0.0,0.0)
            }
            MEDIAN_BlUR -> {
                // 中值滤波
                Imgproc.medianBlur(src, src, 9)
            }

            SHARPEN -> {
                //做卷积
                val kernel = Mat(3, 3, CvType.CV_16SC1)
                kernel.put(
                    0, 0,
                    0.0, -1.0, 0.0,
                    -1.0, 5.0, -1.0,
                    0.0, -1.0, 0.0
                )
                Imgproc.filter2D(src, src, src.depth(), kernel)
            }
            DILATE -> {
                // 膨胀
                val dilateKernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, Size(3.0, 3.0))
                Imgproc.dilate(src, src, dilateKernel)
            }
            ERODE -> {
                // 腐蚀
                val erodeKernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, Size(5.0, 5.0))
                Imgproc.erode(src, src, erodeKernel)
            }
            CVT -> {
                // 自适应阈值
                Imgproc.cvtColor(src, src, Imgproc.COLOR_RGBA2GRAY)
                Imgproc.adaptiveThreshold(
                    src,
                    src,
                    255.0,
                    Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
                    Imgproc.THRESH_BINARY,
                    3,
                    0.0
                )
                Imgproc.cvtColor(src, src, Imgproc.COLOR_GRAY2RGBA)
            }
            GRAY -> {
                Imgproc.cvtColor(src, src, Imgproc.COLOR_RGBA2GRAY)
            }
            GRAYJH -> {
                Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY)
                Imgproc.equalizeHist(src, src)
            }
            COLORJH -> {
                val mats = ArrayList<Mat>()
                // 通道分解
                Core.split(src, mats)
                mats.forEach {
                    // 直方图均衡每个通道
                    Imgproc.equalizeHist(it, it)
                }
                // 通道合并
                Core.merge(mats, src)
            }
        }

        val processBitmap = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888)
        Utils.matToBitmap(src, processBitmap)
        imageResult.setImageBitmap(processBitmap)

    }
}





OK,以上就是本篇但主要内容来

如果对你有帮助但话请点赞关注

未完待续。。。




发布了5 篇原创文章 · 获赞 8 · 访问量 819

猜你喜欢

转载自blog.csdn.net/qq_20158897/article/details/100551808