C#图像处理OpenCV开发指南(CVStar,03)——基于.NET 6的图像处理桌面程序开发实践第一步

news2025/1/23 13:51:18

1 Visual Studio 2022 开发基于.NET 6的OpenCV桌面程序

1.1 为什么选择.NET 6开发桌面应用?

选择 .NET 6(最早称为 .NET Core)而非 Frameworks.NET 的理由是:(1)跨平台;已经支持Windows,Linux及其国产操作系统和国产龙芯CPU;(2).NET 完全开源;没有授权问题;(3)经过多年发展,已经成熟;

1.2 为什么选择开发桌面应用而非 Console 程序?

恰恰是我们这些从Unix,AIX,DOS等走过来的古董级程序员,不想让用户用键盘输入的方式使用软件。Console程序不过是自嗨的代码,不能称为程序,这个太low了。

1.3 如何开始创建基于.NET 6的桌面程序?

这里有个动画演示,比较清楚,可供参考学习。

最后出现这样的画面即可。

1.4 安装OpenCvSharp开发环境。

 这个请阅读另外一篇博客。

OpenCvSharp开发环境的安装、搭建之可视化教程icon-default.png?t=N7T8https://blog.csdn.net/beijinghorn/article/details/125528673

2 开发OpenCvSharp桌面程序的实践

2.1 需求分析

凡是程序,而非代码,必须有其需求与分析。

本桌面程序的基本功能需求是:

(1)可读取任意文件夹的各种图片文件,并显示于窗口;

(2)图片可自动缩放;

(3)窗口可缩放;

(4)窗口居中;

(5)图片可转为灰色图;

(6)结果可指定文件夹、文件名保存(另存为);

2.2 相关核心代码

核心代码集中于 Form1.cs 文件。下面分别做一个介绍,最后在归总。

2.2.1 定义图片文件的后缀

string[] ImgExtentions = {
    "*.*|*.*",
    "JPEG|*.jpg;*.jpeg",
    "GIF|*.gif",
    "PNG|*.png",
    "TIF|*.tif;*.tiff",
    "BMP|*.bmp"
};

2.2.2 定义窗口内的各种控件

Panel? panelTop { get; set; } = null;
Panel? panelBotton { get; set; } = null;
PictureBox? picSource { get; set; } = null;
PictureBox? picResult { get; set; } = null;
Button? btnLoad { get; set; } = null;
Button? btnGray { get; set; } = null;
Button? btnSave { get; set; } = null;

private int original_width { get; set; } = 0;
private int original_height { get; set; } = 0;

2.2.3 图片自适应(大小、比例) 

private void PicAutosize(PictureBox pb)
{
    if (pb == null) return;
    if (pb.Image == null) return;
    Image img = pb.Image;
    int w = original_width;
    int h = w * img.Height / img.Width;
    if (h > original_height)
    {
        h = original_height;
        w = h * img.Width / img.Height;
    }
    pb.SizeMode = PictureBoxSizeMode.Zoom;
    pb.Width = w;
    pb.Height = h;
    pb.Image = img;
    pb.Refresh();
}

2.2.4 创建窗口内的相关控件(按钮、图片)


private void GUI()
{
    if (panelTop == null) panelTop = new Panel();
    panelTop.Parent = this;
    panelTop.Top = 5;
    panelTop.Left = 5;
    panelTop.Width = this.Width - 26;
    panelTop.Height = 55;
    panelTop.BorderStyle = BorderStyle.FixedSingle;

    if (panelBotton == null) panelBotton = new Panel();
    panelBotton.Parent = this;
    panelBotton.Top = panelTop.Top + panelTop.Height + 3;
    panelBotton.Left = 5;
    panelBotton.Width = panelTop.Width;
    panelBotton.Height = this.Height - panelBotton.Top - 55;
    panelBotton.BorderStyle = BorderStyle.FixedSingle;

    if (picSource == null) picSource = new PictureBox();
    picSource.Parent = panelBotton;
    picSource.Left = 5;
    picSource.Top = 5;
    picSource.Width = (panelBotton.Width - 10) / 2;
    picSource.Height = (panelBotton.Height - 10);
    picSource.BorderStyle = BorderStyle.FixedSingle;

    if (picResult == null) picResult = new PictureBox();
    picResult.Parent = panelBotton;
    picResult.Left = picSource.Left + picSource.Width + 5;
    picResult.Top = picSource.Top;
    picResult.Width = picSource.Width;
    picResult.Height = picSource.Height;
    picResult.BorderStyle = BorderStyle.FixedSingle;

    original_width = picSource.Width;
    original_height = picSource.Height;

    if (btnLoad == null) btnLoad = new Button();
    btnLoad.Parent = panelTop;
    btnLoad.Left = 5;
    btnLoad.Top = 5;
    btnLoad.Width = 90;
    btnLoad.Height = 38;
    btnLoad.Cursor = Cursors.Hand;
    btnLoad.Text = "Load";
    btnLoad.Click += Load_Image;

    if (btnGray == null) btnGray = new Button();
    btnGray.Parent = panelTop;
    btnGray.Left = btnLoad.Left + btnLoad.Width + 5;
    btnGray.Top = btnLoad.Top;
    btnGray.Width = 90;
    btnGray.Height = 38;
    btnGray.Cursor = Cursors.Hand;
    btnGray.Text = "Gray";
    btnGray.Click += ToGray;

    if (btnSave == null) btnSave = new Button();
    btnSave.Parent = panelTop;
    btnSave.Left = btnGray.Left + btnGray.Width + 5;
    btnSave.Top = btnLoad.Top;
    btnSave.Width = 90;
    btnSave.Height = 38;
    btnSave.Cursor = Cursors.Hand;
    btnSave.Text = "Save";
    btnSave.Click += Save;

    PicAutosize(picSource);
    PicAutosize(picResult);
}

运行时是这样滴。

 2.2.5 支持窗口、图片同步缩放的代码

private void FormResize(object? sender, EventArgs? e)
{
    if (this.Width < 200) { this.Width = 320; return; }
    if (this.Height < 200) { this.Height = 320; return; }
    GUI();
}

2.2.6 读取图片并显示

private void Load_Image(object? sender, EventArgs? e)
{
    OpenFileDialog openFileDialog = new OpenFileDialog();
    openFileDialog.Filter = String.Join("|", ImgExtentions);
    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
        sourceImage = openFileDialog.FileName;
        picSource.Image = Image.FromFile(sourceImage);
        picResult.Image = picSource.Image;
        PicAutosize(picSource);
        PicAutosize(picResult);
    }
}

2.2.7 转为灰色图片

private void ToGray(object? sender, EventArgs? e)
{
    Mat src = Cv2.ImRead(sourceImage);
    Mat dst = new Mat();
    Cv2.CvtColor(src, dst, ColorConversionCodes.BGR2GRAY);
    picResult.Image = CVUtility.Mat2Bitmap(dst);
    PicAutosize(picResult);
}

2.2.8 图片另存为

private void Save(object? sender, EventArgs? e)
{
    SaveFileDialog saveFileDialog = new SaveFileDialog();
    if (saveFileDialog.ShowDialog() == DialogResult.OK)
    {
        picResult.Image.Save(saveFileDialog.FileName);
        MessageBox.Show("Image Save to " + saveFileDialog.FileName);
    }
}

2.3 运行效果

读取图片

转为灰度图 

图片另存为

2.4 完整的 Form1.cs 文件

using OpenCvSharp;

#pragma warning disable CS8602

namespace Legal.Truffer.CVStar
{
    public partial class Form1 : Form
    {
        string[] ImgExtentions = {
            "*.*|*.*",
            "JPEG|*.jpg;*.jpeg",
            "GIF|*.gif",
            "PNG|*.png",
            "TIF|*.tif;*.tiff",
            "BMP|*.bmp"
        };
        private int original_width { get; set; } = 0;
        private int original_height { get; set; } = 0;
        private string sourceImage { get; set; } = "";

        Panel? panelTop { get; set; } = null;
        Panel? panelBotton { get; set; } = null;
        PictureBox? picSource { get; set; } = null;
        PictureBox? picResult { get; set; } = null;
        Button? btnLoad { get; set; } = null;
        Button? btnGray { get; set; } = null;
        Button? btnSave { get; set; } = null;


        public Form1()
        {
            InitializeComponent();

            this.Text = "OPENCV C#编程入手教程 POWERED BY 深度混淆(CSDN.NET)";
            this.StartPosition = FormStartPosition.CenterScreen;

            GUI();
            this.Resize += FormResize;
        }


        private void FormResize(object? sender, EventArgs? e)
        {
            if (this.Width < 200) { this.Width = 320; return; }
            if (this.Height < 200) { this.Height = 320; return; }
            GUI();
        }

        private void GUI()
        {
            if (panelTop == null) panelTop = new Panel();
            panelTop.Parent = this;
            panelTop.Top = 5;
            panelTop.Left = 5;
            panelTop.Width = this.Width - 26;
            panelTop.Height = 55;
            panelTop.BorderStyle = BorderStyle.FixedSingle;

            if (panelBotton == null) panelBotton = new Panel();
            panelBotton.Parent = this;
            panelBotton.Top = panelTop.Top + panelTop.Height + 3;
            panelBotton.Left = 5;
            panelBotton.Width = panelTop.Width;
            panelBotton.Height = this.Height - panelBotton.Top - 55;
            panelBotton.BorderStyle = BorderStyle.FixedSingle;

            if (picSource == null) picSource = new PictureBox();
            picSource.Parent = panelBotton;
            picSource.Left = 5;
            picSource.Top = 5;
            picSource.Width = (panelBotton.Width - 10) / 2;
            picSource.Height = (panelBotton.Height - 10);
            picSource.BorderStyle = BorderStyle.FixedSingle;

            if (picResult == null) picResult = new PictureBox();
            picResult.Parent = panelBotton;
            picResult.Left = picSource.Left + picSource.Width + 5;
            picResult.Top = picSource.Top;
            picResult.Width = picSource.Width;
            picResult.Height = picSource.Height;
            picResult.BorderStyle = BorderStyle.FixedSingle;

            original_width = picSource.Width;
            original_height = picSource.Height;

            if (btnLoad == null) btnLoad = new Button();
            btnLoad.Parent = panelTop;
            btnLoad.Left = 5;
            btnLoad.Top = 5;
            btnLoad.Width = 90;
            btnLoad.Height = 38;
            btnLoad.Cursor = Cursors.Hand;
            btnLoad.Text = "Load";
            btnLoad.Click += Load_Image;

            if (btnGray == null) btnGray = new Button();
            btnGray.Parent = panelTop;
            btnGray.Left = btnLoad.Left + btnLoad.Width + 5;
            btnGray.Top = btnLoad.Top;
            btnGray.Width = 90;
            btnGray.Height = 38;
            btnGray.Cursor = Cursors.Hand;
            btnGray.Text = "Gray";
            btnGray.Click += ToGray;

            if (btnSave == null) btnSave = new Button();
            btnSave.Parent = panelTop;
            btnSave.Left = btnGray.Left + btnGray.Width + 5;
            btnSave.Top = btnLoad.Top;
            btnSave.Width = 90;
            btnSave.Height = 38;
            btnSave.Cursor = Cursors.Hand;
            btnSave.Text = "Save";
            btnSave.Click += Save;

            PicAutosize(picSource);
            PicAutosize(picResult);
        }

        private void Load_Image(object? sender, EventArgs? e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = String.Join("|", ImgExtentions);
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                sourceImage = openFileDialog.FileName;
                picSource.Image = Image.FromFile(sourceImage);
                picResult.Image = picSource.Image;
                PicAutosize(picSource);
                PicAutosize(picResult);
            }
        }

        private void PicAutosize(PictureBox pb)
        {
            if (pb == null) return;
            if (pb.Image == null) return;
            Image img = pb.Image;
            int w = original_width;
            int h = w * img.Height / img.Width;
            if (h > original_height)
            {
                h = original_height;
                w = h * img.Width / img.Height;
            }
            pb.SizeMode = PictureBoxSizeMode.Zoom;
            pb.Width = w;
            pb.Height = h;
            pb.Image = img;
            pb.Refresh();
        }

        private void ToGray(object? sender, EventArgs? e)
        {
            Mat src = Cv2.ImRead(sourceImage);
            Mat dst = new Mat();
            Cv2.CvtColor(src, dst, ColorConversionCodes.BGR2GRAY);
            picResult.Image = CVUtility.Mat2Bitmap(dst);
            PicAutosize(picResult);
        }

        private void Save(object? sender, EventArgs? e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = String.Join("|", ImgExtentions);
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                picResult.Image.Save(saveFileDialog.FileName);
                MessageBox.Show("Image Save to " + saveFileDialog.FileName);
            }
        }
    }
}

本代码尽量为你着想,无需设计、修改 Form1.Design.cs 及资源文件。

用该文件替换原来的 Form1.cs 文件即可运行。

3 支持函数

一个基本的支持函数特意摘录出来,以后也需要用上。

using OpenCvSharp;
using OpenCvSharp.Extensions;

public static partial class CVUtility
{
    /// <summary>
    /// Mat 转 Bitmap(32bits)
    /// </summary>
    /// <param name="img"></param>
    /// <returns></returns>
    public static Bitmap Mat2Bitmap(Mat img)
    {
        //if (bytes == 32) return BitmapConverter.ToBitmap(img, PixelFormat.Format32bppArgb);
        //else if (bytes == 24) return BitmapConverter.ToBitmap(img, PixelFormat.Format24bppRgb);
        //else 
        return BitmapConverter.ToBitmap(img);
    }
}

够详细吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1268395.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PyQt基础_008_ 按钮类控件QSpinbox

基本操作 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class spindemo(QWidget):def __init__(self, parentNone):super(spindemo, self).__init__(parent)self.setWindowTitle("SpinBox 例子")self.resize(300,…

错误 LNK2001 无法解析的外部符号 __imp__CrtDbgReport

”属性“ -->”设置“ --> ”c“ – > ”代码生成“ --> ”运行库“ &#xff0c;将 ”多线程(MT)“ 改为 ”多线程(MTD)“。

JavaScript包装类型

前端面试大全JavaScript包装类型 &#x1f31f;经典真题 &#x1f31f;包装类型 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 是否了解 JavaScript 中的包装类型&#xff1f; &#x1f31f;包装类型 在 ES 中&#xff0c;数据的分类分为基本数据类型…

NCo3.1(08) - Nco3 服务器端编程

本篇博文不再重复ABAP调用外部服务器的基础&#xff0c;只介绍 NCo3 开发的过程和要点。需要了解相关知识点的小伙伴们自行参考&#xff1a; SAP接口编程 之JCo3.0系列(06) - Jco服务器端编程 PyRFC 服务器端编程要点 创建项目 新建一个 Console 项目&#xff0c;选择 .Net …

第一个C代码讲解

文章目录 编写C文件创建文本文件编写代码修改文件后缀切换文件路径 编译代码打开命令行使用gcc编译代码运行程序双击运行使用命令行运行 代码分析编译过程 编写C文件 编辑C代码文件的工具有很多&#xff0c;为了让大家初学的时候摆脱编译软件的干扰&#xff0c;更容易理解编译过…

数据结构 -- 并查集与图

目录 1.并查集 1.结构 2.原理 3.代码实现 1.存储 2.寻找根节点 3.是否为同一集合 4.求集合个数 5.合并为同一集合中 整体代码 2.图 1.基本知识 1.各个属性 2.特殊名词 3.图的解释 2.图的表示 1.邻接矩阵 2.邻接表 3.图的遍历 1.BFS--广度优先遍历 2.DFS--…

Python with提前退出:坑与解决方案

Python with提前退出&#xff1a;坑与解决方案 问题的起源 早些时候使用with实现了一版全局进程锁&#xff0c;希望实现以下效果&#xff1a; Python with提前退出&#xff1a;坑与解决方案 全局进程锁本身不用多说&#xff0c;大部分都依靠外部的缓存来实现的&#xff0c;r…

python读取excel自动化生成sql建表语句和java实体类字段

1、首先准备一个excel文件&#xff1a; idtypenameidint学号namestring姓名ageint年龄sexstring性别weightdecimal(20,4)体重scoredecimal(20,4)分数 2、直接生成java字段和注释&#xff1a; import pandas as pddf pd.read_excel(test.xlsx, sheet_nameSheet1)for i in ran…

k8s中批量处理Pod应用的Job和CronJob控制器介绍

目录 一.Job控制器 1.简介 2.Jobs较完整解释 3.示例演示 4.注意&#xff1a;如上例的话&#xff0c;执行“kubectl delete -f myJob.yaml”就可以将job删掉 二.CronJob&#xff08;简写为cj&#xff09; 1.简介 2.CronJob较完整解释 3.案例演示 4.如上例的话&#xf…

【MySQL】常用内置函数:数值函数 / 字符串函数 / 日期函数 / 其他函数

文章目录 数值函数round()&#xff1a;四舍五入ceiling()&#xff1a;上限函数floor()&#xff1a;地板函数abs()&#xff1a;计算绝对值rand()&#xff1a;生成0-1的随机浮点数 字符串函数length()&#xff1a;获取字符串中的字符数upper() / lower()&#xff1a;将字符串转化…

使用Ray创建高效的深度学习数据管道

大家好&#xff0c;用于训练深度学习模型的GPU功能强大但价格昂贵。为了有效利用GPU&#xff0c;开发者需要一个高效的数据管道&#xff0c;以便在GPU准备好计算下一个训练步骤时尽快将数据传输到GPU&#xff0c;使用Ray可以大大提高数据管道的效率。 1.训练数据管道的结构 首…

优化邮件群发效果的方法与策略

怎样优化邮件群发效果&#xff1f;这是许多企业在进行邮件营销时常常被问到的问题。邮件营销是一种高效且经济实惠的市场推广方式&#xff0c;但如何使邮件真正引起接收者的兴趣并产生预期的效果并不容易。好的营销效果可以带来高回报、高收益率&#xff0c;但是怎么提升群发效…

工会排队奖励模式:创新营销策略,实现共赢局面

在当今的商业环境中&#xff0c;创新营销策略的重要性日益凸显。工会排队奖励模式作为一种新型的营销策略&#xff0c;旨在通过结合线上和线下消费&#xff0c;激励消费者购买产品或服务&#xff0c;并获得返现奖励。这种模式通过将消费者的支出和商家的抽成资金纳入奖金池&…

CSS3样式详解之圆角、阴影及变形

目录 前言一、圆角样式&#xff08;border-radius&#xff09;二、元素阴影&#xff08;box-shadow&#xff09;三、过渡动画样式&#xff08;transition&#xff09;1. transition-property(用于设置属性名称)2. transition-duration&#xff08;设置时间&#xff09;3. trans…

7、信息收集(2)

文章目录 一、指纹识别1、Nmap工具2、Wafw00f工具 二、使用Maltego进行情报收集 一、指纹识别 1、Nmap工具 命令一&#xff1a;nmap -sS -sV <ip>&#xff0c;使用TCP SYN的方式&#xff0c;扫描目标主机上常规端口运行的服务版本。 -sS&#xff1a;指定使用TCP SYN的方…

注解(概念、分类、自定义注解)

注解基本概念 注解(元数据)为我们在代码中添加信息提供一种形式化的方法&#xff0c;我们可以在某个时刻非常方便的使用这些数据。将的通俗一点&#xff0c;就是为这个方法增加的说明或功能。 作用&#xff1a; 编写文档&#xff1a;通过代码里标识的注解生成文档【生成doc文…

常使用的定时任务

常使用的定时任务 一、 linux自带的定时任务 1、crontab 有这样一个需求&#xff1a;我们使用Java写一个工具jar包在系统空闲的时候去采集已经部署在Linux系统上的项目的一 些数据&#xff0c;可以使用 linux 系统的 crontab。 运行crontab -e&#xff0c;可以编辑定时器&…

git stash save untracked not staged

git stash save untracked not staged 如图 解决方案&#xff1a; git stash save "tag标记信息" --include-untracked或者&#xff1a; git stash save -u "tag标记信息" git stash clear清空本地暂存代码_zhangphil的博客-CSDN博客文章浏览阅读486次。…

合阔智云:实现API无代码开发,连接ERP系统和CRM系统提高运营效率

概述 合阔智云&#xff0c;一家成立于2011年的科技公司&#xff0c;核心业务是提供云原生和移动化设计的新一代全渠道“云端一体”履约中台和去中心化模式智能门店供应链业务中台。他们的系统可以无需API开发即可实现电商系统和客服系统的连接和集成&#xff0c;大大提高了企业…

通过python脚本上传本地/远程服务器文件到minio

前言 将文件上传到MinIO对象存储后&#xff0c;MinIO会将文件存储为对象(.meta文件)&#xff0c;并为每个对象生成相应的元数据。元数据是描述对象的属性和信息的数据。 通常&#xff0c;元数据包括对象的名称、大小、创建日期等。 在MinIO中&#xff0c;对象的元数据存储在独立…