边缘检测 霍夫曼变换 传统图像处理实例

任务:求如下位置交叉点的坐标

边缘检测 霍夫曼变换 传统图像处理实例

; 实现效果

边缘检测 霍夫曼变换 传统图像处理实例

思路:

  1. 通过YOLOv5 检测出目标位置。
  2. 再通过传统图像方法处理。

边缘检测 霍夫曼变换 传统图像处理实例

传统图像方法的处理思路:

  1. HSV提取红颜色,得到位置X形状的图案。
  2. 二值化处理,得到黑白图像。
  3. 膨胀腐蚀处理,去除噪点,得到干净的图案。
  4. 边缘检测。
  5. 霍夫曼变换,得到许多直线。
  6. 过滤多余直线,得到两条交叉的直线。
  7. 联立方程组得到中心点坐标。

HSV 颜色提取

标志颜色分明,因此用HSV来提取红色区域。使用网站来提取图片颜色所在的色调,饱和度和明度。
http://color.lukas-stratmann.com/color-systems/hsv.html

边缘检测 霍夫曼变换 传统图像处理实例
也可以写个小脚本通过鼠标来获取图像的HSV。
    def get_hsv_value(self, data_path):
        def getpos(event, x, y, flags, param):
            if event == cv2.EVENT_LBUTTONDOWN:
                print(HSV[y, x])

        for img_name in os.listdir(data_path):
            img_path = os.path.join(data_path, img_name)
            image = cv2.imread(img_path)
            image = cv2.resize(image, (960, 960))
            HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

            cv2.imshow("imageHSV", HSV)
            cv2.imshow('image', image)
            cv2.setMouseCallback("imageHSV", getpos)
            cv2.waitKey(0)

            cv2.setMouseCallback("image", getpos)
            cv2.waitKey(0)

如下图,第二幅即为通过HSV提取的图像。

边缘检测 霍夫曼变换 传统图像处理实例

二值化处理

得到第三幅图像。

def RGB2Black(self, img, thresh=80):
    thresh = 10

    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img = cv2.threshold(gray_img, thresh, 255, cv2.THRESH_BINARY)[1]
    return img

膨胀腐蚀

通过两次膨胀操作和一次腐蚀操作得到最后一张图像,这个主要根据图像的实际情况调整。膨胀腐蚀操作通常是为了去除噪点,得到干净清晰的轮廓。

def dilate_img(self, img):
    kernel = np.ones((5, 5), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    return img

def erode_img(self, img):
    kernel = np.ones((5, 5), np.uint8)
    img = cv2.erode(img, kernel, iterations=1)
    return img

边缘检测 霍夫曼变换

下面对Canny和HoughLines方法的参数做一点解读。

cv2.Canny

  1. image 为 8 位输入图像。
  2. threshold1 表示处理过程中的第一个阈值。
  3. threshold2 表示处理过程中的第二个阈值。

第2,3个参数越小,那么获取得到的细节信息就越丰富。

cv2.HoughLines

  1. image 为 8 位输入图像。
  2. 第二个和第三个参数分别是rho 和theta的精度。
  3. 第四个参数是线长阈值。
dst = cv2.Canny(src, 50, 200, None, 3)

cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

lines = cv2.HoughLines(dst, 1, np.pi / 180, 80, None, 0, 0)

多余线过滤

过滤条件

  1. 角度小于10度。
  2. 边缘的线条不要,只要中间两条交叉线。
  3. 最后得到的线条如果大于两条,那么不要。

    logger.debug("######################################## angle inspection")
    angle_approve_dict = {}
    for i, (pt1, pt2) in enumerate(pts_lines):

        angle = int(math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]) * (180 / math.pi))
        if angle_approve_dict == {}:
            angle_approve_dict[str(angle)] = [(pt1, pt2)]
        else:

            for an in angle_approve_dict:
                int_an = int(an)
                if abs(angle - int_an) < simi_angle_thres or 180 - abs(angle - int_an) < simi_angle_thres:
                    angle_approve_dict[an].append((pt1, pt2))
                    break
            else:
                angle_approve_dict[str(angle)] = [(pt1, pt2)]
        logger.debug(f"angle_approve_dict:{angle_approve_dict}")

    angle_approved_dist_max = []
    logger.debug("######################################## dist min after angle inspection")
    for an in angle_approve_dict:
        dist_line2center_list = [get_dist_line2center(center_x, pt1, pt2) for (pt1, pt2) in angle_approve_dict[an]]
        logger.debug("dist_line2center_list:{}".format(dist_line2center_list))
        logger.debug("np.argmin(dist_line2center_list):{}".format(np.argmin(dist_line2center_list)))
        angle_approved_dist_max.append(angle_approve_dict[an][np.argmin(dist_line2center_list)])

    logger.debug("######################################## remove the line that is too far from the center")
    angle_approved_dist_max_2 = angle_approved_dist_max.copy()
    for (pt1, pt2) in angle_approved_dist_max_2:
        logger.debug("##############################")
        if len(angle_approved_dist_max) == 2:
            break
        dist_line2center = get_dist_line2center(center_x, pt1, pt2)
        if dist_line2center > center_radius_thres:
            del angle_approved_dist_max[angle_approved_dist_max.index((pt1, pt2))]
        logger.debug(f"pt1:{pt1}")
        logger.debug(f"pt2:{pt2}")
        logger.debug(f"dist_line2center:{dist_line2center}")
        logger.debug("angle_approved_dist_max:{}".format(angle_approved_dist_max))

    if len(angle_approved_dist_max) != 2:
        logger.debug(f"line number is not 2, the actual line number is:{len(angle_approved_dist_max)}")
        return None

    inter_pts = get_intersection_pts(angle_approved_dist_max)
    logger.debug(f"inter_pts:{inter_pts}")

联立方程组求交点

已知的两条直线分别由各自线上的两个点来表示。
联立方程组,最后返回交点坐标。

def get_intersection_pts(two_lines):
    (pt1, pt2) = two_lines[0]
    (pt3, pt4) = two_lines[1]
    A = np.array([
        [pt1[1] - pt2[1], pt2[0] - pt1[0]],
        [pt3[1] - pt4[1], pt4[0] - pt3[0]]
    ])
    b = np.array([
        [pt1[1] * pt2[0] - pt1[0] * pt2[1]],
        [pt3[1] * pt4[0] - pt3[0] * pt4[1]]
    ])
    x, y = np.linalg.solve(A, b)
    return (x, y)

代码

转黑白图像

class cropped_X_shaped_dataset:
    def __init__(self):
        self.data_path = r"datasets\shibie\cropped"
        self.result_path = r"datasets\shibie\result"
        self.out_path = r"datasets\shibie\processed"

    def random_sample(self, sample_size):
        img_list = os.listdir(self.data_path)
        random.shuffle(img_list)
        return img_list[sample_size:]

    def extract_whole_dataset(self):
        delete_folders(self.out_path, self.result_path)
        create_folders(self.out_path, self.result_path)
        for img_name in self.random_sample(20):
            img_path = os.path.join(self.data_path, img_name)
            img = cv2.imread(img_path)
            img = cv2.resize(img, (960, 960))

            transform_seq = [self.process_img,

                             self.RGB2Black,
                             self.dilate_img,
                             self.dilate_img,

                             self.erode_img,
                             ]

            result, img_proc = self.transform_guy(transform_seq, img)

            img_save_name = img_name.replace(".jpg", "_proc.jpg")

            result_save_name = img_name.replace(".jpg", "_result.jpg")
            cv2.imwrite(os.path.join(self.out_path, img_save_name), img_proc)

            result = cv2.resize(result, (960, 960))

            cv2.imwrite(os.path.join(self.result_path, result_save_name), result)

    def process_img(self, img: np.ndarray) -> np.ndarray:

        result = img.copy()

        image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

        high_V = 245
        low_V = 0
        high_S = 255
        low_S = 30

        lower1 = np.array([0, low_S, low_V])
        upper1 = np.array([40, high_S, high_V])

        lower2 = np.array([130, low_S, low_V])
        upper2 = np.array([179, high_S, high_V])

        lower_mask = cv2.inRange(image, lower1, upper1)
        upper_mask = cv2.inRange(image, lower2, upper2)

        full_mask = lower_mask + upper_mask

        img_ = cv2.bitwise_and(result, result, mask=full_mask)

        return img_

    def concat_img(self, *img_lists):
"""

        :param img_lists: [img_1, img_2, img_3, ...] img_1: np.ndarray
        :return:
"""
        cat_one_cv2 = img_lists[0]
        if len(cat_one_cv2.shape) == 2:
            cat_one_cv2 = cv2.cvtColor(cat_one_cv2, cv2.COLOR_GRAY2BGR)
        cat_one_shape = cat_one_cv2.shape
        for i in range(1, len(img_lists)):
            cat_other_one_cv2 = img_lists[i]
            if len(cat_other_one_cv2.shape) == 2:
                cat_other_one_cv2 = cv2.cvtColor(cat_other_one_cv2, cv2.COLOR_GRAY2BGR)

            cat_other_one_cv2 = cv2.resize(cat_other_one_cv2, (cat_one_shape[1], cat_one_shape[0]))

            cat_one_cv2 = np.hstack((cat_one_cv2, cat_other_one_cv2))
        return cat_one_cv2

    def dilate_img(self, img):
        kernel = np.ones((5, 5), np.uint8)
        img = cv2.dilate(img, kernel, iterations=1)
        return img

    def erode_img(self, img):
        kernel = np.ones((5, 5), np.uint8)
        img = cv2.erode(img, kernel, iterations=1)
        return img

    def RGB2Black(self, img, thresh=80):
        thresh = 10

        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img = cv2.threshold(gray_img, thresh, 255, cv2.THRESH_BINARY)[1]
        return img

    def get_hsv_value(self, data_path):
        def getpos(event, x, y, flags, param):
            if event == cv2.EVENT_LBUTTONDOWN:
                print(HSV[y, x])

        for img_name in os.listdir(data_path):
            img_path = os.path.join(data_path, img_name)
            image = cv2.imread(img_path)
            image = cv2.resize(image, (960, 960))
            HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

            cv2.imshow("imageHSV", HSV)
            cv2.imshow('image', image)

            cv2.setMouseCallback("image", getpos)
            cv2.waitKey(0)

    def transform_guy(self, transform_seq, img):
        transformed_list = [img]
        for transform in transform_seq:
            img = transform(img)
            transformed_list.append(img)

        concat_img = self.concat_img(*transformed_list)
        return transformed_list[-1], concat_img

    def line_test(self):
        data_path = self.result_path
        for img_name in os.listdir(data_path):
            img_path = os.path.join(data_path, img_name)
            img = cv2.imread(img_path)
            edged = cv2.Canny(img, 30, 150)

            cv2.imshow("image", edged)
            cv2.waitKey(0)

if __name__ == '__main__':
    tac_cropped = cropped_X_shaped_dataset()
    tac_cropped.extract_whole_dataset()

line_detection.py

"""
@file hough_lines.py
@brief This program demonstrates line finding with the Hough transform
"""
import logging
import os
import random
import shutil
import sys
import math
import time

import cv2 as cv2
import numpy as np

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
FORMAT = '%(asctime)s %(processName)-9s %(threadName)-9s  %(message)s'
logging.basicConfig(format=FORMAT)

def get_intersection_pts(two_lines):
    (pt1, pt2) = two_lines[0]
    (pt3, pt4) = two_lines[1]
    A = np.array([
        [pt1[1] - pt2[1], pt2[0] - pt1[0]],
        [pt3[1] - pt4[1], pt4[0] - pt3[0]]
    ])
    b = np.array([
        [pt1[1] * pt2[0] - pt1[0] * pt2[1]],
        [pt3[1] * pt4[0] - pt3[0] * pt4[1]]
    ])
    x, y = np.linalg.solve(A, b)
    return (x, y)

def line_detect_one_img(src):

    dst = cv2.Canny(src, 50, 200, None, 3)

    cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

    lines = cv2.HoughLines(dst, 1, np.pi / 180, 80, None, 0, 0)
    center_x = (480, 480)
    center_radius_thres = 300
    simi_angle_thres = 40
    if lines is not None:
        pts_lines = get_pts_lines(lines)
    else:
        return None

    logger.debug("######################################## angle inspection")
    angle_approve_dict = {}
    for i, (pt1, pt2) in enumerate(pts_lines):

        angle = int(math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]) * (180 / math.pi))
        if angle_approve_dict == {}:
            angle_approve_dict[str(angle)] = [(pt1, pt2)]
        else:

            for an in angle_approve_dict:
                int_an = int(an)
                if abs(angle - int_an) < simi_angle_thres or 180 - abs(angle - int_an) < simi_angle_thres:
                    angle_approve_dict[an].append((pt1, pt2))
                    break
            else:
                angle_approve_dict[str(angle)] = [(pt1, pt2)]
        logger.debug(f"angle_approve_dict:{angle_approve_dict}")

    angle_approved_dist_max = []
    logger.debug("######################################## dist min after angle inspection")
    for an in angle_approve_dict:
        dist_line2center_list = [get_dist_line2center(center_x, pt1, pt2) for (pt1, pt2) in angle_approve_dict[an]]
        logger.debug("dist_line2center_list:{}".format(dist_line2center_list))
        logger.debug("np.argmin(dist_line2center_list):{}".format(np.argmin(dist_line2center_list)))
        angle_approved_dist_max.append(angle_approve_dict[an][np.argmin(dist_line2center_list)])

    logger.debug("######################################## remove the line that is too far from the center")
    angle_approved_dist_max_2 = angle_approved_dist_max.copy()
    for (pt1, pt2) in angle_approved_dist_max_2:
        logger.debug("##############################")
        if len(angle_approved_dist_max) == 2:
            break
        dist_line2center = get_dist_line2center(center_x, pt1, pt2)
        if dist_line2center > center_radius_thres:
            del angle_approved_dist_max[angle_approved_dist_max.index((pt1, pt2))]
        logger.debug(f"pt1:{pt1}")
        logger.debug(f"pt2:{pt2}")
        logger.debug(f"dist_line2center:{dist_line2center}")
        logger.debug("angle_approved_dist_max:{}".format(angle_approved_dist_max))

    if len(angle_approved_dist_max) != 2:
        logger.debug(f"line number is not 2, the actual line number is:{len(angle_approved_dist_max)}")
        return None

    inter_pts = get_intersection_pts(angle_approved_dist_max)
    logger.debug(f"inter_pts:{inter_pts}")

    for (pt1, pt2) in angle_approved_dist_max:
        dist_line2center_list = [get_dist_line2center(center_x, pt1, pt2) for (pt1, pt2) in angle_approved_dist_max]
        cv2.line(cdst, pt1, pt2, (0, 0, 255), 3, cv2.LINE_AA)
    cv2.circle(cdst, (int(inter_pts[0]), int(inter_pts[1])), 10, (0, 255, 255), -1)

    cv2.circle(cdst, center_x, center_radius_thres, (0, 0, 255), )

    return cdst, inter_pts

def get_pts_lines(lines):
    pts_lines = []
    for i in range(0, len(lines)):
        rho = lines[i][0][0]
        theta = lines[i][0][1]
        a = math.cos(theta)
        b = math.sin(theta)
        x0 = a * rho
        y0 = b * rho
        pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))
        pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))
        pts_lines.append((pt1, pt2))

    return pts_lines

def get_dist_line2center(center_x, pt1, pt2):
    dist_pt1_center = math.sqrt((pt1[0] - center_x[0]) ** 2 + (pt1[1] - center_x[1]) ** 2)
    distance_pt2_center = math.sqrt((pt2[0] - center_x[0]) ** 2 + (pt2[1] - center_x[1]) ** 2)
    distance_pt1_pt2 = math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)

    s = (dist_pt1_center + distance_pt2_center + distance_pt1_pt2) / 2
    area = math.sqrt(s * (s - dist_pt1_center) * (s - distance_pt2_center) * (s - distance_pt1_pt2))
    dist_line_center = 2 * area / distance_pt1_pt2
    return dist_line_center

def tackle_one_img_after_yolo(img, pts, save_path):
    pts = [int(pt.cpu().numpy()) for pt in pts]
    img_ = img[pts[1]:pts[3], pts[0]:pts[2]]
    ori_shape = img_.shape[:2]
    img_ = cv2.resize(img_, (960, 960))
    cXsd = cropped_X_shaped_dataset()
    img_ = cXsd.tackle_one_img(img_)
    line_detected_img, inter_pts = line_detect_one_img(img_)

    inter_pts = np.array(inter_pts).squeeze()
    inter_pts_warped = inter_pts * (1.0 / 960) * ori_shape[::-1]
    inter_pts_warped_biased = inter_pts_warped + np.array([pts[0], pts[1]])
    cv2.imwrite(str(save_path), line_detected_img)
    return inter_pts_warped_biased

def delete_folders(*folder_path):
    for folder in folder_path:
        if os.path.exists(folder):
            shutil.rmtree(folder)

def create_folders(*folders):
    for folder in folders:
        if not os.path.exists(folder):
            os.makedirs(folder)

class cropped_X_shaped_dataset:
    def tackle_one_img(self, img):
        transform_seq = [self.process_img,
                         self.RGB2Black,
                         self.dilate_img,
                         self.dilate_img,
                         self.erode_img,
                         ]

        result = self.transform_guy(transform_seq, img)
        return result

    def process_img(self, img: np.ndarray) -> np.ndarray:

        result = img.copy()

        image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

        high_V = 245
        low_V = 0
        high_S = 255
        low_S = 30

        lower1 = np.array([0, low_S, low_V])
        upper1 = np.array([40, high_S, high_V])

        lower2 = np.array([130, low_S, low_V])
        upper2 = np.array([179, high_S, high_V])

        lower_mask = cv2.inRange(image, lower1, upper1)
        upper_mask = cv2.inRange(image, lower2, upper2)

        full_mask = lower_mask + upper_mask

        img_ = cv2.bitwise_and(result, result, mask=full_mask)
        return img_

    def dilate_img(self, img):
        kernel = np.ones((5, 5), np.uint8)
        img = cv2.dilate(img, kernel, iterations=1)
        return img

    def erode_img(self, img):
        kernel = np.ones((5, 5), np.uint8)
        img = cv2.erode(img, kernel, iterations=1)
        return img

    def RGB2Black(self, img, thresh=80):
        thresh = 10

        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img = cv2.threshold(gray_img, thresh, 255, cv2.THRESH_BINARY)[1]
        return img

    def transform_guy(self, transform_seq, img):
        for transform in transform_seq:
            img = transform(img)
        return img

if __name__ == "__main__":

    data_path = r"d:\ANewspace\code\yolov5_new\datasets\shibie\result"
    out_dir = r"d:\ANewspace\code\yolov5_new\datasets\shibie\line_detected"
    if os.path.exists(out_dir):
        shutil.rmtree(out_dir)
    os.makedirs(out_dir)
    for img_name in os.listdir(data_path):
        logger.debug("########################################")
        img = cv2.imread(cv2.samples.findFile(os.path.join(data_path, img_name)), cv2.IMREAD_GRAYSCALE)
        line_detected_img, inter_pts = line_detect_one_img(img)
        if line_detected_img is not None:
            cv2.imwrite(os.path.join(out_dir, img_name), line_detected_img)

若有不懂,可尽量私信发来。

Original: https://blog.csdn.net/qq_28423665/article/details/126267152
Author: kuhn_hawaha
Title: 边缘检测 霍夫曼变换 传统图像处理实例

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/703026/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球