Android OpenCV(四十一):图像分割(漫水填充法)

  • 选择种子点(x,y);
  • 以种子点为中心,判断4邻域或者8邻域的像素值与种子点像素值的差值,将差值小于阈值的像素点添加进区域内。
  • 将新加入的像素点作为新的种子点,反复执行第二步,直到没有新的像素点被添加进该区域。

计算方式:

  • in case of a grayscale image and floating range(灰度图,浮动范围)

src(x′,y′)−loDiff≤src(x,y)≤src(x′,y′)+upDiff\texttt{src} (x’,y’)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} (x’,y’)+ \texttt{upDiff}src(x′,y′)−loDiff≤src(x,y)≤src(x′,y′)+upDiff

  • in case of a grayscale image and fixed range(灰度图,固定范围)

src(seedPoint.x,seedPoint.y)−loDiff≤src(x,y)≤src(seedPoint.x,seedPoint.y)+upDiff\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)+ \texttt{upDiff}src(seedPoint.x,seedPoint.y)−loDiff≤src(x,y)≤src(seedPoint.x,seedPoint.y)+upDiff

  • in case of a color image and floating range(彩色图,浮动范围)

src(x′,y′)r−loDiffr≤src(x,y)r≤src(x′,y′)r+upDiffr,\texttt{src} (x’,y’)_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} (x’,y’)_r+ \texttt{upDiff} _r,src(x′,y′)r​−loDiffr​≤src(x,y)r​≤src(x′,y′)r​+upDiffr​,

src(x′,y′)g−loDiffg≤src(x,y)g≤src(x′,y′)g+upDiffg\texttt{src} (x’,y’)_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} (x’,y’)_g+ \texttt{upDiff} _gsrc(x′,y′)g​−loDiffg​≤src(x,y)g​≤src(x′,y′)g​+upDiffg​

and

src(x′,y′)b−loDiffb≤src(x,y)b≤src(x′,y′)b+upDiffb\texttt{src} (x’,y’)_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \

本文已被开源项目:【一线大厂面试真题解析+核心总结学习笔记+最新全套讲解视频+实战项目源码讲义】收录

leq \texttt{src} (x’,y’)_b+ \texttt{upDiff} _bsrc(x′,y′)b​−loDiffb​≤src(x,y)b​≤src(x′,y′)b​+upDiffb​

  • in case of a color image and fixed range(彩色图,固定范围)

src(seedPoint.x,seedPoint.y)r−loDiffr≤src(x,y)r≤src(seedPoint.x,seedPoint.y)r+upDiffr,\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r+ \texttt{upDiff} _r,src(seedPoint.x,seedPoint.y)r​−loDiffr​≤src(x,y)r​≤src(seedPoint.x,seedPoint.y)r​+upDiffr​,

src(seedPoint.x,seedPoint.y)g−loDiffg≤src(x,y)g≤src(seedPoint.x,seedPoint.y)g+upDiffg\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g+ \texttt{upDiff} _gsrc(seedPoint.x,seedPoint.y)g​−loDiffg​≤src(x,y)g​≤src(seedPoint.x,seedPoint.y)g​+upDiffg​

and

src(seedPoint.x,seedPoint.y)b−loDiffb≤src(x,y)b≤src(seedPoint.x,seedPoint.y)b+upDiffb\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b+ \texttt{upDiff} _bsrc(seedPoint.x,seedPoint.y)b​−loDiffb​≤src(x,y)b​≤src(seedPoint.x,seedPoint.y)b​+upDiffb​

上述公式中,src(x′,y′)表示该区域内已知的相邻像素的值。简言之,当为浮动范围时,只有和已经属于某区域内的邻域相差足够小(满足公式范围),才能被选中进入该区域;当为固定范围时,只需要和种子像素相差足够小,就可以被选中进入该区域。

API

public static int floodFill(Mat image, Mat mask, Point seedPoint, Scalar newVal, Rect rect, Scalar loDiff, Scalar upDiff, int flags)

  • 返回值:填充像素数目。

  • 参数一:image,输入和输出图像,图像可以为CV_8U或者CV_32F类型的单通道或者三通道图像。当最后一个参数设置为FLOODFILL_MASK_ONLY标志时,不改变原始图像。

  • 参数二:mask,操作掩码,为单通道8位图像,比输入图像宽2像素,高2像素。由于mask既是输入参数又是输出参数,必须初始化。漫水填充不会填充掩码中的非零区域。例如,边缘检测的输出可以用作操作掩码来防止漫水填充边缘。

  • 参数三:seedPoint,种子点。

  • 参数四:newVal,重新绘制的域像素的新值。

  • 参数五:rect,默认为 0,用于设置 floodFill 函数将要重绘的最小边界矩形区域,即若漫水填充区域 < rect,则不进行填充。

  • 参数六:loDiff,添加进种子点区域条件的下界差值。表示当前观察像素值与其邻域像素值或待加入的种子像素值之间的亮度或颜色的最大负差。

  • 参数七:upDiff,添加进种子点区域条件的上界差值。表示当前观察像素值与其邻域像素值或待加入的种子像素值之间的亮度或颜色的最大正差。

  • 参数八:flags,漫水填充法的操作标志位。该标志由3部分组成,第一部分表示邻域的种类,4邻域或者8邻域;第二部分表示掩码矩阵中被填充像素点的新像素值;第三部分是填充算法的规则标志。int 类型操作标识符,默认值为 4,一共 23 位。

  • 低八位(0~7):用于控制算法的连通性,可取 4(默认值)或 8。如果设为 4,表示填充算法只考虑当前像素水平或处置方向的相邻点,如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。

  • 中间八位(8~15):用于指定填充掩码图像的值的,如果中间八位的值为 0,则掩码会用 1 来填充。

  • 高八位(16~23):可以为 0,或者以下两种选择标识符的组合。

FLOODFILL_FIXED_RANGE:如果设置为这个标识符,就会考虑当前像素与种子之间的差,否则就考虑当前像素与其邻域像素的差。

FLOODFILL_MASK_ONLY,如果设置为这个标识符,函数不会去填充改变原始图像,而是去填充掩膜图像。也就是忽略第三个参数newVal

// C++: enum FloodFillFlags
public static final int
FLOODFILL_FIXED_RANGE = 1 << 16,
FLOODFILL_MASK_ONLY = 1 << 17;

所以, flag 可以用 按位或,即‘|’ 连接起来。例如想用 4 邻域填充,并填充固定像素范围,填充掩码而不是填充原图,以及设置填充值为 250,那么输入的参数为

4 or (250 shl 8) or Imgproc.FLOODFILL_FIXED_RANGE or Imgproc.FLOODFILL_MASK_ONLY

操作

/**

  • 图像分割–漫水填充法
  • author: yidong
  • 2020/11/7
    */
    class FloodFillActivity : AppCompatActivity() {
    private val mBinding by lazy { ActivityFloodFillBinding.inflate(layoutInflater) }
    private lateinit var mMenuDialog: BottomSheetDialog
    private lateinit var mMenuDialogBinding: LayoutFloodFillMenuBinding

private var mConnectionType = 4
private var mFloodFillFlag = 0
private var mScalarNumber = 250 shl 8

private lateinit var mRgb: Mat
private var loDiff = 0.0
set(value) {
field = value
doFloodFill()
}
private var upDiff = 0.0
set(value) {
field = value
doFloodFill()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
val bgr = Utils.loadResource(this, R.drawable.wedding)
mRgb = Mat()
Imgproc.cvtColor(bgr, mRgb, Imgproc.COLOR_BGR2RGB)
mBinding.ivLena.showMat(mRgb)
mBinding.sbLow.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
mBinding.tvLoDiff.text = p1.toString()
loDiff = p1.toDouble()
}

override fun onStartTrackingTouch(p0: SeekBar?) {
}

override fun onStopTrackingTouch(p0: SeekBar?) {
}

})
mBinding.sbUp.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
mBinding.tvUpDiff.text = p1.toString()
upDiff = p1.toDouble()
}

override fun onStartTrackingTouch(p0: SeekBar?) {
}

override fun onStopTrackingTouch(p0: SeekBar?) {
}

})
mBinding.btFlag.setOnClickListener {
showMenuDialog()
}
doFloodFill()
}

private fun doFloodFill() {
val tmp = mRgb.clone()
val maskers = Mat(mRgb.rows() + 2, mRgb.cols() + 2, CV_8UC1, Scalar.all(0.0))
Imgproc.floodFill(
tmp,
maskers,
Point(7.0, 7.0),
Scalar(65.0, 105.0, 225.0),
Rect(),
Scalar.all(loDiff),
Scalar.all(upDiff),
mConnectionType or mFloodFillFlag or mScalarNumber
)
if (mFloodFillFlag and Imgproc.FLOODFILL_MASK_ONLY == Imgproc.FLOODFILL_MASK_ONLY) {

尾声

以薪资待遇为基础,以发展为最终目标,要在高薪资的地方,谋求最好的发展!

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

L_MASK_ONLY == Imgproc.FLOODFILL_MASK_ONLY) {

尾声

以薪资待遇为基础,以发展为最终目标,要在高薪资的地方,谋求最好的发展!

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

[外链图片转存中…(img-VXw2MxX5-1723367820330)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值