图像二值化处理

自动求取最佳阈值的方法:最右阈值法

最右阈值法:找到图像直方图的各个波峰和波谷的位置,然后取最右边的波谷的位置作为最佳阈值,如图:

在这里插入图片描述

  1. 读取样例图片并调用处理方法:
    采用L5.jpg如图
    L5.jpg
#ImgPreProc.py
# -*-coding:utf-8 -*-
# @Author : Ming

import  cv2 as cv
import numpy as np
import PeakAndWave
import histogram as hg

def preProcPlot(img):
    #对比度和亮度,参数自己调节
    src=hg.contrast_brightness_image(img,1.02,0.5)
    #计算原图直方图
    hist=hg.cal_histogram(src)
    #对直方图进行卷积平滑
    smooth_hist=np.array(hg.convolution_smooth(hist))
    #计算一阶差分
    first_dif=PeakAndWave.first_dif(smooth_hist)
    #消除一阶差分的微小抖动
    smooth_first_dif=PeakAndWave.dis_slight_jitter(first_dif,2)
    peak,wave=PeakAndWave.peakAndwaveNew(smooth_first_dif,5,9)
    #消除相似点    peak,wave=PeakAndWave.remove_similarPoints(peak),PeakAndWave.remove_similarPoints(wave)
    im_new=PeakAndWave.img_bz(src,wave[len(wave)-1])
    cv.imwrite(r'E:/dev_lab/pumch/img_pre/result.jpg',im_new)
    return  im_new
  1. 画直方图和对直方图进行卷积平滑
    对直方图进行卷积平滑而不是对原图像进行降噪处理,这样取得的直方图的波谷更佳。
#histogram.py
import  cv2 as cv
import  numpy as np
import matplotlib.pyplot as plt

# 粗略的调节对比度和亮度
def contrast_brightness_image(img, a, g):
    try:
        h,w= img.shape# 获取shape的数值,height和width、通道
        # 新建全零图片数组src2,将height和width,类型设置为原图片的通道类型(色素全为零,输出为全黑图片)
        src2 = np.zeros([h, w], img.dtype)
        dst = cv.addWeighted(img, a, src2, 1 - a, g)  # addWeighted函数说明如下
        return  dst
    except BaseException as e:
        print('contrast_brightness_image :',e)

#画直方图
def cal_histogram(img):
    hist=cv.calcHist([img],[0],None,[256],[0,256])
    plt.figure()  # 新建一个图像
    plt.title(u"原直方图")  # 图像的标题
    plt.xlabel("Bins")  # X轴标签
    plt.ylabel("# of Pixels")  # Y轴标签
    plt.plot(hist)  # 画图
    plt.xlim([0, 256])  # 设置x坐标轴范围
    plt.show()  # 显示图像
    return hist



#卷积(权重)法对直方图平滑
"""卷积函数 cv2.filter2D(img,-1,kernel) 第二个参数目标图像的所需深度。如果是负数,则与原图像深度相同 第三个参数是卷积内核"""
def convolution_smooth(img):
    #将图像转换成数值类型
    img1 = np.float32(img)
    kernel = np.ones((4,4), np.float32) / 16
    dst = cv.filter2D(img1,-1, kernel)
    plt.figure()  # 新建一个图像
    plt.xlabel("Bins")  # X轴标签
    plt.ylabel("# of Pixels")  # Y轴标签
    plt.title(u"卷积平滑处理后的波峰波谷位置")
    plt.xlim([0, 256])
    plt.plot(dst)
    plt.show()
    return dst

样图的原直方图:
在这里插入图片描述
经过卷积平滑处理后的直方图:
在这里插入图片描述
3. 求波峰波谷

#PeakAndWave.py
# _*_ coding:utf-8 _*_
# @Author : Ming

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
from enum import  Enum

#阈值二值化
def img_bz(img,val):
    """ cv2.THRESH_BINARY;大于阈值的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于阈值的像素点的灰度值设定为0。
    """
    ret,thresh=cv.threshold(img,val,255,cv.THRESH_BINARY )
    return  thresh


#plt.rcParams['font.sans-serif'] = ['SimHei']


def first_dif(arr):
    '''
    计算一阶差分
    :param arr:
    :return:
    '''
    L = []
    for i in range(1, len(arr)):
        L.append(arr[i] - arr[i - 1])
    L.append(0)
    return L


def dis_slight_jitter(fd_arr, val):
    '''
    消除一阶差分的微小抖动,绝对值小于val的判定为0
    :param arr:
    :param val:
    :return:
    '''
    L = []
    for i in range(0, len(fd_arr)):
        if abs(fd_arr[i]) <= val:
            L.append(0)
        else:
            L.append(fd_arr[i])
    return L


def val_miner(val):
    '''
    如果 val >0 返回1;val =0 返回 0;val 小于0 返回 -1
    '''
    return 1 if val >= 0 else (-1 if val < 0 else 0)


def arrounding(arr, i, n):
    '''
    计算i点左右各n个数的归一化值
    :param arr:
    :param i: 当前位置
    :param n:
    :return:
    '''
    left_dif_total, right_dif_total = 0, 0  # 临时存储一段变化的值
    for k in range(0, n):  # 用判断器计算当前点左边和右边的变化(累加)情况
        left_dif_total = left_dif_total + val_miner(arr[i - k])
        right_dif_total = right_dif_total + val_miner(arr[i + k + 1])
    return left_dif_total, right_dif_total

#定义方向:左边或者右边
class Direction(Enum):
    LEFT = -1
    RIGHT = 1
#定义变化趋势:增或者减
class Ratio(Enum):
    PLUS = 1
    MINUS = -1

def trend_res_old(arr, i, direction, step, ratio):
    '''
    判断数组arr[i] 的位置 前面或者后面是不是有 step个 数连续增或者减
    :param arr:
    :param i: 位置
    :param direction:-1 前面;1 后面
    :param step:
    :param ratio: 斜率,表示增或者减. -1 为减 1 为增
    :return:
    '''
    total=0
    for k in range(0, step):
        if i < k or i + k + 1 > len(arr) - 1:
            continue;
        if direction is Direction.LEFT:  # 左边
            val = arr[i - k]
        else:
            val = arr[i + k + 1]
        total = total + val
        if ratio is Ratio.PLUS:  # 增
            if val < 0: return False
        elif ratio == -1:
            if val > 0: return False
    #如下两行过滤掉增量非常小的波峰和波谷
    if ratio is Ratio.PLUS and int(total / step) < 3:
        return False
    if ratio is Ratio.MINUS and int(total / step) > -3:
        return False
    return True

def trend_res(arr, i, direction, step, ratio):
    '''
    判断数组arr[i] 的位置 前面或者后面是不是有 step个 数连续增或者减
    :param arr:
    :param i: 位置
    :param direction:-1 前面;1 后面
    :param step:
    :param ratio: 斜率,表示增或者减. -1 为减 1 为增
    :return:
    '''
    total,count=0,0 #total:总数;counter:增或者减的计数器
    for k in range(0, step):
        if i < k or i + k + 1 > len(arr) - 1:
            continue;
        if direction is Direction.LEFT:  # 左边
            val = arr[i - k]
        else:
            val = arr[i + k + 1]
        total = total + val
        if ratio is Ratio.PLUS:  # 增
            if val >= 0:
                count = count + 1
            else:
                count = count - 1
        elif ratio is Ratio.MINUS:  # 减
            if val <= 0:
                count = count + 1
            else:
                count = count - 1
    # if abs(int(total / step))<3:
    #     return False
    return count > (count / 2 + 1)

def remove_similarPoints_new(smooth_hist,arr,mod, step=8):
    '''

    :param smooth_hist: 平滑后的直方图
    :param arr: 波峰或则波谷的数组
    :param mod: 'PEAK|WAVE' 波峰或者波谷
    :param step: 表示连续,两个波峰或者波谷的距离最小值
    :return:
    '''
    arr_new, seg_ments, continuous_list = [], [], []
    if len(arr) >= 1:
        arr = sorted(arr)
        for i in range(1, len(arr)):
            if abs(arr[i] - arr[i - 1]) <= step:  # 两个波峰或者波谷位置间距< step 扔到todo_list中
                continuous_list.append(arr[i - 1])
            else:  # 如果需要处理的todo_list 集合>0 ,则处理todolist
                continuous_list.append(arr[i - 1])
                seg_ments.append(continuous_list)
                continuous_list = []
        if arr[-1] - arr[-2] <= step:  # 判断最后一个点
            seg_ments[-1].append(arr[-1])
        else:
            seg_ments.append([arr[-1]])

        for i in range(0, len(seg_ments)):
            arr_new.append(selectAppropriate(smooth_hist, seg_ments[i], mod))
        return arr_new
    else:
        return arr

#从group中选出最好的一个位置
def selectAppropriate(smooth_hist,group,mod):
    '''
    :param smooth_hist:平滑后的直方图
    :param group:待处理的组
    :param mod:'PEAK|WAVE' 波峰或者波谷
    :return:
    '''
    ylist,xlist=[],[]  #ylist存放直方图y轴的值,即某一灰度个数;xlist存放 一组group中 波谷或者波峰的点,可能为多个,所以最后返回中间那个
    for n in range(0, len(group)):
         ylist.append(smooth_hist[group[n]])
    val = (np.max(ylist) if mod == 'PEAK' else np.min(ylist))# 如果波峰取最大值;波谷取最小值

    for n in range(0, len(group)):
        if val == smooth_hist[group[n]]:
            xlist.append(group[n])  # 把y值相等的点放到temp中作为候选点
            if len(xlist) == 1:  # 候选点个数为1,直接加到arr_new中
                return xlist[0]
            else:  # 候选点个数>1 ,取中间点添加到arr_new中
                return xlist[len(xlist) / 2]

# 删除拐点中距离比较近的点
def remove_similarPoints(arr, step=8):
    del_list = []
    if len(arr) > 1:
        arr = sorted(arr)
        for i in range(1, len(arr)):
            if abs(arr[i] - arr[i - 1]) <= step:
                del_list.append(arr[i-1])
        for i in del_list:
            arr.remove(i)
        return arr
    else:
        return arr

def peakAndwave(first_dif, dif_score, stable_score):
    '''
    求波峰和波谷
    波谷:一阶差分连续11个为0(经验观察值),则取连续n个0中间的地方为波谷;或者左侧超过连续5个小于0,然后右侧超过连续5个大于0;
    波峰:左侧连续超5个大于0,右侧连续超5个小于0
    :param first_dif: 一阶差分数组,反应出变化趋势
    :param dif_score: 发生变化的前后连续变化的点数>这个值时,表示可能到了波峰或者波谷的位置
    :param stable_score: 连续n个0时,表示可能处于波谷位置
    :return:
    '''
    peak, wave = [], []
    for i in range(dif_score, len(first_dif) - dif_score):
        if first_dif[i] > 0 and first_dif[i + 1] <= 0:  # 左边增,右边减,可能是波峰
            if trend_res(first_dif, i, Direction.LEFT, dif_score, Ratio.PLUS) and trend_res(first_dif, i, Direction.RIGHT, dif_score, Ratio.MINUS):
                peak.append(i + 1)
        elif first_dif[i] < 0 and first_dif[i + 1] >= 0:  # 左边减,右边增,可能是波谷
            if trend_res(first_dif, i, Direction.LEFT, dif_score, Ratio.MINUS) and trend_res(first_dif, i, Direction.RIGHT, dif_score, Ratio.PLUS):
                wave.append(i)
        elif first_dif[i] == 0 and first_dif[i + 1] != 0 and trend_res(first_dif, i, Direction.RIGHT, dif_score, Ratio.PLUS):
            zero_total = 0
            for n in range(1, i):
                if first_dif[i - n] == 0:
                    zero_total = zero_total + 1
                else:
                    break
            if zero_total >= stable_score:  # 连续很多个0,处于波谷时,取连续0中间的位置为波谷
                wave.append(i - zero_total / 2)
    return peak, wave


def peakAndwaveNew(first_dif, dif_score, stable_score):
    peak, wave = [], []
    for i in range(1, len(first_dif) - 1):
        if first_dif[i] * first_dif[i + 1] < 0 or (first_dif[i] == 0 and first_dif[i + 1] != 0) or (
                first_dif[i] != 0 and first_dif[i + 1] == 0):  # 符号发生变化
            RIGHT_PLUS = trend_res(first_dif, i, Direction.RIGHT, dif_score, Ratio.PLUS)
            RIGHT_MINUS = trend_res(first_dif, i, Direction.RIGHT, dif_score, Ratio.MINUS)

            if RIGHT_PLUS:
                if trend_res(first_dif, i, Direction.LEFT, dif_score, Ratio.MINUS):
                    wave.append(i + 1)
            if RIGHT_MINUS:
                if trend_res(first_dif, i, Direction.LEFT, dif_score, Ratio.PLUS):
                    peak.append(i + 1)
            zero_total = 0
            for n in range(1, i):
                if first_dif[i - n] == 0:
                    zero_total = zero_total + 1
                else:
                    break
            if zero_total >= stable_score:  # 连续很多个0,处于波谷时,取连续0中间的位置为波谷
                if RIGHT_PLUS:
                    wave.append(i - zero_total / 2 + 1)
                elif RIGHT_MINUS:
                    peak.append(i - zero_total / 2 + 1)
    return peak, wave
  1. test方法测试
import cv2 as cv
import ImgPreProc
img=cv.imread(r'E:/dev_lab/pumch/img_pre/L3.jpg',0)
cv.imshow('img',img)
img_res=ImgPreProc.preProcPlot(img)
cv.imshow('img_res',img_res)
cv.waitKey(0)
cv.destroyAllWindows()

处理后的图片如图所示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值