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

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 CS8602namespace 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.mzph.cn/news/181705.shtml

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

相关文章

Jenkins部署python接口自动化测试

一、点击新建Item 二、指定源码和分支 私钥位置&#xff1a;C:\Users\Administrator\.ssh 文件下 三、构建脚本编写 四、构建后操作 指定输出的allure 结果目录 总结&#xff1a; 感谢每一个认真阅读我文章的人&#xff01;&#xff01;&#xff01; 作为一位过来人也是希望…

Python开发——工具篇 Pycharm的相关配置,Python相关操作 持续更新

前言 本篇博客是python开发的工具篇相关&#xff0c;介绍pycharm的使用和相关配置&#xff0c;收录python的相关操作&#xff0c;比如如何启动jupyter。 目录 前言引出Pycharmpycharm如何不同等级日志显示不同颜色设置不同pycharm的python环境 Python操作如何启动Jupyter 总结…

【linux】信号——信号产生

信号产生 1.预备知识2.信号产生2.1通过键盘发送信号2.2系统调用接口向进程发送信号2.3硬件异常产生信号2.4软件条件2.5总结 自我名言&#xff1a;只有努力&#xff0c;才能追逐梦想&#xff0c;只有努力&#xff0c;才不会欺骗自己。 喜欢的点赞&#xff0c;收藏&#xff0c;关…

unity程序中的根目录

在unity程序中如果要解析或保存文件时&#xff0c;其根目录为工程名的下一级目录&#xff0c;也就是Assets同级的目标

WPF中DataGrid解析

效果如图&#xff1a; 代码如下&#xff1a; <DataGrid Grid.Row"1" x:Name"dataGrid" ItemsSource"{Binding DataList}" AutoGenerateColumns"False"SelectedItem"{Binding SelectedItem,UpdateSourceTriggerPropertyChange…

【开题报告】海洋多源数据质量控制应用服务的WebServer设计与实现

开 题 报 告 内 容 论文选题的意义、主要研究内容和文献资料调研情况 一、选题意义 在当今世界研究自然环境的大背景下&#xff0c;计算机技术与各学科、各领域的综合应用逐渐增多。作为地球上最广阔的水体&#xff0c;同时也是地球上决定气候发展的主要的因素之一&#xff0…

android13(T) 客制化预置语言列表

效果图 需求分析 这个列表界面一般都是后来手动添加后才现实的&#xff0c;通过分析源码发现通过如下值可控 adb shell settings get system system_locales zh-CN,ja-JP,en-AT 所以只需查询出这个值&#xff0c;然后加在 SettingProvider 中即可 隐藏 bug 如果客户要求默…

马骑顿部署实在RPA,所产价值超该项目投入6倍

“数字化最大的挑战在于我们增长太快了&#xff0c;对于全域经营的DTC品牌来说&#xff0c;让一个部门去管理所有平台数据&#xff0c;形成品牌全域生意的协同实非易事。”马骑顿运营部负责人表示。2022年&#xff0c;马骑顿搭建企业数据中台&#xff0c;统一管理线上数据&…

C++ day43 最后一块石头的重量 目标和 一和零

题目1&#xff1a;1049 最后一块石头的重量 题目链接&#xff1a;最后一块石头的重量 对题目的理解 整数数组stone[i]表示第i块石头的重量&#xff0c;每次从中选出任意两块石头(x<y)粉碎 如果两块石头重量相等&#xff0c;就会被完全粉碎&#xff1b;如果不等&#xff…

实在品牌墙又双叒叕扩容,数十家龙头品牌签约实在RPA

近日&#xff0c;“实在智能 华夏行”数智化赋能之旅火爆前行&#xff0c;为孩子王、视客眼镜、心海伽蓝、洁丽雅、诗裴丝、视贝、博纳泉、鑫荣懋、艾莱得、布诗等数十家优秀企业带来超自动化智能解决方案&#xff0c;帮助商家解决重复低效工作&#xff0c;降低运营成本&#x…

RFC4493——AES-CMAC

文章目录 Abstract1 Introduction2 Specification of AES-CMAC2.1 Basic Definitions2.2 Overview2.3 Subkey Generation Algorithm2.4 MAC Generation Algorithm2.5 MAC Verification Algorithm 3 Security Considerations4 Test Vectors5 测试代码5.1 C语言版本5.2 Python语言…

阿里健康再得阿里巴巴资产注入 实现“自营+佣金+营销”多轮驱动

11月28日晚间&#xff0c;阿里健康发布公告宣布&#xff0c;正式与阿里巴巴集团签署股权认购协议&#xff0c;以135.12亿港元对价&#xff0c;获得阿里妈妈医疗健康类目的独家营销审核权及附属权利。交易完成后&#xff0c;阿里巴巴集团在阿里健康的持股比例将从56.99%上升到63…

鸿蒙原生应用/元服务开发-AGC分发如何下载管理Profile

一、收到通知 尊敬的开发者&#xff1a; 您好&#xff0c;为支撑鸿蒙生态发展&#xff0c;HUAWEI AppGallery Connect已于X月XX日完成存量HarmonyOS应用/元服务的Profile文件更新&#xff0c;更新后Profile文件中已扩展App ID信息&#xff1b;后续上架流程会检测API9以上Harm…

RHCE第五次作业

目录 1.算数运算命令有哪几种&#xff1f; 1.&#xff08;&#xff08;&#xff09;&#xff09; 2&#xff0c;let 3.[] 4.declare -i 5.expr 6.bc 7.awk 总结&#xff1a; 2..定义变量urlhttps://blog.csdn.net/weixin_45029822/article/details/10356881…

springboot集成springsecurity

转载自&#xff1a;www.javaman.cn 1、整合springsecurity 添加pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>2、springsecurity认证授权流程…

OpenCV | 模版匹配

import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline 模版匹配 模版匹配和卷积原理很像&#xff0c;模版在原图像上从原点开始滑动&#xff0c;计算模版与&#xff08;图像被模版覆盖的地方&#xff…

go标准库

golang标准库io包 input output io操作是一个很庞大的工程&#xff0c;被封装到了许多包中以供使用 先来讲最基本的io接口 Go语言中最基本的I/O接口是io.Reader和io.Writer。这些接口定义了读取和写入数据的通用方法&#xff0c;为不同类型的数据源和数据目标提供了统一的接…

同旺科技 USB 转 RS-485 适配器 -- 隔离型(定制款)

内附链接 1、USB 转 RS-485 适配器 隔离版主要特性有&#xff1a; ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11 系统32 / 64位&#xff1b; ● 支持Windows …

一文例说嵌入式 C 程序的内聚和耦合

1 - 原理篇 低耦合&#xff0c;是指模块之间尽可能的使其独立存在&#xff0c;模块之间不产生联系不可能&#xff0c;但模块与模块之间的接口应该尽量少而简单。这样&#xff0c;高内聚从整个程序中每一个模块的内部特征角度&#xff0c;低耦合从程序中各个模块之间的关联关系…

多个加速度计/麦克风连接指引

座舱内的振动投诉&#xff1a;如乘客/驾驶员在车厢内感受到传动轴、方向盘抖动剧烈 图1.三轴模式下的单个加速度计 图2.软件设置界面 如果您只有一个加速度计&#xff0c;可以在三轴模式下使用一个加速度计找出客户投诉车厢内振动最强烈的区域。例如将加速度计连接到驾驶员座椅…