【超分辨】SRGAN详解及其pytorch代码解释

article/2025/11/8 22:56:36

SRGAN详解

  • 介绍
  • 网络结构
  • 损失函数
  • 数据处理
  • 网络训练

介绍

「2023年更新」本代码是学习参考代码,一般不能直接运行,想找现成能运行的建议看看其他的。
SRGAN是一个超分辨网络,利用生成对抗网络的方法实现图片的超分辨。
关于生成对抗网络,后面我会专门发一篇博客讲解。
论文地址:http://arxiv.org/abs/1609.04802
本文代码传送门:https://github.com/zzbdr/DL/tree/main/Super-resolution/SRGAN

以下是原论文的效果展示:
网络效果
顺带解释一下超分辨是什么:通俗来讲就是将尺寸小的图片变成尺寸大的图片,但是这和普通的图片缩放不一样,超分辨放大的图片保留的细节更多

网络结构

下面介绍SRGAN的网络结构,和其他对抗生成网络一样,SRGAN有生成网络和辨别网络,我们先看生成网络:
来自原论文
这是摘自原论文的网络结构图, 其中k代表卷积核的尺寸,n代表卷积输出的通道数,s代表步长,不同指向的箭头表示残差结构,Elementwise Sun就是残差中相加的操作。
相同颜色表示相同的操作,低分辨率图片(LR)输入网络后输出高分辨率图片(HR)。

下面来看辨别网络:
来自原论文
辨别网络没有残差结构,其中的符号表示的意思和上面解释的一样,辨别网络输入一张图片,判断这张图片是原始高分辨率的图片还是生成网络输出的高分辨率图片。

下面是pytorch代码:
简单实现残差结构

class Block(nn.Module):def __init__(self, input_channel=64, output_channel=64, kernel_size=3, stride=1, padding=1):super().__init__()self.layer = nn.Sequential(nn.Conv2d(input_channel, output_channel, kernel_size, stride, bias=False, padding=1),nn.BatchNorm2d(output_channel),nn.PReLU(),nn.Conv2d(output_channel, output_channel, kernel_size, stride, bias=False, padding=1),nn.BatchNorm2d(output_channel))def forward(self, x0):x1 = self.layer(x0)return x0 + x1

生成网络

class Generator(nn.Module):def __init__(self, scale=2):"""放大倍数是scale的平方倍"""super().__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 64, 9, stride=1, padding=4),nn.PReLU())self.residual_block = nn.Sequential(Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),Block(),)self.conv2 = nn.Sequential(nn.Conv2d(64, 64, 3, stride=1, padding=1),nn.BatchNorm2d(64),)self.conv3 = nn.Sequential(nn.Conv2d(64, 256, 3, stride=1, padding=1),nn.PixelShuffle(scale),nn.PReLU(),nn.Conv2d(64, 256, 3, stride=1, padding=1),nn.PixelShuffle(scale),nn.PReLU(),)self.conv4 = nn.Conv2d(64, 3, 9, stride=1, padding=4)def forward(self, x):x0 = self.conv1(x)x = self.residual_block(x0)x = self.conv2(x)x = self.conv3(x + x0)x = self.conv4(x)return x

辨别网络

class DownSalmpe(nn.Module):def __init__(self, input_channel, output_channel,  stride, kernel_size=3, padding=1):super().__init__()self.layer = nn.Sequential(nn.Conv2d(input_channel, output_channel, kernel_size, stride, padding),nn.BatchNorm2d(output_channel),nn.LeakyReLU(inplace=True))def forward(self, x):x = self.layer(x)return xclass Discriminator(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 64, 3, stride=1, padding=1),nn.LeakyReLU(inplace=True),)self.down = nn.Sequential(DownSalmpe(64, 64, stride=2, padding=1),DownSalmpe(64, 128, stride=1, padding=1),DownSalmpe(128, 128, stride=2, padding=1),DownSalmpe(128, 256, stride=1, padding=1),DownSalmpe(256, 256, stride=2, padding=1),DownSalmpe(256, 512, stride=1, padding=1),DownSalmpe(512, 512, stride=2, padding=1),)self.dense = nn.Sequential(nn.AdaptiveAvgPool2d(1),nn.Conv2d(512, 1024, 1),nn.LeakyReLU(inplace=True),nn.Conv2d(1024, 1, 1),nn.Sigmoid())def forward(self, x):x = self.conv1(x)x = self.down(x)x = self.dense(x)return x

运行测试

if __name__ == '__main__':g = Generator()a = torch.rand([1, 3, 64, 64])print(g(a).shape)d = Discriminator()b = torch.rand([2, 3, 512, 512])print(d(b).shape)
torch.Size([1, 3, 256, 256])
torch.Size([2, 1, 1, 1])

损失函数

SRGAN生成的网络损失函数为感知损失,由两部分组成content loss,和adversarial loss
来自原论文
content loss是生成的HR和真实的HR通过VGG网络前16层得到的特征之间的MSE损失,可以表示为:
content loss
adversarial loss:
来自原论文
其中的表示辨别器判断生成图片为真实的高分辨率图片的概率。

正则项:
来自原文
顺便一提,SRGAN目前提交了五个版本,自第三版开始,作者在论文中删除了正则项。

下面上代码:
content loss

import torch
import torch.nn as nn
import torchvision.models as modelsclass VGG(nn.Module):def __init__(self, device):super(VGG, self).__init__()vgg = models.vgg19(True)for pa in vgg.parameters():pa.requires_grad = Falseself.vgg = vgg.features[:16]self.vgg = self.vgg.to(device)def forward(self, x):out = self.vgg(x)return outclass ContentLoss(nn.Module):def __init__(self, device):super().__init__()self.mse = nn.MSELoss()self.vgg19 = VGG(device)def forward(self, fake, real):feature_fake = self.vgg19(fake)feature_real = self.vgg19(real)loss = self.mse(feature_fake, feature_real)return loss

adversial loss

class AdversarialLoss(nn.Module):def __init__(self):super().__init__()def forward(self, x):loss = torch.sum(-torch.log(x))return loss

上面两个加起来

class PerceptualLoss(nn.Module):def __init__(self, device):super().__init__()self.vgg_loss = ContentLoss(device)self.adversarial = AdversarialLoss()def forward(self, fake, real, x):vgg_loss = self.vgg_loss(fake, real)adversarial_loss = self.adversarial(x)return vgg_loss + 1e-3*adversarial_loss

正则项

class RegularizationLoss(nn.Module):def __init__(self):super().__init__()def forward(self, x):a = torch.square(x[:, :, :x.shape[2]-1, :x.shape[3]-1] - x[:, :, 1:x.shape[2], :x.shape[3]-1])b = torch.square(x[:, :, :x.shape[2]-1, :x.shape[3]-1] - x[:, :, :x.shape[2]-1, 1:x.shape[3]])loss = torch.sum(torch.pow(a+b, 1.25))return loss

数据处理

将图片通过缩放操作放小作为低分辨率图片,原始图像作为真实的高分辨率图片,数据处理文件如下:

import os
from PIL import Image
from torchvision import transforms as tfsdef get_crop_size(crop_size, upscale=2):return crop_size - (crop_size % upscale)def input_transform(img, idx, boxes, crop_size, upscale_factor=2):x1, y1, w, h = list(map(int, boxes[idx].strip().split()[1:]))img = img.crop([x1, y1, x1+w, y1+h])return tfs.Compose([tfs.CenterCrop(crop_size),tfs.Resize(crop_size // upscale_factor, interpolation=Image.BICUBIC)])(img)def target_transform(img, idx, boxes, crop_size):x1, y1, w, h = list(map(int, boxes[idx].strip().split()[1:]))img = img.crop([x1, y1, x1 + w, y1 + h])return tfs.Compose([tfs.CenterCrop(crop_size)])(img)def generate_data(row_path, save_path, file_path, upscale_factor=4, divide=0.95):all_data = os.listdir(row_path)data_length = 30000train_stop = int(data_length * divide)crop_size = get_crop_size(128, upscale_factor)f = open(file_path)boxes = f.readlines()[2:]if not os.path.exists(os.path.join(save_path, "train")):os.makedirs(os.path.join(save_path, "train"))f_train = open(os.path.join(save_path, "train.txt"), "w")if not os.path.exists(os.path.join(save_path, "val")):os.makedirs(os.path.join(save_path, "val"))f_val = open(os.path.join(save_path, "val.txt"), "w")for t in range(0, train_stop):img = Image.open(os.path.join(row_path, all_data[t].strip()))label = img.copy()img = input_transform(img, t, boxes, crop_size, upscale_factor)label = target_transform(label, t, boxes, crop_size)if not os.path.exists(os.path.join(save_path, "train", "img")):os.makedirs(os.path.join(save_path, "train", "img"))img.save(os.path.join(save_path, "train", "img", "{}.jpg".format(t)))if not os.path.exists(os.path.join(save_path, "train", "label")):os.makedirs(os.path.join(save_path, "train", "label"))label.save(os.path.join(save_path, "train", "label", "{}.jpg".format(t)))f_train.write(f"{t}.jpg\n")f_train.flush()for v in range(train_stop, data_length):img = Image.open(os.path.join(row_path, all_data[v].strip()))label = img.copy()img = input_transform(img, v, boxes, crop_size, upscale_factor)label = target_transform(label, v, boxes, crop_size)if not os.path.exists(os.path.join(save_path, "val", "img")):os.makedirs(os.path.join(save_path, "val", "img"))img.save(os.path.join(save_path, "val", "img", "{}.jpg".format(v - train_stop)))if not os.path.exists(os.path.join(save_path, "val", "label")):os.makedirs(os.path.join(save_path, "val", "label"))label.save(os.path.join(save_path, "val", "label", "{}.jpg".format(v - train_stop)))f_val.write(f"{v - train_stop}.jpg\n")f_val.flush()

处理后的目录展示:

下面是自定义的数据集:

import os
from PIL import Image
from torch.utils.data import Dataset
import torchvision.transforms as tfsclass SRGANDataset(Dataset):def __init__(self, data_path, ty="train"):self.dataset = []self.path = data_pathself.ty = tyf = open(os.path.join(data_path, "{}.txt".format(ty)))self.dataset.extend(f.readlines())f.close()self.tfs = tfs.Compose([tfs.ToTensor(),tfs.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])def __len__(self):return len(self.dataset)def __getitem__(self, index):img_name = self.dataset[index].strip()img = Image.open(os.path.join(self.path, self.ty, "img", img_name))label = Image.open(os.path.join(self.path, self.ty, "label", img_name))img = self.tfs(img)label = self.tfs(label)return img, label

网络训练

网络训练部分代码比较长,我这里不过多解释,我把代码贴在这里,有问题联系我

import torch
import dataset
import os
import argparse
from torch.utils.data import DataLoader
import models
import time
import matplotlib.pyplot as plt
import lossclass Trainer:record = {"train_loss_d": [], "train_loss_g": [], "train_psnr": [], "val_loss": [], "val_psnr": []}x_epoch = []def __init__(self, args):self.args = argsself.device = self.args.deviceself.gnet = models.Generator()self.dnet = models.Discriminator()batch = self.args.batchself.train_loader = DataLoader(dataset.SRGANDataset(self.args.data_path, "train"),batch_size=batch, shuffle=True, drop_last=True)self.val_loader = DataLoader(dataset.SRGANDataset(self.args.data_path, "val"),batch_size=batch, shuffle=False, drop_last=True)self.criterion_g = loss.PerceptualLoss(self.device)self.regularization = loss.RegularizationLoss()self.criterion_d = torch.nn.BCELoss()self.epoch = 0self.lr = 1e-3self.best_psnr = 0.if self.args.resume:if not os.path.exists(self.args.save_path):print("No params, start training...")else:param_dict = torch.load(self.args.save_path)self.epoch = param_dict["epoch"]self.lr = param_dict["lr"]self.dnet.load_state_dict(param_dict["dnet_dict"])self.gnet.load_state_dict(param_dict["gnet_dict"])self.best_psnr = param_dict["best_psnr"]print("Loaded params from {}\n[Epoch]: {}   [lr]: {}    [best_psnr]: {}".format(self.args.save_path,self.epoch, self.lr,self.best_psnr))self.dnet.to(self.device)self.gnet.to(self.device)self.optimizer_d = torch.optim.Adam(self.dnet.parameters(), lr=self.lr)self.optimizer_g = torch.optim.Adam(self.gnet.parameters(), lr=self.lr*0.1)self.real_label = torch.ones([batch, 1, 1, 1]).to(self.device)self.fake_label = torch.zeros([batch, 1, 1, 1]).to(self.device)@staticmethoddef calculate_psnr(img1, img2):return 10. * torch.log10(1. / torch.mean((img1 - img2) ** 2))def train(self, epoch):self.dnet.train()self.gnet.train()train_loss_d = 0.train_loss_g = 0.train_loss_all_d = 0.train_loss_all_g = 0.psnr = 0.total = 0start = time.time()print("Start epoch: {}".format(epoch))for i, (img, label) in enumerate(self.train_loader):img = img.to(self.device)label = label.to(self.device)fake_img = self.gnet(img)loss_g = self.criterion_g(fake_img, label, self.dnet(fake_img)) + 2e-8*self.regularization(fake_img)self.optimizer_g.zero_grad()loss_g.backward()self.optimizer_g.step()if i % 2 == 0:real_out = self.dnet(label)fake_out = self.dnet(fake_img.detach())loss_d = self.criterion_d(real_out, self.real_label) + self.criterion_d(fake_out, self.fake_label)self.optimizer_d.zero_grad()loss_d.backward()self.optimizer_d.step()train_loss_d += loss_d.item()train_loss_all_d += loss_d.item()train_loss_g += loss_g.item()train_loss_all_g += loss_g.item()psnr += self.calculate_psnr(fake_img, label).item()total += 1if (i+1) % self.args.interval == 0:end = time.time()print("[Epoch]: {}[Progress: {:.1f}%]time:{:.2f} dnet_loss:{:.5f} gnet_loss:{:.5f} psnr:{:.4f}".format(epoch, (i+1)*100/len(self.train_loader), end-start,train_loss_d/self.args.interval,train_loss_g/self.args.interval, psnr/total))train_loss_d = 0.train_loss_g = 0.print("Save params to {}".format(self.args.save_path1))param_dict = {"epoch": epoch,"lr": self.lr,"best_psnr": self.best_psnr,"dnet_dict": self.dnet.state_dict(),"gnet_dict": self.gnet.state_dict()}torch.save(param_dict, self.args.save_path)return train_loss_all_d/len(self.train_loader), train_loss_all_g/len(self.train_loader), psnr/totaldef val(self, epoch):self.gnet.eval()self.dnet.eval()print("Test start...")val_loss = 0.psnr = 0.total = 0start = time.time()with torch.no_grad():for i, (img, label) in enumerate(self.train_loader):img = img.to(self.device)label = label.to(self.device)fake_img = self.gnet(img).clamp(0.0, 1.0)loss = self.criterion_g(fake_img, label, self.dnet(fake_img))val_loss += loss.item()psnr += self.calculate_psnr(fake_img, label).item()total += 1mpsnr = psnr / totalend = time.time()print("Test finished!")print("[Epoch]: {} time:{:.2f} loss:{:.5f} psnr:{:.4f}".format(epoch, end - start, val_loss / len(self.val_loader), mpsnr))if mpsnr > self.best_psnr:self.best_psnr = mpsnrprint("Save params to {}".format(self.args.save_path))param_dict = {"epoch": epoch,"lr": self.lr,"best_psnr": self.best_psnr,"gnet_dict": self.gnet.state_dict(),"dnet_dict": self.dnet.state_dict()}torch.save(param_dict, self.args.save_path1)return val_loss/len(self.val_loader), mpsnrdef draw_curve(self, fig, epoch, train_loss_d, train_loss_g, train_psnr, val_loss, val_psnr):ax0 = fig.add_subplot(121, title="loss")ax1 = fig.add_subplot(122, title="psnr")self.record["train_loss_d"].append(train_loss_d)self.record["train_loss_g"].append(train_loss_g)self.record["train_psnr"].append(train_psnr)self.record["val_loss"].append(val_loss)self.record["val_psnr"].append(val_psnr)self.x_epoch.append(epoch)ax0.plot(self.x_epoch, self.record["train_loss_d"], "bo-", label="train_d")ax0.plot(self.x_epoch, self.record["train_loss_g"], "go-", label="train_g")ax0.plot(self.x_epoch, self.record["val_loss"], "ro-", label="val_g")ax1.plot(self.x_epoch, self.record["train_psnr"], "bo-", label="train")ax1.plot(self.x_epoch, self.record["val_psnr"], "ro-", label="val")if epoch == 0:ax0.legend()ax1.legend()fig.savefig(r"./train_fig/train_{}.jpg".format(epoch))def lr_update(self):for param_group in self.optimizer_d.param_groups:param_group['lr'] = self.lr * 0.1self.lr = self.optimizer_d.param_groups[0]["lr"]for param_group in self.optimizer_g.param_groups:param_group['lr'] = self.lrprint("===============================================")print("Learning rate has adjusted to {}".format(self.lr))def main(args):t = Trainer(args)fig = plt.figure()for epoch in range(t.epoch, t.epoch + args.num_epochs):train_loss_d, train_loss_g, train_psnr = t.train(epoch)val_loss, val_psnr = t.val(epoch)t.draw_curve(fig, epoch, train_loss_d, train_loss_g, train_psnr, val_loss, val_psnr)# if (epoch + 1) % 10 == 0:#     t.lr_update()if __name__ == '__main__':parser = argparse.ArgumentParser(description="Training SRGAN with celebA")parser.add_argument("--device", default="cuda", type=str)parser.add_argument("--data_path", default=r"T:\srgan", type=str)parser.add_argument("--resume", default=False, type=bool)parser.add_argument("--num_epochs", default=100, type=int)parser.add_argument("--save_path", default=r"./weight01.pt", type=str)parser.add_argument("--save_path1", default=r"./weight00.pt", type=str)parser.add_argument("--interval", default=20, type=int)parser.add_argument("--batch", default=8, type=int)args1 = parser.parse_args()main(args1)

本人水平有限,文中发现错误敬请指正。(看到这了,点个关注点个赞吧!)


http://chatgpt.dhexx.cn/article/58bpCOLl.shtml

相关文章

超分之一文读懂SRGAN

这篇文章介绍SRResNet网络,以及将SRResNet作为生成网络的GAN模型用于超分,即SRGAN模型。这是首篇在人类感知视觉上进行超分的文章,而以往的文章以PSNR为导向,但那些方式并不能让人眼觉得感知到了高分辨率——Photo-Realistic。 参…

图像超分经典网络 SRGAN 解析 ~ 如何把 GAN 运用在其他视觉任务上

生成对抗网络(GAN)是一类非常有趣的神经网络。借助GAN,计算机能够生成逼真的图片。近年来有许多“AI绘画”的新闻,这些应用大多是通过GAN实现的。实际上,GAN不仅能做图像生成,还能辅助其他输入信息不足的视觉任务。比如SRGAN&…

Oracle常用函数汇总记录

Oracle常用函数汇总记录 一、SUBSTR 截取函数 用法:substr(字符串,截取开始位置,截取长度) //返回截取的字, 字符串的起始位置为1,截取时包含起始位置字符 1.SUBSTR( “Hello World”, 2 ) //返回结果为:ello World,从第二个字符开始截取至末位 2.SUBSTR( “Hello World”, -2…

oracle一些常用函数用法,Oracle常用函数及其用法

01、入门Oracle 本章目标: 掌握oracle安装、启动和关闭 基本管理以及常用工具 简单备份和恢复 熟练使用sql,掌握oracle常用对象 掌握数据库设计和优化基本方法 http://jingyan.baidu.com/article/5d6edee228308899eadeec3f.html oracle数据库&#xff1a…

oracle常用函数详解(详细)

Oracle SQL 提供了用于执行特定操作的专用函数。这些函数大大增强了 SQL 语言的功能。函数可以接受零个或者多个输入参数,并返回一个输出结果。 Oracle 数据库中主要使用两种类型的函数: 1. 单行函数:对每一个函数应用在表的记录中时&#…

event对象的offsetX、clientX、pageX、screenX及 window.innerWidth、outerWidth使用详解

目录 offset client screen page window.innerWidht offset offsetX、offsetY为当前鼠标点击位置距离当前元素参考原点(左上角)的距离,而不同浏览器参考原点的位置不尽相同,FF及Chrome中参考原点为内容区域左上角,不…

什么?你还不知道offsetX、offsetY和clientX、clientY和pageX、pageY和screenX、screenY的区别,进来唠唠

offsetX、offsetY: 鼠标相对于事件源元素的X,Y坐标。比如说,给黄色的盒子定义一个点击事件,则这个offset的坐标原点就在这个黄色盒子的左上角,offsetX、offsetY就是相对于这个盒子的x、y坐标 clientX、clientY: 鼠标相对于浏览器窗口可视区域…

event对象的offsetX, clientX, pageX, screenX

现在需要对event对象的几大与坐标有关的属性做一个总结,以便于认识 offsetX|offsetY offsetX/Y获取到是触发点相对被触发dom的左上角距离(包括padding在内,不包括border),不过左上角基准点在不同浏览器中有区别,以内容区左上角为基…

vue拖拽指令之offsetX、clientX、pageX、screenX

自己一直很想做个拖拽生成静态页面的东西,说简单也简单,这个东西按道理用jsx语法是最好的,用render方法渲染生成的json。只是自己对这块还是没信心。今天写个vue的拖拽指令,顺便理一下offsetX、pageX、clientX、screenX这几个属性…

鼠标事件offsetX会传递给子元素

鼠标事件offsetX会传递给子元素 本来是想要通过e.offset来创建XY轴二维坐标系的,当事实上很难办到,所以我改用了通过div.offsetWidth(元素宽度包括border)、div.offsetLeft(元素距整个网页左边缘的长度)、e…

类似淘宝商品放大镜功能,以及offsetX、offsetY造成的鼠标移动时阴影部分会一闪一闪的不断回到左上角问题

效果:效果是当鼠标移入的时候小图出现一小块是以鼠标为中心的遮罩层,鼠标在小图移动时大图放大显示遮罩层所在的局部,且遮罩层不能超出小图位置。如下图所示: html部分 从代码可知大图和小图是分别两个img标签图片其实也是一样…

event.offsetX event.pageX event.clientX 和 obj.offsetLeft学习笔记

一、问题描述 工作中,需要在航拍图中 添加摄像头在航拍图中的位置,因此,需要开发一个功能:鼠标点击航拍图(背景),显示鼠标点击位置在页面中的位置(pageX和pageY),然后将…

html什么代码确定x坐标,HTML MouseEvent offsetX用法及代码示例

MouseEvent offsetX属性是一个只读属性,用于返回鼠标指针相对于目标元素的x坐标。 用法: event.offsetX 返回值:它返回一个数字,该数字表示鼠标指针的水平坐标,以像素为单位。 以下示例程序旨在说明MouseEvent offsetX属性&#x…

写轮播图时,关于offsetX和pageX的选择以及一些坑

一、不要使用offsetX 前几天在公司实习,由于使用了swiper来做c端的滑动效果,在自定义的过程中,出现了一系列的坑,我看了源码,看了文档,也还是没有很好的理解,这个swiper的标准操作流程&#xff…

js e.offsetX 和 e.offsetY

本人自己在写代码的过程中&#xff0c;一直对e.offsetX 与 e.offsetY 分不清&#xff0c;今天好好探究一下e.offsetX 与 e.offsetY e.offsetX 与 e.offsetY 下相对与事件源的距离&#xff0c;也就是距离e.target的距离&#xff0c; 大家看下面例子,给出几张截图。 <!DOCTY…

html5 offsetx,原生HTML5关于Div对象的.clientLeft、.offsetLeft、.clientX、.offsetX区分

本篇主要介绍clientLeft、offsetLeft、clientX、offsetX这四种元素属性的区别&#xff0c;首先我们要理解清楚它们的概念&#xff1a; clientLeft&#xff1a;该元素对象的左边框宽度。 clientWidth&#xff1a;该元素对象的左内边框至右内边框的距离。 offsetLeft&#xff1a;…

html5 offsetx,event对象中offsetX,clientX,pageX,screenX的区别

1、offsetX offset意为偏移量,是事件对象距左上角为参考原点的距离。以元素盒子模型的内容区域的左上角为参考点。不包括border。 2、clientX 事件对象相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条 3、pageX 事件对象相对于整个文档的坐标以像素…

彻底搞懂 offsetX、scrollX、clientX 的区别

无论在 iOS 还是前端开发中&#xff0c;关于如何定位一个元素是必须要掌握的知识&#xff0c;而在前端中&#xff0c;元素定位比较难理解&#xff0c;我们今天一起学习下。 在 DOM 设计中&#xff0c;主要通过这些 API 来确定某个元素的具体位置。 offsetTop, offsetLeft, offs…

Java数组赋值时内存中的变化

java中的方法区存放的是编译后的文件 xxx.class文件当创建数组对象时&#xff0c;数组对象会存放在堆里面&#xff0c;数据也存在于堆。当给数组赋值时&#xff0c;方法会进栈&#xff0c;然后拿着数组的地址去堆里面寻找数据并赋值

Java数组:用fill()方法给数组赋值

Arrays类可以在指定位置进行数值填充&#xff0c;但是只能使用同一个数值进行填充&#xff1a; Arrays.fill(Object[] a,Object value);a表示数组&#xff0c;value表示填充的值 例1 public static void main(String[] args) {int[] a new int[6];//声明创建一个数组System.o…