php 在线升级

article/2025/8/29 22:18:04

php 在线升级 功能

    • 目前已修改为通用解决方案 点击下方链接下载 此为最新优化过的版本 之后的贴图 说明 仅用来做思路参考 我就不再修改了
  • 下面是思路解析
    • 思路一
    • 思路二
    • 升级程序
    • 服务器检测版本
    • 至此整个升级程序已经结束 感谢大家参考
    • 有任何问题都可以直接联系我咨询 一定不吝赐教
    • VX/QQ 同号 422495701

目前已修改为通用解决方案 点击下方链接下载 此为最新优化过的版本 之后的贴图 说明 仅用来做思路参考 我就不再修改了

请点击这个链接下载

下面是思路解析

有很久没有写博客了 最近这家公司让做一个类似cms框架的在线自动升级功能,参照了一下织梦的升级流程 自己使用TP5编写了一套程序 可能不是最优方案,但是功能实现了,以下是两种实现思路 由于时间紧迫本人选择了第一种并进行了流程简化 并且附上了免费的程序资源 可自行下载本地测试 sql文件在上方 最新压缩包 优化为通用解决方案 由于已上传服务器 内容做了一些更改 下面的代码部分就不再变动了 请参考程序压缩包内的内容 目前已经取消了对本地数据库的查询 直接通过json文件操作

在此友情提醒 做后端千万不要相信客户会正常操作不要相信前端会帮你进行参数和各种情况的判断处理,既然后端负责逻辑处理就要做到滴水不漏,考虑到各种情况 避免因小失大 导致整个服务器挂掉

思路一

1: 更新服务器指定目录需要一个每次升级的版本记录文件
例如格式: 更新日期,字符集,版本号,更新标题,更新文件的压缩包(压缩包内是更新需要的文件)
20140415,V5.7.41,20140415常规更新补丁,http://upgrade.diyi01.com/upgrade/upgrade-20140415.zip

2: 获取客户网站的最近更新日期,该日期可存于数据库或者文件 建议存储于文件 尽量避免对数据库的访问

3: 读取远程更新服务器更新文件信息,然后比对客户网站需要更新那些压缩包

4: 读取远程压缩包的配置文件(该文件记录压缩包内的文件列表),并根据配置文件判断是否客户网站是否有写入权限,
如:
下载的文件临时存放在文件夹(…/data/20140415)内,如果某些文件自己有改动导致更新中途中错,您可以从这文件夹提取文件手工更新。
本次升级需要在下面文件夹写入更新文件,请注意文件夹是否有写入权限:
…/lib/ 状态:[√正常]
…/lib/Article/ 状态:[√正常]
…/Public/ 状态:[√正常]

5: 下载更新的文件到临时目录(可以直接下载压缩包在解压出文件)下载过程需要显示文件是否下载ok,完成提示安装更新按钮

6: 点击更新首先判断是否有sql文件,有先更新sql,在更新文件

7: 更新完成改写客户网站更新时间,并删除下载的临时更新文件

思路二

写两个程序,一个是主程序;一个是升级程序;所有升级任务都由升级程序完成。

1.启动升级程序,升级程序连接到网站,下载新的主程序(当然还包括支持的库文件、XML配置文档等)到临时文件夹;

2.升级程序获取服务器端XML配置文件中新版本程序的更新日期或版本号或文件大小;

3.升级程序获取原有客户端应用程序的最近一次更新日期或版本号或文件大小,两者进行比较;如果发现升级程序的日期大于原有程序的最新日期,则提示用户是否升级;或者是采用将现有版本与最新版本作比较,发现最新的则提示用户是否升级;也有人用其它属性如文件大小进行比较,发现升级程序的文件大小大于旧版本的程序的大小则提示用户升级。本文主要采用比较新旧版本更新日期号来提示用户升级。

4.如果用户选择升级,则获取升级程序压缩包,开始进行下载;

5.升级程序检测旧的主程序是否活动,若活动则关闭旧的主程序;

6.删除旧的主程序,拷贝临时文件夹中的文件到相应的位置;

7.检查主程序的状态,若状态为活动的,则启动新的主程序;

8.关闭升级程序,升级完成

请注意,以下代码是一个已经在本地phpstudy 实现功能的demo,里面很多参数需要大家自行根据情况修改,但是整体思路和流程没有问题,其中引用的model是一个空模型 只是为了控制器操作数据库 测试方法就是直接在WWW目录创建两个项目类似这种结构
本地项目目录

升级程序

<?php
namespace app\system\controller;use think\Controller;
use think\Db;
use app\common\model\Info;class Upgrade extends Controller
{public function index(){return 'connected';}// 外部请求public function dataRequest($url,$https=true,$method='get',$data=null){if (trim($url) == '') {return false;}//初始化curl$ch = curl_init($url);//字符串不直接输出,进行一个变量的存储curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//https请求if ($https === true) {//确保https请求能够请求成功curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);}//post请求if ($method == 'post') {curl_setopt($ch,CURLOPT_POST,true);curl_setopt($ch,CURLOPT_POSTFIELDS,$data);}//发送请求$str = curl_exec($ch);$aStatus = curl_getinfo($ch);//关闭连接curl_close($ch);if(intval($aStatus["http_code"])==200){// json数据处理return json_decode($str);// return $str;}else{return false;}}// 检测是否有新版本public function check_version(){// 打开远程版本记录文件比对本地记录文件// 设定目录$server_dir = 'http://192.168.31.64/tp5/public/update/up_log.txt';$local_dir = ROOT_PATH . 'public/update/ver.txt';// 获取版本记录文件$server = $this->get_file($server_dir);if ($server === false) {$result= ['code'=>406,'msg'=>'服务器版本记录文件获取失败','data'=>''];} else {// 最新版本$server = explode(",", $server);$last_version = end($server);// 本地版本$local = $this->get_file($local_dir);if ($local === false) {$result= ['code'=>406,'msg'=>'本地版本记录文件获取失败','data'=>''];} else {// 比较版本$data = ['last_version' =>$last_version,];if (intval($last_version) > intval($local)) {$result= ['code'=>200,'msg'=>'服务器有新版本','data'=>$data];} else {$result= ['code'=>204,'msg'=>'已经是最新版本','data'=>$data];}}}return json($result);}/*** 解压缩* @param $file 要解压的文件* @param $todir 要存放的目录* @return str 包含所有文件及目录的数组*/public function deal_zip($file,$todir){if (trim($file) == '') {return 406;}if (trim($todir) == '') {return 406;}$zip = new \ZipArchive;// 中文文件名要使用ANSI编码的文件格式if ($zip->open($file) === TRUE) {//提取全部文件$zip->extractTo($todir);$zip->close();$result = 200;} else {$result = 406;}return $result;}/*** 遍历当前目录不包含下级目录* @param $dir 要遍历的目录* @param $file 要过滤的文件* @return str 包含所有文件及目录的数组*/public function scan_dir($dir,$file=''){if (trim($dir) == '') {return false;}$file_arr = scandir($dir);$new_arr = [];foreach($file_arr as $item){if($item!=".." && $item !="." && $item != $file){$new_arr[] = $item;}}return $new_arr;}/*** 合并目录且只覆盖不一致的文件* @param $source 要合并的文件夹* @param $target 要合并的目的地* @return int 处理的文件数*/public function copy_merge($source, $target) {if (trim($source) == '') {return false;}if (trim($target) == '') {return false;}// 路径处理$source = preg_replace ( '#/\\\\#', DIRECTORY_SEPARATOR, $source );$target = preg_replace ( '#\/#', DIRECTORY_SEPARATOR, $target );$source = rtrim ( $source, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;$target = rtrim ( $target, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;// 记录处理了多少文件$count = 0;// 如果目标目录不存在,则创建。if (! is_dir ( $target )) {mkdir ( $target, 0777, true );$count ++;}// 搜索目录下的所有文件foreach ( glob ( $source . '*' ) as $filename ) {if (is_dir ( $filename )) {// 如果是目录,递归合并子目录下的文件。$count += $this->copy_merge ( $filename, $target . basename ( $filename ) );} elseif (is_file ( $filename )) {// 如果是文件,判断当前文件与目标文件是否一样,不一样则拷贝覆盖。// 这里使用的是文件md5进行的一致性判断,可靠但性能低。if (! file_exists ( $target . basename ( $filename ) ) || md5 ( file_get_contents ( $filename ) ) != md5 ( file_get_contents ( $target . basename ( $filename ) ) )) {copy ( $filename, $target . basename ( $filename ) );$count ++;}}}// 返回处理了多少个文件return $count;}/*** 遍历删除文件* @param $dir 要删除的目录* @return bool 成功与否*/public function deldir($dir) {if (trim($dir) == '') {return false;}//先删除目录下的文件:$dh=opendir($dir);while ($file=readdir($dh)) {if($file!="." && $file!="..") {$fullpath=$dir."/".$file;if(!is_dir($fullpath)) {unlink($fullpath);} else {$this-> deldir($fullpath);}}}closedir($dh);//删除当前文件夹:if(rmdir($dir)) {return true;} else {return false;}}/*** 遍历执行sql文件* @param $dir 要执行的目录* @return bool 成功与否*/public function carry_sql($dir){if (trim($dir) == '') {return false;}$sql_file_res = $this->scan_dir($dir);if (!empty($sql_file_res)) {foreach ($sql_file_res as $k => $v) {if (!empty(strstr($v,'.sql'))) {$sql_content = file_get_contents($dir.$v);$sql_arr = explode(';', $sql_content);//执行sql语句foreach ($sql_arr as $vv) {if (!empty($vv)) {$sql_res = Db::execute($vv.';');if (empty($sql_res)) {return false;}}}}}} else {return false;}return true;}/*** 下载程序压缩包文件* @param $url 要下载的url* @param $save_dir 要存放的目录* @return res 成功返回下载信息 失败返回false*/function down_file($url, $save_dir) {if (trim($url) == '') {return false;}if (trim($save_dir) == '') {return false;}if (0 !== strrpos($save_dir, '/')) {$save_dir.= '/';}$filename = basename($url);//创建保存目录if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {return false;}//开始下载$ch = curl_init();$timeout = 5;curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);$content = curl_exec($ch);$status = curl_getinfo($ch);curl_close($ch);// 判断执行结果if ($status['http_code'] ==200) {$size = strlen($content);//文件大小$fp2 = @fopen($save_dir . $filename , 'a');fwrite($fp2, $content);fclose($fp2);unset($content, $url);$res = ['status' =>$status['http_code'] ,'file_name' => $filename,'save_path' => $save_dir . $filename];} else {$res = false;}return $res;}/*** 获取文件内容* @param $url 要获取的url* @return res 成功返回内容 失败返回false*/public function get_file($url){if (trim($url) == '') {return false;}$opts = array('http'=>array('method'=>"GET",'timeout'=>3,//单位秒));$cnt=0;while($cnt<3 && ($res=@file_get_contents($url, false, stream_context_create($opts)))===FALSE) $cnt++;if ($res === false) {return false;} else {return $res;}}// 在线更新public function system_update(){// 有效期核验// 查询用户身份标识并请求接口$Info = new Info();$sn = $Info->where('id', 1)->field('sn')->find();$data = ['sn'=>$sn->sn];// 访问服务器判断有效期:$res = $this -> dataRequest('http://192.168.31.64/tp5/public/update/server/check_date',false,'post',$data);if ($res->data === false) {// 不在有效期$result = ['code'=>401,'msg'=>'已过服务有效期','data'=>''];} else {// 有效期内 开始更新// 设定目录// 根目录$base_dir = ROOT_PATH;// 服务器更新路径$update_res = 'http://192.168.31.64/tp5/public/update/';// 本地更新路径$local_up_dir = $base_dir.'public/update/';// 本地缓存路径$path = $base_dir . 'public\update\cache';// 没有就创建if(!is_dir($path)){mkdir(iconv("UTF-8", "GBK", $path),0777,true);}// 设定缓存目录名称$cache_dir = $path.'\\';// 看看需要下载几个版本的压缩包// 服务器更新日志存放路径$server = $this->get_file($update_res.'up_log.txt');if ($server === false) {$result = ['code'=>406,'msg'=>'服务器更新日志获取失败','data'=>''];}else{// 版本记录$server = explode(",", $server);$local = $this->get_file($local_up_dir.'ver.txt');if ($local === false) {$result = ['code'=>406,'msg'=>'本地更新日志获取失败','data'=>''];} else {// 循环比较是否需要下载 更新foreach ($server as $key => $value) {if ($local < $value) {// 获取更新信息// 服务器各个程序包日志存放路径$up_info = $this->get_file($update_res.$value.'/version.txt');// 判断是否存在if ($up_info === false) {$result = ['code'=>406,'msg'=>'服务器更新包不存在','data'=>''];} else {// 信息以json格式存储便于增减和取值 故解析json对象$up_info = json_decode($up_info);// 下载文件$back = $this->down_file($up_info->download,$cache_dir);if (empty($back)) {$result = ['code'=>406,'msg'=>'升级程序包下载失败','data'=>''];} else {//下载成功 解压缩$zip_res = $this->deal_zip($back['save_path'] ,$cache_dir);// 判断解压是否成功if ($zip_res == 406) {$result = ['code'=>406,'msg'=>'文件解压缩失败','data'=>''];} else {// 开始更新数据库和文件// sql文件//读取文件内容遍历执行sql$sql_res = $this->carry_sql($cache_dir.'mysql\\');if ($sql_res === false) {$result = ['code'=>406,'msg'=>'sql文件写入失败','data'=>''];} else {// php文件合并 返回处理的文件数$file_up_res = $this->copy_merge($cache_dir.'program\\',$base_dir);if (empty($file_up_res)) {$result = ['code'=>406,'msg'=>'文件移动合并失败','data'=>''];}else{// 更新完改写网站本地版号$write_res = file_put_contents($local_up_dir . 'ver.txt', $value);if (empty($write_res)) {$result = ['code'=>406,'msg'=>'本地更新日志改写失败','data'=>''];}else{// 删除临时文件$del_res = $this->deldir($cache_dir);if (empty($del_res)) {$result = ['code'=>406,'msg'=>'更新缓存文件删除失败','data'=>''];}else{$result = ['code'=>200,'msg'=>'在线升级已完成','data'=>''];}}}}}}}}else{$result = ['code'=>406,'msg'=>'本地已经是最新版','data'=>''];}}}}}return json($result);}}

服务器检测版本

<?phpnamespace app\update\controller;use think\controller;
use app\common\model\Sn;class Server extends \think\Controller
{// 有效期判断public function check_date(){$data = input('post.sn','','trim');if (empty($data)) {$result = ['code' =>406,'msg' =>'未接收到身份识别码','data' =>true];}else{// 查询 身份是否过有效期$Sn = new Sn;$res = $Sn->where('sn',$data)->find();$now_time = time();if ($res->over_date >= $now_time) {// 未过有效期$result = ['code' =>204,'msg' =>'很高兴为您服务','data' =>true];} else {// 已过有效期$result = ['code' =>416,'msg' =>'对不起,已过服务有效期','data' =>false];}}return json($result);}}

以下是支撑这个功能的一些文件格式 文件夹格式 大家可以自行本地创建修改 但是要对应升级程序中的命名避免报错 需要注意的是txt文件一定要用编辑器去写内容 保证是utf8无bom格式 否则容易出错还很难察觉
本地记录版本信息的文件夹
以下是文件内容,仅记录了版号用于比对版本判断是否需要更新
在这里插入图片描述

接下来是服务器端文件 这里比较重要在这里插入图片描述
up_log内容记录了每次服务器更新的版本号
用于本地跨版本升级时把之前没有升级的版本更新都补上
在这里插入图片描述
升级文件夹内部包含了本次版本记录信息和程序压缩包
在这里插入图片描述
这里需要注意的是我的记录文件使用的是json格式 便于后期增减参数 升级程序只需要格式化json数据后读取特定键值即可 大家可以自行使用json_encode 函数直接对数组进行格式化后存储进去
在这里插入图片描述

压缩包包含了 .sql文件和主程序
在这里插入图片描述

sql文件中的内容是使用 navicat for mysql软件增加字段后 直接预览 sql语句 复制的 说白了数据库这块的升级无非也就是增加表字段 本来预想备份数据库数据之后重新创建与远端服务器一样的表再插入现有备份的表数据 但是考虑到安全性以及避免升级过程中出错导致整个本地服务器挂掉 选择了更稳妥的办法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其中的主程序就是直接把 有更改的文件 从程序根目录开始进行了添加 这里大家可以直接把整个根目录下的文件都做成program 因为我在更新覆盖时进行的是文件md5校验 没有变更的文件是不会被更新的
在这里插入图片描述

至此整个升级程序已经结束 感谢大家参考

有任何问题都可以直接联系我咨询 一定不吝赐教

VX/QQ 同号 422495701


http://chatgpt.dhexx.cn/article/0GuLoIyR.shtml

相关文章

软件在线升级系统设计

需求描述 一直用Qt开发PC版本的程序&#xff0c;常用的方法都是打包发布。目前开发的程序比较复杂&#xff0c;涉及到上百个独立的进程以及4000多个相关文件&#xff0c;采用原来的打包发布就变得很麻烦&#xff0c;第一改动比较频繁&#xff0c;打包次数过多&#xff1b;第二…

Nginx在线升级

1、查看当前nginx版本&#xff0c;nginx -v&#xff08;小写v&#xff09; 2、下载最新版本nginx包&#xff0c;nginx-1.23.1.tar.gz 3、解压tar -zxvf nginx-1.23.1.tar.gz 4、进入解压目录cd nginx-1.23.1.tar.gz 5、查看nginx当前配置&#xff0c;nginx -V注意这里是大写…

在线升级:OTA升级的原理和实现方式

目录 1、OTA 在线升级 2、实现方式 3、操作方式 3.1、后台式升级 3.2、非后台式式更新 4、STM32 的在线升级 4.1、划分 Flash 区域 4.2、实操1 - Flash空间地址的划分 4.3、实操2 - 设置工程 4.4、实操3 - 接收固件更新包 4.5、实操4 - 拷贝程序至Flash 4.6、实操5 - 跳转至 Ap…

halcon 彩色图转灰度图

read_image (Image, jiao1.bmp) //读取图像 get_image_size (Image, Width, Height) //获取宽高 dev_close_window () //关闭图形窗口 rgb1_to_gray (Image, GrayImage) //彩色图转灰度图 dev_open_window (0, 0, Width, Height, black, WindowHandle) //打开图形窗口 d…

Python中使用PIL快速实现灰度图

效果 原图 效果图 实现 新建文件夹grayImage&#xff0c;在此文件夹下新建gray.py from PIL import Image imgImage.open(1111.jpg) imgimg.convert(L) img.save(灰度图.jpg) 其中1111.jpg是原图&#xff0c;将其放在同目录下。 运行即可。 源码以及资源下载 https://do…

计算机灰度分析,计算机中的256级灰度图像

【建议1】使用GltraEdit软件观察字符“((Z20享有声望的学校联盟)”的内部代码. 以下说法正确 A. 字符“(”的内部代码占用两个字节 B. 字符“ Lian”的代码值的二进制表示形式是11010001 10101010 C. 图片中有5个ASCⅡ字符,其中字符“ 2”的代码值的十六进制表示为32 D. 字符“…

C++-灰度图上色GrayToColor

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 场景需求 最近有客户提出&#xff0c;想要将解包裹图像转化为有颜色的图像&#xff0c;具备更佳的视觉效果。解包裹图是一个floa…

灰度图与二值化

图像处理的灰度化和二值化 在图像处理中&#xff0c;用RGB三个分量&#xff08;R&#xff1a;Red&#xff0c;G&#xff1a;Green&#xff0c;B&#xff1a;Blue&#xff09;&#xff0c;即红、绿、蓝三原色来表示真彩色&#xff0c;R分量&#xff0c;G分量&#xff0c;B分量的…

opencv学习5:cvtColor RGB图像转灰度图像原理

简介 将彩色图像转化成为灰度图像的过程成为图像的灰度化处理。彩色图像中的每个像素的颜色有R、G、B三个分量决定&#xff0c;而每个分量有255中值可取&#xff0c;这样一个像素点可以有1600多万&#xff08;255*255*255&#xff09;的颜色的变化范围。而灰度图像是R、G、…

Matplotlib显示灰度图

引言 matplotlib中的imshow()函数不能自动显示灰度图像&#xff0c;这一点应该是众所周知的&#xff0c;需要调用cmap“gray"以进行设置&#xff0c;但是cmap"gray"实际上并不是如opencv中的imshow函数一样将单通道图显示为灰度图&#xff0c;私以为是引入了灰度…

python显示灰度图像,Python读取图像并显示灰度图的实现

python读取图像 原图: import cv2 # 利用opencv读取图像 import numpy as np # 利用matplotlib显示图像 import matplotlib.pyplot as plt img cv2.imread("./lena.png") #读取图像 # 显示图像 plt.imshow(img) plt.axis(off) plt.show() 效果&#xff1a; 问&#…

OpenCV灰度图

什么是灰度图&#xff1a; 百度百科 什么是灰度图 #include <opencv2/opencv.hpp> #include <iostream>using namespace cv; using namespace std;int main(int argc, char** argv) {Mat src Mat(4,4,CV_8UC3,Scalar(28,128,228));cout << src << en…

RGB图像转化为灰度图原理

RGB图像转化为灰度图原理 1 原理 利用MATLAB对RGB图像进行读取&#xff1a; imgimread(蒙娜丽莎.jpg);可以知道存储RGB图像数据为 256 256 3 u i n t 8 256\times256 \times3\quad uint8 2562563uint8&#xff0c;其中 256 256 256\times256 256256表示长和宽的像素个数&…

彩色图像转换灰度图像

数字图像 现在我们所接触到的图像绝大多数都是数字图像&#xff0c;图像数字化后&#xff0c;每个像素点就可以看作是一个小方格&#xff0c;每个小方格里面存储的就是图像的像素信息。如果把一副数字图像抽象出来&#xff0c;就是一个二维矩阵&#xff08;灰度图&#xff09;或…

Python灰度图像彩色化

1️⃣作业要求 给定一幅灰度图像&#xff0c;使用任意方法将其变成一幅彩色图像&#xff0c;并尽量使得添加的色彩显得较为真实。 2️⃣核心代码 这里我们是直接调用了eccv16和siggraph17的模型&#xff0c;运行程序时会自动下载这两个model文件&#xff0c;然后经过对图像的…

matlab 绘制灰度图

matlab 将矩阵中不同数值所对应的区域用 不同深度的灰度图画出来&#xff0c;不显示坐标轴标签&#xff0c;并设置在画布上全屏显示。 if 1close allrng (7)mask_allrand(256,256)*10;mask_allround(mod(mask_all,2));mask_allsort(mask_all);mask_all(1:100,1:50)0;mask_all(…

matlab读取一幅灰度图,Matlab处理灰度图

作业2&#xff1a;通过图像分析的方法对如下图像进行分析&#xff0c;获取颗粒特性参数。具体参数包括图像中的颗粒个数&#xff0c;颗粒面积&#xff0c;颗粒等效直径&#xff0c;非球形颗粒的长/短轴&#xff0c;非球形颗粒的方位。 图1 待处理颗粒图像 步骤&#xff1a; (1)…

python灰度图

任务描述 背景   真彩色图像和灰度图像是数字图像的两种常见类型&#xff0c;如下图所示&#xff0c;左图是真彩色图像&#xff0c;右图是灰度图像。    在真彩色图像中&#xff0c;像素颜色是 RGB 颜色&#xff0c;每个颜色包含 R、G、B 三个颜色分量。而在灰度图像中&…

图像处理--灰度图

灰度图 灰度图&#xff0c;Gray Scale Image 或是Grey Scale Image&#xff0c;又称灰阶图。把白色与黑色之间按对数关系分为若干等级&#xff0c;称为灰度。灰度分为256阶。 灰度图定义 什么叫灰度图&#xff1f;任何颜色都有红、绿、蓝三原色组成&#xff0c;假如原来某点…

OpenCV(三)彩色图灰度化、通道分离、单通道反差处理(灰度图)、多通道反差处理(彩色图)

目录 一、彩色图灰度化 1、主要函数cvtColor()介绍 2、代码 3、效果 二、通道分离 1、向量介绍 2、总代码 3、效果 三、单通道(灰度图)反差处理 1、单通道向量访问 2、代码 3、效果 四、多通道(彩色图)反差处理&#xff08;彩色图的反差处理&#xff09; 1、多…