OpenCV 笔记(2):图像的属性以及像素相关的操作
Part11. 图像的属性
11.1 Mat 的主要属性
在前文中,我们大致了解了 Mat 的基本结构以及它的创建与赋值。接下来我们通过一个例子,来看看 Mat 所包含的常用属性。
先创建一个 3*4 的四通道的矩阵,并打印出其相关的属性,稍后会详细解释每个属性的含义。
Mat srcImage(3, 4, CV_16UC4, Scalar_<uchar>(1, 2, 3, 4));cout << srcImage << endl;cout << "dims:" << srcImage.dims << endl;
cout << "rows:" << srcImage.rows << endl;
cout << "cols:" << srcImage.cols << endl;
cout << "channels:" << srcImage.channels() << endl;
cout << "type:" << srcImage.type() << endl;
cout << "depth:" << srcImage.depth() << endl;
cout << "elemSize:" << srcImage.elemSize() << endl;
cout << "elemSize1:" << srcImage.elemSize1() << endl;
cout << "step:" << srcImage.step << endl;
cout << "step[0]:" << srcImage.step[0] << endl;
cout << "step[1]:" << srcImage.step[1] << endl;
cout << "step1[0]:" << srcImage.step1(0) << endl;
cout << "step1[1]:" << srcImage.step1(1) << endl;
输出结果:
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4;1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4;1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
dims:2
rows:3
cols:4
channels:4
type:26
depth:2
elemSize:8
elemSize1:2
step:32
step[0]:32
step[1]:8
step1[0]:16
step1[1]:4
在上述例子中我们打印了 Mat 的很多属性,它们主要包括:
rows: 表示图像的高度。
cols:表示图像的宽度。
dims:表示矩阵的维度。
data:表示 Mat 对象中的指针(uchar 类型的指针),指向内存中存放矩阵数据的一块内存 (uchar* data)。
channels:表示通道数量;例如常见的 RGB、HSV 彩色图像,则 channels=3;若为灰度图,则 channels=1。
depth:表示图像的深度,它用来度量每一个像素中每一个通道的精度,它本身与通道数无关,它的数值越大表示精度越高。
数据类型 | depth 的值 | 数据类型 | 取值范围 | 对应 C++ 的类型 |
---|---|---|---|---|
CV_8U | 0 | 8 位无符号类型 | 0—255 | uchar, unsigned char |
CV_8S | 1 | 8 位有符号类型 | -128—127 | char |
CV_16U | 2 | 16 位无符号类型 | 0—65535 | ushort, unsigned short, unsigned short int |
CV_16S | 3 | 16 位有符号类型 | -32768—32767 | short, short int |
CV_32S | 4 | 32 位整数数据类型 | -2147483648—2147483647 | int, long |
CV_32F | 5 | 32 位浮点数类型 | ±(1.18e-38……3.40e38) | float |
CV_64F | 6 | 32 位双精度类型 | ±(2.23e-308……1.79e308) | double |
type:表示矩阵的数据类型,它包含矩阵中元素的类型以及通道数信息。
数据类型 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
CV_8U | CV_8UC1 | CV_8UC2 | CV_8UC3 | CV_8UC4 |
CV_8S | CV_8SC1 | CV_8SC2 | CV_8SC3 | CV_8SC4 |
CV_16U | CV_16UC1 | CV_16UC2 | CV_16UC3 | CV_16UC4 |
CV_16S | CV_16SC1 | CV_16SC2 | CV_16SC3 | CV_16SC4 |
CV_32S | CV_32SC1 | CV_32SC2 | CV_32SC3 | CV_32SC4 |
CV_32F | CV_32FC1 | CV_32FC2 | CV_32FC3 | CV_32FC4 |
CV_64F | CV_64FC1 | CV_64FC2 | CV_64FC3 | CV_64FC4 |
elemSize:表示矩阵中每一个元素的数据大小,它与通道数相关,单位是字节。 举几个例子: 如果 Mat 中的数据类型是 CV_8UC1 或 CV_8SC1,那么 elemSize=1(1 * 8 / 8 = 1 bytes); 如果 Mat 中的数据类型是 CV_8UC3 或 CV_8SC3,那么 elemSize=3(3 * 8 / 8 = 3 bytes); 如果 Mat 中的数据类型是 CV_16UC3 或 CV_16SC3,那么 elemSize=6(3 * 16 / 8 = 6 bytes); 如果 Mat 中的数据类型是 CV_32SC3 或 CV_32FC3,那么 elemSize=12(3 * 32 / 8 = 12 bytes);
elemSize1:表示矩阵中每一个元素单个通道的数据大小,单位是字节。满足:
step: 字面意思是“步长”,实际上它描述了矩阵的形状。 step[] 为一个数组,矩阵有几维,step[] 数组就有几个元素。以一个三维矩阵为例,step[0] 表示一个平面的字节总数,step[1] 表示一行元素的字节总数,step[2] 表示每一个元素的字节总数。
在 OpenCV 的官方文档中,关于解释 step 时曾提到矩阵数据元素
的地址:对于我们常用的二维数组,上述公式可化简为:
这里的 step[0] 表示一行元素的字节总数,step[1] 表示每一个元素的字节总数。

step1: step1 也是一个数组。step1 不再以字节为单位,而是以 elemSize1 为单位,满足:
Part22. 图像的像素操作
22.1 像素的类型
我们最常用的图像是二维数组,灰度图像(CV_8UC1)会存放 C++ 的 uchar 类型,RGB 彩色图像一般会存放 Vec3b 类型。
其中,单通道数据存放格式:
三通道数据存放格式:
对于彩色图像而言,在 OpenCV 中通道的顺序是 B、G、R,这跟我们通常所说的 RGB 三原色正好相反。
当然,灰度图像也不一定都是 CV_8UC1 类型,也可能是 CV_16SC1、CV_32FC1 等,它们会存放 C++ 的 short、float 等基本类型。类似地,彩色图像也可能是 CV_16SC3、CV_32FC3 等,那它们是怎么存放的呢?
OpenCV 定义了一系列的 Vec 类,它是一个一维的向量,代表像素的类型。
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
其中 b、s、w、i、f、d 分别表示如下的含义:
数据类型 | |
---|---|
b | unsigned char |
s | short int |
w | unsigned short |
i | int |
f | float |
d | double |
Vec 类又被称为固定向量类,在编译时就知道向量的大小。类似 Vec 这样的类还有:Matx、Point、Size、Rect
我们用一张表,总结一下矩阵中的数据类型和像素的类型的对应关系:
数据类型 | C1 | C2 | C3 | C4 | C6 |
---|---|---|---|---|---|
CV_8U | uchar | Vec2b | Vec3b | Vec4b | |
CV_8S | char | Vec<char, 2> | Vec<char, 3> | Vec<char, 4> | |
CV_16U | ushort | Vec2w | Vec3w | Vec4w | |
CV_16S | short | Vec2s | Vec3s | Vec4s | |
CV_32S | int | Vec2i | Vec3i | Vec4i | |
CV_32F | float | Vec2f | Vec3f | Vec4f | Vec6f |
CV_64F | double | Vec2d | Vec3d | Vec4d | Vec6d |
基于上述表格我们可以回答刚才的问题,CV_16SC3 类型的图像存放的是 Vec3s 类型,CV_32FC3 类型的图像存放的是 Vec3f 类型。
32.2 像素点的读取
Mat 的 at() 函数实现了对矩阵中的某个像素的读写操作。
下面的代码展示了 at() 函数对灰度图像像素的读写:
Scalar value = grayImage.at<uchar>(y, x);
Scalar.at<uchar>(y, x) = 128;
三通道彩色的图像的读取:
Vec3b value = image.at<Vec3b>(y, x);uchar blue = value.val[0];
uchar green = value.val[1];
uchar red = value.val[2];
三通道彩色图像的赋值:
image.at<Vec3b>(y,x)[0]=128;
image.at<Vec3b>(y,x)[1]=128;
image.at<Vec3b>(y,x)[2]=128;
下面的例子结合像素的类型,展示了将加载的图像转换成灰度图像,以及对灰度图像进行取反的操作。
Mat srcImage = imread("/Users/tony/beautiful.jpg");
if (srcImage.empty())
{cout << "could not load image ..." << endl;return -1;
}
imshow("src", srcImage);Mat grayImage;
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY); // 灰度处理
imshow("gray",grayImage);int height = grayImage.rows;
int width = grayImage.cols;for (int row=0; row<height; row++)
{for (int col=0; col<width; col++){int gray = grayImage.at<uchar>(row, col);grayImage.at<uchar>(row, col) = 255- gray;}
}imshow("invert", grayImage);

简单提一下,上述例子中 cvtColor() 函数的作用是将图像从一个颜色空间转换到另一个颜色空间。例如,可以将图像从 BGR 色彩空间转换成灰度色彩空间,或者从 BGR 色彩空间转换成 HSV 色彩空间等等。
42.3 图像的遍历
2.3.1 基于数组遍历
前面 2.2 介绍过 at() 函数可以对某个像素进行读写操作,并用例子展示了对单通道进行遍历。
对于三通道的彩色图像可以这样遍历。
for(int i=0;i<srcImage.rows;i++){for(int j=0;j<srcImage.cols;j++){srcImage.at<Vec3b>(i,j)[0]=... //B通道srcImage.at<Vec3b>(i,j)[1]=... //G通道srcImage.at<Vec3b>(i,j)[2]=... //R通道}
}
2.3.2 基于指针遍历
Mat 类提供了更高效的 ptr() 函数,它可以得到图像任意行首地址。
下面的代码,它返回第 i+1 行的首地址,也就是指向第 i+1 行第一个元素的指针。
uchar* data = srcImage.ptr<uchar>(i);
at() 函数跟 ptr() 函数在使用上有一定的区别:
at<类型>(i,j)
ptr<类型>(i)
当然,使用 ptr() 函数访问某个像素也是可以的,采用如下的方式:
mat.ptr<type>(row)[col]
它返回的是 <> 中的模板类型指针,指向的是第 row+1 行 col+1 列的元素。
对于单通道图像的遍历:
for(int i=0;i<srcImage.rows;i++){uchar* data=srcImage.ptr<uchar>(i);for(int j=0;j<srcImage.cols;j++){data[j]=...}
}
对于三通道图像的遍历:
for(int i=0;i<srcImage.rows;i++){Vec3b* data=srcImage.ptr<Vec3b>(i);for(int j=0;j<srcImage.cols;j++){data[j][0]=... //B通道data[j][1]=... //G通道data[j][2]=... //R通道}
}
2.3.3 基于迭代器遍历
C++ STL 对每个集合类都定义了对应的迭代器类,OpenCV 也提供了 cv::Mat 的迭代器类,并且与 C++ STL 中的标准迭代器兼容。
对于单通道图像的遍历:
Mat_<uchar>::iterator begin = srcImage.begin<uchar>();
Mat_<uchar>::iterator end = srcImage.end<uchar>();for (auto it = begin; it != end; it++)
{*it = ...
}
迭代器 Mat_ 是 Mat 的模版子类,它重载了 operator() 让我们可以更方便的取图像上的点。类似的迭代器还有 Matlterator_。
对于三通道图像的遍历:
Mat_<cv::Vec3b>::iterator begin = srcImage.begin<cv::Vec3b>();
Mat_<cv::Vec3b>::iterator end = srcImage.end<cv::Vec3b>();for (auto it = begin; it != end; it++)
{(*it)[0] = ... //B通道(*it)[1] = ... //G通道(*it)[2] = ... //R通道
}
使用迭代器遍历图像会便捷一些,但是效率没有使用指针的效率高。
52.3.4 基于 LUT 遍历
LUT (LOOK -UP-TABLE) 意为查找表。
在数据结构中,查找表是由同一类型的 数据元素 构成的集合,它是一种以查找为“核心”,同时包括其他运算的非常灵活的数据结构。
在图像处理中,经常会通过事先建立一张查找表对图像进行映射。
例如,将灰度图由某个区间映射到另一个区间,或者将单通道映射到三通道。它们都是以像素灰度值作为索引,以灰度值映射后的数值作为表中的内容,通过索引号与映射后的输出值建立联系。
一般灰度图像会有 0-255 个灰度值,有时我们不需要这么精确的灰度级,例如黑白图像。下面我们来展示如何建立一个 LUT,将 64 到 196 之间的灰度值变成 0,其余变成 1。
Mat lut(1, 256, CV_8U);
for (int i = 0; i < 256; i++)
{if (i > 64 and i < 196){lut.at<uchar>(i) = 0;}else{lut.at<uchar>(i) = i;}
}
从上述代码可以看出,通过改变图像中像素的灰度值,LUT 可以降低灰度级提高运算速度。
LUT 只适用于 CV_8U 类型的图像。
当然,查找表并不一定都是单通道的。
如果输入图像为单通道,那么查找表为单通道。
如果输入图像为三通道,那么查找表可以为单通道或者三通道。
使用 LUT 进行遍历,采用的是颜色空间缩减的方式:把 unsigned char 类型的值除以一个 int 类型的值,得到仍然是一个 char 类型的数值。
我们采用如下的公式:
其中,Q 表示量化级别,当 Q= 10 时则灰度值 1-10 用灰度值 1 表示,灰度值 11-20 用灰度值 11 表示,以此类推。256 个灰度值的灰度图像可以用 26 个数值表示,那么彩色的图像就可以用 26 * 26 * 26 个数值表示,比原先小了很多。
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>using namespace std;
using namespace cv;#define QUAN_VAL1 10
#define QUAN_VAL2 20
#define QUAN_VAL3 100void createLookupTable(Mat& table, uchar quanVal)
{table.create(1,256,CV_8UC1);uchar *p = table.data;for(int i = 0; i < 256; ++i){p[i] = quanVal*(i/quanVal); // 颜色缩减运算}
}int main()
{Mat srcImage = imread("/Users/tony/beautiful.jpg");if (srcImage.empty()){cout << "could not load image ..." << endl;return -1;}imshow("src", srcImage); // 原图Mat table,dst1,dst2,dst3;createLookupTable(table, QUAN_VAL1);LUT(srcImage, table, dst1);createLookupTable(table, QUAN_VAL2);LUT(srcImage, table, dst2);createLookupTable(table, QUAN_VAL3);LUT(srcImage, table, dst3);imshow("dst1", dst1); // Q=10imshow("dst2", dst2); // Q=20imshow("dst3", dst3); // Q=100waitKey(0);return 0;
}

上述例子在创建查找表时,遍历了矩阵的每一个像素以及运用颜色空间缩减的运算公式。并且分别展示了原图、Q=10、Q=20、Q=100 的图片。可以看到当 Q = 100 时,图像压缩得比较厉害丢失了很多信息。
Part33. 图像像素值的统计
63.1 均值与标准差
均值和标准差是统计学的概念。
均值的公式:
标准差公式:
在图像处理中,它们能帮助我们了解图像通道中像素值的分布情况。均值表示图像整体的亮暗程度,图像的均值越大则表示图像越亮。标准差表示图像中明暗变化的对比程度,标准差越大表示图像中明暗变化越明显。
在图像分析的时候,我们通过图像像素值的统计,可以对图像的有效信息作出判断。当标准差很小时,图像所携带的有效信息会很少,便于我们判断这是否是我们所需要的图像。说一个题外话,曾经我看到过一段很震惊的代码,某同事写的判断传送带上手机是否亮屏。当时的代码可能是为了偷懒,只通过判断图像的均值,当均值超过某个阈值时就认为手机是亮屏的。后来我接手后,当即做了大量的修改。
下面举个例子,通过 meanStdDev() 函数获取图像的均值和标准差,以及每个通道的均值和标准差。
Mat srcImage = imread("/Users/tony/beautiful.jpg");
if (srcImage.empty())
{cout << "could not load image ..." << endl;return -1;
}
imshow("src", srcImage);Mat mean, stddev;
meanStdDev(srcImage, mean, stddev);
std::cout << "mean:" << std::endl << mean << std::endl;
std::cout << "stddev:" << std::endl<< stddev << std::endl;
printf("blue channel mean:%.2f, stddev: %.2f \n", mean.at<double>(0, 0), stddev.at<double>(0, 0));
printf("green channel mean:%.2f, stddev: %.2f \n", mean.at<double>(1, 0), stddev.at<double>(1, 0));
printf("red channel mean:%.2f, stddev: %.2f \n", mean.at<double>(2, 0), stddev.at<double>(2, 0));
输出结果:
mean:
[91.28189117330051;104.7030620995939;118.9715339648672]
stddev:
[77.24017058254671;79.5424883584348;83.89088339080149]
blue channel mean:91.28, stddev: 77.24
green channel mean:104.70, stddev: 79.54
red channel mean:118.97, stddev: 83.89
Part44. 总结
本文过一个简单的例子,介绍了 Mat 经常使用的属性和方法。后续还介绍了像素的类型和多种图像遍历的方式、像素值的统计。
在几种图像遍历方式中,除了 LUT 遍历外,其他的几种方式它们的效率从高到低依次为:指针 > 迭代器 > 数组。在实际生产环境中,我们经常会用指针遍历的方式。
本文介绍的内容是对前面一篇文章内容的补充,它们都是 OpenCV 最基础的内容,接下来的文章会经常使用这些内容。本文还引申出了 LUT 以及图像像素值的统计, 特别是均值和标准差它们在图像预处理中经常用到。
相关文章:

OpenCV 笔记(2):图像的属性以及像素相关的操作
Part11. 图像的属性 11.1 Mat 的主要属性 在前文中,我们大致了解了 Mat 的基本结构以及它的创建与赋值。接下来我们通过一个例子,来看看 Mat 所包含的常用属性。 先创建一个 3*4 的四通道的矩阵,并打印出其相关的属性,稍后会详细…...

基于指数分布优化的BP神经网络(分类应用) - 附代码
基于指数分布优化的BP神经网络(分类应用) - 附代码 文章目录 基于指数分布优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.指数分布优化BP神经网络3.1 BP神经网络参数设置3.2 指数分布算法应用 4.测试结果…...

Python--练习:使用while循环求1~100之间,所有偶数的和(涉及if判断是不是偶数)
案例:求1~100之间,所有偶数的和 思考: 先套用原有基础模式,之后再思考其他的。 其实就是在之前文章 Python--练习:使用while循环求1..100的和-CSDN博客 的基础上,再判断如果获取到里面的全部偶数&#…...

带温度的softmax
用pytorch写一下使用带有温度的softmax的demo import torch import torch.nn.functional as F# 定义带有温度的softmax函数 def temperature_softmax(logits, temperature1.0):return F.softmax(logits / temperature, dim-1)# 输入logits logits torch.tensor([[1.0, 2.0, 3.…...
js函数调用的方式有几种
在 JavaScript 中,函数可以通过不同的方式进行调用。以下是常见的几种函数调用方式: 函数调用:使用函数名称后跟一对小括号来调用函数,这是最基本的调用方式。 functionName(); 方法调用:函数可以作为对象的方法进行调…...

聊聊设计模式--简单工厂模式
简单工厂模式 前面也学了很多各种微服务架构的组件,包括后续的服务部署、代码管理、Docker等技术,那么作为后端人员,最重要的任务还是代码编写能力,如何让你的代码写的漂亮、易扩展,让别人一看赏心悦目,…...

Python基础教程:内置函数之字典函数的使用方法
嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 len(字典名): 返回键的个数,即字典的长度 # len(字典名): # 返回键的个数,即字典的长度dic {a:123,b:456,c:789…...

Pytorch从零开始实战06
Pytorch从零开始实战——明星识别 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——明星识别环境准备数据集模型选择开始训练模型可视化模型预测总结 环境准备 本文基于Jupyter notebook,使用Python3.8,Pytorch2.0.1c…...

RT-Thread学习笔记(三):线程管理
线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…...

pymysql连接Mariadb/Mysql出现错误(配置正确情况下)解决办法
场景:在kali中使用python中pymysql对Mariadb进行连接,在整个过程中配置全部正确,但是就是无法进行连接,提示结果如下: Access denied for user rootlocalhost解决办法:进入数据库中,将默认密码…...

数据仓库扫盲系列(1):数据仓库诞生原因、基本特点、和数据库的区别
数据仓库的诞生原因 随着互联网的普及,信息技术已经深入到各行各业,并逐步融入到企业的日常运营中。然而,当前企业在信息化建设过程中遇到了一些困境与挑战。 1、历史数据积存。 过去企业的业务系统往往是在较长时间内建设的,很…...

DataX-web安装部署和使用
DataX-web的环境准备 MySQL (5.5) 必选,对应客户端可以选装, Linux服务上若安装mysql的客户端可以通过部署脚本快速初始化数据库 JDK (1.8.0_xxx) 必选 DataX 必选 Python (2.x) (支持Python3需要修改替换datax/bin下面的三个python文件,替换文件在do…...
sqlmap防御以及文件读写
一.防御 过滤 1.使用过滤函数 $email filter_var($_POST[email], FILTER_VALIDATE_EMAIL); if ($email) { // input is a valid email address } else { // input is not a valid email address 使用 filter_var() 函数和 FILTER_VALIDATE_EMAIL 过滤器来验证用户输…...

【源码】C/C++运动会计分系统 期末设计源码
文章目录 题目介绍功能源码效果展示带报告(内容) 题目介绍 使用语言: 两个版本都会发: 版本1:C语言 版本2: C 代码量: 500 题目介绍: 要求:初始化输入:N-参赛…...
Ubuntu安装Docker
卸载官方库中之前的旧版本 sudo apt-get remove docker docker-engine docker-ce docker.io更新安装包列表 sudo apt-get update安装以下包以使apt可以通过HTTPS使用存储库 sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common添…...
useReducer+createContext真的可以代替Redux吗?
概念 useReducer useReducer 是 React 提供的一个状态管理钩子,通常用于管理组件的复杂状态逻辑。它采用两个参数:reducer 函数和初始状态。Reducer 函数接受当前状态和一个操作(action),并返回一个新的状态。这有点…...
Mysql忘记登入密码找回 方法(超详细)
如果你找不到 MySQL 的安装路径,可以尝试以下几种方法: 检查环境变量:打开命令提示符,并运行以下命令: echo %PATH% 这会显示系统的环境变量。查找其中是否包含 MySQL 相关的路径,例如 C:\Program Files…...

NodeMCU ESP8266 读取按键外部输入信号详解(图文并茂)
NodeMCU ESP8266 读取按键外部输入信号教程(图文并茂) 文章目录 NodeMCU ESP8266 读取按键外部输入信号教程(图文并茂)前言按键输入常用接口pinModedigitalRead 示例代码结论 前言 ESP8266如何检测外部信号的输入,通常…...
Oracle如何插入图片数据?
1、创建表,注意:插入图片的列要定义成BLOB类型 create table image_lob(t_id varchar2(5) not null,t_image blob not null);2、创建图片目录,images为目录名 create or replace directory "images" as f:\pic\;3、创建存储过程&am…...

C++特性——inline内联函数
1. 内联函数 1.1 C语言的宏 在C语言中,我们学习了用#define定义的宏函数,例如: #define Add(x, y) ((x) (y)) //两数相加相较于函数,我们知道宏替换具有如下比较明显的优点: 性能优势: 宏在预处理阶段…...

OGG-01635 OGG-15149 centos服务器远程抽取AIX oracle11.2.0.4版本
背景描述 有一套ogg远程抽取的环境,源端是AIX7.1环境的oracle 11.2.0.4版本的数据库,中间是OGG抽取服务器,目标端是centos 7.9环境的oracle 19c。 采用集成模式远程抽取源端数据正常,但是经典模式远程抽取源数据的时候抽取进程启…...
一、ES6-let声明变量【解刨分析最详细】
一、块级作用域 { let Tim"Tim是靓仔!" } console.log("Tim:",Tim) 打印结果:Tim未进行任何定义! 原因:因为Tim定义再块级{}里面,它的声音Tim只服务于该块级里面。而打印结果是再块级外面&#…...
当前市场环境下,软件行业的突围之道:技术演进与商业模式重构
一、行业背景:软件行业进入结构性调整期 2024年至今,软件行业面临三重挑战: 宏观经济承压:全球经济放缓,企业IT预算趋于谨慎; 资本市场收缩:融资环境收紧,盈利能力成为生死线&…...

使用vue3+ts+input封装上传组件,上传文件显示文件图标
效果图: 代码 <template><div class"custom-file-upload"><div class"upload"><!-- 显示已选择的文件 --><div class"file-list"><div v-for"(item, index) in state.filsList" :key&q…...
nano编辑器的详细使用教程
以下是 Linux 下 nano 编辑器 的详细使用指南,涵盖安装、基础操作、高级功能、快捷键以及常见问题处理。 一、安装 nano 大多数 Linux 发行版已预装 nano。如果没有安装,可以通过以下命令安装: Debian/Ubuntu 系:sudo apt update…...
Windows设置之网络路由
在 Windows 系统中,可以通过配置路由表来实现特定 IP 地址通过无线网卡(Wi-Fi)连接,而其他流量通过有线以太网连接。 比如,让101.132.45.129 走无线网卡,其他的走有线以太网的具体步骤如下: 通…...
多标签多分类 用什么函数激活
在多标签多分类任务中,激活函数的选择需要根据任务特性和输出层的设计来决定。以下是常见的激活函数及其适用场景: 一、多标签分类任务的特点 每个样本可以属于多个类别(标签之间非互斥,例如一篇文章可能同时属于 “科技” 和 “…...
三十五、面向对象底层逻辑-Spring MVC中AbstractXlsxStreamingView的设计
在Web应用开发中,大数据量的Excel导出功能是常见需求。传统Apache POI的XSSF实现方式在处理超大数据集时,会因全量加载到内存导致OOM(内存溢出)问题。Spring MVC提供的AbstractXlsxStreamingView通过流式处理机制,有效…...

【ArcGIS技巧】—村庄规划规划用地规划状态字段生成工具
"国土空间规划后续也是走向数据治理,数据建库已经是涉及到城市规划、建筑、市政、农业、地理信息、测绘等等方方面面。不得不说以后数据库建设跟维护,是很多专业的必修课。小编就湖南省的村庄规划建库过程中规划用地用海中规划状态字段写了个小工具…...

嵌入式学习--江协stm32day5
USART 1. 引脚与接口层 异步引脚: TX:发送数据输出;RX:接收数据输入;SW_RX:单线半双工模式的接收引脚(替代 RX)。 同步引脚:SCLK:同步模式下的时钟输出&…...