【图像处理】根据边缘轮廓求曲率以及方向

1. 思路

  • 遍历轮廓的像素值,求取依次相连的位置
  • 根据离散点求曲率以及曲率点的方向

2. 获取边缘点的坐标

参考链接:https://blog.csdn.net/qq_36614557/article/details/115315449

def findpoint(x_arr, y_arr, x_next, y_next):
	n = len(x_arr)
	f = 1
	for i in range(n):
		if x_arr[i] == x_next and y_arr[i] == y_next:
			f = 0
			break
	return f

# 依次获取边缘轮廓上的点
def getAllPoint(img, img_c3):
	_, bw_img = cv2.threshold(img, 0, 1, cv2.THRESH_BINARY)
	thin_img = morphology.skeletonize(bw_img)
	thin_img = thin_img.astype(np.uint8) * 255

	x = []
	y = []
	sx, sy = np.where(thin_img==255)
	sx_first = sx[0]
	sy_first = sy[0]
	x.append(sx_first)
	y.append(sy_first) 
	dir=[[-1,-1],[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1]]
   
	while True:
		f = 0
		for i in range(8):
			if thin_img[sx_first + dir[i][0]][sy_first + dir[i][1]] == 255 and findpoint(x, y, (sx_first + dir[i][0]), (sy_first + dir[i][1])):
				sx_first +=  dir[i][0]
				sy_first +=  dir[i][1]
				f = 1
				break
		if f == 0:
			break
		if sx_first == x[0] and sy_first == y[0]:
			break
		x.append(sx_first)
		y.append(sy_first)

	# for i in range(len(x)):
	# 	cv2.circle(img_c3, (y[i], x[i]), 1, (0, 0, 255), -1)
	# cv2.imshow('img', img_c3)
	# cv2.waitKey(0)
	return x, y

将遍历的点映射到原始图像上,效果图如下:
在这里插入图片描述

2. 利用离散点求曲率以及方向

参考:https://zhuanlan.zhihu.com/p/72083902

def PJcurvature(x,y):
    """
    input  : the coordinate of the three point
    output : the curvature and norm direction
    refer to https://github.com/Pjer-zhang/PJCurvature for detail
    """
    t_a = LA.norm([x[1]-x[0],y[1]-y[0]])
    t_b = LA.norm([x[2]-x[1],y[2]-y[1]])
    
    M = np.array([
        [1, -t_a, t_a**2],
        [1, 0,    0     ],
        [1,  t_b, t_b**2]
    ])

    a = np.matmul(LA.inv(M),x)
    b = np.matmul(LA.inv(M),y)
	
    kappa = 2*(a[2]*b[1]-b[2]*a[1])/(a[1]**2.+b[1]**2.)**(1.5)
    norm_direction = [b[1],-a[1]]/np.sqrt(a[1]**2.+b[1]**2.)

    return kappa, norm_direction

3. 运行

import cv2
import copy
import numpy as np
from skimage import morphology
import numpy.linalg as LA
import matplotlib.pyplot as plt
import math



def findpoint(x_arr, y_arr, x_next, y_next):
	n = len(x_arr)
	f = 1
	for i in range(n):
		if x_arr[i] == x_next and y_arr[i] == y_next:
			f = 0
			break
	return f


def PJcurvature(x,y):
    """
    input  : the coordinate of the three point
    output : the curvature and norm direction
    refer to https://github.com/Pjer-zhang/PJCurvature for detail
    """
    t_a = LA.norm([x[1]-x[0],y[1]-y[0]])
    t_b = LA.norm([x[2]-x[1],y[2]-y[1]])
    
    M = np.array([
        [1, -t_a, t_a**2],
        [1, 0,    0     ],
        [1,  t_b, t_b**2]
    ])

    a = np.matmul(LA.inv(M),x)
    b = np.matmul(LA.inv(M),y)
	
    kappa = 2*(a[2]*b[1]-b[2]*a[1])/(a[1]**2.+b[1]**2.)**(1.5)
    norm_direction = [b[1],-a[1]]/np.sqrt(a[1]**2.+b[1]**2.)

    return kappa, norm_direction


# 依次获取边缘轮廓上的点
def getAllPoint(img, img_c3):
	_, bw_img = cv2.threshold(img, 0, 1, cv2.THRESH_BINARY)
	thin_img = morphology.skeletonize(bw_img)
	thin_img = thin_img.astype(np.uint8) * 255

	x = []
	y = []
	sx, sy = np.where(thin_img==255)
	sx_first = sx[0]
	sy_first = sy[0]
	x.append(sx_first)
	y.append(sy_first) 
	dir=[[-1,-1],[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1]]
   
	while True:
		f = 0
		for i in range(8):
			if thin_img[sx_first + dir[i][0]][sy_first + dir[i][1]] == 255 and findpoint(x, y, (sx_first + dir[i][0]), (sy_first + dir[i][1])):
				sx_first +=  dir[i][0]
				sy_first +=  dir[i][1]
				f = 1
				break
		if f == 0:
			break
		if sx_first == x[0] and sy_first == y[0]:
			break
		x.append(sx_first)
		y.append(sy_first)

	# for i in range(len(x)):
	# 	cv2.circle(img_c3, (y[i], x[i]), 1, (0, 0, 255), -1)
	# cv2.imshow('img', img_c3)
	# cv2.waitKey(0)
	return x, y

# 计算点之间的欧氏距离
def euclidean_Dis(x1, y1, x2, y2):
	d12 = np.sqrt((x1 - x2)**2 + (y1 - y2)**2)
	return d12

# 计算直线方程
def getLineFunc(x1, y1, x2, y2, tmp_x, num):
    k = (y2 - y1) / (x2 - x1)
    line_k = - (1 / k)
    b1 = y1 - line_k*x1
    b2 = y2 - line_k*x2
    L = np.abs(b1-b2)
    step_L = L / 20
    if line_k < 0:
        if b1 < b2:
            tmp_y = line_k * tmp_x + (b1 + step_L * num)
        else:
            tmp_y = line_k * tmp_x + (b2 + step_L * num)
    else:
        if b1 > b2:
            tmp_y = line_k * tmp_x + (b1 - step_L * num)
        else:
            tmp_y = line_k * tmp_x + (b2 - step_L * num)
    return tmp_y


def AutoEF(gray_ED, gray_ES):
    ED_ES_arr = [gray_ED, gray_ES]
    ED_ES_value = []
    for i in range(2):
        gray = ED_ES_arr[i]
        LV_img = copy.deepcopy(gray)
        LV_img[LV_img != 255] = 0
        LV_img_Gaussian = cv2.GaussianBlur(LV_img, (3, 3), 1)
        LV_img_Gaussian[LV_img_Gaussian > 0] = 255
        LV_img_uint8 = LV_img_Gaussian.astype("uint8")
        LV_edge = cv2.Canny(LV_img_uint8, 1, 1)
        # cv2.imwrite("ED.png", LV_edge)

        img_c3 = np.expand_dims(LV_edge, axis=2).repeat(3, axis=2)

        # # 计算曲率
        arr_x, arr_y = getAllPoint(LV_edge, img_c3)
        step_point = 8
        j = 0
        p = step_point
        ka = []
        no = []
        po = []
        for i in range(len(arr_x)):	
            if i <  step_point:
                input_x = None
                input_y = None
                idx_first = len(arr_x) - p
                idx_cur = i
                idx_next = i + step_point
                input_x = [arr_x[idx_first], arr_x[idx_cur], arr_x[idx_next]]
                input_y = [arr_y[idx_first], arr_y[idx_cur], arr_y[idx_next]]
                p -= 1 
            if i <  len(arr_x) - step_point and i >= step_point:
                input_x = None
                input_y = None
                idx_first = i - step_point
                idx_cur = i
                idx_next = i + step_point
                input_x = [arr_x[idx_first], arr_x[idx_cur], arr_x[idx_next]]
                input_y = [arr_y[idx_first], arr_y[idx_cur], arr_y[idx_next]]
            if i >= len(arr_x) - step_point:
                idx_first = i - step_point
                idx_cur = i
                idx_next = j
                input_x = [arr_x[idx_first], arr_x[idx_cur], arr_x[idx_next]]
                input_y = [arr_y[idx_first], arr_y[idx_cur], arr_y[idx_next]]
                j += 1
            kappa, norm_direction = PJcurvature(input_x, input_y)

            if kappa > 0.1:
                po.append([arr_x[idx_cur], arr_y[idx_cur]])     # 保持位置
                ka.append(kappa)                                # 保持斜率
                no.append(norm_direction)                       # 保持方向
                # cv2.circle(img_c3, (arr_y[idx_cur], arr_x[idx_cur]), 1, (0, 0, 255), -1)


        # 显示曲率方向
        po = np.array(po)
        no = np.array(no)
        ka = np.array(ka)

        # plt.imshow(img_c3)
        # plt.quiver(po[:,1], po[:,0], ka*no[:,0],ka*no[:,1], color='r', angles='uv',scale_units='xy',scale=0.01)  # uv表示方向, xy位置参数,此时uv表示其增量
        # plt.show()

        # cv2.imshow('img', img_c3)
        # cv2.waitKey(0)

        # 分堆,将三块区域分开
        x = po[:, 0]
        y = po[:, 1]
        arr_1 = []
        arr_2 = []
        arr_3 = []
        arr_4 = []
        cls_1_p = []
        cls_2_p = []
        cls_3_p = []
        cls_4_p = []
        x1 = x[0]
        y1 = y[0]
        arr_1.append([x1, y1])
        cls_1_p.append(ka[0])
        for i in range(1, len(x)):
            x2 = x[i]
            y2 = y[i]
            d = euclidean_Dis(x1, y1, x2, y2)
            if d < 10:
                arr_1.append([x2, y2])
                cls_1_p.append(ka[i])
            else:
                arr_2.append([x2, y2])
                cls_2_p.append(ka[i])

        cls_2_p = np.array(cls_2_p)
        arr_2 = np.array(arr_2)
        array_x = arr_2[:, 0]
        array_y = arr_2[:, 1]
        point_x1 = array_x[0]
        point_y1 = array_y[0]
        arr_4.append([point_x1, point_y1])
        cls_4_p.append(cls_2_p[0])
        for j in range(1, len(array_x)):
            point_x2 = array_x[j]
            point_y2 = array_y[j]
            d1 = euclidean_Dis(point_x1, point_y1, point_x2, point_y2)
            if d1 < 10:
                arr_4.append([point_x2, point_y2])
                cls_4_p.append(cls_2_p[j])
            else:
                arr_3.append([point_x2, point_y2])
                cls_3_p.append(cls_2_p[j])


        # 获取三个区域的数组 心尖和二尖瓣  找到每个区域中曲率最大的值
        # 分堆后的位置
        cls_1 = np.array(arr_1)
        cls_2 = np.array(arr_3)
        cls_3 = np.array(arr_4)
        # 分堆后对应的概率
        cls_1_arrp = cls_1_p
        cls_2_arrp = cls_3_p
        cls_3_arrp = cls_4_p

        cls_1_tem = copy.deepcopy(cls_1_arrp)
        cls_2_tem = copy.deepcopy(cls_2_arrp)
        cls_3_tem = copy.deepcopy(cls_3_arrp)
        for _ in range(1):
            if len(cls_1) == 1:
                cls_1_max_pos = cls_1[0]
            else:
                number1 = max(cls_1_tem)
                index1 = cls_1_tem.index(number1)
                cls_1_tem[index1] = -np.inf
                cls_1_max_pos = cls_1[index1]
            if len(cls_2) == 1:
                cls_2_max_pos = cls_2[0]
            else:
                number2 = max(cls_2_tem)
                index2 = cls_2_tem.index(number2)
                cls_2_tem[index2] = -np.inf
                cls_2_max_pos = cls_2[index2]
            if len(cls_3) == 1:
                cls_3_max_pos = cls_3[0]
            else:
                number3 = max(cls_3_tem)
                index3 = cls_3_tem.index(number3)
                cls_3_tem[index3] = -np.inf
                cls_3_max_pos = cls_3[index3]


        final_pos = np.array([cls_1_max_pos, cls_2_max_pos, cls_3_max_pos])
        mid_x = (cls_2_max_pos[0] + cls_3_max_pos[0]) // 2
        mid_y = (cls_2_max_pos[1] + cls_3_max_pos[1]) // 2

        # 显示三点的连线
        for i in range(3):
            final_x, final_y = final_pos[i][0], final_pos[i][1]
            cv2.circle(img_c3, (final_y, final_x), 1, (0, 0, 255), -1)
            cv2.line(img_c3, (cls_2_max_pos[1], cls_2_max_pos[0]), (cls_3_max_pos[1], cls_3_max_pos[0]), (0, 0, 255), 1, lineType=cv2.LINE_AA)
            cv2.line(img_c3, (cls_1_max_pos[1], cls_1_max_pos[0]), (mid_y, mid_x), (0, 0, 255), 1, lineType=cv2.LINE_AA)


        # 遍历 找线段与边缘的近似交点
        line_x1, line_y1 = cls_1_max_pos[0], cls_1_max_pos[1]
        line_x2, line_y2 = mid_x, mid_y
        arr_tmp_x = arr_x
        arr_tmp_y = arr_y
        S_sum = 0
        line_num = 0
        for num in range(1, 20):
            arr_tmp = []
            arr_loss = []
            for i in range(len(arr_tmp_x)):
                pix_x = arr_tmp_x[i]
                pix_y = arr_tmp_y[i]
                result_y  = getLineFunc(line_x1, line_y1, line_x2, line_y2, pix_x, num)
                loss = abs(result_y - pix_y)
                arr_loss.append(loss)
                arr_tmp.append([pix_x, pix_y])

            t = copy.deepcopy(arr_loss)
            arr_tmp = np.array(arr_tmp)
            L_x = arr_tmp[:, 0]
            L_y = arr_tmp[:, 1]
            new_arr_loss = []
            new_arr_tmp = []
            for _ in range(4):
                number = min(t)
                index = t.index(number)
                t[index] = np.inf
                new_arr_loss.append(number)
                new_arr_tmp.append([L_x[index], L_y[index]])
            new_arr_tmp = np.array(new_arr_tmp)
            new_L_x = new_arr_tmp[:, 0]
            new_L_y = new_arr_tmp[:, 1]
            # 待优化  四个点中选两个直线更加垂直于直线(求斜率进行判断)
            for i in range(len(new_L_x)):
                d_1 = euclidean_Dis(new_L_x[0], new_L_y[0], new_L_x[i], new_L_y[i])    
                if d_1 > 10:
                    cv2.circle(img_c3, (new_L_y[i], new_L_x[i]), 1, (0, 0, 255), -1)
                    cv2.line(img_c3, (new_L_y[0], new_L_x[0]), (new_L_y[i], new_L_x[i]), (0, 0, 255), 1, lineType=cv2.LINE_AA)
                    line_L = np.sqrt((new_L_y[i] - new_L_y[0])**2 + (new_L_x[i] - new_L_x[0]) ** 2)
                    line_num += 1
                    # 圆盘面积
                    S = math.pi * ((line_L / 2) ** 2)
                    S_sum += S
                    break

        # 二尖瓣连线的长度
        line_last = np.sqrt((cls_2_max_pos[1] - cls_3_max_pos[1])**2 + (cls_2_max_pos[0] - cls_3_max_pos[0]) ** 2)
        # 心尖与二尖瓣连线中点的长度
        line_max = np.sqrt((line_y1 - line_y2)**2 + (line_x1 - line_x2) ** 2)
        line_num = line_num + 1
        print(line_num)
        S_sum = S_sum + math.pi * ((line_last / 2) ** 2)
        # EF的计算 圆盘面积之和乘
        V_value = (S_sum * line_max) / line_num
        ED_ES_value.append(V_value)
        # print(EF_value)
        cv2.imshow('img', img_c3)
        cv2.waitKey(0)
    EF = (ED_ES_value[0] - ED_ES_value[1]) / ED_ES_value[0]
    EF = str(EF * 100)[:5] + "%"

    return EF


img = cv2.imread("ED_img.png")
gray_ED = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img1 = cv2.imread("ES_img.png")
gray_ES = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
EF = AutoEF(gray_ED, gray_ES)
print(EF)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只搬烫手的砖

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值