全流程机器视觉工程开发(三)任务前瞻 - 从opencv的安装编译说起,到图像增强和分割

前言

最近开始做这个裂缝识别的任务了,大大小小的问题我已经摸得差不多了,然后关于识别任务和分割任务我现在也弄的差不多了。

现在开始做正式的业务,也就是我们说的裂缝识别的任务。作为前言,先来说说场景:

现在相机返回过来的照片:

  1. 都是jpeg格式的照片,当然也可能是别的格式,目前主流是jpeg格式
  2. 大小约为11mb-14mb左右
  3. 图片大小为5120x5120

我们现在如果说想直接使用这个图片来进行图片识别的话,会有很多的问题。其中最主要的问题就是图片实在是过大,5120x5120的图片会导致图片不论是训练还是推理期都太长了,并且为了更好地处理图片,我们都会要求图片的训练期和推断图片都必须是比较小的,这样才有利于我们后续不论是实例分割还是目标识别任务。

目标

前言说了,我们的目标主要是两个,那就是

  1. 将现有图片分割成1280 * 1280 的小块。
    为什么这么做?因为我们的图片边界刚好是5120,5120 / 4 = 1280,也就是说我们要把原先一张照片切成 4 * 4 = 16块

  2. 将现有图片全部增亮
    这里需要注意一点,就是在增亮图片的同时,不能让图片过曝。这里我们需要做一点特殊的处理,下文中会说。

准备

既然有了目标,那么我们就来一步步写实现。这里因为我们后续要使用opencv等各种库,所以从现在开始我们所有的c++项目统一使用cmake管理。

那么在正式开始之前,我们需要准备opencv环境

这里可以参考以下文章
windows下安装Visual Studio + CMake+OpenCV + OpenCV contrib+TensorRT
有关cmake编译与安装opencv的内容,我觉得是比较中肯的,这里因为我已经安装好了opencv,就不再重复这个内容了。

那么现在默认就是大伙已经安装上了opencv 了,那么我这里就开始写图像分割和增强了。

首先使用vs 2022新建一个cmake项目:

在这里插入图片描述

创建完毕后,打开cmake gui程序

在这里插入图片描述
这里我使用vs 2022来编译和管理这个项目,点击configure,然后再点击gernerate,这样一个空项目就被我编辑出来了,就在我们的build目录下

在这里插入图片描述

点击Lev_PictureManager.sln进入项目管理。

在这里插入图片描述
当然了,我这里开发的这个工程是需要以后去做移植的,所以肯定是做成dll的,这个有关图形的库后续我也会做一些更新,相当于是一个自己的小工具箱,所以为了更好的使用这里需要修改一下 cmake 工程

Lev_PictureManager的CMakeList.txt改成这样,每一条为什么这么改我都写在了注释里面,可以看看

# CMakeList.txt: Lev_PictureManager 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
#如果环境变量中没有声明opencv的路径则需要加上这一条,如果加上了则无所谓
set(OpenCV_DIR C:\\Program Files (x86)\\opencv\\build)
#找到opencv 的 package
find_package(OpenCV REQUIRED)
#引入opencv的头文件
include_directories(${OpenCV_INCLUDE_DIRS})
#导出windows下的.lib静态链接库用以链接符号
set(WINDOWS_EXPORT_ALL_SYMBOLS ON)
# 导出为动态链接库的形式
add_library (Lev_PictureManager SHARED "Lev_PictureManager.cpp" "Lev_PictureManager.h")
#链接到opencv的.lib文件
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES})if (CMAKE_VERSION VERSION_GREATER 3.12)set_property(TARGET Lev_PictureManager PROPERTY CXX_STANDARD 20)
endif()# TODO: 如有需要,请添加测试并安装目标。

接下来在项目中编译一下这个CMakeList.txt,再尝试在头文件中引入

#include "opencv2/opencv.hpp"

如果引入成功,则说明调用是成功的,接下来就可以进入正式的 编码阶段了

流程

我们之前说的有两个内容,一个是切分图片,一个是提升亮度

切分图片

切分图片的话,主要是用到了opencv的Rect来对roi切分,具体思路见下代码:

	std::vector<cv::Mat> splitImage(const cv::Mat& inputImage, int rows, int cols) {std::vector<cv::Mat> subImages;int subImageWidth = inputImage.cols / cols;int subImageHeight = inputImage.rows / rows;for (int y = 0; y < rows; ++y) {for (int x = 0; x < cols; ++x) {// 定义矩形区域,切分图像cv::Rect roi(x * subImageWidth, y * subImageHeight, subImageWidth, subImageHeight);// 获取子图像cv::Mat subImage = inputImage(roi).clone();// 添加到结果集subImages.push_back(subImage);}}return subImages;}

修改亮度

	cv::Mat enhanceBrightnessImage(const cv::Mat& image) {// 转换为YUV颜色空间cv::Mat imgYUV;cv::cvtColor(image, imgYUV, cv::COLOR_BGR2YUV);// 应用CLAHE到Y通道(YUV中的Y代表亮度)cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(2.0, cv::Size(8, 8));clahe->apply(0, imgYUV);  // 修正此行代码// 将结果转换回BGR格式cv::Mat imgOutput;cv::cvtColor(imgYUV, imgOutput, cv::COLOR_YUV2BGR);return imgOutput;}

项目整体代码如下:
头文件Lev_PictureManager.h

// Lev_PictureManager.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。#pragma once#include <iostream>
// TODO: 在此处引用程序需要的其他标头。
#include "filesystem"
#include "opencv2/opencv.hpp"namespace Lev_PictureManager {/// <summary>/// 判断文件后缀是否为支持的图片/// </summary>/// <param name="extension">传入后缀,支持输入.jpg .png .jpeg</param>/// <returns>是否支持</returns>bool isImageFileExtend(const std::string extension);/// <summary>/// 缩放图片大小到指定大小/// </summary>/// <param name="insertPath">输入图片的绝对路径,包括图片名称</param>/// <param name="outputPath">输出图片的绝对路径,包括图片名称</param>/// <param name="tar_width">目标缩放宽度</param>/// <param name="tar_height">目标缩放高度</param>/// <returns>成功与否</returns>bool ResizeSingleImage(const std::string& insert_picture, const std::string& output_path, int tar_width, int tar_height);/// <summary>/// 将指定图片路径输入,导到指定目录中去/// </summary>/// <param name="insert_picture">输入图片路径</param>/// <param name="output_path">输出分裂图片的路径</param>/// <param name="row">指定切分行数</param>/// <param name="col">指定切分列数</param>/// <param name="suffix">后缀,默认是_,不同的图片会在指定后缀后,加上row-col的后缀比如 test_1-12.jpeg</param>/// <returns>是否切分成功</returns>bool TearPicture(const std::string& insert_picture, const std::string& output_path, int row, int col,const std::string& suffix);/// <summary>/// 调整图片亮度到最佳/// </summary>/// <param name="input_picture">输入图片的路径</param>/// <param name="output_path">图片输出路径</param>/// <returns></returns>bool adjustBrightness(const std::string& input_picture, const std::string& output_path);}

Lev_PictureManager.cpp

// Lev_PictureManager.cpp: 定义应用程序的入口点。
//#include "Lev_PictureManager.h"
using namespace std;
namespace fs = std::filesystem;
//使用命名空间是一种美德:D
namespace Lev_PictureManager {bool isImageFileExtend(const std::string extension){// 在这里添加你需要支持的图片格式的判断条件return extension == ".jpg" || extension == ".png" || extension == ".jpeg";}bool ResizeSingleImage(const std::string& insertPath, const std::string& outputPath, int tar_width, int tar_height) {cv::Mat originalImage = cv::imread(cv::String(insertPath), cv::IMREAD_COLOR);cv::String outString = cv::String(outputPath);if (originalImage.empty()) {std::cerr << "Failed to open image!" << std::endl;return false;}// 获取原始图像的宽高int originalWidth = originalImage.cols;int originalHeight = originalImage.rows;// 计算缩放比例double scaleWidth = static_cast<double>(tar_width) / originalWidth;double scaleHeight = static_cast<double>(tar_height) / originalHeight;// 使用 resize 函数进行缩放cv::Mat resizedImage;cv::resize(originalImage, resizedImage, cv::Size(), scaleWidth, scaleHeight, cv::INTER_LINEAR);// 保存压缩后的图像if (!cv::imwrite(outString, resizedImage)) return false;return true;}std::vector<cv::Mat> splitImage(const cv::Mat& inputImage, int rows, int cols) {std::vector<cv::Mat> subImages;int subImageWidth = inputImage.cols / cols;int subImageHeight = inputImage.rows / rows;for (int y = 0; y < rows; ++y) {for (int x = 0; x < cols; ++x) {// 定义矩形区域,切分图像cv::Rect roi(x * subImageWidth, y * subImageHeight, subImageWidth, subImageHeight);// 获取子图像cv::Mat subImage = inputImage(roi).clone();// 添加到结果集subImages.push_back(subImage);}}return subImages;}bool TearPicture(const std::string& insert_picture, const std::string& output_path, int row, int col, const std::string& suffix){//判断输入图片是否是支持的文件fs::path insertPath(insert_picture);if(!fs::is_regular_file(insertPath)) return false;if(!isImageFileExtend(insertPath.extension().string())) return false;//不带后缀的后缀名std::string insert_picture_name = insertPath.stem().string();//将图片切分成vectorcv::Mat mat_insert = cv::imread(insert_picture);std::vector<cv::Mat> vec_ret = splitImage(mat_insert, row, col);//将vector中的图片保存到指定路径for (int i = 0; i < row; ++i) {for (int j = 0; j < col; ++j) {std::string output_name = output_path + "/" + insert_picture_name + "_" + std::to_string(i) + "-" + std::to_string(j) + "." + suffix;if (!cv::imwrite(output_name, vec_ret[i + j])) return false;}}return true;}cv::Mat enhanceBrightnessImage(const cv::Mat& image) {// 转换为YUV颜色空间cv::Mat imgYUV;cv::cvtColor(image, imgYUV, cv::COLOR_BGR2YUV);// 应用CLAHE到Y通道(YUV中的Y代表亮度)cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(2.0, cv::Size(8, 8));clahe->apply(0, imgYUV);  // 修正此行代码// 将结果转换回BGR格式cv::Mat imgOutput;cv::cvtColor(imgYUV, imgOutput, cv::COLOR_YUV2BGR);return imgOutput;}bool adjustBrightness(const std::string& input_picture, const std::string& output_path){//判断输入图片是否是支持的文件fs::path insertPath(input_picture);if (!fs::is_regular_file(insertPath)) return false;if (!isImageFileExtend(insertPath.extension().string())) return false;std::string input_filename = insertPath.filename().string();std::string output_filename = output_path + "/" + input_filename;cv::Mat mat_input = cv::imread(input_picture);cv::Mat mat_output = enhanceBrightnessImage(mat_input);if (!cv::imwrite(output_filename, mat_output)) return false;return true;}}

完整工程(带Demo)见本人Github:

LeventureQys/Lev_PictureManager

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

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

相关文章

0131-2-关于事件捕获和冒泡

关于事件捕获和冒泡 DOM事件流分为三个阶段&#xff1a;捕获阶段、目标阶段、冒泡阶段 点击目标元素后&#xff0c;不会马上触发目标元素&#xff0c;而是先执行事件捕获&#xff0c;从顶部逐步到目标元素&#xff1b;处于目标阶段的时候触发目标元素&#xff1b;最后冒泡阶段…

如何在Raspberry Pi上启用SSH并结合cpolar内网穿透实现公网远程访问本地树莓派

文章目录 如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地址4.5 ssh公网…

我的创作纪念日和前端碎碎念

机缘 作为一个前端开发者&#xff0c;我一直热衷于将设计和技术相结合&#xff0c;尽可能提升用户体验。我最初成为创作者的初心源于学习记录&#xff0c;把创作当作一个笔记&#xff0c;希望把自己遇到的问题&#xff0c;以及学习到的实用技巧记录下来&#xff0c;方便学习回…

Qt Excel读写 - QXlsx的安装配置以及测试

Qt Excel读写 - QXlsx的安装配置以及测试 引言一、安装配置二、简单测试 引言 Qt无自带的库处理Excel 文件&#xff0c;但可通过QAxObject 借助COM接口进行Excel的读写1。亦可使用免费的开源第三方库&#xff1a;QXlsx&#xff0c;一个基于Qt库开发的用于读写Microsoft Excel文…

CUDA下载安装教程,新手详细

目录 一、下载二、安装三、 设置环境变量四、补丁安装 由于项目需要安装特定版本的CUDA&#xff0c;现记录安装过程。 一、下载 进入官方下载地址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 选择自己需要的版本。如果没有明确要求版本号&#xff0c;那么…

安卓视图基础

目录 设置视图的宽高 设置视图的间隔 设置视图的对齐方式 设置视图的宽高 设置视图的间隔 设置视图的对齐方式 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"a…

聊聊java中的Eureka和Nacos

本文主要来自于黑马课程中 1.提供者与消费者 在服务调用关系中&#xff0c;会有两个不同的角色&#xff1a; 服务提供者&#xff1a;一次业务中&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务&#xff09; 服务消费者&#xff1a;一次业务中&#xff0…

javaweb项目,springboot幼儿园健康管理系统,界面美观,增删改查。

javaweb项目&#xff0c;幼儿园健康管理系统&#xff0c;界面美观&#xff0c;增删改查。 管理员&#xff0c;老师&#xff0c;学生三个角色。 功能&#xff1a;权限管理&#xff0c;咨询列表&#xff0c;教师列表&#xff0c;班级列表&#xff0c;健康档案列表&#xff0c;评…

python数据类型-列表

1 python中列表的定义 python中列表是一种有序和可更改的集合&#xff0c;允许重复的成员&#xff0c;列表中的元素之间数据类型可以不同&#xff08;元素之间数据类型可以不相同&#xff0c;这一点和其它的面向对象的开发语言有很大的不同&#xff0c;如C#、Java&#xff09;…

【Django自学】Django入门:如何使用django开发一个web项目(非常详细)

测试机器&#xff1a;windows11 x64 python版本&#xff1a;3.11 一、安装Django 安装步骤非常简单&#xff0c;使用pip安装就行 pip install django安装完成之后&#xff0c;python的 Scripts 文件夹下&#xff0c;会多一个 django-admin.exe (管理创建django项目的工具)。…

【鸿蒙】大模型对话应用(二):对话界面设计与实现

Demo介绍 本demo对接阿里云和百度的大模型API&#xff0c;实现一个简单的对话应用。 DecEco Studio版本&#xff1a;DevEco Studio 3.1.1 Release HarmonyOS SDK版本&#xff1a;API9 关键点&#xff1a;ArkTS、ArkUI、UIAbility、网络http请求、列表布局、层叠布局 对话页…

springboot整合mqtt实现消息订阅和推送

前言 mica-mqtt-client-spring-boot-starter是一个基于Spring Boot的MQTT客户端启动器&#xff0c;它集成了mica-mqtt客户端&#xff0c;提供了在Spring Boot应用程序中使用MQTT协议进行消息通信的能力。以下是关于mica-mqtt-client-spring-boot-starter的简介&#xff1a; 特…

C++ fill()函数最详细介绍

文章目录 函数参数介绍函数功能函数使用注意点使用例子1.将数组arr[5]所有元素初始化为02.字符数组初始化3.vector对象 fill函数是C标准库中的一个算法函数&#xff0c;用于将指定范围内的元素赋值为给定的值。 函数参数介绍 fill( first, last, value );它接受三个参数&#…

idea激活教程(2020.1.4及以上版本)

首先点击试用版本&#xff0c;进入软件&#xff0c;再依次进行一下操作 一、在idea的Plugins配置中添加Z大的插件市场 上图中加载出来的插件是默认的&#xff0c;大家不用在意&#xff0c;直接点击“Manage Plugin Repositoryies…”打开配置弹窗 点击号&#xff0c;添加一行…

Qt 基础之QDataTime

Qt 基础之QDataTime 引言一、获取(设定)日期和时间二、时间戳三、时间计算 (重载运算符) 引言 QDataTime是Qt框架中用于处理日期和时间的类。它提供了操作和格式化日期、时间和日期时间组合的功能。QDataTime可以用于存储和检索日期和时间、比较日期和时间、对日期和时间执行算…

linux kernel 内存踩踏之KASAN(一)

一、背景 linux 内核出现内存类问题时&#xff0c;我们常用的调试工具就是kasan&#xff0c;kasan有三种模式&#xff1a; 1. Generic KASAN &#xff08;这个就是我们最常用的&#xff0c;1 debug byte indicate 8 bytes use state, 对标用户层 asan&#xff09; 2. Softwa…

Java强训day13(选择题编程题)

选择题 编程题 题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String s sc.nextLine();char[] c s.toCharArray();int i 0;int t 0;while (i < c.length) {if (c[i] ! \") {…

音视频数字化(数字与模拟-音频广播)

在互联网飞速发展的今天,每晚能坐在电视机前面的人越来越少,但是每天收听广播仍旧是很多人的习惯。 从1906年美国费森登在实验室首次进行无线电广播算起,“广播”系统已经陪伴人们115年了。1916年,收音机开始上市,收音机核心是“矿石”。1920年开始“调幅”广播,1941年开…

1.理解AOP,使用AOP

目录 1AOP基础 1.1 AOP概述 1.2AOP快速使用 2.3 AOP核心概念 1AOP基础 首先介绍一下什么是AOP&#xff0c;再通过一个快速入门程序&#xff0c;让大家快速体验AOP程序的开发。最后再介绍AOP当中所涉及到的一些核心的概念。 1.1 AOP概述 什么是AOP&#xff1f; 说白了&am…

晶体塑性有限元 Abaqus 三维泰森多边形(voronoi模型)插件 V8.0

更多内容见公众号“320科技工作室”&#xff0c;有需要欢迎通过公众号联系我们。