全流程机器视觉工程开发(三)任务前瞻 - 从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();

		//将图片切分成vector
		cv::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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微服务的幂等性

微服务架构设计的中心思想是将服务进行拆分&#xff0c;但是在这个过程中&#xff0c;如果被依赖的服务发生奔溃&#xff0c;就会引起一系列问题。为了解决这个问题&#xff0c;就会引入重试的机制&#xff0c;重试又会引入幂等性的问题&#xff0c;下面我们就分析这个过程&…

电磁兼容(EMC):传导骚扰差模频段超标机理及解决方案

目录 1 家用电器传导发射限值要求 2 传导测量等效电路 3 传导测试数据差、共模干扰分段 4 差模干扰源分析及解决方案 电子产品需要满足电磁兼容EMC要求&#xff0c;EMC包括抗扰度试验&#xff08;EMS&#xff09;和辐射类试验&#xff08;EMI&#xff09;&#xff0c;发射类…

如何编写接口测试用例

作为测试人&#xff0c;我们经常要对项目中的接口进行接口测试&#xff0c;那么在做接口测试的时候&#xff0c;如何写接口测试用例呢&#xff1f; 什么是接口测试 首先我们要了解一下&#xff0c;什么是接口测试&#xff1f; 那么首先要搞清楚&#xff0c;我们一般说的接口…

<设计模式>单例模式懒汉和饿汉

目录 一、单例模式概述 二、懒汉模式和饿汉模式 1.饿汉模式 1.1代码实现 1.2实现细节 1.3模式优劣 2.懒汉模式 2.1代码实现 2.2实现细节 2.3模式优劣 三、多线程下的线程安全问题 1.懒汉和饿汉线程安全问题分析 1.1安全的饿汉模式 1.2不安全的懒汉模式 2.懒汉线程…

Jenkins(三):自动化部署SpringBoot项目

前言 在软件开发过程中&#xff0c;自动化部署已经成为不可或缺的一环。Jenkins是一个广泛使用的开源自动化部署工具&#xff0c;它提供了强大的功能和灵活的配置选项&#xff0c;可以帮助开发团队实现高效的持续集成和持续部署。本文将详细介绍如何使用Jenkins自动化部署Spri…

爬取58二手房并用SVR模型拟合

目录 一、前言 二、爬虫与数据处理 三、模型 一、前言 爬取数据仅用于练习和学习。本文运用二手房规格sepc(如3室2厅1卫)和二手房面积area预测二手房价格price&#xff0c;只是练习和学习&#xff0c;不代表如何实际意义。 二、爬虫与数据处理 import requests import cha…

EasyX图形库学习(二、文字输出)

目录 一、文字绘制函数 字体属性结构体:logfont 文字输出 outtextxy 在指定位置输出字符串。 ​编辑 但如果直接使用,可能有以下报错&#xff1a; 三种解决方案&#xff1a; 将一个int类型的分数,输出到图形界面上 如果直接使用&#xff1a; 会把score输入进去根据A…

【Vue.js设计与实现】第二篇:响应系统-阅读笔记(持续更新)

从高层设计的角度去探讨框架需要关注的问题。 系列目录&#xff1a; 标题博客第一篇&#xff1a;框架设计概览【Vue.js设计与实现】第一篇&#xff1a;框架设计概览-阅读笔记第二篇&#xff1a;响应系统【Vue.js设计与实现】第二篇&#xff1a;响应系统-阅读笔记第三篇&#x…

洗地机哪个品牌质量好?盘点当下最值得买的4款洗地机型号推荐

随着生活节奏的加快&#xff0c;人们对于家庭清洁的需求也越来越迫切。而洗地机作为家庭清洁利器备受青睐&#xff0c;但洗地机也分为很多款式&#xff0c;每一个款式都具备不同的清洁效果&#xff0c;可以节省不少时间。接下来&#xff0c;就由笔者为大家详细介绍一下洗地机哪…

如何去除图片水印?三个简单实用方法

随着数字时代的来临&#xff0c;我们每天都会接触到大量的图片&#xff0c;然而&#xff0c;许多图片却因为水印而影响了美观。为了解决这个问题&#xff0c;我们需要图片去水印的方法。今天&#xff0c;我们就来为大家介绍几个简单实用的方法&#xff0c;可以轻松去除水印&…

备战蓝桥杯---搜索(优化1)

显然&#xff0c;我们可以用BFS解决&#xff0c;具体实现与八数码类似&#xff1a; 下面是代码&#xff1a; #include<bits/stdc.h> using namespace std; #define N 3000000 string a,b; int hh,dis[N],cnt; struct node{string u,v; }bian[7]; map<string,int>…

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参

前言 本文主要讲解 Flutter 和 Android原生之间&#xff0c;页面相互跳转、传参&#xff0c; 但其中用到了两端相互通信的知识&#xff0c;非常建议先看完这篇 讲解通信的文章&#xff1a; Flutter 与 Android原生 相互通信&#xff1a;BasicMessageChannel、MethodChannel、…

MongoDB复制集实战及原理分析

文章目录 MongoDB复制集复制集架构三节点复制集模式PSS模式&#xff08;官方推荐模式&#xff09;PSA模式 典型三节点复制集环境搭建复制集注意事项环境准备配置复制集复制集状态查询使用mtools创建复制集安全认证复制集连接方式 复制集成员角色属性一&#xff1a;Priority 0属…

match-case与if/elif/else(python)

if/elif/else语句应对一般场景&#xff0c;match-case主打复杂条件分支语句。 (笔记模板由python脚本于2024年01月28日 18:27:37创建&#xff0c;本篇笔记适合有一定编程基础&#xff0c;对python基础已比较扎实的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1…

uniapp使用u-popup组件弹窗出现页面还可滑动

*1、问题所在&#xff1a; 弹窗遮罩层出现了页面依旧可以上下滑动 2、要求: 为了用户更好交互体验&#xff0c;弹窗出现后应禁止页面往下滑动 3、实现思路&#xff1a; 在弹窗盒子外层添加个阻止触摸冒泡事件&#xff0c;使用touchmove.stop.prevent 4、代码如下&#xff…

eosio.token 智能合约介绍

一、目的 eosio.token系统合约定义了允许用户为基于EOSIO的区块链创建、发行和管理代币的结构和操作&#xff0c;它演示了一种实现允许创建和管理代币的智能合约的方法。本文详细介绍了eosio.token系统合约并在本地测试链上实际发行了代币进行演示&#xff0c;适用于EOS智能合…

OJ刷题:《剑指offer》之单身狗1、2 !(巧用位操作符,超详细讲解!)

目录 1.单身狗1 1.1 题目描述 1.2排序寻找 1.3巧用位操作符 2.单身狗2 1.1 题目描述 1.2排序寻找 1.3巧用位操作符 不是每个人都能做自己想做的事&#xff0c;成为自己想成为的人。 克心守己&#xff0c;律己则安&#xff01; 创作不易&#xff0c;宝子们&#xff01;如…

homework day3

第三章 类与构造函数 一&#xff0e;选择题 1、下列不能作为类的成员的是&#xff08;B&#xff09; A. 自身类对象的指针 B. 自身类对象 C. 自身类对象的引用 D. 另一个类的对象 2、假定AA为一个类&#xff0c;a()为该类公有的函数成员&#xff0c;x为该类的一个对象&am…

如何在一台MacBook上构建大模型知识库?

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《构建大模型知识库案例实战》 —1— 如何在一台 MacBook 上构建企业知识库&#xff1f; 最核心最重要的是我们手上的文档资料出于安全要求&#xff0c;不能随便上传到云服务&#xff0c;也就无法实际验证知识…

单链表的经典题目练习

哈喽&#xff0c;小伙伴们&#xff0c;上一次我们学习了单链表的知识&#xff0c;这次我们就要运用学到的知识来做一些相关的题目。我们都知道&#xff0c;要学好数据结构与算法&#xff0c;一定要多刷相关的题目才能有所提高。所以我们一起来学习一些单链表的经典题目算法题。…