整合去雾网页工具

This commit is contained in:
admin
2026-06-10 17:42:11 +08:00
commit 6db15ebc3f
101 changed files with 10167 additions and 0 deletions

49
GCANet/All_in_One.sh Normal file
View File

@@ -0,0 +1,49 @@
#!/bin/bash
# 原图像位置
Dir_src_pics="./imgs/src"
Dir_result="./imgs/result"
Dir_ori_src_pics="/root/Dehaze/SRC_files/src"
mkdir -p $Dir_src_pics $Dir_result
PS3='All in one choice : '
applications=("Delete_src_pics" "Delete_generate_pics" "Copy_src_pics" "Run_program" "quit")
select fav in "${applications[@]}"; do
case $fav in
# 删除原始文件选项
"Delete_src_pics")
# 删除src文件
echo "Delete all src files in $Dir_src_pics"
rm $Dir_src_pics/*
;;
# 删除生成文件选项
"Delete_generate_pics")
# 删除result文件
echo "Delete all src files in $Dir_result"
rm $Dir_result/*
;;
# 复制待处理文件选项
"Copy_src_pics")
# 删除src文件
echo "Copy all src files in $Dir_ori_src_pics"
ln -s $Dir_ori_src_pics/* $Dir_src_pics
;;
# 运行程序
"Run_program")
source ~/miniconda/bin/activate Dehaze_GCANet
python test.py --task dehaze --gpu_id 0 --indir ./imgs/src --outdir ./imgs/result
;;
# 退出选项
"quit")
echo "User requested exit"
exit
;;
# 其他选项
*) echo "invalid option $REPLY";;
esac
done

102
GCANet/GCANet.py Normal file
View File

@@ -0,0 +1,102 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class ShareSepConv(nn.Module):
def __init__(self, kernel_size):
super(ShareSepConv, self).__init__()
assert kernel_size % 2 == 1, 'kernel size should be odd'
self.padding = (kernel_size - 1)//2
weight_tensor = torch.zeros(1, 1, kernel_size, kernel_size)
weight_tensor[0, 0, (kernel_size-1)//2, (kernel_size-1)//2] = 1
self.weight = nn.Parameter(weight_tensor)
self.kernel_size = kernel_size
def forward(self, x):
inc = x.size(1)
expand_weight = self.weight.expand(inc, 1, self.kernel_size, self.kernel_size).contiguous()
return F.conv2d(x, expand_weight,
None, 1, self.padding, 1, inc)
class SmoothDilatedResidualBlock(nn.Module):
def __init__(self, channel_num, dilation=1, group=1):
super(SmoothDilatedResidualBlock, self).__init__()
self.pre_conv1 = ShareSepConv(dilation*2-1)
self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
self.pre_conv2 = ShareSepConv(dilation*2-1)
self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
def forward(self, x):
y = F.relu(self.norm1(self.conv1(self.pre_conv1(x))))
y = self.norm2(self.conv2(self.pre_conv2(y)))
return F.relu(x+y)
class ResidualBlock(nn.Module):
def __init__(self, channel_num, dilation=1, group=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
def forward(self, x):
y = F.relu(self.norm1(self.conv1(x)))
y = self.norm2(self.conv2(y))
return F.relu(x+y)
class GCANet(nn.Module):
def __init__(self, in_c=4, out_c=3, only_residual=True):
super(GCANet, self).__init__()
self.conv1 = nn.Conv2d(in_c, 64, 3, 1, 1, bias=False)
self.norm1 = nn.InstanceNorm2d(64, affine=True)
self.conv2 = nn.Conv2d(64, 64, 3, 1, 1, bias=False)
self.norm2 = nn.InstanceNorm2d(64, affine=True)
self.conv3 = nn.Conv2d(64, 64, 3, 2, 1, bias=False)
self.norm3 = nn.InstanceNorm2d(64, affine=True)
self.res1 = SmoothDilatedResidualBlock(64, dilation=2)
self.res2 = SmoothDilatedResidualBlock(64, dilation=2)
self.res3 = SmoothDilatedResidualBlock(64, dilation=2)
self.res4 = SmoothDilatedResidualBlock(64, dilation=4)
self.res5 = SmoothDilatedResidualBlock(64, dilation=4)
self.res6 = SmoothDilatedResidualBlock(64, dilation=4)
self.res7 = ResidualBlock(64, dilation=1)
self.gate = nn.Conv2d(64 * 3, 3, 3, 1, 1, bias=True)
self.deconv3 = nn.ConvTranspose2d(64, 64, 4, 2, 1)
self.norm4 = nn.InstanceNorm2d(64, affine=True)
self.deconv2 = nn.Conv2d(64, 64, 3, 1, 1)
self.norm5 = nn.InstanceNorm2d(64, affine=True)
self.deconv1 = nn.Conv2d(64, out_c, 1)
self.only_residual = only_residual
def forward(self, x):
y = F.relu(self.norm1(self.conv1(x)))
y = F.relu(self.norm2(self.conv2(y)))
y1 = F.relu(self.norm3(self.conv3(y)))
y = self.res1(y1)
y = self.res2(y)
y = self.res3(y)
y2 = self.res4(y)
y = self.res5(y2)
y = self.res6(y)
y3 = self.res7(y)
gates = self.gate(torch.cat((y1, y2, y3), dim=1))
gated_y = y1 * gates[:, [0], :, :] + y2 * gates[:, [1], :, :] + y3 * gates[:, [2], :, :]
y = F.relu(self.norm4(self.deconv3(gated_y)))
y = F.relu(self.norm5(self.deconv2(y)))
if self.only_residual:
y = self.deconv1(y)
else:
y = F.relu(self.deconv1(y))
return y

View File

@@ -0,0 +1,102 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class ShareSepConv(nn.Module):
def __init__(self, kernel_size):
super(ShareSepConv, self).__init__()
assert kernel_size % 2 == 1, 'kernel size should be odd'
self.padding = (kernel_size - 1)//2
weight_tensor = torch.zeros(1, 1, kernel_size, kernel_size)
weight_tensor[0, 0, (kernel_size-1)//2, (kernel_size-1)//2] = 1
self.weight = nn.Parameter(weight_tensor)
self.kernel_size = kernel_size
def forward(self, x):
inc = x.size(1)
expand_weight = self.weight.expand(inc, 1, self.kernel_size, self.kernel_size).contiguous()
return F.conv2d(x, expand_weight,
None, 1, self.padding, 1, inc)
class SmoothDilatedResidualBlock(nn.Module):
def __init__(self, channel_num, dilation=1, group=1):
super(SmoothDilatedResidualBlock, self).__init__()
self.pre_conv1 = ShareSepConv(dilation*2-1)
self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
self.pre_conv2 = ShareSepConv(dilation*2-1)
self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
def forward(self, x):
y = F.relu(self.norm1(self.conv1(self.pre_conv1(x))))
y = self.norm2(self.conv2(self.pre_conv2(y)))
return F.relu(x+y)
class ResidualBlock(nn.Module):
def __init__(self, channel_num, dilation=1, group=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
def forward(self, x):
y = F.relu(self.norm1(self.conv1(x)))
y = self.norm2(self.conv2(y))
return F.relu(x+y)
class GCANet(nn.Module):
def __init__(self, in_c=4, out_c=3, only_residual=True):
super(GCANet, self).__init__()
self.conv1 = nn.Conv2d(in_c, 64, 3, 1, 1, bias=False)
self.norm1 = nn.InstanceNorm2d(64, affine=True)
self.conv2 = nn.Conv2d(64, 64, 3, 1, 1, bias=False)
self.norm2 = nn.InstanceNorm2d(64, affine=True)
self.conv3 = nn.Conv2d(64, 64, 3, 2, 1, bias=False)
self.norm3 = nn.InstanceNorm2d(64, affine=True)
self.res1 = SmoothDilatedResidualBlock(64, dilation=2)
self.res2 = SmoothDilatedResidualBlock(64, dilation=2)
self.res3 = SmoothDilatedResidualBlock(64, dilation=2)
self.res4 = SmoothDilatedResidualBlock(64, dilation=4)
self.res5 = SmoothDilatedResidualBlock(64, dilation=4)
self.res6 = SmoothDilatedResidualBlock(64, dilation=4)
self.res7 = ResidualBlock(64, dilation=1)
self.gate = nn.Conv2d(64 * 3, 3, 3, 1, 1, bias=True)
self.deconv3 = nn.ConvTranspose2d(64, 64, 4, 2, 1)
self.norm4 = nn.InstanceNorm2d(64, affine=True)
self.deconv2 = nn.Conv2d(64, 64, 3, 1, 1)
self.norm5 = nn.InstanceNorm2d(64, affine=True)
self.deconv1 = nn.Conv2d(64, out_c, 1)
self.only_residual = only_residual
def forward(self, x):
y = F.relu(self.norm1(self.conv1(x)))
y = F.relu(self.norm2(self.conv2(y)))
y1 = F.relu(self.norm3(self.conv3(y)))
y = self.res1(y1)
y = self.res2(y)
y = self.res3(y)
y2 = self.res4(y)
y = self.res5(y2)
y = self.res6(y)
y3 = self.res7(y)
gates = self.gate(torch.cat((y1, y2, y3), dim=1))
gated_y = y1 * gates[:, [0], :, :] + y2 * gates[:, [1], :, :] + y3 * gates[:, [2], :, :]
y = F.relu(self.norm4(self.deconv3(gated_y)))
y = F.relu(self.norm5(self.deconv2(y)))
if self.only_residual:
y = self.deconv1(y)
else:
y = F.relu(self.deconv1(y))
return y

View File

@@ -0,0 +1,104 @@
import os
import bisect
import threading
import torch
import numpy as np
import numpy.random as random
from PIL import Image
from torch.utils.data import Dataset
from folder_loader import FolderLoader
import torchvision.transforms as transforms
from utils import batch_edge_compute
def pil_loader(img_path):
return Image.open(img_path).convert("RGB")
class ImagePairPrefixFolder(Dataset):
def __init__(self, input_folder, gt_folder, max_img_size=0, size_unit=1, force_rgb=False):
super(ImagePairPrefixFolder, self).__init__()
self.gt_loader = FolderLoader(gt_folder)
# build the map from image name to index
self.gt_map = dict()
for idx, img_name in enumerate(self.gt_loader.img_names):
self.gt_map[os.path.splitext(img_name)[0].split('_')[0]] = idx
self.input_loader = FolderLoader(input_folder)
assert all([os.path.splitext(x)[0].split('_')[0] in self.gt_map for x in self.input_loader.img_names]), \
'cannot find corresponding gt names'
self.input_folder = input_folder
self.gt_folder = gt_folder
self.max_img_size = max_img_size
self.size_unit = size_unit
self.force_rgb = force_rgb
def __getitem__(self, index):
input_name, input_img = self.input_loader[index]
input_basename = os.path.splitext(input_name)[0].split('_')[0]
gt_idx = self.gt_map[input_basename]
gt_name, gt_img = self.gt_loader[gt_idx]
if self.force_rgb:
input_img = input_img.convert('RGB')
gt_img = gt_img.convert('RGB')
im_w, im_h = input_img.size
gt_w, gt_h = gt_img.size
assert im_w==gt_w and im_h==gt_h, 'input image and gt image size not match'
im_w, im_h = input_img.size
if 0 < self.max_img_size < max(im_w, im_h):
if im_w < im_h:
out_h = int(self.max_img_size) // self.size_unit * self.size_unit
out_w = int(im_w / im_h * out_h) // self.size_unit * self.size_unit
else:
out_w = int(self.max_img_size) // self.size_unit * self.size_unit
out_h = int(im_h / im_w * out_w) // self.size_unit * self.size_unit
else:
out_w = im_w // self.size_unit * self.size_unit
out_h = im_h // self.size_unit * self.size_unit
if im_w != out_w or im_h != out_h:
input_img = input_img.resize((out_w, out_h), Image.BILINEAR)
gt_img = gt_img.resize((out_w, out_h), Image.BILINEAR)
im_w, im_h = input_img.size
input_img = np.array(input_img).astype('float')
gt_img = np.array(gt_img).astype('float')
if len(input_img.shape) == 2:
input_img = input_img[:, :, np.newaxis]
if len(gt_img.shape) == 2:
gt_img = gt_img[:, :, np.newaxis]
return {'input_img': input_img, 'gt_img': gt_img, 'input_h': im_h, "input_w": im_w}
def get_input_info(self, index):
image_name = os.path.splitext(self.input_loader.img_names[index])[0]
return self.input_loader, image_name
def __len__(self):
return len(self.input_loader)
def var_custom_collate(batch):
min_h, min_w = 10000, 10000
for item in batch:
min_h = min(min_h, item['input_h'])
min_w = min(min_w, item['input_w'])
inc = 1 if len(batch[0]['input_img'].shape)==2 else batch[0]['input_img'].shape[2]
batch_input_images = torch.Tensor(len(batch), inc, min_h, min_w)
batch_gt_images = torch.Tensor(len(batch), inc, min_h, min_w)
for idx, item in enumerate(batch):
off_y = 0 if item['input_h']==min_h else random.randint(0, item['input_h'] - min_h)
off_x = 0 if item['input_w']==min_w else random.randint(0, item['input_w'] - min_w)
crop_input_img = item['input_img'][off_y:off_y + min_h, off_x:off_x + min_w, :]
crop_gt_img = item['gt_img'][off_y:off_y + min_h, off_x:off_x + min_w, :]
batch_input_images[idx] = torch.from_numpy(crop_input_img.transpose((2, 0, 1))) - 128
batch_gt_images[idx] = torch.from_numpy(crop_gt_img.transpose((2, 0, 1)))
batch_input_edges = batch_edge_compute(batch_input_images) - 128
return batch_input_images, batch_input_edges, batch_gt_images

View File

@@ -0,0 +1,19 @@
import io
import os
import utils
import struct
from PIL import Image
class FolderLoader(object):
def __init__(self, fold_path):
super(FolderLoader, self).__init__()
self.fold_path = fold_path
self.img_paths = utils.make_dataset(self.fold_path)
self.img_names = [os.path.basename(x) for x in self.img_paths]
def __getitem__(self, index):
img = Image.open(self.img_paths[index])#.convert('RGB')
return self.img_names[index], img
def __len__(self):
return len(self.img_names)

View File

@@ -0,0 +1,66 @@
import os
import argparse
import numpy as np
from PIL import Image
import torch
from torch.autograd import Variable
from utils import make_dataset, edge_compute
parser = argparse.ArgumentParser()
parser.add_argument('--network', default='GCANet')
parser.add_argument('--task', default='dehaze', help='dehaze | derain')
parser.add_argument('--gpu_id', type=int, default=0)
parser.add_argument('--indir', default='examples/')
parser.add_argument('--outdir', default='output')
opt = parser.parse_args()
assert opt.task in ['dehaze', 'derain']
## forget to regress the residue for deraining by mistake,
## which should be able to produce better results
opt.only_residual = opt.task == 'dehaze'
opt.model = 'models/wacv_gcanet_%s.pth' % opt.task
opt.use_cuda = opt.gpu_id >= 0
if not os.path.exists(opt.outdir):
os.makedirs(opt.outdir)
test_img_paths = make_dataset(opt.indir)
if opt.network == 'GCANet':
from GCANet import GCANet
net = GCANet(in_c=4, out_c=3, only_residual=opt.only_residual)
else:
print('network structure %s not supported' % opt.network)
raise ValueError
if opt.use_cuda:
torch.cuda.set_device(opt.gpu_id)
net.cuda()
else:
net.float()
net.load_state_dict(torch.load(opt.model, map_location='cpu'))
net.eval()
for img_path in test_img_paths:
img = Image.open(img_path).convert('RGB')
im_w, im_h = img.size
if im_w % 4 != 0 or im_h % 4 != 0:
img = img.resize((int(im_w // 4 * 4), int(im_h // 4 * 4)))
img = np.array(img).astype('float')
img_data = torch.from_numpy(img.transpose((2, 0, 1))).float()
edge_data = edge_compute(img_data)
in_data = torch.cat((img_data, edge_data), dim=0).unsqueeze(0) - 128
in_data = in_data.cuda() if opt.use_cuda else in_data.float()
with torch.no_grad():
pred = net(Variable(in_data))
if opt.only_residual:
out_img_data = (pred.data[0].cpu().float() + img_data).round().clamp(0, 255)
else:
out_img_data = pred.data[0].cpu().float().round().clamp(0, 255)
out_img = Image.fromarray(out_img_data.numpy().astype(np.uint8).transpose(1, 2, 0))
out_img.save(os.path.join(opt.outdir, os.path.splitext(os.path.basename(img_path))[0] + '_%s.png' % opt.task))

View File

@@ -0,0 +1,41 @@
import numpy as np
import os
import ntpath
import time
import utils
from scipy.misc import imresize
from tensorboardX import SummaryWriter
class TFVisualizer():
def __init__(self, opt):
self.tf_visualizer = SummaryWriter(os.path.join(opt.logDir, opt.name))
self.opt = opt
self.saved = False
self.ncols = 4
self.log_name = os.path.join(opt.checkpoints_dir, opt.name, 'loss_log.txt')
with open(self.log_name, "a") as log_file:
now = time.strftime("%c")
log_file.write('================ Training Loss (%s) ================\n' % now)
def reset(self):
self.saved = False
# |visuals|: dictionary of images to display or save
def display_current_results(self, visuals, iter_mark, epoch, save_result):
for label, image in visuals.items():
img_gid = utils.tensor2imgrid(image)
self.tf_visualizer.add_image(label, img_gid, iter_mark)
# losses: dictionary of error labels and values
def plot_current_losses(self, iter_mark, losses):
# for label, loss in losses.items():
# self.tf_visualizer.add_scalar(label, loss, iter_mark)
self.tf_visualizer.add_scalars('training loss', losses, iter_mark)
def print_logs(self, message):
print(message)
with open(self.log_name, "a") as log_file:
log_file.write('%s\n' % message)

View File

@@ -0,0 +1,228 @@
import os
import datetime
import argparse
import numpy as np
import torch
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from ImagePairPrefixFolder import ImagePairPrefixFolder, var_custom_collate
from utils import MovingAvg
from tf_visualizer import TFVisualizer
parser = argparse.ArgumentParser()
parser.add_argument('--network', default='GCANet')
parser.add_argument('--name', default='default_exp')
parser.add_argument('--gpu_ids', default='0')
parser.add_argument('--epochs', type=int, default=100)
parser.add_argument('--lr', type=float, default=0.001)
parser.add_argument('--lr_step', type=int, default=40)
parser.add_argument('--lr_gamma', type=float, default=0.1)
parser.add_argument('--weight_decay', type=float, default=0.0005)
parser.add_argument('--checkpoints_dir', default='checkpoint')
parser.add_argument('--logDir', default='tblogdir')
parser.add_argument('--resume_dir', default='')
parser.add_argument('--resume_epoch', type=int, default=0)
parser.add_argument('--save_epoch', type=int, default=5)
parser.add_argument('--save_latest_freq', type=int, default=5000)
parser.add_argument('--test_epoch', type=int, default=5)
parser.add_argument('--test_max_size', type=int, default=1080)
parser.add_argument('--size_unit', type=int, default=8)
parser.add_argument('--print_iter', type=int, default=100)
parser.add_argument('--input_folder', default='')
parser.add_argument('--gt_folder', default='')
parser.add_argument('--test_input_folder', default='')
parser.add_argument('--test_gt_folder', default='')
parser.add_argument('--num_workers', type=int, default=16)
parser.add_argument('--batch_size', type=int, default=4)
parser.add_argument('--only_residual', action='store_true', help='regress residual rather than image')
parser.add_argument('--loss_func', default='l2', help='l2|l1')
parser.add_argument('--inc', type=int, default=3)
parser.add_argument('--outc', type=int, default=3)
parser.add_argument('--force_rgb', action='store_true')
parser.add_argument('--no_edge', action='store_true')
opt = parser.parse_args()
opt.input_folder = os.path.expanduser(opt.input_folder)
opt.gt_folder = os.path.expanduser(opt.gt_folder)
opt.test_input_folder = os.path.expanduser(opt.test_input_folder)
opt.test_gt_folder = os.path.expanduser(opt.test_gt_folder)
if not os.path.exists(os.path.join(opt.checkpoints_dir, opt.name)):
os.makedirs(os.path.join(opt.checkpoints_dir, opt.name))
opt.resume_dir = opt.resume_dir if opt.resume_dir != '' else os.path.join(opt.checkpoints_dir, opt.name)
visualizer = TFVisualizer(opt)
### Log out
with open(os.path.realpath(__file__), 'r') as fid:
visualizer.print_logs(fid.read())
## print argument
for key, val in vars(opt).items():
visualizer.print_logs('%s: %s' % (key, val))
opt.gpu_ids = [int(x) for x in opt.gpu_ids.split(',')]
assert all(0 <= x <= torch.cuda.device_count() for x in opt.gpu_ids), 'gpu id should ' \
'be 0~{0}'.format(torch.cuda.device_count())
torch.cuda.set_device(opt.gpu_ids[0])
train_dataset = ImagePairPrefixFolder(opt.input_folder, opt.gt_folder, size_unit=opt.size_unit, force_rgb=opt.force_rgb)
train_dataloader = DataLoader(train_dataset, batch_size=opt.batch_size, shuffle=True,
collate_fn=var_custom_collate, pin_memory=True,
num_workers=opt.num_workers)
opt.do_test = opt.test_gt_folder != ''
if opt.do_test:
test_dataset = ImagePairPrefixFolder(opt.test_input_folder, opt.test_gt_folder,
max_img_size=opt.test_max_size, size_unit=opt.size_unit, force_rgb=opt.force_rgb)
test_dataloader = DataLoader(test_dataset, batch_size=1, shuffle=False,
collate_fn=var_custom_collate, pin_memory=True,
num_workers=1)
total_inc = opt.inc if opt.no_edge else opt.inc + 1
if opt.network == 'GCANet':
from GCANet import GCANet
net = GCANet(in_c=total_inc, out_c=3, only_residual=opt.only_residual)
else:
print('network structure %s not supported' % opt.network)
raise ValueError
if opt.loss_func == 'l2':
loss_crit = torch.nn.MSELoss()
elif opt.loss_func == 'l1':
loss_crit = torch.nn.SmoothL1Loss()
else:
print('loss_func %s not supported' % opt.loss_func)
raise ValueError
pnsr_crit = torch.nn.MSELoss()
if len(opt.gpu_ids) > 0:
net.cuda()
if len(opt.gpu_ids) > 1:
net = torch.nn.DataParallel(net)
loss_crit = loss_crit.cuda()
pnsr_crit = pnsr_crit.cuda()
optimizer = optim.Adam(net.parameters(), lr=opt.lr)
step_optim_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=opt.lr_step, gamma=opt.lr_gamma)
loss_avg = MovingAvg(pool_size=50)
start_epoch = 0
total_iter = 0
if os.path.exists(os.path.join(opt.checkpoints_dir, opt.name, 'latest.pth')):
print('resuming from latest.pth')
latest_info = torch.load(os.path.join(opt.checkpoints_dir, opt.name, 'latest.pth'))
start_epoch = latest_info['epoch']
total_iter = latest_info['total_iter']
if isinstance(net, torch.nn.DataParallel):
net.module.load_state_dict(latest_info['net_state'])
else:
net.load_state_dict(latest_info['net_state'])
optimizer.load_state_dict(latest_info['optim_state'])
if opt.resume_epoch > 0:
start_epoch = opt.resume_epoch
total_iter = opt.resume_epoch * len(train_dataloader)
resume_path = os.path.join(opt.resume_epoch, 'net_epoch_%d.pth') % opt.resume_epoch
print('resume from : %s' % resume_path)
assert os.path.exists(resume_path), 'cannot find the resume model: %s ' % resume_path
if isinstance(net, torch.nn.DataParallel):
net.module.load_state_dict(torch.load(resume_path))
else:
net.load_state_dict(torch.load(resume_path))
for epoch in range(start_epoch, opt.epochs):
visualizer.print_logs("Start to train epoch %d" % epoch)
net.train()
for iter, data in enumerate(train_dataloader):
total_iter += 1
optimizer.zero_grad()
step_optim_scheduler.step(epoch)
batch_input_img, batch_input_edge, batch_gt = data
if len(opt.gpu_ids) > 0:
batch_input_img, batch_input_edge, batch_gt = batch_input_img.cuda(), batch_input_edge.cuda(), batch_gt.cuda()
if opt.no_edge:
batch_input = batch_input_img
else:
batch_input = torch.cat((batch_input_img, batch_input_edge), dim=1)
batch_input_v = Variable(batch_input)
if opt.only_residual:
batch_gt_v = Variable(batch_gt - (batch_input_img+128))
else:
batch_gt_v = Variable(batch_gt)
pred = net(batch_input_v)
loss = loss_crit(pred, batch_gt_v)
avg_loss = loss_avg.set_curr_val(loss.data)
loss.backward()
optimizer.step()
if iter % opt.print_iter == 0:
visualizer.plot_current_losses(total_iter, { 'loss': loss})
visualizer.print_logs('%s Step[%d/%d], lr: %f, mv_avg_loss: %f, loss: %f' %
(str(datetime.datetime.now()).split(' ')[1], iter, len(train_dataloader),
step_optim_scheduler.get_lr()[0], avg_loss, loss))
if total_iter % opt.save_latest_freq == 0:
latest_info = {'total_iter': total_iter,
'epoch': epoch,
'optim_state': optimizer.state_dict()}
if len(opt.gpu_ids) > 1:
latest_info['net_state'] = net.module.state_dict()
else:
latest_info['net_state'] = net.state_dict()
print('save lastest model.')
torch.save(latest_info, os.path.join(opt.checkpoints_dir, opt.name, 'latest.pth'))
if (epoch+1) % opt.save_epoch == 0 :
visualizer.print_logs('saving model for epoch %d' % epoch)
if len(opt.gpu_ids) > 1:
torch.save(net.module.state_dict(), os.path.join(opt.checkpoints_dir, opt.name, 'net_epoch_%d.pth' % (epoch+1)))
else:
torch.save(net.state_dict(), os.path.join(opt.checkpoints_dir, opt.name, 'net_epoch_%d.pth' % (epoch + 1)))
if opt.do_test:
avg_psnr = 0
task_cnt = 0
net.eval()
with torch.no_grad():
for iter, data in enumerate(test_dataloader):
batch_input_img, batch_input_edge, batch_gt = data
if len(opt.gpu_ids) > 0:
batch_input_img, batch_input_edge, batch_gt = batch_input_img.cuda(), batch_input_edge.cuda(), batch_gt.cuda()
if opt.no_edge:
batch_input = batch_input_img
else:
batch_input = torch.cat((batch_input_img, batch_input_edge), dim=1)
batch_input_v = Variable(batch_input)
batch_gt_v = Variable(batch_gt)
pred = net(batch_input_v)
if opt.only_residual:
loss = pnsr_crit(pred+Variable(batch_input_img+128), batch_gt_v)
else:
loss = pnsr_crit(pred, batch_gt_v)
avg_psnr += 10 * np.log10(255 * 255 / loss.item())
task_cnt += 1
visualizer.print_logs('Testing for epoch: %d' % epoch)
visualizer.print_logs('Average test PNSR is %f for %d images' % (avg_psnr/task_cnt, task_cnt))

View File

@@ -0,0 +1,198 @@
import os
import torch
import torch
import numpy as np
from PIL import Image
import os
from scipy import signal
from torchvision.utils import make_grid
IMG_EXTENSIONS = [
'.jpg', '.JPG', '.jpeg', '.JPEG',
'.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP',
]
def is_image_file(filename):
return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)
def make_dataset(dir):
images = []
assert os.path.isdir(dir), '%s is not a valid directory' % dir
for root, _, fnames in sorted(os.walk(dir)):
for fname in fnames:
if is_image_file(fname):
path = os.path.join(root, fname)
images.append(path)
return images
def edge_compute(x):
x_diffx = torch.abs(x[:,:,1:] - x[:,:,:-1])
x_diffy = torch.abs(x[:,1:,:] - x[:,:-1,:])
y = x.new(x.size())
y.fill_(0)
y[:,:,1:] += x_diffx
y[:,:,:-1] += x_diffx
y[:,1:,:] += x_diffy
y[:,:-1,:] += x_diffy
y = torch.sum(y,0,keepdim=True)/3
y /= 4
return y
def batch_edge_compute(x):
x_diffx = torch.abs(x[:,:,:,1:] - x[:,:,:,:-1])
x_diffy = torch.abs(x[:,:,1:,:] - x[:,:,:-1,:])
y = x.new(x.size())
y.fill_(0)
y[:,:,:,1:] += x_diffx
y[:,:,:,:-1] += x_diffx
y[:,:,1:,:] += x_diffy
y[:,:,:-1,:] += x_diffy
y = torch.sum(y,1,keepdim=True)/3
y /= 4
return y
# Converts a Tensor into an image array (numpy)
# |imtype|: the desired type of the converted numpy array
def tensor2im(input_image, imtype=np.uint8):
if isinstance(input_image, torch.Tensor):
image_tensor = input_image.data
else:
return input_image
image_numpy = image_tensor[0].cpu().float().numpy()
if image_numpy.shape[0] == 1:
image_numpy = np.tile(image_numpy, (3, 1, 1))
image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * 255.0
image_numpy = image_numpy.clip(0, 255)
return image_numpy.astype(imtype)
def tensor2imgrid(input_image):
im_grid = make_grid(input_image[:4, ...], nrow=2, normalize=True, range=(-128, 128))
return im_grid
# ndarr = im_grid.mul(255).clamp(0, 255).byte().permute(1, 2, 0).cpu().numpy()
# im = Image.fromarray(ndarr)
# return im
def diagnose_network(net, name='network'):
mean = 0.0
count = 0
for param in net.parameters():
if param.grad is not None:
mean += torch.mean(torch.abs(param.grad.data))
count += 1
if count > 0:
mean = mean / count
print(name)
print(mean)
def save_image(image_numpy, image_path):
image_pil = Image.fromarray(image_numpy)
image_pil.save(image_path)
def print_numpy(x, val=True, shp=False):
x = x.astype(np.float64)
if shp:
print('shape,', x.shape)
if val:
x = x.flatten()
print('mean = %3.3f, min = %3.3f, max = %3.3f, median = %3.3f, std=%3.3f' % (
np.mean(x), np.min(x), np.max(x), np.median(x), np.std(x)))
def mkdirs(paths):
if isinstance(paths, list) and not isinstance(paths, str):
for path in paths:
mkdir(path)
else:
mkdir(paths)
def mkdir(path):
if not os.path.exists(path):
os.makedirs(path)
def fspecial_gauss(size, sigma):
"""Function to mimic the 'fspecial' gaussian MATLAB function
"""
x, y = np.mgrid[-size // 2 + 1:size // 2 + 1, -size // 2 + 1:size // 2 + 1]
g = np.exp(-((x ** 2 + y ** 2) / (2.0 * sigma ** 2)))
return g / g.sum()
def filter2(x, kernel, mode='same'):
return signal.convolve2d(x, np.rot90(kernel, 2), mode=mode)
def ssim(img1, img2, cs_map=False):
"""Return the Structural Similarity Map corresponding to input images img1
and img2 (images are assumed to be uint8)
This function attempts to mimic precisely the functionality of ssim.m a
MATLAB provided by the author's of SSIM
https://ece.uwaterloo.ca/~z70wang/research/ssim/ssim_index.m
"""
img1 = img1.astype(np.float64)
img2 = img2.astype(np.float64)
size = 11
sigma = 1.5
window = fspecial_gauss(size, sigma)
K1 = 0.01
K2 = 0.03
L = 255 # bitdepth of image
C1 = (K1 * L) ** 2
C2 = (K2 * L) ** 2
mu1 = filter2(img1, window, mode='valid')
mu2 = filter2(img2, window, mode='valid')
mu1_sq = mu1 * mu1
mu2_sq = mu2 * mu2
mu1_mu2 = mu1 * mu2
sigma1_sq = filter2(img1 * img1, window, mode='valid') - mu1_sq
sigma2_sq = filter2(img2 * img2, window, mode='valid') - mu2_sq
sigma12 = filter2(img1 * img2, window, mode='valid') - mu1_mu2
if cs_map:
return np.mean(np.mean((((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
(sigma1_sq + sigma2_sq + C2)),
(2.0 * sigma12 + C2) / (sigma1_sq + sigma2_sq + C2))))
else:
return np.mean(np.mean(((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
(sigma1_sq + sigma2_sq + C2))))
class MovingAvg(object):
def __init__(self, pool_size=100):
from queue import Queue
self.pool = Queue(maxsize=pool_size)
self.sum = 0
self.curr_pool_size = 0
self.pool_size = pool_size
def set_curr_val(self, val):
if not self.pool.full():
self.curr_pool_size += 1
self.pool.put_nowait(val)
else:
last_first_val = self.pool.get_nowait()
self.pool.put_nowait(val)
self.sum -= last_first_val
self.sum += val
return self.sum / self.curr_pool_size
def reset(self):
from queue import Queue
self.pool = Queue(maxsize=self.pool_size)
self.sum = 0
self.curr_pool_size = 0

46
GCANet/README.md Normal file
View File

@@ -0,0 +1,46 @@
Gated Context Aggregation Network for Image Dehazing and Deraining
=======
![image](imgs/net_arch.png)
This is the implementation of our WACV 2019 paper *"Gated Context Aggregation Network for Image Dehazing and Deraining"* by [Dongdong Chen](<http://www.dongdongchen.bid/>), [Mingming He](<https://github.com/hmmlillian>), [Qingnan Fan](<https://fqnchina.github.io/>), *et al.*
In this paper, we propose a new end-to-end gated context aggregation network GCANet for image dehazing, in which the smoothed dilated convolution is used to avoid the gridding artifacts and a gated subnetwork is applied to fuse the features of different levels. Experiments show that GCANet can obtain much better performance than all the previous state-of-the-art image dehazing methods both qualitatively and quantitatively
![image](imgs/dehaze_visual.png)
We further apply our proposed GCANet to the image deraining task, which also outperforms previous state-of-the-art image deraining methods and demonstrates its generality.
![image](imgs/derain_visual.png)
## Getting Started
This paper is implemented with Pytorch framework.
Demo
----
Directly put all your test images under one directory. Then run:
```bash
python test.py --task [dehaze | derain] --gpu_id [gpu_id] --indir [input directory] --outdir [output directory]
```
For training, please download the training code from <https://drive.google.com/file/d/1T7X1HYztbz6S75vTRNtREgGEOI269KDk/view?usp=sharing>
Cite
----
You can use our codes for research purpose only. And please cite our paper when you use our codes.
```
@article{chen2018gated,
title={Gated Context Aggregation Network for Image Dehazing and Deraining},
author={Chen, Dongdong and He, Mingming and Fan, Qingnan and Liao, Jing and Zhang, Liheng and Hou, Dongdong and Yuan, Lu and Hua, Gang},
journal={WACV 2019},
year={2018}
}
```
Contact
-------
If you find any bugs or have any ideas of optimizing these codes, please contact me via cddlyf [at] gmail [dot] com

Binary file not shown.

Binary file not shown.

67
GCANet/test.py Normal file
View File

@@ -0,0 +1,67 @@
import os
import argparse
import numpy as np
from PIL import Image
import torch
from torch.autograd import Variable
from utils import make_dataset, edge_compute
parser = argparse.ArgumentParser()
parser.add_argument('--network', default='GCANet')
parser.add_argument('--task', default='dehaze', help='dehaze | derain')
parser.add_argument('--gpu_id', type=int, default=0)
parser.add_argument('--indir', default='examples/')
parser.add_argument('--outdir', default='output')
opt = parser.parse_args()
assert opt.task in ['dehaze', 'derain']
## forget to regress the residue for deraining by mistake,
## which should be able to produce better results
opt.only_residual = opt.task == 'dehaze'
opt.model = 'models/wacv_gcanet_%s.pth' % opt.task
opt.use_cuda = opt.gpu_id >= 0
if not os.path.exists(opt.outdir):
os.makedirs(opt.outdir)
test_img_paths = make_dataset(opt.indir)
if opt.network == 'GCANet':
from GCANet import GCANet
net = GCANet(in_c=4, out_c=3, only_residual=opt.only_residual)
else:
print('network structure %s not supported' % opt.network)
raise ValueError
if opt.use_cuda:
torch.cuda.set_device(opt.gpu_id)
net.cuda()
else:
net.float()
net.load_state_dict(torch.load(opt.model, map_location='cpu'))
net.eval()
for img_path in test_img_paths:
img = Image.open(img_path).convert('RGB')
im_w, im_h = img.size
if im_w % 4 != 0 or im_h % 4 != 0:
img = img.resize((int(im_w // 4 * 4), int(im_h // 4 * 4)))
img = np.array(img).astype('float')
img_data = torch.from_numpy(img.transpose((2, 0, 1))).float()
edge_data = edge_compute(img_data)
in_data = torch.cat((img_data, edge_data), dim=0).unsqueeze(0) - 128
in_data = in_data.cuda() if opt.use_cuda else in_data.float()
with torch.no_grad():
pred = net(Variable(in_data))
if opt.only_residual:
out_img_data = (pred.data[0].cpu().float() + img_data).round().clamp(0, 255)
else:
out_img_data = pred.data[0].cpu().float().round().clamp(0, 255)
out_img = Image.fromarray(out_img_data.numpy().astype(np.uint8).transpose(1, 2, 0))
print("-"*5,"图片存储在:",os.path.join(opt.outdir, os.path.splitext(os.path.basename(img_path))[0] + '_%s.png' % opt.task))
out_img.save(os.path.join(opt.outdir, os.path.splitext(os.path.basename(img_path))[0] + '_%s.png' % opt.task))

39
GCANet/utils.py Normal file
View File

@@ -0,0 +1,39 @@
import os
import torch
IMG_EXTENSIONS = [
'.jpg', '.JPG', '.jpeg', '.JPEG',
'.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP',
]
def is_image_file(filename):
return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)
def make_dataset(dir):
images = []
assert os.path.isdir(dir), '%s is not a valid directory' % dir
for root, _, fnames in sorted(os.walk(dir)):
for fname in fnames:
if is_image_file(fname):
path = os.path.join(root, fname)
images.append(path)
return images
def edge_compute(x):
x_diffx = torch.abs(x[:,:,1:] - x[:,:,:-1])
x_diffy = torch.abs(x[:,1:,:] - x[:,:-1,:])
y = x.new(x.size())
y.fill_(0)
y[:,:,1:] += x_diffx
y[:,:,:-1] += x_diffx
y[:,1:,:] += x_diffy
y[:,:-1,:] += x_diffy
y = torch.sum(y,0,keepdim=True)/3
y /= 4
return y