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

news2024/11/16 7:53:19

前言

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

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

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

  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

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

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

相关文章

初识webpack(一)概念、入口配置、输出配置、loader等

目录 (一)概念 webpack的依赖图 (二)webpack的基本使用 (三)webpack的配置文件 1.入口(entry)配置 2.输出(output)配置 (三)loader 1.css文件处理 (1)安装css-loader和style-loader (2)在webpack.config.js中配置loader 2.less文件处理 3.postcss的使用 (1)安装…

相片修复框架-GFPGAN

一 GFPGAN 介绍 GFPGAN 是一个由腾讯 ARC 团队开发的用于人脸图像生成和优化的 GAN 模型。在github可以找到开源的代码&#xff0c;它由两个主要模块组成&#xff1a; 退化移除模块 (U-Net)&#xff1a;用于从低分辨率、低质量的人脸图像中恢复出高质量的人脸图像。 生成式脸部…

软件工程知识梳理6-运行和维护

软件维护需要的工作量很大&#xff0c;大型软件的维护成本高达开发成本的4倍左右。所以&#xff0c;软件工程的主要目的就是要提高软件的可维护性&#xff0c;减少软件维护所需要的工作量&#xff0c;降低软件系统的总成本。 定义&#xff1a;软件已经交付使用之后&#xff0c;…

【爬虫专区】批量下载PDF (无反爬)

天命&#xff1a;只要没反爬&#xff0c;一切都简单 这次爬取的是绿盟的威胁情报的PDF 先看一下结构&#xff0c;很明显就是一个for循环渲染 burp抓包会发现第二次接口请求 接口请求一次就能获取到了所有的数据 然后一个循环批量下载数据即可&#xff0c;其实没啥难度的 imp…

腾讯云幻兽帕鲁Palworld服务器价格表,2024年2月最新

腾讯云幻兽帕鲁服务器价格32元起&#xff0c;4核16G12M配置32元1个月、96元3个月、156元6个月、312元一年&#xff0c;支持4-8个玩家&#xff1b;8核32G22M幻兽帕鲁服务器115元1个月、345元3个月&#xff0c;支持10到20人在线开黑。腾讯云百科txybk.com分享更多4核8G12M、16核6…

PVE 7.4-17 中开启vGPU显卡虚拟化

要为VM提供图形引擎&#xff0c;一般分为3种&#xff1a; 1、软件模拟图形-性能差 2、显卡直通-性能最好&#xff0c;一个虚拟机独享一个显卡 3、vGPU-性能好&#xff0c;多个虚拟机共享一个显卡 目前市面上的主流GPU厂家有intel、amd、nvidia&#xff0c;本篇文章主要介绍nvi…

Intercom与HelpLook:搭建知识库哪个更符合你的需求?

在当今信息化日益发展的时代&#xff0c;知识库作为一种集中存储和管理企业信息的重要工具&#xff0c;已经被越来越多的公司所采用。它有助于企业提升效率&#xff0c;改进服务质量&#xff0c;更好地与客户进行沟通。Intercom和HelpLook近两年在这个领域的讨论度不断变高&…

网络层 IP协议(1)

前置知识 主机:配有IP地址,但是不进行路由控制的设备 路由器:既配置了IP地址,又能进行路由控制的设备 节点:主机和路由器的总称 IP协议主要完成的任务就是 地址管理和路由选择 地址管理:使用一套地址体系,将网络设备的地址描述出来 路由选择:一个数据报如何从源地址到目的地址 …

摄影分享|基于Springboot的摄影分享网站设计与实现(源码+数据库+文档)

摄影分享网站目录 目录 基于Springboot的摄影分享网站设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、图片素材管理 3、视频素材管理 4、公告信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐…

excel 选中指定区域

问题 excel 选中指定区域 详细问题 笔者有一个excel数据集&#xff0c;数据量较大&#xff0c;如何快速选中指定区域 解决方案 步骤1、 点击起始单元格 确定单元格坐标&#xff08;建议直接CtrlC复制至剪贴板&#xff09; 具体操作入下图所示 步骤2、 点击结束单元格 …

数与抽象之自然数

自然数 “自然数的抽象与内在属性&#xff1a;图7中的纯粹五性” “自然”是数学家对我们所熟悉的1&#xff0c;2&#xff0c;3&#xff0c;4这样的数字所赋予的称呼。自然数是最基本的数学对象&#xff0c;但它们似乎并没有引导我们去抽象地思考。毕竟&#xff0c;单单一个数…

【Linux网络编程一】网络基础(网络框架)

【Linux网络编程一】网络基础&#xff08;网络框架&#xff09; 一.什么是协议1.通信问题2.协议本质3.网络协议标准 二.协议分层1.为什么协议要分层2.如何具体的分层 三.操作系统OS与网络协议栈的关系1.核心点&#xff1a;网络通信贯穿协议栈 四.局域网中通信的基本原理1.封装&…

【教学类-34-01】20240130纸尺1.0 (A4横版5条,刻度25*5=125CM)

作品展示&#xff1a; 背景需求&#xff1a; 大3班一位孩子用“骰子统计纸”制作了一个身高刻度表 【教学类-40-01】A4骰子纸模制作1.0&#xff08;飞机形 5.5CM纸盒骰子1个记录纸1条&#xff09;&#xff08;点数是不同的符号图案&#xff09;-CSDN博客文章浏览阅读160次。【…

查看阿里云maven仓中某个库有哪些版本

起因 最近项目上有做视频业务&#xff0c;方案是使用阿里云的短视频服务&#xff0c;其中也有使用到阿里云的上传SDK&#xff0c;过程中有遇一个上传SDK的内部崩溃&#xff0c;崩溃栈如下&#xff1a; Back traces starts. java.lang.NullPointerException: Attempt to invok…

【Qt】—— Qt Creator界⾯认识

目录 &#xff08;一&#xff09;左边栏 &#xff08;二&#xff09;代码编辑区 &#xff08;三&#xff09;UI设计界⾯ &#xff08;四&#xff09;构建区 &#xff08;一&#xff09;左边栏 在编辑模式下&#xff0c;左边竖排的两个窗⼝叫做"边栏"。 ①是项⽬…

指针的深入理解(三)

这一节主要使用复习回调函数&#xff0c; 利用冒泡模拟实现qsort函数。 qsort 排序使用冒泡排序&#xff0c;主要难点在于运用元素个数和字节数以及基地址控制元素的比较&#xff1a; if里面使用了一个判断函数&#xff0c;qsort可以排序任意的数据&#xff0c;原因就是因为可…

立体边界,让arcgis出图更酷炫一些

就是这样子的那个图—— 本期我们还是用长沙市为例&#xff0c; 来手把手的演示制作立体边界&#xff0c; 就是这个样子的边界—— 第一步—准备底图 其实你准备什么底图都可以哈&#xff0c;例如调用天地图、下载个影像图&#xff0c;或者用其他什么的底图&#xff0c;都是…

如何使用Docker部署JSON Crack

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

【数据结构】(分治策略)中位数的查询和最接近点对问题

中位数查询&#xff1a; 寻找一组字符串中第k小的数&#xff0c;返回其值和下标。 不可以有重复值&#xff08;在缩小规模的时候&#xff0c;会导致程序死循环&#xff09; 相对位置的转换体现了分治策略的思想。> 划分函数 int partition(int *nums,int left, int rig…

Unity点乘和叉乘

前言 Unity中经常会用到向量的运算来计算目标的方位&#xff0c;朝向&#xff0c;角度等相关数据&#xff0c;而这些计算中最常用的就是点乘和叉乘 点乘 一、点乘是什么&#xff1f; 定义&#xff1a;ab|a||b|cos<a,b> 【注&#xff1a;小写字母表示向量&#xff0c;&…