漫水填充法
漫水填充算法是根据像素灰度值之间的差值寻找相同区域实现分割。我们可以将图像的灰度值理解成像素点的高度,这样一张图像可以看成崎岖不平的地面或者山区,向地面上某一个低洼的地方倾倒一定量的水,水将会掩盖低于某个高度的区域。漫水填充法利用的就是这样的原理,其形式与注水相似,因此被称形象的称为“漫水”。
与向地面注水一致,漫水填充法也需要在图像选择一个注水像素,该像素被称为种子点,种子点按照一定规则不断向外扩散,从而形成具有相似特征的独立区域,进而实现图像分割。漫水填充分割法主要分为以下三个步骤:
- 选择种子点(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()
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
资源分享
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
d要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!