秃姐学AI系列之:语义分割 + 数据集 | 转置卷积 + 代码

语义分割

语义分割将图片中的每个像素分类到对应的类别

通常来说现在的会议软件的背景虚化这个功能用的就是语义分割技术

无人车进行路面识别也是语义分割技术

 语义分割 vs 实例分割

  • 语义分割将图像划分为若干组成区域,这类问题的方法通常利用图像中像素之间的相关性。它在训练时不需要有关图像像素的标签信息,在预测时也无法保证分割出的区域具有我们希望得到的语义。以 上图的猫和狗的图像 作为输入,图像分割可能会将狗分为两个区域:一个覆盖以黑色为主的嘴和眼睛,另一个覆盖以黄色为主的其余部分身体。

  • 实例分割也叫同时检测并分割(simultaneous detection and segmentation),它研究如何识别图像中各个目标实例的像素级区域。与语义分割不同,实例分割不仅需要区分语义,还要区分不同的目标实例。例如,如果图像中有两条狗,则实例分割需要区分像素属于的两条狗中的哪一条。

实例分割可以理解成目标检测的进化版本:

        目标检测是把你每个目标检测出来,实例分割把你物体的边缘还检测出来

语义分割数据集

在语义分割里面最重要的数据集之一是Pascal VOC2012

Pascal是一个组织,VOC是一个竞赛,这个数据集是组织在12年做的一个竞赛数据集

为什么着重选择12年,因为可以认为后面的竞赛都是在12年的基础上做了修改

下载下来2GB的样子

下载&解压 

%matplotlib inline
import os
import torch
import torchvision
from d2l import torch as d2l# 从网址上把原始数据集下载下来,解压放在文件夹里面
d2l.DATA_HUB['voc2012'] = (d2l.DATA_URL + 'VOCtrainval_11-May-2012.tar','4e443f8a2eca6b1dac8a6c57641b67dd40621a49')voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')

将所有输入的图像和标签读入内存

一个很暴力的方法,通常很大的数据集不会这样做

进入数据集之后,我们可以看到数据集的不同组件。 ImageSets/Segmentation路径包含用于训练和测试样本的文本文件,而JPEGImagesSegmentationClass路径分别存储着每个示例的输入图像和标签。

此处的标签也采用图像格式,其尺寸和它所标注的输入图像的尺寸相同。 此外,标签中颜色相同的像素属于同一个语义类别。

语义分割不同的地方在于需要对每个像素有一个label。最好的存储方法就是存成一张图片,但是如果存成JPEG会对图片有一些边缘的模糊;所以最好的方法就是存成一张PNG的图片

下面将read_voc_images函数定义为将所有输入的图像和标签读入内存。

def read_voc_images(voc_dir, is_train=True):"""读取所有VOC图像并标注"""txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation','train.txt' if is_train else 'val.txt')mode = torchvision.io.image.ImageReadMode.RGBwith open(txt_fname, 'r') as f:images = f.read().split()features, labels = [], []for i, fname in enumerate(images):# 读取原始文件:JPEGfeatures.append(torchvision.io.read_image(os.path.join(voc_dir, 'JPEGImages', f'{fname}.jpg')))# 读取label:PNGlabels.append(torchvision.io.read_image(os.path.join(voc_dir, 'SegmentationClass' ,f'{fname}.png'), mode))return features, labelstrain_features, train_labels = read_voc_images(voc_dir, True)

可能会有朋友觉得把标签存成一张图很难接受。为了便于理解,我们可视化一下输入图像及其标签

在标签图像中,白色和黑色分别表示边框和背景,而其他颜色则对应不同的类别

n = 5
imgs = train_features[0:n] + train_labels[0:n]
# 画的时候需要把channel permute到最后
imgs = [img.permute(1,2,0) for img in imgs]
d2l.show_images(imgs, 2, n);

第一张图片:飞机(红色像素),飞机的边框(白色的像素),背景(黑色像素)

第二张图片:显示器(蓝色),背景(黑色),边界线(白色)

第三张图片:凳子(红色),猫(紫色),边界线(白色)

......

列举RGB颜色值和类名

我们接下来就需要知道每个RGB的数值表示的类是什么

通过下面定义的两个常量,我们可以方便地查找标签中每个像素的类索引。

数据集的readme会告诉我们这个信息

VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],[0, 0, 128], [128, 0, 128], [0, 128, 128], [128, 128, 128],[64, 0, 0], [192, 0, 0], [64, 128, 0], [192, 128, 0],[64, 0, 128], [192, 0, 128], [64, 128, 128], [192, 128, 128],[0, 64, 0], [128, 64, 0], [0, 192, 0], [128, 192, 0],[0, 64, 128]]VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat','bottle', 'bus', 'car', 'cat', 'chair', 'cow','diningtable', 'dog', 'horse', 'motorbike', 'person','potted plant', 'sheep', 'sofa', 'train', 'tv/monitor']

查询标签中每个像素的类索引

做两个辅助函数来帮助我们从RGB的值换算成类别标号,以及把标号换算会RGB值

下面定义了voc_colormap2label函数来构建从上述RGB颜色值到类别索引的映射,而voc_label_indices函数将RGB值映射到在Pascal VOC2012数据集中的类别索引。

没那么简单,如果用简单的python来做,会发现性能很差。因为图片下来几万个像素,一个个去算是一件很慢的事情

def voc_colormap2label():"""构建从RGB到VOC类别索引的映射"""# 先开一个非常大的tensorcolormap2label = torch.zeros(256 ** 3, dtype=torch.long)for i, colormap in enumerate(VOC_COLORMAP):# 乘258可以理解成左移8位(即做了一个256进制,因为像素最多是从0-255)# 通过这样计算,把label换算成一个整型(换算成10进制),再把tensor中刚刚算出来的index对应的数值换成icolormap2label[(colormap[0] * 256 + colormap[1]) * 256 + colormap[2]] = i# 最终返回一个类似于字典一样的东西return colormap2labeldef voc_label_indices(colormap, colormap2label):"""将VOC标签中的RGB值映射到它们的类别索引"""colormap = colormap.permute(1, 2, 0).numpy().astype('int32')idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256+ colormap[:, :, 2])return colormap2label[idx]

预处理数据

在图片增广技术介绍中,我们通过再缩放图像使其符合模型的输入形状。在语义分割中,这样做需要将预测的像素类别重新映射回原始尺寸的输入图像。 这样的映射可能不够精确,尤其在不同语义的分割区域。 为了避免这个问题,我们将图像裁剪为固定尺寸,而不是再缩放。 具体来说,我们使用图像增广中的随机裁剪,裁剪输入图像和标签的相同区域。

def voc_rand_crop(feature, label, height, width):"""随机裁剪特征和标签图像"""# get_params():可以返回裁剪的边框rect = torchvision.transforms.RandomCrop.get_params(feature, (height, width))# 调用真正的crop()来裁剪(*rect:把框的四个坐标展开,用于裁剪)feature = torchvision.transforms.functional.crop(feature, *rect)# 同样的道理对标号也处理一下label = torchvision.transforms.functional.crop(label, *rect)return feature, labelimgs = []
# n在上面的代码里赋值为5
# 即随机做了5次的RandomCrop
for _ in range(n):imgs += voc_rand_crop(train_features[0], train_labels[0], 200, 300)# 可视化结果
imgs = [img.permute(1, 2, 0) for img in imgs]
d2l.show_images(imgs[::2] + imgs[1::2], 2, n);

自定义语义分割数据集类

我们通过继承高级API提供的Dataset类,自定义了一个语义分割数据集类VOCSegDataset

通过实现__getitem__函数,我们可以任意访问数据集中索引为idx的输入图像及其每个像素的类别索引。

由于数据集中有些图像的尺寸可能小于随机裁剪所指定的输出尺寸,这些样本可以通过自定义的filter函数移除掉。

此外,我们还定义了normalize_image函数,从而对输入图像的RGB三个通道的值分别做标准化。

class VOCSegDataset(torch.utils.data.Dataset):"""一个用于加载VOC数据集的自定义数据集"""def __init__(self, is_train, crop_size, voc_dir):# 做一次RGB三个channal的均值方差normalize(均值方差是从ImageNet拿来的,因为后面想用ImageNet那个模型)self.transform = torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])# 存一下crop_sizeself.crop_size = crop_size# 读入数据、标签features, labels = read_voc_images(voc_dir, is_train=is_train)# 先filter一下# 然后normalizeself.features = [self.normalize_image(feature)for feature in self.filter(features)]self.labels = self.filter(labels)# 构造好字典self.colormap2label = voc_colormap2label()print('read ' + str(len(self.features)) + ' examples')# 把RGB规范化一下def normalize_image(self, img):return self.transform(img.float() / 255)# 筛掉一些图片原本尺寸比我的裁剪尺寸还要小的数据def filter(self, imgs):return [img for img in imgs if (img.shape[1] >= self.crop_size[0] andimg.shape[2] >= self.crop_size[1])]# __getitem__:每次返回第i个样本要干什么事情def __getitem__(self, idx):# 做一次RandomCrop()feature, label = voc_rand_crop(self.features[idx], self.labels[idx],*self.crop_size)# 把RGB换成标号return (feature, voc_label_indices(label, self.colormap2label))def __len__(self):return len(self.features)

读取数据集

我们通过自定义的VOCSegDataset类来分别创建训练集和测试集的实例。

假设我们指定随机裁剪的输出图像的形状为320×480, 下面我们可以查看训练集和测试集所保留的样本个数。

crop_size = (320, 480)
voc_train = VOCSegDataset(True, crop_size, voc_dir)
voc_test = VOCSegDataset(False, crop_size, voc_dir)# 输出
read 1114 examples
read 1078 examples

可以看出不是一个很大的数据集。

通常来说 语义分割 的数据集会比 图片分类&目标检测 的数据集小很多,因为标起来很贵!!!!

举例子:找人标一张图片分类,一分钱两分钱;标一个目标检测,一毛钱;标一个图片分割就得几块钱

所以现在语义分割的数据集,主要集中在无人车那块(无人车大家都不缺钱......标的数据相对来说多一些)

设批量大小为64,我们定义训练集的迭代器。 打印第一个小批量的形状会发现:与图像分类或目标检测不同,这里的标签是一个三维数组。

batch_size = 64
train_iter = torch.utils.data.DataLoader(voc_train, batch_size, shuffle=True,drop_last=True,num_workers=d2l.get_dataloader_workers())
for X, Y in train_iter:print(X.shape)print(Y.shape)break# 输出
torch.Size([64, 3, 320, 480])    #(batch_size, channel(RGB), 高, 宽)
torch.Size([64, 320, 480])    #这里已经换算成了标号的整型,所以没有了3这个维度

整合所有组件

最后,我们定义以下load_data_voc函数来下载并读取Pascal VOC2012语义分割数据集。

它返回训练集和测试集的数据迭代器。

def load_data_voc(batch_size, crop_size):"""加载VOC语义分割数据集"""voc_dir = d2l.download_extract('voc2012', os.path.join('VOCdevkit', 'VOC2012'))num_workers = d2l.get_dataloader_workers()train_iter = torch.utils.data.DataLoader(VOCSegDataset(True, crop_size, voc_dir), batch_size,shuffle=True, drop_last=True, num_workers=num_workers)test_iter = torch.utils.data.DataLoader(VOCSegDataset(False, crop_size, voc_dir), batch_size,drop_last=True, num_workers=num_workers)return train_iter, test_iter

小结

  • 语义分割通过将图像划分为属于不同语义类别的区域,来识别并理解图像中像素级别的内容。

  • 语义分割的一个重要的数据集叫做Pascal VOC2012。

  • 由于语义分割的输入图像和标签在像素上一一对应,输入图像会被随机裁剪为固定尺寸而不是缩放。

转置卷积

到目前为止,我们所见到的卷积神经网络层,例如 卷积层 和 Pooling层,通常会减少下采样输入图像的空间维度(高和宽)。

然而如果输入和输出图像的空间维度相同,在以像素级分类的语义分割中将会很方便。 例如,输出像素所处的通道维可以保有输入像素在同一位置上的分类结果。

为了实现这一点,尤其是在空间维度被卷积神经网络层缩小后,我们可以使用另一种类型的卷积神经网络层,它可以增加上采样中间层特征图的空间维度。即转置卷积(transposed convolution)用于逆转下采样导致的空间尺寸减小。

基本操作

  • 卷积不会增大输入的高宽,通常要么不变、要么减半
  • 转置卷积则可以用来增大输入高宽

当然你可以padding,但是如果你padding了很多0,输出也是0

其实无法很有效的增加你的输出

让我们暂时忽略通道,从基本的转置卷积开始,设步幅为1且没有填充。

假设我们有一个n_{h} × n_{w}的输入张量和一个k_{h} x k_{w}的卷积核。 以步幅为1滑动卷积核窗口,每行nn_{w}次,每列n_{h}次,共产生n_{w}n_{h}个中间结果。每个中间结果都是一个(n_{h}+k_{h}−1)×(n_{w}+k_{w}−1)的张量,初始化为0。

为了计算每个中间张量,输入张量中的每个元素都要乘以卷积核,从而使所得的k_{h}×k_{w}张量替换中间张量的一部分。 请注意,每个中间张量被替换部分的位置与输入张量中元素的位置相对应。最后,所有中间结果相加以获得最终结果。

有点感觉跟卷积反过来了的操作:

卷积:核大小的输入区域和核相乘再相加写进每个单个的格子里面;

转置卷积:每单个元素与核的每个元素相乘,写进核大小的格子区域;

(转置卷积的padding是在输出上面padding,等下我们在代码上面将padding更好理解一些)

为什么称之为“转置”

  • 对于卷积 Y = X ⭐ W

    • 可以对 W 构造一个 V,使得卷积等价于矩阵乘法 Y^{'} = V^{T}X^{'}

    • 这里Y^{'}X^{'}是Y,X对应的向量版本

  • 转置卷积则等价于 Y^{'} = V^{T}X^{'}

  • 如果卷积将输入从(h,w)变成了(h^{'}w^{'}

    • 同样超参数的转置卷积则从(h^{'}w^{'})变成(h,w)

代码实现

import torch
from torch import nn
from d2l import torch as d2l

实现基本的转置卷积运算

我们可以对输入矩阵X和卷积核矩阵K实现基本的转置卷积运算trans_conv

def trans_conv(X, K):h, w = K.shapeY = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1))for i in range(X.shape[0]):for j in range(X.shape[1]):Y[i: i + h, j: j + w] += X[i, j] * Kreturn Y# 验证上述实现
X = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
trans_conv(X, K)# 输出
tensor([[ 0.,  0.,  1.],[ 0.,  4.,  6.],[ 4., 12.,  9.]])

与通过卷积核“减少”输入元素的常规卷积相比,转置卷积通过卷积核“广播”输入元素,从而产生大于输入的输出。此实现是基本的二维转置卷积运算。

使用高级API获得相同的结果

当输入X和卷积核K都是四维张量时,我们可以使用高级API获得相同的结果。

X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
tconv.weight.data = K
tconv(X)# 输出
tensor([[[[ 0.,  0.,  1.],[ 0.,  4.,  6.],[ 4., 12.,  9.]]]], grad_fn=<ConvolutionBackward0>)

填充、步幅和多通道

填充

与常规卷积不同,在转置卷积中,填充被应用于的输出(常规卷积将填充应用于输入)。

例如,当将高和宽两侧的填充数指定为1时,转置卷积的输出中将删除第一和最后的行与列。

tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, padding=1, bias=False)
tconv.weight.data = K
tconv(X)# 输出
tensor([[[[4.]]]], grad_fn=<ConvolutionBackward0>)

步幅

在转置卷积中,步幅被指定为中间结果(输出),而不是输入。

将步幅从1更改为2会增加中间张量的高和权重

tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, stride=2, bias=False)
tconv.weight.data = K
tconv(X)# 输出
tensor([[[[0., 0., 0., 1.],[0., 0., 2., 3.],[0., 2., 0., 3.],[4., 6., 6., 9.]]]], grad_fn=<ConvolutionBackward0>)

多通道

对于多个输入和输出通道,转置卷积与常规卷积以相同方式运作

假设输入有c_{i}个通道,且转置卷积为每个输入通道分配了一个k_{h} x k_{w}的卷积核张量。 当指定多个输出通道时,每个输出通道将有一个c_{i}×k_{h} x k_{w}的卷积核。

同样,如果我们将 X 代入卷积层 f 来输出Y=f(X),并创建一个与 f 具有相同的超参数、但输出通道数量是 X 中通道数的转置卷积层 g,那么 g(Y) 的形状将与 X 相同。 下面的示例可以解释这一点。

X = torch.rand(size=(1, 10, 16, 16))
conv = nn.Conv2d(10, 20, kernel_size=5, padding=2, stride=3)
tconv = nn.ConvTranspose2d(20, 10, kernel_size=5, padding=2, stride=3)
tconv(conv(X)).shape == X.shape# 输出
True

注意我们这里说的是shape!是形状!!而不是完全还原卷积,和内在的值没关系的!!

它确实可以通过学习,来还原卷积,但是转置卷积不是用来干这个的!!

不属于上采样!!!

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

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

相关文章

1 -《本地部署开源大模型》如何选择合适的硬件配置

如何选择合适的硬件配置 为了在本地有效部署和使用开源大模型&#xff0c;深入理解硬件与软件的需求至关重要。在硬件需求方面&#xff0c;关键是配置一台或多台高性能的个人计算机系统或租用配备了先进GPU的在线服务器&#xff0c;确保有足够的内存和存储空间来处理大数据和复…

html+css+js实现Notification 通知

实现效果&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Notif…

【Linux】为什么环境变量具有全局性?共享?写时拷贝优化?

环境变量表具有全局性的原因&#xff1a; 环境变量表之所以具有全局性的特征&#xff0c;主要是因为它们是在进程上下文中维护的&#xff0c;并且在大多数操作系统中&#xff0c;当一个进程创建另一个进程&#xff08;即父进程创建子进程&#xff09;时&#xff0c;子进程会继承…

SAP 批量复制角色处理办法

SAP 批量复制角色处理办法 1. 定义2. 功能3. 应用场景4. 操作步骤5. 注意事项6.业务场景7.操作步骤(1)导出旧角色(2)导出文件数据修改(3)上传修改好的角色(4)生成角色参数文件(5) 调用函数批量创建角色(6)关于权限常用功能 SAP 批量复制角色&#xff08;Batch Role Copy&#x…

【系统规划与管理师】历年各章节分值汇总(论文)

【移动端浏览】☞【系统规划与管理师】历年各章节分值汇总&#xff08;论文&#xff09; 第4章 IT服务规划设计 第5章 IT服务部署实施 第6章 IT服务运营管理 第7章 IT服务持续改进 第8章 监督管理 第9章 IT服务营销 第10章 团队建设与管理

【二】企业级JavaScript开发之代码编辑器

代码编辑器 程序员接触时间最长的就是代码编辑器。 代码编辑器主要分两种&#xff1a;IDE&#xff08;集成开发环境&#xff09;和轻量编辑器。很多人喜欢这两种各选一个。 当然还有很多其他很好的编辑器&#xff0c;你可以选择一个你最喜欢的。 选择编辑器就像选择其他工具…

开源OpenStack

1.查询HCS基于OpenStack哪个版本开发 2.九大核心组件 OpenStack可以对接FC也可以对接KVM主机&#xff1b;&#xff08;OpenStack 对接华为FusionCompute&#xff0c;一个集群对应 openstack 一台计算主机&#xff09;-引申出nova compute 2.1nova nova两个核心组件nova contro…

马拉车算法(C/C++)

#1024程序员节 | 征文# 马拉车算法&#xff08;Manachers Algorithm&#xff09;是一种用于在字符串中查找最长回文子串的线性时间复杂度算法。该算法由Udi Manacher在1980年代提出&#xff0c;因此得名。它的核心思想是利用已知的回文信息来减少不必要的比较&#xff0c;从而提…

【Linux】-权限

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;深入代码世界&#xff0c;了解掌握 Linux 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 一、权限的概念 在Linux 中&…

软件测试与软件缺陷的基础知识

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

技术面没过,竟然是因为我没用过Pytest框架?

想象一下&#xff0c;你在一次技术面试中满怀信心&#xff0c;答完所有问题&#xff0c;结果却被告知没通过&#xff0c;原因竟然是——你没用过Pytest框架&#xff01;是的&#xff0c;在当今软件测试的世界里&#xff0c;Pytest已经成为了许多公司的“必备”技能。 那么问题…

数据库表的创建

运用的环境是pychram python3.11.4 创建一个表需要用到以下语法 注释已经写清楚各种语法的含义&#xff0c;记住缩进是你程序运行的关键&#xff0c;因为程序是看你的缩进来判断你的运行逻辑&#xff0c;像我这个就是缩进不合理导致的报错 那么今天分享就到这里&#xff0c;谢…

QScrollBar滑动条控件

人机验证简化版案例 //设置垂直滑动条的范围是0-100ui->verticalScrollBar->setRange(0,100);ui->horizontalScrollBar->setRange(0,100);//设置初始数值ui->verticalScrollBar->setValue(50);//void valueChanged(int value);connect(ui->verticalScroll…

uniapp修改input中placeholder样式

Uniapp官方提供了两种修改的属性方法&#xff0c;但经过测试&#xff0c;只有 placeholder-class 属性能够生效 <input placeholder"请输入手机验证码" placeholder-class"input-placeholder"/><!-- css --> <style lang"scss" s…

基于图像拼接开题报告

选题的背景与意义 在日常生活中&#xff0c;使用普通相机获取宽视野的场景图像时&#xff0c;必须通过调节相机的焦距才可以提取完整的场景。由于相机的分辨率有限&#xff0c;拍摄场景越大&#xff0c;得到的图像分辨率就越低&#xff0c;因此只能通过缩放相机镜头减小拍摄的…

VSCode按ctrl与鼠标左键无法实现跳转的解决办法

vscode编译环境老是出问题&#xff0c;下面介绍两种解决方法 需要提前配置好代码编译需要的库以及编译器位置等等。 ctrlshiftp,输入 >C/C配置&#xff08;JSON&#xff09; 打开生成的c_cpp_properties.json {"configurations": [{"name": "Li…

NSSCTF-WEB-easy_eval

目录 前言 正文 思路 序列化构造 后渗透 思路点1:Redis 思路2:蚁剑插件绕过disable_functinons 结尾 作者的其他文章 前言 说是easy,实际很difficult 正文 思路 <?php class A{public $code "";function __call($method,$args){//最后执行命令eval($th…

github加速 DevSidecar 1.8.8

DevSidecar 1.8.8 更多配置请参考&#xff1a;github开源

impdp+remap_schema导入后登录报ORA-01017: Invalid Username/password

环境说明&#xff1a;有个11.2.0.4的rac数据库&#xff0c;现需要把USR_OA克隆一份出来做测试&#xff0c;新用户名是TEST_OA&#xff0c;直接是expdp导出用户&#xff0c;再用impdpremap_schema生成TEST_OA&#xff0c; 业务人员使用PLSQL(版本12.0.1.1814) 登录TEST_OA时总…

GJS-WCP

不懂的就问&#xff0c;但我也是二把手......哭死 web GJS-ezssti 很常规的ssti模板注入&#xff0c;只过滤了"/","flag"。 过滤了/,flag 可以利用bash的特性绕过&#xff0c;如字符串截取&#xff0c;环境变量等等。payload1: {{url_for.__globals__[…