C#命令行执行Python脚本

C#命令行执行Python脚本

一、环境

1. 软件环境

  1. Anaconda
  2. PyCharm
  3. Visual Studio 2015

2. Python环境

  1. opencv_python
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv_python
  1. matplotlib
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib

二、Python代码

# encoding: utf-8
"""
@Time:2022/3/21 16:01
@Author: shujin sun
@Desc: 缺陷检测测试
"""

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import math
import sys

# 中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 显示中文
plt.rcParams['axes.unicode_minus'] = False  # 显示坐标轴负号


class DetectDefect:
    def __init__(self):
        self.little_img_file = ''
        self.big_img_file = ''

        self.big_img = np.array(0)
        self.little_img = np.array(0)
        self.roi_img = np.array(0)
        # 缺陷检测二值化结果
        self.bw_img = np.array(0)

        # big image 的大小
        self.w = 0
        self.h = 0
        # 重点关注区域,即中间区域
        self.roi_rect = [0, 0, 0, 0]
        self.roi_patchs = []
        # 缺陷检测阈值
        self.thresh_value = 3000

        # 所有的匹配方法
        self.match_methods = ['cv.TM_CCOEFF',  # 相关系数匹配法
                              'cv.TM_CCOEFF_NORMED',  # 归一化版本
                              'cv.TM_CCORR',  # 相关匹配法
                              'cv.TM_CCORR_NORMED',
                              'cv.TM_SQDIFF',  # 平方差匹配法
                              'cv.TM_SQDIFF_NORMED']

    def set_little_image(self, _file):
        self.little_img_file = _file
        self.little_img = cv.imread(_file)# cv.IMREAD_GRAYSCALE

    def set_big_image(self, _file):
        self.big_img_file = _file
        self.big_img = cv.imread(_file)
        print(self.big_img.shape)
        self.h, self.w = self.big_img.shape[:2]
        print('big image shape: ', self.w, self.h)

    def set_thresh_value(self, thresh_value):
        """设置缺陷检测阈值门限"""
        self.thresh_value = thresh_value

    def conduct_edge(self, src):
        '''边缘检测'''
        # 计算像素中位数
        gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        median_intensity = np.median(gray_img)

        lower_threshold = int(max(0, (1.0 - 0.33) * median_intensity))
        upper_threshold = int(min(255, (1.0 + 0.33) * median_intensity))

        canny_img = cv.Canny(gray_img, lower_threshold, upper_threshold)

        plt.imshow(canny_img, cmap='gray')
        plt.title('canny edge detect result')
        plt.xticks([]), plt.yticks([])
        plt.show()

    def conduct_binary_adaptive(self, src):
        """二值化操作,自适应方法"""
        src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        bw_img = cv.adaptiveThreshold(src,
                                      255,
                                      cv.ADAPTIVE_THRESH_MEAN_C,#cv.ADAPTIVE_THRESH_GAUSSIAN_C,
                                      cv.THRESH_BINARY,
                                      5,
                                      7)
        return bw_img

    def conduct_binary(self, src):
        """二值化操作"""
        src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        mean_intensity = np.mean(src)
        median_intensity = np.median(src)
        # print('图像灰度均值、中值:', mean_intensity, median_intensity)
        _, bw_img = cv.threshold(src, mean_intensity, 255, cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
        return bw_img

    def match_template(self, big, little):
        img = big.copy()
        # eval函数,将字符串作为脚本代码来执行
        idx = 3  # 1 or 3方法效果最好
        print('选择的匹配算法:', self.match_methods[idx])
        method = eval(self.match_methods[idx])
        res = cv.matchTemplate(img, little, method)

        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
        top_left = max_loc
        h, w = little.shape[:2]
        bottom_right = (top_left[0] + w, top_left[1] + h)
        return top_left, bottom_right

    def split_patches(self):
        """异常检测
        1.两种choice匹配
        choice1:选择所有匹配算法计算,通过比较评估值,计算最优匹配结果
        choice2:仅挑选一种匹配算法计算,通过前期测试验证,事先固化最稳健的算法
        另外,可考虑图像偏转一定角度(极小范围,可多个值,该角度值若能用算法估算更佳),旋转后再进行匹配、检测

        2.上下扩充
        在roi区域中匹配结束之后,上下进行扩充选择,进行缺陷区域检测,防止漏网之鱼

        3.二次匹配、检测
        在结果中按照固定尺寸分割小块,随机选择1块,or二值化求取面积最大快作为模板块(或者在little_img中截取)

        4.通过模板块,在其他区域进行相关或者相减计算(考虑先进行二值化),根据一定阈值,给出是否缺陷判决

        在roi块中匹配结束之后,再转换至整体图像中位置(视需求而定,忽视此操作也可)
        """
        top_left, bottom_right = self.match_template(self.roi_img, self.little_img)
        print('roi中间结果:', top_left, bottom_right)

        # 通过上下平移获取所有可能的匹配结果
        h = self.little_img.shape[0]  # 小图高度
        n_upper = -int(top_left[1] / h)
        n_bottom = int((self.roi_img.shape[0] - bottom_right[1]) / h)
        patch_arrays = [] # 保存切片在ROI中的位置

        tmp_img = self.roi_img.copy()
        for i in range(n_upper, n_bottom + 1):
            # 上下拓展,包括自身
            t = top_left[1] + i * h
            b = bottom_right[1] + i * h
            if i != 0:
                t = t + int(i / abs(i))
                b = b + int(i / abs(i))
            print('i={0},范围:{1}-{2}'.format(i, t, b))

            # 截取图像切片
            patch_arrays.append([t, b, top_left[0], bottom_right[0]])
            src = tmp_img[t:b, top_left[0]:bottom_right[0]]

            # 保存为文件
            # save_name = '{0}_slice_{1}.bmp'.format(big_img_file[:-4], i)
            # print(src.shape, save_name)
            # cv.imwrite(save_name, src)

            # 显示中间匹配结果
            cv.rectangle(tmp_img, (top_left[0], t), (bottom_right[0], b), 255, 2)

        self.roi_patchs = np.array(patch_arrays)
        print('切片位置:', self.roi_patchs, self.roi_patchs.shape)
        plt.imshow(tmp_img, cmap='gray')
        plt.title('ROI匹配中间结果')
        plt.show()

    def set_roi_rect(self, ratio_left=0.27, ratio_right=0.82, ratio_upper=0.2, ratio_bottom=0.8):
        '''设置中间关注区域,左右上下位置的ratio比例'''
        self.roi_rect[0] = int(self.w * ratio_left)
        self.roi_rect[1] = int(self.w * ratio_right)
        self.roi_rect[2] = int(self.h * ratio_upper)
        self.roi_rect[3] = int(self.h * ratio_bottom)

    def show_roi_img(self):
        """显示中间roi图像"""
        print('roi:', self.roi_rect)
        self.roi_img = self.big_img[self.roi_rect[2]:self.roi_rect[3], self.roi_rect[0]:self.roi_rect[1],:]
        # 保存中间roi图像
        save_name = '{0}_roi.bmp'.format(big_img_file[:-4])
        cv.imwrite(save_name, self.roi_img)

        tmp_img = self.big_img.copy()
        top_left = (self.roi_rect[1], self.roi_rect[3])
        bottom_right = (self.roi_rect[0], self.roi_rect[2])
        cv.rectangle(tmp_img, top_left, bottom_right, 255, 2)
        plt.subplot(131), plt.title('Big image')
        plt.imshow(tmp_img)
        plt.subplot(132), plt.title('roi image')
        plt.imshow(self.roi_img, cmap='gray')
        # plt.xticks([]), plt.yticks([])
        plt.subplot(133), plt.title('little image')
        plt.imshow(self.little_img, cmap='gray')

        plt.show()

    def show_defect_image(self):
        '''显示缺陷检测结果图像'''
        tmp_img = self.big_img.copy()
        top_left = (50, 50)
        bottom_right = (100, 100)
        cv.rectangle(tmp_img, top_left, bottom_right, (255, 0, 0), 4)

        plt.imshow(tmp_img)
        plt.title('缺陷检测结果')
        plt.show()

    def defect_analyze_patchs(self):
        '''对切片逐个进行二值化和连通分析,返回连通分析结果'''
        is_defected = False
        img_output = self.big_img.copy()
        n_patches = self.roi_patchs.shape[0]
        roi_img_output = self.roi_img.copy()
        for i in range(0, n_patches):
            pos = self.roi_patchs[i, :]
            # patch 在roi中的位置,按照左右上下顺序排列
            path_in_roi = pos[2], pos[0]
            print('patch 在ROI中位置:', path_in_roi)
            patch = self.roi_img[pos[0]:pos[1], pos[2]:pos[3]]
            # 每个小方块的检测结果
            result, width_square = self.square_detect_in_patch(patch)
            for j in range(0, result.shape[0]):
                if result[j, 0] == 1:
                    is_defected = True
                    topleft = int(j*width_square + result[j, 1]), int(result[j, 2])
                    bottomright = int(topleft[0] + result[j, 3]), int(result[j, 4])
                    print('patch 中位置:', topleft, bottomright)

                    # 在切片上画图
                    # cv.rectangle(patch, topleft, bottomright, (255, 0, 0), 10)

                    # 将位置转换至ROI图像上, x,y,width,height
                    pos_in_patch = topleft[0], topleft[1], result[j, 3], result[j, 4]
                    pos_in_roi = self.cvt_pos_to_roi(pos_in_patch, path_in_roi)

                    # 在ROI上画图
                    topleft, bottomright = self.cvt_top_bottom(pos_in_roi[0],
                                                               pos_in_roi[1],
                                                               pos_in_roi[2],
                                                               pos_in_roi[3])
                    print('ROI中位置:', topleft, bottomright)
                    cv.rectangle(roi_img_output, topleft, bottomright, (255, 0, 0), 5)

                    # 将位置转换至整个图像上
                    pos_in_img = self.cvt_pos_to_image(pos_in_patch, path_in_roi)
                    topleft, bottomright = self.cvt_top_bottom(pos_in_img[0],
                                                               pos_in_img[1],
                                                               pos_in_img[2],
                                                               pos_in_img[3])

                    cv.rectangle(img_output, topleft, bottomright, (255, 0, 0), 5)

        plt.subplot(121), plt.imshow(roi_img_output)
        plt.title('ROI区域')
        plt.subplot(122), plt.imshow(img_output)
        title = '有缺陷' if is_defected else '正常'
        plt.title(title)
        plt.show()

    def cvt_top_bottom(self, x, y, width, height):
        '''将x,y,w,h转换为矩阵的topleft和bottomright'''
        topleft = x, y
        bottomright = x + width, y + height

        return topleft, bottomright

    def cvt_pos_to_roi(self, pos_in_patch, patch_in_roi):
        '''将patch坐标转换至roi上'''
        pos_in_roi= []

        x = pos_in_patch[0] + patch_in_roi[0]
        y = pos_in_patch[1] + patch_in_roi[1]

        width = pos_in_patch[2]
        height = pos_in_patch[3]

        pos_in_roi = int(x), int(y), int(width), int(height)

        return pos_in_roi

    def cvt_pos_to_image(self, pos_in_patch, patch_in_roi):
        '''将patch坐标转换至image上'''
        pos_in_image = []

        x = pos_in_patch[0] + patch_in_roi[0] + self.roi_rect[0]
        y = pos_in_patch[1] + patch_in_roi[1] + self.roi_rect[2]

        width = pos_in_patch[2]
        height = pos_in_patch[3]

        pos_in_image = int(x), int(y), int(width), int(height)

        return pos_in_image

    def white_ratio_analyze(self, src):
        """白色比例分析"""
        w, h = src.shape
        # print("ratio分析:", w, h, src.sum(), src.max())
        ratio = src.sum() / (w * h * 255)
        return ratio

    def connect_component(self, src_in):
        """连通区域分析"""
        src = src_in.copy()
        n, labels, stats, centroids = cv.connectedComponentsWithStats(src, connectivity=8)
        # print('----' * 20, "\n", stats)

        # 计算面积 同 长宽比值,或者面积最大值
        # 此处直接使用面积最大值
        # stats:x y width, height, area,行数为n,首行为背景
        array_ratio = stats[1:, -1]

        # print('剔除背景之后:', array_ratio)
        idx = array_ratio.argmax() + 1
        max_ratio = array_ratio.max()
        # print('像素面积:', stats[:, 4], sum(stats[:, 4]))
        # print('面积最大值:', max_ratio, idx)

        # 根据像素点计算实际面积
        mask1 = labels == idx
        # print('实际像素个数:', sum(sum(mask1)))

        # 不同的连通域赋予不同的颜色
        output = np.zeros((labels.shape[0], labels.shape[1], 3), np.uint8)
        for i in range(1, n):
            mask = labels == i
            output[:, :, 0][mask] = np.random.randint(0, 255)
            output[:, :, 1][mask] = np.random.randint(0, 255)
            output[:, :, 2][mask] = np.random.randint(0, 255)

        # 矩形区域
        x, y, width, height, areas = stats[idx, :]
        topleft = x, y
        bottomright = x + width, y + height
        cv.rectangle(output, topleft, bottomright, (255, 255, 0), 2, 1, 0)
        # 中心点
        center = int(centroids[idx, 0]), int(centroids[idx, 1])
        cv.circle(output, center, 5, (255, 0, 0), 1)
        # cv.putText(output,"{0}".format(areas),center,cv.FONT_HERSHEY_SIMPLEX,0.5,(255,0,0),1)

        # for idx_i in range(0,n):
        #     # 矩形区域
        #     x, y, width, height, areas = stats[idx_i, :]
        #     print(idx_i, '循环:', areas)
        #     topleft = x, y
        #     bottomright = x + width, y + height
        #     #cv.rectangle(output, topleft, bottomright, (255, 255, 0), 1, 1, 0)
        #     # 中心点
        #     center = int(centroids[idx_i, 0]), int(centroids[idx_i, 1])
        #     cv.circle(output, center, 5, (255, 0, 0), 1)
        #     # cv.putText(output,"{0}".format(areas),center,cv.FONT_HERSHEY_SIMPLEX,0.5,(255,0,0),1)

        return output, stats[idx, :]

    def square_detect_in_patch(self, patch):
        # ROI图像中共11块
        N = 11
        result = np.zeros((N, 6))
        h, w = patch.shape[:2]
        one_w = int(w / N)
        for i in range(0, N):
            # print(f'----------------------分块序号:{i}---------------------')
            # 抽取块,然后计算相关系数
            tmp_img = patch[:, i * one_w + 3:(i + 1) * one_w + 3]
            bw_img = self.conduct_binary(tmp_img)

            # 形态学滤波
            bw_img_in = bw_img.copy()
            kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
            morph_open_img = cv.morphologyEx(bw_img_in, cv.MORPH_OPEN, kernel, 3)
            # morph_close_img = cv.morphologyEx(bw_img, cv.MORPH_CLOSE, kernel, 3)

            # 白色比例分析
            ratio_value = self.white_ratio_analyze(morph_open_img)

            # 连通区域分析,这里可设置阈值判断是否为缺陷区域
            morph_open_img_label, stat = self.connect_component(morph_open_img)
            # print('分块序号:{0},最大区域:{1}'.format(i, stat))
            if stat[4] > self.thresh_value:
                result[i, 0] = 1
                result[i, 1:] = stat

        return result, one_w


if __name__ == '__main__':
    # 匹配文件名称
    template_img_file = sys.argv[1].replace('\\','/')
    big_img_file = sys.argv[2].replace('\\','/')
    # print(template_img_file)
    # print(big_img_file)
    # print(int(sys.argv[3]))
    detector = DetectDefect()
    detector.set_big_image(big_img_file)
    detector.set_little_image(template_img_file)
    detector.set_roi_rect()
    # detector.set_thresh_value(thresh_value=6500) # 2000, 6500
    detector.set_thresh_value(thresh_value=int(sys.argv[3]))  # 2000, 6500
    detector.show_roi_img()
    detector.split_patches()
    detector.defect_analyze_patchs()

三、C#工程

1. C#界面截图

在这里插入图片描述

2. 控件变量

控件objectName属性设置备注
Buttonbutton1text: 运行运行按钮
Buttonbutton2text: 选择Template图片打开文件夹,选择template图片
Buttonbutton3text: 选择Sample图片打开文件夹,选择sample图片
TextBoxtextbox1enable:falsetemplate图片文件路径显示
TextBoxtextbox2enable:falsesample图片文件路径显示
TextBoxtextbox3text: 6500thresh_value值
Labellabel1text: thresh_value

3.功能代码

  1. 打开文件夹,选择文件,获取文件路径

    		public string GetOpenFileDialogReturnFileFullName(bool multiSelect = false)
    
            {
                System.Windows.Forms.OpenFileDialog opdialog = new System.Windows.Forms.OpenFileDialog();
                opdialog.Multiselect = multiSelect;
                opdialog.Filter = "bmp|*.bmp";
                string files = null;
                if (opdialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    files = System.IO.Path.GetFullPath(opdialog.FileName);                             //绝对路径
                }
    
                return files;
            }
    
  2. 打开cmd,运行命令行语句

    		public void RunCMDCommand(out string outPut, params string[] command)
            {
                using (Process pc = new Process())
                {
                    pc.StartInfo.FileName = "cmd.exe";
                    //pc.StartInfo.CreateNoWindow = false;//隐藏窗口运行
                    pc.StartInfo.RedirectStandardError = true;//重定向错误流
                    pc.StartInfo.RedirectStandardInput = true;//重定向输入流
                    pc.StartInfo.RedirectStandardOutput = true;//重定向输出流
                    pc.StartInfo.UseShellExecute = false;
                    pc.Start();
                    int lenght = command.Length;
                    foreach (string com in command)
                    {
                        pc.StandardInput.WriteLine(com);//输入CMD命令
                    }
                    pc.StandardInput.WriteLine("exit");//结束执行,很重要的
                    pc.StandardInput.AutoFlush = true;
    
                    outPut = pc.StandardOutput.ReadToEnd();//读取结果        
    
                    pc.WaitForExit();
                    pc.Close();
                }
            }
    
  3. 按钮点击

    		private void button1_Click(object sender, EventArgs e)
            {
                string resultStr = "";
                thresh_value = textBox3.Text;
                string strInput1 = "conda activate myopencv";
                string strInput2 = "python F:/PycharmProjects/MatchTemplate/demo_defect_detection.py " + template_dir + " " + 					sample_dir + " " + thresh_value;
                
                RunCMDCommand(out resultStr, strInput1, strInput2);
    
                Console.WriteLine(resultStr);
            }
    
    		private void button2_Click(object sender, EventArgs e)
            {
                template_dir = GetOpenFileDialogReturnFileFullName();
                textBox1.Text = template_dir;
            }
    
            private void button3_Click(object sender, EventArgs e)
            {
                sample_dir = GetOpenFileDialogReturnFileFullName();
                textBox2.Text = sample_dir;
            }
    

四、运行结果

结果录屏

五、项目整体代码

项目代码(gitee)

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页
评论

打赏作者

Computer-Rookie

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值