《OpenCV算法精解——基于Python与C++》第六章阈值分割
第六章代码总结:
Github地址
本章通过选取阈值点,对图像主体与背景进行分割。分为手动与自动。
自动:直方图技术法,熵算法(太麻烦了),Otsu(全局法中最棒),自适应阈值法(无敌)
- 手动阈值
thresh = 150#阈值
maxVal = 255#输出主体的像素值
type = cv2.THRESH_BINARY#用0替代,大于阈值输出maxVal,小于等于输出0;THRESH_BINARY_INV=1相反
dst = cv2.threshold(src,thresh,maxVal,type)
- 自动阈值 效果Otsu > Triangle > histogram = 熵
#Otsu(只支持8位图)
otsu_thresh = 0
otsu_thresh,dst_otsu = cv2.threshold(src,otsu_thresh,255,8)#最后的8,代表type为cv2.THRESH_OTSU
#Triangle三角形法(直方图法原理同三角形法)
triangle_thresh = 0
triangle_thresh,dst_tri = cv2.threshold(src,triangle_thresh,255,16)
#直方图histogram与熵算法效果几乎相同,过程太复杂,想看原理的看6.2,6.3
- 自适应阈值法:
- 全局法对光照不均的图像效果不好,根据每个像素点位置及其领域来设置每一像素点的阈值就是自适应阈值法。
- 先需对图像进行平滑处理:均值(mean),高斯(gaussian),中值(median)
- 计算像素领域灰度均值乘以比例系数(1-ratio),ratio一般指定0.15。
- 结果作为阈值参考值。平滑算子高宽为基数,须大于被识别主体宽度保证效果。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#平滑处理采用均值平滑(cv2.blur或者cv2.boxFilter)
def adaptiveThresh(I,winSize,ratio=0.15):#比例系数ratio一般0.15I_mean = cv2.blur(I,winSize)#I_mean = cv2.boxFilter(I,cv2.CV_32FC1,winSize)#结果相同,必须指定32位out = I - (1.0-ratio)*I_meanout[out>=0] = 255out[out<0] = 0out = out.astype(np.uint8)#图像格式转换为255,return outsrc = cv2.imread("image3.png",0)
dst = adaptiveThresh(src,(7,7),0.15)#PythonAPI,效果输出没上面编写的好,奇怪,可能是我src没预先平滑好?评论大佬解答下
#7代表平滑算子(7,7),0.15为ratio(不懂的,就在上段啊喂,别跳),中值平滑adaptive.Threshold没给预设
dst_adaptive_mean = cv2.adaptiveThreshold(src,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,7,0.15)
dst_adaptive_gaussian = cv2.adaptiveThreshold(src,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,7,0.15)
最后6.6介绍二值图的逻辑运算,要求图像格式相同
- dst_and = cv2.bitwise_and(src1,src2)#与运算
- dst_or = cv2.bitwise_or(src1,src2)#或运算
6.1_阈值分割
阈值分割(cv2.threshold)又称二值化处理,通过选取阈值分割,对图像分割成不同的像素集合。对前景与背景有较强对比度的特别有效。
threshold(src,dst,thresh,maxval,type)
thresh:阈值;maxVal:输出的最大灰度,一般为255;type:THRESH_BINARY = 0 , 大于阈值输出maxVal,小于等于输出0;THRESH_BINARY_INV =1 相反THRESH_OTSU = 8 ,自动计算阈值,并默认搭配THRESH_BINARY输出。THRESH_TRIANGLE =16 ,同OTSU
Python API:
#-*- coding:utf-8 -*-
import numpy as np
import cv2
src = np.array([[123,234,68],[33,51,17],[48,98,234],[129,89,27],[45,167,134]],np.uint8)#手动设置阈值
thresh = 150
maxVal = 255
dst = cv2.threshold(src,thresh,maxVal,cv2.THRESH_BINARY)
print dst
#otsu自动阈值处理
otsu_thresh = 0
otsu_thresh,dst_otsu = cv2.threshold(src,otsu_thresh,255,8)#最后的参数8代表cv2.THRESH_OTSU
print otsu_thresh,dst_otsu#TRIANGLE处理
triangle_thresh = 0
triangle_thresh,dst_tri = cv2.threshold(src,triangle_thresh,255,16)
print triangle_thresh,dst_tri
(150.0, array([[ 0, 255, 0],[ 0, 0, 0],[ 0, 0, 255],[ 0, 0, 0],[ 0, 255, 0]], dtype=uint8))
98.0 [[255 255 0][ 0 0 0][ 0 0 255][255 0 0][ 0 255 255]]
232.0 [[ 0 255 0][ 0 0 0][ 0 0 255][ 0 0 0][ 0 0 0]]
原理:
import numpy as np
src = np.array([[123,234,68],[33,51,17],[48,98,234],[129,89,27],[45,167,134]])
#大于阈值的像素点输出255,小于等于阈值输出0
src[src>150]=255
src[src<=150]=0
src
array([[ 0, 255, 0],[ 0, 0, 0],[ 0, 0, 255],[ 0, 0, 0],[ 0, 255, 0]])
6.2_直方图技术法
- 0:黑色,255:白色,理解为亮度值;
- 直方图技术法通过选出直方图中的两峰顶间的最小峰谷作为阈值。
- 方法同THRESH_TRIANGLE:三角法是将最高峰顶置于图像亮侧,连接原点与峰顶,选择直方图距离这条连线的最远点就为峰谷,及阈值。
原理:
import numpy as npdef calcGrayHist(image):#灰度图像矩阵的高,宽rows,cols = image.shape#存储灰度直方图grayHist = np.zeros([256],np.uint64)for r in xrange(rows):for c in xrange(cols):grayHist[image[r][c]] +=1 #这里应该指的是,image[r][c]对应灰度数值,在grayHist上+1return grayHistdef threshTwoPeaks(image):#计算灰度直方图histogram = calcGrayHist(image)#找最大峰顶maxLoc = np.where(histogram==np.max(histogram))firstPeak = maxLoc[0][0]#寻找第二个峰值对应的灰度值measureDists = np.zeros([256],np.float32)for k in xrange(256):measureDists[k] = pow(k-firstPeak,2)*histogram[k]maxLoc2 = np.where(measureDists==np.max(measureDists))secondPeak = maxLoc2[0][0]#找到两个峰值间的最小值对应的灰度值thresh = 0 #先给thresh赋值,不然无定义if firstPeak > secondPeak:temp = histogram[int(secondPeak):int(firstPeak)]minLoc = np.where(temp == np.min(temp))thresh = secondPeak + minLoc[0][0] + 1else:temp = histogram[int(firstPeak):int(secondPeak)]minLoc = np.where(np.min(temp))thresh = firstPeak + minLoc[0][0] + 1#找到阈值后,对图像进行阈值化处理threshImage_out = image.copy()#把src拷贝一份threshImage_out[threshImage_out>thresh]=255threshImage_out[threshImage_out<=thresh]=0return (thresh,threshImage_out)
import cv2
import numpy as np
import matplotlib.pyplot as pltsrc = cv2.imread("img7.jpg",0)#otsu
the_otsu = 0
the_otsu,dst_otsu = cv2.threshold(src,the_otsu,255,8)#triangle
the_tra = 0
the_tra,dst_tra = cv2.threshold(src,the_tra,255,16)#histogram
the_hist = 0
the_hist,dst_hist = threshTwoPeaks(src)titles = ["src","otsu","tra","histogram"]
images = [src,dst_otsu,dst_tra,dst_hist]
for i in range(4):plt.subplot(1,4,i+1)plt.title(titles[i])plt.imshow(images[i])
plt.show()
讲道理,otsu自动计算的更好
6.3_熵算法
- 熵算法使用信息论的概念,将图像看做信源,来计算灰度级熵,一顿操作猛如虎计算出阈值,我反正没看懂。
- 结果与histogram相近
# -*- coding: utf-8 -*-
import sys
import numpy as np
import cv2
import math
import matplotlib.pyplot as plt
#计算图像灰度直方图
def calcGrayHist(image):#灰度图像矩阵的宽高rows,cols = image.shape#存储灰度直方图grayHist = np.zeros([256],np.uint32)for r in xrange(rows):for c in xrange(cols):grayHist[image[r][c]] +=1return grayHist
#熵阈值法
def threshEntroy(image):rows,cols = image.shape#求灰度直方图grayHist = calcGrayHist(image)#归一化灰度直方图normGrayHist = grayHist/float(rows*cols)#计算累加直方图,也称零阶累加矩zeroCumuMoment = np.zeros([256],np.float32)for k in xrange(256):if k==0:zeroCumuMoment[k] = normGrayHist[k]else:zeroCumuMoment[k] = zeroCumuMoment[k-1] + normGrayHist[k]#计算各个灰度级的熵entropy = np.zeros([256],np.float32)for k in xrange(256):if k==0:if normGrayHist[k] ==0:entropy[k] = 0else:entropy[k] = - normGrayHist[k]*math.log10(normGrayHist[k])else:if normGrayHist[k] ==0:entropy[k] = entropy[k-1]else:entropy[k] = entropy[k-1] - normGrayHist[k]*math.log10(normGrayHist[k])#找阈值fT = np.zeros([256],np.float32)ft1,ft2 = 0.0,0.0totalEntroy = entropy[255]for k in xrange(255):#找最大值maxFront = np.max(normGrayHist[0:k+1])maxBack = np.max(normGrayHist[k+1:256])if(maxFront == 0 or zeroCumuMoment[k] == 0 or maxFront==1 or zeroCumuMoment[k]==1 or totalEntroy==0):ft1 = 0else:ft1 =entropy[k]/totalEntroy*(math.log10(zeroCumuMoment[k])/math.log10(maxFront))if(maxBack == 0 or 1 - zeroCumuMoment[k]==0 or maxBack == 1 or 1-zeroCumuMoment[k] ==1):ft2 = 0else:if totalEntroy==0:ft2 = (math.log10(1-zeroCumuMoment[k])/math.log10(maxBack))else:ft2 = (1-entropy[k]/totalEntroy)*(math.log10(1-zeroCumuMoment[k])/math.log10(maxBack))fT[k] = ft1+ft2#找最大值的索引,作为得到的阈值threshLoc = np.where(fT==np.max(fT))thresh = threshLoc[0][0]#阈值处理threshold = np.copy(image)threshold[threshold > thresh] = 255threshold[threshold <= thresh] = 0return (threshold,thresh)
#主函数
image = cv2.imread("img7.jpg",0)
#阈值处理
threshold,thresh = threshEntroy(image);
#显示阈值后的二值化图像
cv2.imshow("threshEntroy",threshold)
print thresh
cv2.waitKey(0)
cv2.destroyAllWindows()
plt.title("entroy")
plt.imshow(threshold)
plt.show
95
效果与histogram直方图法差不多,原理太复杂
6.4_Otsu
终于要介绍Otsu算法了,Otsu算法是最大方差法。在前面已经使用过了
- otsu_thresh,dst_otsu =
cv2.threshold(src,otsu_thresh,255,8)#最后的参数8代表cv2.THRESH_OTSU - 只支持8位图
原理:
def calcGrayHist(image):rows,cols = image.shapegrayHist = np.zeros([256],np.unit64)for r in xrange(rows):for c in xrange(cols):grayHist[image[r][c]] += 1return grayHistdef otsu(image):rows,cols =image.shape#计算灰度直方图histogram = calcGrayHist(image)#归一化uniformGrayHist = grayHist/float(rows*cols)#计算零阶累计矩和一阶累积矩zeroCumuMoment = np.zeros([256],np.float32)oneCumuMoment = np.zeros([256],np.float32)for k in xrange(256):if k == 0:zeroCumuMoment[k] = uniformGrayHist[0]oneCumuMoment[k] = (k)*uniformGrayHist[0]else:zeroCumuMoment[k] = zeroCumuMoment[k-1] + uniformGrayHist[k]oneCumuMoment[k] = oneCumuMoment[k-1] + k*uniformGrayHist[k]#计算类间方差variance = np.zeros([256],np.float32)for k in xrange(255):if zeroCumuMoment(k) == 0 or zeroCumuMoment[k] ==1:variance[k] = 0else:variance[k] = math.pow(oneCumuMonet[255]*zeroCumuMoment[k] - oneCumuMoment[k],2)/(zeroCumuMoment[k]*(1.0-zeroCumuMoment[k]))#找到阈值threshLoc = np.where(variance[0:255] == np.max(variance[0:255]))thresh = threshLoc[0][0]#阈值处理threshold = np.copy(image)threshold[threshold > thresh] = 255threshold[threshold <=thresh] = 0return (threshold,thresh)
Otsu核心在计算最大类间方差,找到其所在像素值也就对应了阈值。效果相当可以。
6.5_自适应阈值
- 全局阈值分割以Otsu算法为首,表现出了很好的分割效果。缺点是对光照不均的图面,全局阈值分割无法完全分割出主体。
- 自适应阈值算法针对每一个位置的灰度值设置相对应的阈值。通过对图像进行平滑处理(均值(mean),高斯(Gaussian),中值(median)),计算像素邻域的灰度均值,乘以比例系数(1-ratio),ratio一般取0.15,结果作为该像素的阈值参考值。
- 平滑算子宽度决定分割出的物体尺寸,书本经验提出,平滑算子宽度必须大于被识别物体的宽度。当然,高宽为基数(平滑处理要求)
原理:
import cv2
import numpy as np
import matplotlib.pyplot as plt
#平滑处理采用均值平滑(cv2.blur或者cv2.boxFilter)
def adaptiveThresh(I,winSize,ratio=0.15):#比例系数ratio一般0.15#第一步:图像平滑I_mean = cv2.blur(I,winSize)#I_mean = cv2.boxFilter(I,cv2.CV_32FC1,winSize)#结果相同,但不知道blur没有指定32位有没有影响,等下试试#第二步:原图与平滑结果做差out = I - (1.0-ratio)*I_mean#第三步: 当差值大于或等于0,输出255,;小于0,输出0out[out>=0] = 255out[out<0] = 0out = out.astype(np.uint8)#图像格式转换为255,return outsrc = cv2.imread("image3.png",0)
dst = adaptiveThresh(src,(7,7),0.15)
plt.subplot(1,2,1)
plt.imshow(src)
plt.subplot(1,2,2)
plt.imshow(dst)
plt.show
- PythonAPI:cv2.adaptiveThreshold(src,dst,maxValue,adaptiveMethod,thresholdType,blockSize,c)
- maxValue:最大输出灰度值
- adaptiveMethod:
ADAPTIVE_THRESH_MEAN_C:均值平滑
ADAPTIVE_THRESH_GAUSSIAN_C:高斯平滑
ADAPTIVE_THRESH_MEDIAN_C:中值平滑 - thresholdType:THRESH_BINARY、THRESH_BINARY_INV
- blockSIZE:滑动窗口大小,原理里的winSize
- C:比例算子,原理里的ratio
import cv2
import matplotlib.pyplot as pltsrc = cv2.imread("image3.png",0)#Otsu算法
the_Otsu = 0
the_Otsu,dst_Otsu = cv2.threshold(src,the_Otsu,255,8)#用上面定义的adaptiveThresh进行阈值分割
dst_adaptiveThresh = adaptiveThresh(src,(7,7),0.15)#7*7的自适应算法,PythonAPI
dst_adaptive_mean = cv2.adaptiveThreshold(src,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,7,0.15)
dst_adaptive_gaussian = cv2.adaptiveThreshold(src,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,7,0.15)titles = ("src","Otsu","adp","adp_mean","adp_gaussian")
images = (src,dst_Otsu,dst_adaptiveThresh,dst_adaptive_mean,dst_adaptive_gaussian)for i in xrange(5):plt.subplot(2,3,i+1)plt.title(titles[i])plt.imshow(images[i])cv2.imshow(titles[i],images[i])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
通过adaptiveThresh进行阈值分割效果比OpencvAPI好一些,查找资料,尝试对src先进行平滑处理,再自适应阈值分割
重新编写的代码如下
import cv2
import matplotlib.pyplot as pltsrc = cv2.imread("image3.png",0)#Otsu算法
the_Otsu = 0
the_Otsu,dst_Otsu = cv2.threshold(src,the_Otsu,255,8)#用上面定义的adaptiveThresh进行阈值分割
dst_adaptiveThresh = adaptiveThresh(src,(7,7),0.15)#7*7的自适应算法,PythonAPI
src_mean = cv2.blur(src,(7,7))
dst_adaptive_mean = cv2.adaptiveThreshold(src_mean,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,7,0.15)
src_gaussian = cv2.GaussianBlur(src,(7,7),5)
dst_adaptive_gaussian = cv2.adaptiveThreshold(src_gaussian,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,7,0.15)titles = ("src","Otsu","adp","adp_mean","adp_gaussian")
images = (src,dst_Otsu,dst_adaptiveThresh,dst_adaptive_mean,dst_adaptive_gaussian)for i in xrange(5):plt.subplot(2,3,i+1)plt.title(titles[i])plt.imshow(images[i])cv2.imshow(titles[i],images[i])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
效果稍微好一些,噪点去除了,但是边框模糊了,还是没有代码实现的好。
6.6_二值图的逻辑运算
cv2.bitwise_and & cv2.bitwise_or 为与或运算
#-*-coding:utf-8 -*-
import cv2
import numpy as np
src1 = np.array([[255,0,255]])
src2 = np.array([[255,0,0]])#与运算
dst_and = cv2.bitwise_and(src1,src2)
#或运算
dst_or = cv2.bitwise_or(src1,src2)
print dst_and
print dst_or
[[255 0 0]]
[[255 0 255]]
《OpenCV算法精解——基于Python与C++》第六章阈值分割相关推荐
- JVM内存管理------GC算法精解(复制算法与标记/整理算法)
转载自 JVM内存管理------GC算法精解(复制算法与标记/整理算法) 本次LZ和各位分享GC最后两种算法,复制算法以及标记/整理算法.上一章在讲解标记/清除算法时已经提到过,这两种算法都是在此 ...
- 排序算法(五)——堆排序算法详解及Python实现
本文目录 一.简介 二.算法介绍 三.代码实现 排序算法系列--相关文章 一.简介 堆排序(Heap Sort)算法,属于选择排序类,不稳定排序,时间复杂度O(nlogn). 堆排序由Floyd和Wi ...
- Simhash算法详解及python实现
Simhash算法详解及python实现 GoogleMoses Charikar发表的一篇论文"detecting near-duplicates for web crawling&quo ...
- 利用python处理dna序列_详解基于python的全局与局部序列比对的实现(DNA)
程序能实现什么 a.完成gap值的自定义输入以及两条需比对序列的输入 b.完成得分矩阵的计算及输出 c.输出序列比对结果 d.使用matplotlib对得分矩阵路径的绘制 一.实现步骤 1.用户输入步 ...
- kmeans算法详解和python代码实现
kmeans算法详解和python代码实现 kmeans算法 无监督学习和监督学习 监督学习: 是通过已知类别的样本分类器的参数,来达到所要求性能的过程 简单来说,就是让计算机去学习我们已经创建好了的 ...
- python selenium爬虫_详解基于python +Selenium的爬虫
详解基于python +Selenium的爬虫 一.背景 1. Selenium Selenium 是一个用于web应用程序自动化测试的工具,直接运行在浏览器当中,支持chrome.firefox等主 ...
- AdaBoost算法详解与python实现
AdaBoost算法详解与python实现 https://tangshusen.me/2018/11/18/adaboost/
- JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法)
转载自 JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法) 引言 何为终极算法? 其实就是现在的JVM采用的算法,并非真正的终极.说不定若干年以后,还会有新的终极算法, ...
- JVM内存管理------GC算法精解(五分钟让你彻底明白标记/清除算法)
转载自 JVM内存管理------GC算法精解(五分钟让你彻底明白标记/清除算法) 相信不少猿友看到标题就认为LZ是标题党了,不过既然您已经被LZ忽悠进来了,那就好好的享受一顿算法大餐吧.不过LZ丑 ...
- RRT(Rapidly-Exploring Random Trees)算法详解及python实现
RRT(Rapidly-Exploring Random Trees)算法详解及python实现 前言 一.原理 二.伪代码 三.代码详解 总结 前言 快速探索随机树(RRT):作为一种随机数据结构, ...
最新文章
- 三棱锥四面体html css,CSS transform属性实现旋转的四面体
- python机器学习及实践_Python机器学习及实践
- 这 5 个能挣钱的 SpringBoot 项目,真TMD香!
- python(matplotlib3)——ticks(坐标刻度)能见度
- .net开发人员应该知道(一)
- 大剑无锋之Hbase的优化【面试推荐】
- 深度学习“四大名著”发布!Python、TensorFlow、机器学习、深度学习四件套!
- 【Flink】Flink实验特性--reinterpretAsKeyedStream 将DataStream重新解释为KeyedStream
- 时延与传输速率、带宽延时
- 数据库字段数据(昵称)排序,规则: 数字英文字母汉字首字母 兼容简繁体排序
- H3CIE(WLAN)学习笔记(4)——PHY层协议
- 解决qt下VCI_OpenDevice一直返回0的问题
- Arcgis报错 ERROR 999999的解决方法汇总
- ACE_Message_QueueACE_MT_SYNCH::putq ()使用时需要注意的地方
- java 检测表情符号_关于Java:检查字母是否为表情符号
- 3ds max材质编辑器加载不了、不显示vray
- 中国软件:10个人 20年坎坷路
- VScode必备插件大全
- 嵌入式软件开发:自上而下|自下而上开发思想
- 《机器学习实战》学习