2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
models/*/*.json binary
|
||||||
|
*.t7 binary
|
14
.gitignore
vendored
|
@ -1,4 +1,16 @@
|
||||||
*~
|
*~
|
||||||
|
work/
|
||||||
cache/*.png
|
cache/*.png
|
||||||
models/*.png
|
cache/url_*
|
||||||
|
data/*
|
||||||
|
!data/.gitkeep
|
||||||
|
|
||||||
|
models/*
|
||||||
|
!models/anime_style_art
|
||||||
|
!models/anime_style_art_rgb
|
||||||
|
!models/ukbench
|
||||||
|
!models/photo
|
||||||
|
models/*/*.png
|
||||||
|
|
||||||
waifu2x.log
|
waifu2x.log
|
||||||
|
waifu2x-*.log
|
||||||
|
|
1
NOTICE
|
@ -1 +1,2 @@
|
||||||
images/miku_*: CC BY-NC by piapro (http://piapro.net/en_for_creators.html)
|
images/miku_*: CC BY-NC by piapro (http://piapro.net/en_for_creators.html)
|
||||||
|
webgen/assets/bg.png: Generated by chibichara maker (http://tetrabo.com/chibichara/).
|
||||||
|
|
68
README.md
|
@ -1,6 +1,7 @@
|
||||||
# waifu2x
|
# waifu2x
|
||||||
|
|
||||||
Image Super-Resolution for anime-style-art using Deep Convolutional Neural Networks.
|
Image Super-Resolution for Anime-style art using Deep Convolutional Neural Networks.
|
||||||
|
And it supports photo.
|
||||||
|
|
||||||
Demo-Application can be found at http://waifu2x.udp.jp/ .
|
Demo-Application can be found at http://waifu2x.udp.jp/ .
|
||||||
|
|
||||||
|
@ -19,16 +20,11 @@ waifu2x is inspired by SRCNN [1]. 2D character picture (HatsuneMiku) is licensed
|
||||||
|
|
||||||
## Public AMI
|
## Public AMI
|
||||||
```
|
```
|
||||||
AMI ID: ami-0be01e4f
|
TODO
|
||||||
AMI NAME: waifu2x-server
|
|
||||||
Instance Type: g2.2xlarge
|
|
||||||
Region: US West (N.California)
|
|
||||||
OS: Ubuntu 14.04
|
|
||||||
User: ubuntu
|
|
||||||
Created at: 2015-08-12
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Third Party Software
|
## Third Party Software
|
||||||
|
|
||||||
[Third-Party](https://github.com/nagadomi/waifu2x/wiki/Third-Party)
|
[Third-Party](https://github.com/nagadomi/waifu2x/wiki/Third-Party)
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
@ -37,10 +33,12 @@ Created at: 2015-08-12
|
||||||
- NVIDIA GPU
|
- NVIDIA GPU
|
||||||
|
|
||||||
### Platform
|
### Platform
|
||||||
|
|
||||||
- [Torch7](http://torch.ch/)
|
- [Torch7](http://torch.ch/)
|
||||||
- [NVIDIA CUDA](https://developer.nvidia.com/cuda-toolkit)
|
- [NVIDIA CUDA](https://developer.nvidia.com/cuda-toolkit)
|
||||||
|
|
||||||
### lualocks packages (excludes torch7's default packages)
|
### LuaRocks packages (excludes torch7's default packages)
|
||||||
|
- lua-csnappy
|
||||||
- md5
|
- md5
|
||||||
- uuid
|
- uuid
|
||||||
- [turbo](https://github.com/kernelsauce/turbo)
|
- [turbo](https://github.com/kernelsauce/turbo)
|
||||||
|
@ -57,34 +55,44 @@ See: [NVIDIA CUDA Getting Started Guide for Linux](http://docs.nvidia.com/cuda/c
|
||||||
Download [CUDA](http://developer.nvidia.com/cuda-downloads)
|
Download [CUDA](http://developer.nvidia.com/cuda-downloads)
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo dpkg -i cuda-repo-ubuntu1404_7.0-28_amd64.deb
|
sudo dpkg -i cuda-repo-ubuntu1404_7.5-18_amd64.deb
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install cuda
|
sudo apt-get install cuda
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Install Package
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt-get install libsnappy-dev
|
||||||
|
```
|
||||||
|
|
||||||
#### Install Torch7
|
#### Install Torch7
|
||||||
|
|
||||||
See: [Getting started with Torch](http://torch.ch/docs/getting-started.html)
|
See: [Getting started with Torch](http://torch.ch/docs/getting-started.html)
|
||||||
|
|
||||||
|
And install luarocks packages.
|
||||||
|
```
|
||||||
|
luarocks install graphicsmagick # upgrade
|
||||||
|
luarocks install lua-csnappy
|
||||||
|
luarocks install md5
|
||||||
|
luarocks install uuid
|
||||||
|
PREFIX=$HOME/torch/install luarocks install turbo # if you need to use web application
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Getting waifu2x
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone --depth 1 https://github.com/nagadomi/waifu2x.git
|
||||||
|
```
|
||||||
|
|
||||||
#### Validation
|
#### Validation
|
||||||
|
|
||||||
Test the waifu2x command line tool.
|
Testing the waifu2x command line tool.
|
||||||
```
|
```
|
||||||
th waifu2x.lua
|
th waifu2x.lua
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting Up the Web Application Environment (if you needed)
|
|
||||||
|
|
||||||
#### Install packages
|
|
||||||
|
|
||||||
```
|
|
||||||
luarocks install md5
|
|
||||||
luarocks install uuid
|
|
||||||
PREFIX=$HOME/torch/install luarocks install turbo
|
|
||||||
```
|
|
||||||
|
|
||||||
## Web Application
|
## Web Application
|
||||||
Run.
|
|
||||||
```
|
```
|
||||||
th web.lua
|
th web.lua
|
||||||
```
|
```
|
||||||
|
@ -114,11 +122,20 @@ th waifu2x.lua -m noise_scale -noise_level 1 -i input_image.png -o output_image.
|
||||||
th waifu2x.lua -m noise_scale -noise_level 2 -i input_image.png -o output_image.png
|
th waifu2x.lua -m noise_scale -noise_level 2 -i input_image.png -o output_image.png
|
||||||
```
|
```
|
||||||
|
|
||||||
See also `images/gen.sh`.
|
See also `th waifu2x.lua -h`.
|
||||||
|
|
||||||
|
### Using photo model
|
||||||
|
|
||||||
|
Please add `-model_dir models/photo` to command line option, if you want to use photo model.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
```
|
||||||
|
th waifu2x.lua -model_dir models/photo -m scale -i input_image.png -o output_image.png
|
||||||
|
```
|
||||||
|
|
||||||
### Video Encoding
|
### Video Encoding
|
||||||
|
|
||||||
\* `avconv` is `ffmpeg` on Ubuntu 14.04.
|
\* `avconv` is alias of `ffmpeg` on Ubuntu 14.04.
|
||||||
|
|
||||||
Extracting images and audio from a video. (range: 00:09:00 ~ 00:12:00)
|
Extracting images and audio from a video. (range: 00:09:00 ~ 00:12:00)
|
||||||
```
|
```
|
||||||
|
@ -144,6 +161,7 @@ avconv -f image2 -r 24 -i new_frames/%d.png -i audio.mp3 -r 24 -vcodec libx264 -
|
||||||
```
|
```
|
||||||
|
|
||||||
## Training Your Own Model
|
## Training Your Own Model
|
||||||
|
Notes: If you have cuDNN library, you can use cudnn kernel with `-backend cudnn` option. And you can convert trained cudnn model to cunn model with `tools/cudnn2cunn.lua`.
|
||||||
|
|
||||||
### Data Preparation
|
### Data Preparation
|
||||||
|
|
||||||
|
@ -151,7 +169,7 @@ Genrating a file list.
|
||||||
```
|
```
|
||||||
find /path/to/image/dir -name "*.png" > data/image_list.txt
|
find /path/to/image/dir -name "*.png" > data/image_list.txt
|
||||||
```
|
```
|
||||||
(You should use PNG! In my case, waifu2x is trained with 3000 high-resolution-noise-free-PNG images.)
|
You should use noise free images. In my case, waifu2x is trained with 6000 high-resolution-noise-free-PNG images.
|
||||||
|
|
||||||
Converting training data.
|
Converting training data.
|
||||||
```
|
```
|
||||||
|
|
|
@ -3,7 +3,15 @@ require 'pl'
|
||||||
CACHE_DIR="cache"
|
CACHE_DIR="cache"
|
||||||
TTL = 3600 * 24
|
TTL = 3600 * 24
|
||||||
|
|
||||||
local files = dir.getfiles(CACHE_DIR, "*.png")
|
local files = {}
|
||||||
|
local image_cache = dir.getfiles(CACHE_DIR, "*.png")
|
||||||
|
local url_cache = dir.getfiles(CACHE_DIR, "url_*")
|
||||||
|
for i = 1, #image_cache do
|
||||||
|
table.insert(files, image_cache[i])
|
||||||
|
end
|
||||||
|
for i = 1, #url_cache do
|
||||||
|
table.insert(files, url_cache[i])
|
||||||
|
end
|
||||||
local now = os.time()
|
local now = os.time()
|
||||||
for i, f in pairs(files) do
|
for i, f in pairs(files) do
|
||||||
if now - path.getmtime(f) > TTL then
|
if now - path.getmtime(f) > TTL then
|
||||||
|
|
BIN
assets/bg.png
Normal file
After Width: | Height: | Size: 198 KiB |
BIN
assets/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,90 +1,146 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
|
<!-- This file was automatically generated by webgen/gen.rb. Do not make changes to this file manually. -->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<link rel="canonical" href="http://waifu2x.udp.jp/">
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"/>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0,width=device-width">
|
||||||
|
<link href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="mobile.css" rel="stylesheet" type="text/css" media="screen and (max-width: 768px) and (min-width: 0px)">
|
||||||
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
<title>waifu2x</title>
|
<title>waifu2x</title>
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
margin: 1em 2em 1em 2em;
|
|
||||||
background: LightGray;
|
|
||||||
width: 640px;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
.about {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding: 1em 5px 0.2em 0;
|
|
||||||
}
|
|
||||||
.help {
|
|
||||||
font-size: 0.85em;
|
|
||||||
margin: 1em 0 0 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function clear_file() {
|
|
||||||
var new_file = $("#file").clone();
|
|
||||||
new_file.change(clear_url);
|
|
||||||
$("#file").replaceWith(new_file);
|
|
||||||
}
|
|
||||||
function clear_url() {
|
|
||||||
$("#url").val("")
|
|
||||||
}
|
|
||||||
$(function (){
|
|
||||||
$("#url").change(clear_file);
|
|
||||||
$("#file").change(clear_url);
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>waifu2x</h1>
|
<div class="all-page">
|
||||||
<div class="header">
|
<h1 class="main-title">waifu2x</h1>
|
||||||
<div style="position:absolute; display:block; top:0; left:540px; max-height:140px;">
|
<div class="choose-lang">
|
||||||
<img style="position:absolute; display:block; left:0; top:0; width:149px; height:149px; border:0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png">
|
<a href="index.html">
|
||||||
<a href="https://github.com/nagadomi/waifu2x" target="_blank" style="position:absolute; display:block; left:0; top:0; width:149px; height:130px;"></a>
|
English
|
||||||
</div>
|
</a>
|
||||||
<a href="index.html">en</a>/<a href="index.ja.html">ja</a>/<a href="index.ru.html">ru</a>
|
/
|
||||||
</div>
|
<a href="index.ja.html">
|
||||||
<div class="about">
|
日本語
|
||||||
<div>Single-Image Super-Resolution for anime/fan-art using Deep Convolutional Neural Networks. <a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" target="_blank">about</a>.</div>
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ru.html">
|
||||||
|
Русский
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.pt.html">
|
||||||
|
Português
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Single-Image Super-Resolution for Anime-Style Art using Deep Convolutional Neural Networks. And it supports photo.</p>
|
||||||
|
<p class="margin1 link-box">
|
||||||
|
<a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" class="blue-link" target="_blank">
|
||||||
|
Show full demonstration
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="blue-link" target="_blank">
|
||||||
|
Go to GitHub
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
||||||
<fieldset>
|
<div class="option-box first">
|
||||||
<legend>Image</legend>
|
<div class="option-left">Image choosing:</div>
|
||||||
<div>
|
<div class="option-right">
|
||||||
URL: <input id="url" type="text" name="url" style="width:400px"> or
|
<input type="text" id="url" name="url" placeholder="Type URL">
|
||||||
|
<div class="option-right-small">
|
||||||
|
Or choose a file:
|
||||||
|
<input type="file" id="file" name="file"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="option-hint">
|
||||||
FILE: <input id="file" type="file" name="file">
|
Limits: Size: 3MB, Noise Reduction: 2560x2560px, Upscaling: 1280x1280px.
|
||||||
</div>
|
</div>
|
||||||
<div class="help">
|
|
||||||
Limits: Size: 2MB, Noise Reduction: 2560x2560px, Upscaling: 1280x1280px
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<div class="option-box">
|
||||||
<fieldset>
|
<div class="option-left">
|
||||||
<legend>Noise Reduction (expect JPEG Artifact)</legend>
|
Style:
|
||||||
<label><input type="radio" name="noise" value="0"> None</label>
|
</div>
|
||||||
<label><input type="radio" name="noise" value="1" checked="checked"> Medium</label>
|
<div class="option-right">
|
||||||
<label><input type="radio" name="noise" value="2"> High</label>
|
<label><input type="radio" name="style" class="radio" value="art" checked>
|
||||||
<div class="help">When using 2x scaling, we never recommend to use high level of noise reduction, it almost always makes image worse, it makes sense for only some rare cases when image had really bad quality from the beginning.</div>
|
<span class="r-text">
|
||||||
</fieldset>
|
Artwork
|
||||||
<fieldset>
|
</span>
|
||||||
<legend>Upscaling</legend>
|
</label>
|
||||||
<label><input type="radio" name="scale" value="0" checked="checked"> None</label>
|
<label><input type="radio" name="style" class="radio" value="photo">
|
||||||
<label><input type="radio" name="scale" value="1"> 1.6x</label>
|
<span class="r-text">
|
||||||
<label><input type="radio" name="scale" value="2"> 2x</label>
|
Photo
|
||||||
</fieldset>
|
</span>
|
||||||
<input type="submit"/>
|
</label>
|
||||||
</form>
|
</div>
|
||||||
<div class="help">
|
</div>
|
||||||
<ul style="padding-left: 15px;">
|
<div class="option-box">
|
||||||
<li>If you are using Firefox, Please press the CTRL+S key to save image. "Save Image" option doesn't work.
|
<div class="option-left">
|
||||||
|
Noise Reduction:
|
||||||
|
<div class="option-left-small">
|
||||||
|
(expect JPEG artifact)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="0">
|
||||||
|
<span class="r-text">
|
||||||
|
None
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="1" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Medium
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="noise" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
High
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
You need use noise reduction if image actually has noise or it may cause opposite effect.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
Upscaling:
|
||||||
|
<div class="option-left-small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="0" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
None
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="1">
|
||||||
|
<span class="r-text">
|
||||||
|
1.6x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
2x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="button" value="Convert">
|
||||||
|
|
||||||
|
<input type="submit" name="download" value="Download" class="button">
|
||||||
|
<div class="bottom-hint">
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>If you are using Firefox, Please press the CTRL+S key to save image. "Save Image" option doesn't work.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-info">
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="gray-link" target="_blank">waifu2x</a>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,90 +1,146 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
|
<!-- This file was automatically generated by webgen/gen.rb. Do not make changes to this file manually. -->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<link rel="canonical" href="http://waifu2x.udp.jp/">
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"/>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0,width=device-width">
|
||||||
|
<link href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="mobile.css" rel="stylesheet" type="text/css" media="screen and (max-width: 768px) and (min-width: 0px)">
|
||||||
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
<title>waifu2x</title>
|
<title>waifu2x</title>
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
margin: 1em 2em 1em 2em;
|
|
||||||
background: LightGray;
|
|
||||||
width: 640px;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
.about {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: 1em 5px 0.2em 0;
|
|
||||||
}
|
|
||||||
.help {
|
|
||||||
font-size: 0.8em;
|
|
||||||
margin: 1em 0 0 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function clear_file() {
|
|
||||||
var new_file = $("#file").clone();
|
|
||||||
new_file.change(clear_url);
|
|
||||||
$("#file").replaceWith(new_file);
|
|
||||||
}
|
|
||||||
function clear_url() {
|
|
||||||
$("#url").val("")
|
|
||||||
}
|
|
||||||
$(function (){
|
|
||||||
$("#url").change(clear_file);
|
|
||||||
$("#file").change(clear_url);
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>waifu2x</h1>
|
<div class="all-page">
|
||||||
<div class="header">
|
<h1 class="main-title">waifu2x</h1>
|
||||||
<div style="position:absolute; display:block; top:0; left:540px; max-height:140px;">
|
<div class="choose-lang">
|
||||||
<img style="position:absolute; display:block; left:0; top:0; width:149px; height:149px; border:0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png">
|
<a href="index.html">
|
||||||
<a href="https://github.com/nagadomi/waifu2x" target="_blank" style="position:absolute; display:block; left:0; top:0; width:149px; height:130px;"></a>
|
English
|
||||||
</div>
|
</a>
|
||||||
<a href="index.html">en</a>/<a href="index.ja.html">ja</a>/<a href="index.ru.html">ru</a>
|
/
|
||||||
</div>
|
<a href="index.ja.html">
|
||||||
<div class="about">
|
日本語
|
||||||
<div>深層畳み込みニューラルネットワークによる二次元画像のための超解像システム. <a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" target="_blank">about</a>.</div>
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ru.html">
|
||||||
|
Русский
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.pt.html">
|
||||||
|
Português
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<p>深層畳み込みニューラルネットワークによる二次元画像のための超解像システム。 写真にも対応。</p>
|
||||||
|
<p class="margin1 link-box">
|
||||||
|
<a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" class="blue-link" target="_blank">
|
||||||
|
実行例を表示
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="blue-link" target="_blank">
|
||||||
|
プロジェクトページ(GitHub)
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
||||||
<fieldset>
|
<div class="option-box first">
|
||||||
<legend>画像</legend>
|
<div class="option-left">画像を選択:</div>
|
||||||
<div>
|
<div class="option-right">
|
||||||
URL: <input id="url" type="text" name="url" style="width:400px"> or
|
<input type="text" id="url" name="url" placeholder="URLを入力">
|
||||||
|
<div class="option-right-small">
|
||||||
|
ファイルを選択:
|
||||||
|
<input type="file" id="file" name="file"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="option-hint">
|
||||||
FILE: <input id="file" type="file" name="file">
|
制限: サイズ: 3MB, ノイズ除去: 2560x2560px, 拡大(前): 1280x1280px.
|
||||||
</div>
|
</div>
|
||||||
<div class="help">
|
|
||||||
制限: サイズ: 2MB, ノイズ除去: 2560x2560px, 拡大: 1280x1280px
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<div class="option-box">
|
||||||
<fieldset>
|
<div class="option-left">
|
||||||
<legend>ノイズ除去 (JPEGノイズを想定)</legend>
|
スタイル:
|
||||||
<label><input type="radio" name="noise" value="0"> なし</label>
|
</div>
|
||||||
<label><input type="radio" name="noise" value="1" checked="checked"> 弱</label>
|
<div class="option-right">
|
||||||
<label><input type="radio" name="noise" value="2"> 強</label>
|
<label><input type="radio" name="style" class="radio" value="art" checked>
|
||||||
</fieldset>
|
<span class="r-text">
|
||||||
<fieldset>
|
イラスト
|
||||||
<legend>拡大</legend>
|
</span>
|
||||||
<label><input type="radio" name="scale" value="0" checked="checked"> なし</label>
|
</label>
|
||||||
<label><input type="radio" name="scale" value="1"> 1.6x</label>
|
<label><input type="radio" name="style" class="radio" value="photo">
|
||||||
<label><input type="radio" name="scale" value="2"> 2x</label>
|
<span class="r-text">
|
||||||
</fieldset>
|
写真
|
||||||
<input type="submit" value="実行"/>
|
</span>
|
||||||
</form>
|
</label>
|
||||||
<div class="help">
|
</div>
|
||||||
<ul style="padding-left: 15px;">
|
</div>
|
||||||
<li>なし/なしで入力画像を変換せずに出力する。ブラウザのタブで変換結果を比較したい人用。
|
<div class="option-box">
|
||||||
<li>Firefoxの方は、右クリから画像が保存できないようなので、CTRL+SキーかALTキー後 ファイル - ページを保存 で画像を保存してください。
|
<div class="option-left">
|
||||||
|
ノイズ除去:
|
||||||
|
<div class="option-left-small">
|
||||||
|
(JPEGノイズを想定)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="0">
|
||||||
|
<span class="r-text">
|
||||||
|
なし
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="1" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
中
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="noise" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
高
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
ノイズ除去は細部が消えることがあります。JPEGノイズがある場合に使用します。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
拡大:
|
||||||
|
<div class="option-left-small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="0" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
なし
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="1">
|
||||||
|
<span class="r-text">
|
||||||
|
1.6x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
2x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="button" value="実行">
|
||||||
|
|
||||||
|
<input type="submit" name="download" value="実行結果を保存" class="button">
|
||||||
|
<div class="bottom-hint">
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>Firefoxの方は、右クリから画像が保存できないようなので、CTRL+SキーかALTキー後 ファイル - ページを保存 で画像を保存してください。</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-info">
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="gray-link" target="_blank">waifu2x</a>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
146
assets/index.pt.html
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt">
|
||||||
|
<!-- This file was automatically generated by webgen/gen.rb. Do not make changes to this file manually. -->
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"/>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0,width=device-width">
|
||||||
|
<link href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="mobile.css" rel="stylesheet" type="text/css" media="screen and (max-width: 768px) and (min-width: 0px)">
|
||||||
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
|
<title>waifu2x</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="all-page">
|
||||||
|
<h1 class="main-title">waifu2x</h1>
|
||||||
|
<div class="choose-lang">
|
||||||
|
<a href="index.html">
|
||||||
|
English
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ja.html">
|
||||||
|
日本語
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ru.html">
|
||||||
|
Русский
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.pt.html">
|
||||||
|
Português
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p>Single-Image Super-Resolution for Anime-Style Art using Deep Convolutional Neural Networks. And it supports photo.</p>
|
||||||
|
<p class="margin1 link-box">
|
||||||
|
<a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" class="blue-link" target="_blank">
|
||||||
|
Sobre
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="blue-link" target="_blank">
|
||||||
|
Ir para Github
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
||||||
|
<div class="option-box first">
|
||||||
|
<div class="option-left">Imagem:</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<input type="text" id="url" name="url" placeholder="URL">
|
||||||
|
<div class="option-right-small">
|
||||||
|
ARQUIVO:
|
||||||
|
<input type="file" id="file" name="file"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
Limites: Tamanho: 3MB, Redução de ruído: 2560x2560px, Aumento de escala: 1280x1280px.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
Estilo:
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="style" class="radio" value="art" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Arte
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="style" class="radio" value="photo">
|
||||||
|
<span class="r-text">
|
||||||
|
Foto
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
Redução de ruído:
|
||||||
|
<div class="option-left-small">
|
||||||
|
(Exceto artefato JPEG)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="0">
|
||||||
|
<span class="r-text">
|
||||||
|
Nenhuma
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="1" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Média
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="noise" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
Alta
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
Quando usando a escala 2x, Nós nunca recomendamos usar um nível alto de redução de ruído, quase sempre deixa a imagem pior, faz sentido apenas para casos raros quando a imagem tinha uma qualidade muito má desde o começo.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
Aumento de escala:
|
||||||
|
<div class="option-left-small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="0" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Nenhum
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="1">
|
||||||
|
<span class="r-text">
|
||||||
|
1.6x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
2x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="button" value="Converter">
|
||||||
|
|
||||||
|
<input type="submit" name="download" value="Baixar" class="button">
|
||||||
|
<div class="bottom-hint">
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>Se Você estiver usando o Firefox, por favor, aperte CTRL+S para salvar a imagem. A opção "Salvar Imagem" não funciona</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-info">
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="gray-link" target="_blank">waifu2x</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,90 +1,146 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="ru">
|
||||||
|
<!-- This file was automatically generated by webgen/gen.rb. Do not make changes to this file manually. -->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<link rel="canonical" href="http://waifu2x.udp.jp/">
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"/>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0,width=device-width">
|
||||||
|
<link href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="mobile.css" rel="stylesheet" type="text/css" media="screen and (max-width: 768px) and (min-width: 0px)">
|
||||||
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
<title>waifu2x</title>
|
<title>waifu2x</title>
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
margin: 1em 2em 1em 2em;
|
|
||||||
background: LightGray;
|
|
||||||
width: 640px;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
.about {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding: 1em 5px 0.2em 0;
|
|
||||||
}
|
|
||||||
.help {
|
|
||||||
font-size: 0.85em;
|
|
||||||
margin: 1em 0 0 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function clear_file() {
|
|
||||||
var new_file = $("#file").clone();
|
|
||||||
new_file.change(clear_url);
|
|
||||||
$("#file").replaceWith(new_file);
|
|
||||||
}
|
|
||||||
function clear_url() {
|
|
||||||
$("#url").val("")
|
|
||||||
}
|
|
||||||
$(function (){
|
|
||||||
$("#url").change(clear_file);
|
|
||||||
$("#file").change(clear_url);
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>waifu2x</h1>
|
<div class="all-page">
|
||||||
<div class="header">
|
<h1 class="main-title">waifu2x</h1>
|
||||||
<div style="position:absolute; display:block; top:0; left:540px; max-height:140px;">
|
<div class="choose-lang">
|
||||||
<img style="position:absolute; display:block; left:0; top:0; width:149px; height:149px; border:0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png">
|
<a href="index.html">
|
||||||
<a href="https://github.com/nagadomi/waifu2x" target="_blank" style="position:absolute; display:block; left:0; top:0; width:149px; height:130px;"></a>
|
English
|
||||||
</div>
|
</a>
|
||||||
<a href="index.html">en</a>/<a href="index.ja.html">ja</a>/<a href="index.ru.html">ru</a>
|
/
|
||||||
</div>
|
<a href="index.ja.html">
|
||||||
<div class="about">
|
日本語
|
||||||
<div>Увеличение в 4 раза рисованных изображений, например, аниме или фан-арт, а также устранение шума (преимущественно артефактов сжатия JPEG), см. <a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" target="_blank">демонстрацию и сравнения</a></div>
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ru.html">
|
||||||
|
Русский
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.pt.html">
|
||||||
|
Português
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Waifu2x позволяет увеличивать в 4 раза рисованные изображения, например аниме или арт, а также устранять шум на изображении (преимущественно артефакты сжатия JPEG). Теперь также поддерживаются фотографии.</p>
|
||||||
|
<p class="margin1 link-box">
|
||||||
|
<a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" class="blue-link" target="_blank">
|
||||||
|
Посмотреть полную демонстрацию
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="blue-link" target="_blank">
|
||||||
|
Перейти на GitHub
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
||||||
<fieldset>
|
<div class="option-box first">
|
||||||
<legend>Выбор изображения</legend>
|
<div class="option-left">Выбор изображения:</div>
|
||||||
<div>
|
<div class="option-right">
|
||||||
Указать URL: <input id="url" type="text" name="url" style="width:400px">
|
<input type="text" id="url" name="url" placeholder="Укажите URL">
|
||||||
|
<div class="option-right-small">
|
||||||
|
Либо выберите файл:
|
||||||
|
<input type="file" id="file" name="file"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="option-hint">
|
||||||
Или загрузить файл: <input id="file" type="file" name="file">
|
Макс. размер файла — 3MB, устранение шума — макс. 2560x2560px, апскейл — 1280x1280px.
|
||||||
</div>
|
</div>
|
||||||
<div class="help">
|
|
||||||
Макс. размер файла — 2MB, устранение шума — макс. 2560x2560px, апскейл — 1280x1280px
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<div class="option-box">
|
||||||
<fieldset>
|
<div class="option-left">
|
||||||
<legend>Устранение шума (артефактов JPEG)</legend>
|
Тип изображения:
|
||||||
<label><input type="radio" name="noise" value="0"> Нет</label>
|
</div>
|
||||||
<label><input type="radio" name="noise" value="1" checked="checked"> Средне</label>
|
<div class="option-right">
|
||||||
<label><input type="radio" name="noise" value="2"> Сильно (не рекомендуется)</label>
|
<label><input type="radio" name="style" class="radio" value="art" checked>
|
||||||
<div class="help">Устранение шума нужно использовать, если на картинке действительно есть шум, иначе это даст противоположный эффект. Также не рекомендуется сильное устранение шума, оно даёт выгоду только в редких случаях, когда картинка изначально была сильно испорчена.</div>
|
<span class="r-text">
|
||||||
</fieldset>
|
Арт
|
||||||
<fieldset>
|
</span>
|
||||||
<legend>Апскейл (увеличение размера)</legend>
|
</label>
|
||||||
<label><input type="radio" name="scale" value="0" checked="checked"> Нет</label>
|
<label><input type="radio" name="style" class="radio" value="photo">
|
||||||
<label><input type="radio" name="scale" value="1"> 1.6x</label>
|
<span class="r-text">
|
||||||
<label><input type="radio" name="scale" value="2"> 2x</label>
|
Фотография
|
||||||
</fieldset>
|
</span>
|
||||||
<input type="submit"/>
|
</label>
|
||||||
</form>
|
</div>
|
||||||
<div class="help">
|
</div>
|
||||||
<ul style="padding-left: 15px;">
|
<div class="option-box">
|
||||||
<li>Если Вы используете Firefox, для сохранения изображения Вам придётся нажать Ctrl+S (опция в меню "Сохранить изображение" работать не будет!)
|
<div class="option-left">
|
||||||
|
Устранение шума:
|
||||||
|
<div class="option-left-small">
|
||||||
|
(артефактов JPEG)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="0">
|
||||||
|
<span class="r-text">
|
||||||
|
Нет
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="1" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Средне
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="noise" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
Сильно
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
Устранение шума нужно использовать, если на картинке действительно есть шум, иначе это даст противоположный эффект.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
Апскейл:
|
||||||
|
<div class="option-left-small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="0" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
Нет
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="1">
|
||||||
|
<span class="r-text">
|
||||||
|
1.6x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
2x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="button" value="Преобразовать">
|
||||||
|
|
||||||
|
<input type="submit" name="download" value="Скачать" class="button">
|
||||||
|
<div class="bottom-hint">
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>Если Вы используете Firefox, для сохранения изображения нажмите Ctrl+S (перетаскивание изображения и опция "Сохранить изображение" работать не будут).</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-info">
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="gray-link" target="_blank">waifu2x</a>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
28
assets/mobile.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
body {
|
||||||
|
width: 98%;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
.all-page {
|
||||||
|
width: auto;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
.main-title {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.option-left {
|
||||||
|
width: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#url {
|
||||||
|
width: 100%;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
.option-right {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
min-width: 10px;
|
||||||
|
width: 100%;
|
||||||
|
height: 3em;
|
||||||
|
}
|
191
assets/style.css
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input[type="reset"]::-moz-focus-inner,
|
||||||
|
input[type="button"]::-moz-focus-inner,
|
||||||
|
input[type="submit"]::-moz-focus-inner,
|
||||||
|
input[type="submit"]::-moz-focus-inner,
|
||||||
|
input[type="file"] > input[type="button"]::-moz-focus-inner
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
input[type="checkbox"]:focus {
|
||||||
|
-moz-outline-offset: -1px !important;
|
||||||
|
-moz-outline: 1px solid #000 !important;
|
||||||
|
}
|
||||||
|
:focus {
|
||||||
|
outline: none;
|
||||||
|
} /*Remove a dotted line around 1) buttons, 2) checkboxes, 3) links*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
div, span, a, input {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 782px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #ccc url(bg.png) no-repeat center bottom;
|
||||||
|
color: #000;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: Tahoma, Arial, Verdana, Meiryo, "MS Gothic", sans-serif, Lucida Sans;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.all-page {
|
||||||
|
position: relative;
|
||||||
|
width: 690px;
|
||||||
|
margin: 15px auto;
|
||||||
|
padding: 10px 30px 15px 30px;
|
||||||
|
background: #eee;
|
||||||
|
border: 2px solid #999;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.all-page:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1px;
|
||||||
|
top: -1px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
height: calc(100% - 2px);
|
||||||
|
padding: 0 1px;
|
||||||
|
box-shadow: 0px 5px 8px #bbb;
|
||||||
|
z-index: -1;
|
||||||
|
} /*for crop shadow bottom for 4px (2px from border and 2px from calc)*/
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0.6em 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choose-lang {
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin: 0 5px;
|
||||||
|
opacity: 0.9;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
|
p.margin1 { margin: 0.9em 0; }
|
||||||
|
|
||||||
|
.links-box {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example {
|
||||||
|
width: 445px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue-link {
|
||||||
|
color: #36b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gray-link {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.second-title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 1em 0 1em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-box {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-left {
|
||||||
|
display: inline-block;
|
||||||
|
width: 180px;
|
||||||
|
color: #707070;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-left-small {
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: normal;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right-small {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-hint {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#url {
|
||||||
|
width: 300px;
|
||||||
|
height: 23px;
|
||||||
|
padding: 0 3px;
|
||||||
|
border: 1px solid #b0b0b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0 5px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r-text {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio:checked + .r-text { color: #494; }
|
||||||
|
|
||||||
|
.button {
|
||||||
|
min-width: 160px;
|
||||||
|
height: 26px;
|
||||||
|
margin: 0 10px 3px 0;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
background: #f2f2f2;
|
||||||
|
background-image: linear-gradient(to bottom, #f9f9f9, #dadada);
|
||||||
|
border: 1px solid #999;
|
||||||
|
border-radius: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background: #f7f7f7;
|
||||||
|
background-image: linear-gradient(to bottom, #fefefe, #e2e2e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-hint {
|
||||||
|
margin: 0.85em 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
52
assets/ui.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
$(function (){
|
||||||
|
var expires = 365;
|
||||||
|
function clear_file() {
|
||||||
|
var new_file = $("#file").clone();
|
||||||
|
new_file.change(clear_url);
|
||||||
|
$("#file").replaceWith(new_file);
|
||||||
|
}
|
||||||
|
function clear_url() {
|
||||||
|
$("#url").val("")
|
||||||
|
}
|
||||||
|
function on_change_style(e) {
|
||||||
|
var checked = $("input[name=style]:checked");
|
||||||
|
if (checked.val() == "art") {
|
||||||
|
$(".main-title").text("waifu2x");
|
||||||
|
} else {
|
||||||
|
$(".main-title").html("w<s>/a/</s>ifu2x");
|
||||||
|
}
|
||||||
|
$.cookie("style", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function on_change_noise_level(e)
|
||||||
|
{
|
||||||
|
var checked = $("input[name=noise]:checked");
|
||||||
|
$.cookie("noise", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function on_change_scale_factor(e)
|
||||||
|
{
|
||||||
|
var checked = $("input[name=scale]:checked");
|
||||||
|
$.cookie("scale", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function restore_from_cookie()
|
||||||
|
{
|
||||||
|
if ($.cookie("style")) {
|
||||||
|
$("input[name=style]").filter("[value=" + $.cookie("style") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
if ($.cookie("noise")) {
|
||||||
|
$("input[name=noise]").filter("[value=" + $.cookie("noise") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
if ($.cookie("scale")) {
|
||||||
|
$("input[name=scale]").filter("[value=" + $.cookie("scale") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("#url").change(clear_file);
|
||||||
|
$("#file").change(clear_url);
|
||||||
|
$("input[name=style]").change(on_change_style);
|
||||||
|
$("input[name=noise]").change(on_change_noise_level);
|
||||||
|
$("input[name=scale]").change(on_change_scale_factor);
|
||||||
|
|
||||||
|
restore_from_cookie();
|
||||||
|
on_change_style();
|
||||||
|
on_change_scale_factor();
|
||||||
|
on_change_noise_level();
|
||||||
|
})
|
|
@ -1,48 +1,47 @@
|
||||||
require './lib/portable'
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "lib", "?.lua;") .. package.path
|
||||||
|
|
||||||
|
require 'pl'
|
||||||
require 'image'
|
require 'image'
|
||||||
local settings = require './lib/settings'
|
local compression = require 'compression'
|
||||||
local image_loader = require './lib/image_loader'
|
local settings = require 'settings'
|
||||||
|
local image_loader = require 'image_loader'
|
||||||
local function count_lines(file)
|
local iproc = require 'iproc'
|
||||||
local fp = io.open(file, "r")
|
|
||||||
local count = 0
|
|
||||||
for line in fp:lines() do
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
fp:close()
|
|
||||||
|
|
||||||
return count
|
|
||||||
end
|
|
||||||
|
|
||||||
local function crop_4x(x)
|
|
||||||
local w = x:size(3) % 4
|
|
||||||
local h = x:size(2) % 4
|
|
||||||
return image.crop(x, 0, 0, x:size(3) - w, x:size(2) - h)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function load_images(list)
|
local function load_images(list)
|
||||||
local count = count_lines(list)
|
local MARGIN = 32
|
||||||
local fp = io.open(list, "r")
|
local lines = utils.split(file.read(list), "\n")
|
||||||
local x = {}
|
local x = {}
|
||||||
local c = 0
|
for i = 1, #lines do
|
||||||
for line in fp:lines() do
|
local line = lines[i]
|
||||||
local im = crop_4x(image_loader.load_byte(line))
|
local im, alpha = image_loader.load_byte(line)
|
||||||
|
if alpha then
|
||||||
|
io.stderr:write(string.format("\n%s: skip: image has alpha channel.\n", line))
|
||||||
|
else
|
||||||
|
im = iproc.crop_mod4(im)
|
||||||
|
local scale = 1.0
|
||||||
|
if settings.random_half_rate > 0.0 then
|
||||||
|
scale = 2.0
|
||||||
|
end
|
||||||
if im then
|
if im then
|
||||||
if im:size(2) >= settings.crop_size * 2 and im:size(3) >= settings.crop_size * 2 then
|
if im:size(2) > (settings.crop_size * scale + MARGIN) and im:size(3) > (settings.crop_size * scale + MARGIN) then
|
||||||
table.insert(x, im)
|
table.insert(x, compression.compress(im))
|
||||||
|
else
|
||||||
|
io.stderr:write(string.format("\n%s: skip: image is too small (%d > size).\n", line, settings.crop_size * scale + MARGIN))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print("error:" .. line)
|
io.stderr:write(string.format("\n%s: skip: load error.\n", line))
|
||||||
end
|
end
|
||||||
c = c + 1
|
end
|
||||||
xlua.progress(c, count)
|
xlua.progress(i, #lines)
|
||||||
if c % 10 == 0 then
|
if i % 10 == 0 then
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
|
torch.manualSeed(settings.seed)
|
||||||
print(settings)
|
print(settings)
|
||||||
local x = load_images(settings.image_list)
|
local x = load_images(settings.image_list)
|
||||||
torch.save(settings.images, x)
|
torch.save(settings.images, x)
|
||||||
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
require 'cunn'
|
|
||||||
require 'cudnn'
|
|
||||||
require 'cutorch'
|
|
||||||
require './lib/LeakyReLU'
|
|
||||||
local srcnn = require 'lib/srcnn'
|
|
||||||
|
|
||||||
local function cudnn2cunn(cudnn_model)
|
|
||||||
local cunn_model = srcnn.waifu2x("y")
|
|
||||||
local from_seq = cudnn_model:findModules("cudnn.SpatialConvolution")
|
|
||||||
local to_seq = cunn_model:findModules("nn.SpatialConvolutionMM")
|
|
||||||
|
|
||||||
for i = 1, #from_seq do
|
|
||||||
local from = from_seq[i]
|
|
||||||
local to = to_seq[i]
|
|
||||||
to.weight:copy(from.weight)
|
|
||||||
to.bias:copy(from.bias)
|
|
||||||
end
|
|
||||||
cunn_model:cuda()
|
|
||||||
cunn_model:evaluate()
|
|
||||||
return cunn_model
|
|
||||||
end
|
|
||||||
|
|
||||||
local cmd = torch.CmdLine()
|
|
||||||
cmd:text()
|
|
||||||
cmd:text("convert cudnn model to cunn model ")
|
|
||||||
cmd:text("Options:")
|
|
||||||
cmd:option("-model", "./model.t7", 'path of cudnn model file')
|
|
||||||
cmd:option("-iformat", "ascii", 'input format')
|
|
||||||
cmd:option("-oformat", "ascii", 'output format')
|
|
||||||
|
|
||||||
local opt = cmd:parse(arg)
|
|
||||||
local cudnn_model = torch.load(opt.model, opt.iformat)
|
|
||||||
local cunn_model = cudnn2cunn(cudnn_model)
|
|
||||||
torch.save(opt.model, cunn_model, opt.oformat)
|
|
|
@ -1,23 +0,0 @@
|
||||||
-- adapted from https://github.com/marcan/cl-waifu2x
|
|
||||||
require './lib/portable'
|
|
||||||
require './lib/LeakyReLU'
|
|
||||||
local cjson = require "cjson"
|
|
||||||
|
|
||||||
local model = torch.load(arg[1], "ascii")
|
|
||||||
|
|
||||||
local jmodules = {}
|
|
||||||
local modules = model:findModules("nn.SpatialConvolutionMM")
|
|
||||||
for i = 1, #modules, 1 do
|
|
||||||
local module = modules[i]
|
|
||||||
local jmod = {
|
|
||||||
kW = module.kW,
|
|
||||||
kH = module.kH,
|
|
||||||
nInputPlane = module.nInputPlane,
|
|
||||||
nOutputPlane = module.nOutputPlane,
|
|
||||||
bias = torch.totable(module.bias:float()),
|
|
||||||
weight = torch.totable(module.weight:float():reshape(module.nOutputPlane, module.nInputPlane, module.kW, module.kH))
|
|
||||||
}
|
|
||||||
table.insert(jmodules, jmod)
|
|
||||||
end
|
|
||||||
|
|
||||||
io.write(cjson.encode(jmodules))
|
|
|
@ -1,8 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
th waifu2x.lua -noise_level 1 -m noise_scale -i images/miku_small.png -o images/miku_small_waifu2x.png
|
th waifu2x.lua -m scale -i images/miku_small.png -o images/miku_small_waifu2x.png
|
||||||
th waifu2x.lua -noise_level 2 -m noise_scale -i images/miku_small_noisy.png -o images/miku_small_noisy_waifu2x.png
|
th waifu2x.lua -noise_level 2 -m noise_scale -i images/miku_small_noisy.png -o images/miku_small_noisy_waifu2x.png
|
||||||
th waifu2x.lua -noise_level 2 -m noise -i images/miku_noisy.png -o images/miku_noisy_waifu2x.png
|
th waifu2x.lua -noise_level 2 -m noise -i images/miku_noisy.png -o images/miku_noisy_waifu2x.png
|
||||||
th waifu2x.lua -noise_level 2 -m noise_scale -i images/miku_CC_BY-NC_noisy.jpg -o images/miku_CC_BY-NC_noisy_waifu2x.png
|
|
||||||
th waifu2x.lua -noise_level 2 -m noise -i images/lena.png -o images/lena_waifu2x.png
|
th waifu2x.lua -noise_level 2 -m noise -i images/lena.png -o images/lena_waifu2x.png
|
||||||
th waifu2x.lua -m scale -model_dir models/ukbench -i images/lena.png -o images/lena_waifu2x_ukbench.png
|
th waifu2x.lua -m scale -model_dir models/ukbench -i images/lena.png -o images/lena_waifu2x_ukbench.png
|
||||||
|
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 397 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 605 KiB After Width: | Height: | Size: 651 KiB |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 162 KiB |
BIN
images/slide.odp
BIN
images/slide.png
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 499 KiB After Width: | Height: | Size: 493 KiB |
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 368 KiB |
Before Width: | Height: | Size: 378 KiB After Width: | Height: | Size: 352 KiB |
39
lib/ClippedWeightedHuberCriterion.lua
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
-- ref: https://en.wikipedia.org/wiki/Huber_loss
|
||||||
|
local ClippedWeightedHuberCriterion, parent = torch.class('w2nn.ClippedWeightedHuberCriterion','nn.Criterion')
|
||||||
|
|
||||||
|
function ClippedWeightedHuberCriterion:__init(w, gamma, clip)
|
||||||
|
parent.__init(self)
|
||||||
|
self.clip = clip
|
||||||
|
self.gamma = gamma or 1.0
|
||||||
|
self.weight = w:clone()
|
||||||
|
self.diff = torch.Tensor()
|
||||||
|
self.diff_abs = torch.Tensor()
|
||||||
|
--self.outlier_rate = 0.0
|
||||||
|
self.square_loss_buff = torch.Tensor()
|
||||||
|
self.linear_loss_buff = torch.Tensor()
|
||||||
|
end
|
||||||
|
function ClippedWeightedHuberCriterion:updateOutput(input, target)
|
||||||
|
self.diff:resizeAs(input):copy(input)
|
||||||
|
self.diff[torch.lt(self.diff, self.clip[1])] = self.clip[1]
|
||||||
|
self.diff[torch.gt(self.diff, self.clip[2])] = self.clip[2]
|
||||||
|
for i = 1, input:size(1) do
|
||||||
|
self.diff[i]:add(-1, target[i]):cmul(self.weight)
|
||||||
|
end
|
||||||
|
self.diff_abs:resizeAs(self.diff):copy(self.diff):abs()
|
||||||
|
|
||||||
|
local square_targets = self.diff[torch.lt(self.diff_abs, self.gamma)]
|
||||||
|
local linear_targets = self.diff[torch.ge(self.diff_abs, self.gamma)]
|
||||||
|
local square_loss = self.square_loss_buff:resizeAs(square_targets):copy(square_targets):pow(2.0):mul(0.5):sum()
|
||||||
|
local linear_loss = self.linear_loss_buff:resizeAs(linear_targets):copy(linear_targets):abs():add(-0.5 * self.gamma):mul(self.gamma):sum()
|
||||||
|
|
||||||
|
--self.outlier_rate = linear_targets:nElement() / input:nElement()
|
||||||
|
self.output = (square_loss + linear_loss) / input:nElement()
|
||||||
|
return self.output
|
||||||
|
end
|
||||||
|
function ClippedWeightedHuberCriterion:updateGradInput(input, target)
|
||||||
|
local norm = 1.0 / input:nElement()
|
||||||
|
self.gradInput:resizeAs(self.diff):copy(self.diff):mul(norm)
|
||||||
|
local outlier = torch.ge(self.diff_abs, self.gamma)
|
||||||
|
self.gradInput[outlier] = torch.sign(self.diff[outlier]) * self.gamma * norm
|
||||||
|
return self.gradInput
|
||||||
|
end
|
77
lib/DepthExpand2x.lua
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
if w2nn.DepthExpand2x then
|
||||||
|
return w2nn.DepthExpand2x
|
||||||
|
end
|
||||||
|
local DepthExpand2x, parent = torch.class('w2nn.DepthExpand2x','nn.Module')
|
||||||
|
|
||||||
|
function DepthExpand2x:__init()
|
||||||
|
parent:__init()
|
||||||
|
end
|
||||||
|
|
||||||
|
function DepthExpand2x:updateOutput(input)
|
||||||
|
local x = input
|
||||||
|
-- (batch_size, depth, height, width)
|
||||||
|
self.shape = x:size()
|
||||||
|
|
||||||
|
assert(self.shape:size() == 4, "input must be 4d tensor")
|
||||||
|
assert(self.shape[2] % 4 == 0, "depth must be depth % 4 = 0")
|
||||||
|
-- (batch_size, width, height, depth)
|
||||||
|
x = x:transpose(2, 4)
|
||||||
|
-- (batch_size, width, height * 2, depth / 2)
|
||||||
|
x = x:reshape(self.shape[1], self.shape[4], self.shape[3] * 2, self.shape[2] / 2)
|
||||||
|
-- (batch_size, height * 2, width, depth / 2)
|
||||||
|
x = x:transpose(2, 3)
|
||||||
|
-- (batch_size, height * 2, width * 2, depth / 4)
|
||||||
|
x = x:reshape(self.shape[1], self.shape[3] * 2, self.shape[4] * 2, self.shape[2] / 4)
|
||||||
|
-- (batch_size, depth / 4, height * 2, width * 2)
|
||||||
|
x = x:transpose(2, 4)
|
||||||
|
x = x:transpose(3, 4)
|
||||||
|
self.output:resizeAs(x):copy(x) -- contiguous
|
||||||
|
|
||||||
|
return self.output
|
||||||
|
end
|
||||||
|
|
||||||
|
function DepthExpand2x:updateGradInput(input, gradOutput)
|
||||||
|
-- (batch_size, depth / 4, height * 2, width * 2)
|
||||||
|
local x = gradOutput
|
||||||
|
-- (batch_size, height * 2, width * 2, depth / 4)
|
||||||
|
x = x:transpose(2, 4)
|
||||||
|
x = x:transpose(2, 3)
|
||||||
|
-- (batch_size, height * 2, width, depth / 2)
|
||||||
|
x = x:reshape(self.shape[1], self.shape[3] * 2, self.shape[4], self.shape[2] / 2)
|
||||||
|
-- (batch_size, width, height * 2, depth / 2)
|
||||||
|
x = x:transpose(2, 3)
|
||||||
|
-- (batch_size, width, height, depth)
|
||||||
|
x = x:reshape(self.shape[1], self.shape[4], self.shape[3], self.shape[2])
|
||||||
|
-- (batch_size, depth, height, width)
|
||||||
|
x = x:transpose(2, 4)
|
||||||
|
|
||||||
|
self.gradInput:resizeAs(x):copy(x)
|
||||||
|
|
||||||
|
return self.gradInput
|
||||||
|
end
|
||||||
|
|
||||||
|
function DepthExpand2x.test()
|
||||||
|
require 'image'
|
||||||
|
local function show(x)
|
||||||
|
local img = torch.Tensor(3, x:size(3), x:size(4))
|
||||||
|
img[1]:copy(x[1][1])
|
||||||
|
img[2]:copy(x[1][2])
|
||||||
|
img[3]:copy(x[1][3])
|
||||||
|
image.display(img)
|
||||||
|
end
|
||||||
|
local img = image.lena()
|
||||||
|
local x = torch.Tensor(1, img:size(1) * 4, img:size(2), img:size(3))
|
||||||
|
for i = 0, img:size(1) * 4 - 1 do
|
||||||
|
src_index = ((i % 3) + 1)
|
||||||
|
x[1][i + 1]:copy(img[src_index])
|
||||||
|
end
|
||||||
|
show(x)
|
||||||
|
|
||||||
|
local de2x = w2nn.DepthExpand2x()
|
||||||
|
out = de2x:forward(x)
|
||||||
|
show(out)
|
||||||
|
out = de2x:updateGradInput(x, out)
|
||||||
|
show(out)
|
||||||
|
end
|
||||||
|
|
||||||
|
return DepthExpand2x
|
|
@ -1,7 +1,8 @@
|
||||||
if nn.LeakyReLU then
|
if w2nn and w2nn.LeakyReLU then
|
||||||
return
|
return w2nn.LeakyReLU
|
||||||
end
|
end
|
||||||
local LeakyReLU, parent = torch.class('nn.LeakyReLU','nn.Module')
|
|
||||||
|
local LeakyReLU, parent = torch.class('w2nn.LeakyReLU','nn.Module')
|
||||||
|
|
||||||
function LeakyReLU:__init(negative_scale)
|
function LeakyReLU:__init(negative_scale)
|
||||||
parent.__init(self)
|
parent.__init(self)
|
||||||
|
|
31
lib/LeakyReLU_deprecated.lua
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
if nn.LeakyReLU then
|
||||||
|
return nn.LeakyReLU
|
||||||
|
end
|
||||||
|
|
||||||
|
local LeakyReLU, parent = torch.class('nn.LeakyReLU','nn.Module')
|
||||||
|
|
||||||
|
function LeakyReLU:__init(negative_scale)
|
||||||
|
parent.__init(self)
|
||||||
|
self.negative_scale = negative_scale or 0.333
|
||||||
|
self.negative = torch.Tensor()
|
||||||
|
end
|
||||||
|
|
||||||
|
function LeakyReLU:updateOutput(input)
|
||||||
|
self.output:resizeAs(input):copy(input):abs():add(input):div(2)
|
||||||
|
self.negative:resizeAs(input):copy(input):abs():add(-1.0, input):mul(-0.5*self.negative_scale)
|
||||||
|
self.output:add(self.negative)
|
||||||
|
|
||||||
|
return self.output
|
||||||
|
end
|
||||||
|
|
||||||
|
function LeakyReLU:updateGradInput(input, gradOutput)
|
||||||
|
self.gradInput:resizeAs(gradOutput)
|
||||||
|
-- filter positive
|
||||||
|
self.negative:sign():add(1)
|
||||||
|
torch.cmul(self.gradInput, gradOutput, self.negative)
|
||||||
|
-- filter negative
|
||||||
|
self.negative:add(-1):mul(-1 * self.negative_scale):cmul(gradOutput)
|
||||||
|
self.gradInput:add(self.negative)
|
||||||
|
|
||||||
|
return self.gradInput
|
||||||
|
end
|
25
lib/WeightedMSECriterion.lua
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
local WeightedMSECriterion, parent = torch.class('w2nn.WeightedMSECriterion','nn.Criterion')
|
||||||
|
|
||||||
|
function WeightedMSECriterion:__init(w)
|
||||||
|
parent.__init(self)
|
||||||
|
self.weight = w:clone()
|
||||||
|
self.diff = torch.Tensor()
|
||||||
|
self.loss = torch.Tensor()
|
||||||
|
end
|
||||||
|
|
||||||
|
function WeightedMSECriterion:updateOutput(input, target)
|
||||||
|
self.diff:resizeAs(input):copy(input)
|
||||||
|
for i = 1, input:size(1) do
|
||||||
|
self.diff[i]:add(-1, target[i]):cmul(self.weight)
|
||||||
|
end
|
||||||
|
self.loss:resizeAs(self.diff):copy(self.diff):cmul(self.diff)
|
||||||
|
self.output = self.loss:mean()
|
||||||
|
|
||||||
|
return self.output
|
||||||
|
end
|
||||||
|
|
||||||
|
function WeightedMSECriterion:updateGradInput(input, target)
|
||||||
|
local norm = 2.0 / input:nElement()
|
||||||
|
self.gradInput:resizeAs(input):copy(self.diff):mul(norm)
|
||||||
|
return self.gradInput
|
||||||
|
end
|
80
lib/alpha_util.lua
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
local w2nn = require 'w2nn'
|
||||||
|
local reconstruct = require 'reconstruct'
|
||||||
|
local image = require 'image'
|
||||||
|
local iproc = require 'iproc'
|
||||||
|
local gm = require 'graphicsmagick'
|
||||||
|
|
||||||
|
alpha_util = {}
|
||||||
|
|
||||||
|
function alpha_util.make_border(rgb, alpha, offset)
|
||||||
|
if not alpha then
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
local sum2d = nn.SpatialConvolutionMM(1, 1, 3, 3, 1, 1, 1, 1):cuda()
|
||||||
|
sum2d.weight:fill(1)
|
||||||
|
sum2d.bias:zero()
|
||||||
|
|
||||||
|
local mask = alpha:clone()
|
||||||
|
mask[torch.gt(mask, 0.0)] = 1
|
||||||
|
mask[torch.eq(mask, 0.0)] = 0
|
||||||
|
local mask_nega = (mask - 1):abs():byte()
|
||||||
|
local eps = 1.0e-7
|
||||||
|
|
||||||
|
rgb = rgb:clone()
|
||||||
|
rgb[1][mask_nega] = 0
|
||||||
|
rgb[2][mask_nega] = 0
|
||||||
|
rgb[3][mask_nega] = 0
|
||||||
|
|
||||||
|
for i = 1, offset do
|
||||||
|
local mask_weight = sum2d:forward(mask:cuda()):float()
|
||||||
|
local border = rgb:clone()
|
||||||
|
for j = 1, 3 do
|
||||||
|
border[j]:copy(sum2d:forward(rgb[j]:reshape(1, rgb:size(2), rgb:size(3)):cuda()))
|
||||||
|
border[j]:cdiv((mask_weight + eps))
|
||||||
|
rgb[j][mask_nega] = border[j][mask_nega]
|
||||||
|
end
|
||||||
|
mask = mask_weight:clone()
|
||||||
|
mask[torch.gt(mask_weight, 0.0)] = 1
|
||||||
|
mask_nega = (mask - 1):abs():byte()
|
||||||
|
end
|
||||||
|
rgb[torch.gt(rgb, 1.0)] = 1.0
|
||||||
|
rgb[torch.lt(rgb, 0.0)] = 0.0
|
||||||
|
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
function alpha_util.composite(rgb, alpha, model2x)
|
||||||
|
if not alpha then
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
if not (alpha:size(2) == rgb:size(2) and alpha:size(3) == rgb:size(3)) then
|
||||||
|
if model2x then
|
||||||
|
alpha = reconstruct.scale(model2x, 2.0, alpha)
|
||||||
|
else
|
||||||
|
alpha = gm.Image(alpha, "I", "DHW"):size(rgb:size(3), rgb:size(2), "Sinc"):toTensor("float", "I", "DHW")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local out = torch.Tensor(4, rgb:size(2), rgb:size(3))
|
||||||
|
out[1]:copy(rgb[1])
|
||||||
|
out[2]:copy(rgb[2])
|
||||||
|
out[3]:copy(rgb[3])
|
||||||
|
out[4]:copy(alpha)
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
local function test()
|
||||||
|
require 'sys'
|
||||||
|
require 'trepl'
|
||||||
|
torch.setdefaulttensortype("torch.FloatTensor")
|
||||||
|
|
||||||
|
local image_loader = require 'image_loader'
|
||||||
|
local rgb, alpha = image_loader.load_float("alpha.png")
|
||||||
|
local t = sys.clock()
|
||||||
|
rgb = alpha_util.make_border(rgb, alpha, 7)
|
||||||
|
print(sys.clock() - t)
|
||||||
|
print(rgb:min(), rgb:max())
|
||||||
|
image.display({image = rgb, min = 0, max = 1})
|
||||||
|
image.save("out.png", rgb)
|
||||||
|
end
|
||||||
|
--test()
|
||||||
|
|
||||||
|
return alpha_util
|
|
@ -1,9 +1,5 @@
|
||||||
require './lib/portable'
|
|
||||||
require './lib/LeakyReLU'
|
|
||||||
|
|
||||||
torch.setdefaulttensortype("torch.FloatTensor")
|
|
||||||
|
|
||||||
-- ref: https://github.com/torch/nn/issues/112#issuecomment-64427049
|
-- ref: https://github.com/torch/nn/issues/112#issuecomment-64427049
|
||||||
|
|
||||||
local function zeroDataSize(data)
|
local function zeroDataSize(data)
|
||||||
if type(data) == 'table' then
|
if type(data) == 'table' then
|
||||||
for i = 1, #data do
|
for i = 1, #data do
|
||||||
|
@ -14,7 +10,6 @@ local function zeroDataSize(data)
|
||||||
end
|
end
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Resize the output, gradInput, etc temporary tensors to zero (so that the
|
-- Resize the output, gradInput, etc temporary tensors to zero (so that the
|
||||||
-- on disk size is smaller)
|
-- on disk size is smaller)
|
||||||
local function cleanupModel(node)
|
local function cleanupModel(node)
|
||||||
|
@ -27,7 +22,7 @@ local function cleanupModel(node)
|
||||||
if node.finput ~= nil then
|
if node.finput ~= nil then
|
||||||
node.finput = zeroDataSize(node.finput)
|
node.finput = zeroDataSize(node.finput)
|
||||||
end
|
end
|
||||||
if tostring(node) == "nn.LeakyReLU" then
|
if tostring(node) == "nn.LeakyReLU" or tostring(node) == "w2nn.LeakyReLU" then
|
||||||
if node.negative ~= nil then
|
if node.negative ~= nil then
|
||||||
node.negative = zeroDataSize(node.negative)
|
node.negative = zeroDataSize(node.negative)
|
||||||
end
|
end
|
||||||
|
@ -46,23 +41,8 @@ local function cleanupModel(node)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
collectgarbage()
|
|
||||||
end
|
end
|
||||||
|
function w2nn.cleanup_model(model)
|
||||||
local cmd = torch.CmdLine()
|
|
||||||
cmd:text()
|
|
||||||
cmd:text("cleanup model")
|
|
||||||
cmd:text("Options:")
|
|
||||||
cmd:option("-model", "./model.t7", 'path of model file')
|
|
||||||
cmd:option("-iformat", "binary", 'input format')
|
|
||||||
cmd:option("-oformat", "binary", 'output format')
|
|
||||||
|
|
||||||
local opt = cmd:parse(arg)
|
|
||||||
local model = torch.load(opt.model, opt.iformat)
|
|
||||||
if model then
|
|
||||||
cleanupModel(model)
|
cleanupModel(model)
|
||||||
torch.save(opt.model, model, opt.oformat)
|
return model
|
||||||
else
|
|
||||||
error("model not found")
|
|
||||||
end
|
end
|
17
lib/compression.lua
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-- snapply compression for ByteTensor
|
||||||
|
require 'snappy'
|
||||||
|
|
||||||
|
local compression = {}
|
||||||
|
compression.compress = function (bt)
|
||||||
|
local enc = snappy.compress(bt:storage():string())
|
||||||
|
return {bt:size(), torch.ByteStorage():string(enc)}
|
||||||
|
end
|
||||||
|
compression.decompress = function(data)
|
||||||
|
local size = data[1]
|
||||||
|
local dec = snappy.decompress(data[2]:string())
|
||||||
|
local bt = torch.ByteTensor(unpack(torch.totable(size)))
|
||||||
|
bt:storage():string(dec)
|
||||||
|
return bt
|
||||||
|
end
|
||||||
|
|
||||||
|
return compression
|
124
lib/data_augmentation.lua
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
require 'image'
|
||||||
|
local iproc = require 'iproc'
|
||||||
|
local gm = require 'graphicsmagick'
|
||||||
|
|
||||||
|
local data_augmentation = {}
|
||||||
|
|
||||||
|
local function pcacov(x)
|
||||||
|
local mean = torch.mean(x, 1)
|
||||||
|
local xm = x - torch.ger(torch.ones(x:size(1)), mean:squeeze())
|
||||||
|
local c = torch.mm(xm:t(), xm)
|
||||||
|
c:div(x:size(1) - 1)
|
||||||
|
local ce, cv = torch.symeig(c, 'V')
|
||||||
|
return ce, cv
|
||||||
|
end
|
||||||
|
function data_augmentation.color_noise(src, p, factor)
|
||||||
|
factor = factor or 0.1
|
||||||
|
if torch.uniform() < p then
|
||||||
|
local src, conversion = iproc.byte2float(src)
|
||||||
|
local src_t = src:reshape(src:size(1), src:nElement() / src:size(1)):t():contiguous()
|
||||||
|
local ce, cv = pcacov(src_t)
|
||||||
|
local color_scale = torch.Tensor(3):uniform(1 / (1 + factor), 1 + factor)
|
||||||
|
|
||||||
|
pca_space = torch.mm(src_t, cv):t():contiguous()
|
||||||
|
for i = 1, 3 do
|
||||||
|
pca_space[i]:mul(color_scale[i])
|
||||||
|
end
|
||||||
|
local dest = torch.mm(pca_space:t(), cv:t()):t():contiguous():resizeAs(src)
|
||||||
|
dest[torch.lt(dest, 0.0)] = 0.0
|
||||||
|
dest[torch.gt(dest, 1.0)] = 1.0
|
||||||
|
|
||||||
|
if conversion then
|
||||||
|
dest = iproc.float2byte(dest)
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
else
|
||||||
|
return src
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function data_augmentation.overlay(src, p)
|
||||||
|
if torch.uniform() < p then
|
||||||
|
local r = torch.uniform()
|
||||||
|
local src, conversion = iproc.byte2float(src)
|
||||||
|
src = src:contiguous()
|
||||||
|
local flip = data_augmentation.flip(src)
|
||||||
|
flip:mul(r):add(src * (1.0 - r))
|
||||||
|
if conversion then
|
||||||
|
flip = iproc.float2byte(flip)
|
||||||
|
end
|
||||||
|
return flip
|
||||||
|
else
|
||||||
|
return src
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function data_augmentation.unsharp_mask(src, p)
|
||||||
|
if torch.uniform() < p then
|
||||||
|
local radius = 0 -- auto
|
||||||
|
local sigma = torch.uniform(0.5, 1.5)
|
||||||
|
local amount = torch.uniform(0.1, 0.9)
|
||||||
|
local threshold = torch.uniform(0.0, 0.05)
|
||||||
|
local unsharp = gm.Image(src, "RGB", "DHW"):
|
||||||
|
unsharpMask(radius, sigma, amount, threshold):
|
||||||
|
toTensor("float", "RGB", "DHW")
|
||||||
|
|
||||||
|
if src:type() == "torch.ByteTensor" then
|
||||||
|
return iproc.float2byte(unsharp)
|
||||||
|
else
|
||||||
|
return unsharp
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return src
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function data_augmentation.shift_1px(src)
|
||||||
|
-- reducing the even/odd issue in nearest neighbor scaler.
|
||||||
|
local direction = torch.random(1, 4)
|
||||||
|
local x_shift = 0
|
||||||
|
local y_shift = 0
|
||||||
|
if direction == 1 then
|
||||||
|
x_shift = 1
|
||||||
|
y_shift = 0
|
||||||
|
elseif direction == 2 then
|
||||||
|
x_shift = 0
|
||||||
|
y_shift = 1
|
||||||
|
elseif direction == 3 then
|
||||||
|
x_shift = 1
|
||||||
|
y_shift = 1
|
||||||
|
elseif flip == 4 then
|
||||||
|
x_shift = 0
|
||||||
|
y_shift = 0
|
||||||
|
end
|
||||||
|
local w = src:size(3) - x_shift
|
||||||
|
local h = src:size(2) - y_shift
|
||||||
|
w = w - (w % 4)
|
||||||
|
h = h - (h % 4)
|
||||||
|
local dest = iproc.crop(src, x_shift, y_shift, x_shift + w, y_shift + h)
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
function data_augmentation.flip(src)
|
||||||
|
local flip = torch.random(1, 4)
|
||||||
|
local tr = torch.random(1, 2)
|
||||||
|
local src, conversion = iproc.byte2float(src)
|
||||||
|
local dest
|
||||||
|
|
||||||
|
src = src:contiguous()
|
||||||
|
if tr == 1 then
|
||||||
|
-- pass
|
||||||
|
elseif tr == 2 then
|
||||||
|
src = src:transpose(2, 3):contiguous()
|
||||||
|
end
|
||||||
|
if flip == 1 then
|
||||||
|
dest = image.hflip(src)
|
||||||
|
elseif flip == 2 then
|
||||||
|
dest = image.vflip(src)
|
||||||
|
elseif flip == 3 then
|
||||||
|
dest = image.hflip(image.vflip(src))
|
||||||
|
elseif flip == 4 then
|
||||||
|
dest = src
|
||||||
|
end
|
||||||
|
if conversion then
|
||||||
|
dest = iproc.float2byte(dest)
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
return data_augmentation
|
|
@ -1,74 +1,108 @@
|
||||||
local gm = require 'graphicsmagick'
|
local gm = require 'graphicsmagick'
|
||||||
local ffi = require 'ffi'
|
local ffi = require 'ffi'
|
||||||
|
local iproc = require 'iproc'
|
||||||
require 'pl'
|
require 'pl'
|
||||||
|
|
||||||
local image_loader = {}
|
local image_loader = {}
|
||||||
|
|
||||||
function image_loader.decode_float(blob)
|
local clip_eps8 = (1.0 / 255.0) * 0.5 - (1.0e-7 * (1.0 / 255.0) * 0.5)
|
||||||
local im, alpha = image_loader.decode_byte(blob)
|
local clip_eps16 = (1.0 / 65535.0) * 0.5 - (1.0e-7 * (1.0 / 65535.0) * 0.5)
|
||||||
if im then
|
local background_color = 0.5
|
||||||
im = im:float():div(255)
|
|
||||||
end
|
function image_loader.encode_png(rgb, depth)
|
||||||
return im, alpha
|
depth = depth or 8
|
||||||
end
|
rgb = iproc.byte2float(rgb)
|
||||||
function image_loader.encode_png(rgb, alpha)
|
if depth < 16 then
|
||||||
if rgb:type() == "torch.ByteTensor" then
|
rgb = rgb:clone():add(clip_eps8)
|
||||||
error("expect FloatTensor")
|
rgb[torch.lt(rgb, 0.0)] = 0.0
|
||||||
end
|
rgb[torch.gt(rgb, 1.0)] = 1.0
|
||||||
if alpha then
|
rgb = rgb:mul(255):long():float():div(255)
|
||||||
if not (alpha:size(2) == rgb:size(2) and alpha:size(3) == rgb:size(3)) then
|
|
||||||
alpha = gm.Image(alpha, "I", "DHW"):size(rgb:size(3), rgb:size(2), "Sinc"):toTensor("float", "I", "DHW")
|
|
||||||
end
|
|
||||||
local rgba = torch.Tensor(4, rgb:size(2), rgb:size(3))
|
|
||||||
rgba[1]:copy(rgb[1])
|
|
||||||
rgba[2]:copy(rgb[2])
|
|
||||||
rgba[3]:copy(rgb[3])
|
|
||||||
rgba[4]:copy(alpha)
|
|
||||||
local im = gm.Image():fromTensor(rgba, "RGBA", "DHW")
|
|
||||||
im:format("png")
|
|
||||||
return im:toBlob(9)
|
|
||||||
else
|
else
|
||||||
local im = gm.Image(rgb, "RGB", "DHW")
|
rgb = rgb:clone():add(clip_eps16)
|
||||||
im:format("png")
|
rgb[torch.lt(rgb, 0.0)] = 0.0
|
||||||
return im:toBlob(9)
|
rgb[torch.gt(rgb, 1.0)] = 1.0
|
||||||
|
rgb = rgb:mul(65535):long():float():div(65535)
|
||||||
end
|
end
|
||||||
|
local im
|
||||||
|
if rgb:size(1) == 4 then -- RGBA
|
||||||
|
im = gm.Image(rgb, "RGBA", "DHW")
|
||||||
|
elseif rgb:size(1) == 3 then -- RGB
|
||||||
|
im = gm.Image(rgb, "RGB", "DHW")
|
||||||
|
elseif rgb:size(1) == 1 then -- Y
|
||||||
|
im = gm.Image(rgb, "I", "DHW")
|
||||||
|
-- im:colorspace("GRAY") -- it does not work
|
||||||
end
|
end
|
||||||
function image_loader.save_png(filename, rgb, alpha)
|
return im:depth(depth):format("PNG"):toString(9)
|
||||||
local blob, len = image_loader.encode_png(rgb, alpha)
|
end
|
||||||
|
function image_loader.save_png(filename, rgb, depth)
|
||||||
|
depth = depth or 8
|
||||||
|
local blob = image_loader.encode_png(rgb, depth)
|
||||||
local fp = io.open(filename, "wb")
|
local fp = io.open(filename, "wb")
|
||||||
fp:write(ffi.string(blob, len))
|
if not fp then
|
||||||
|
error("IO error: " .. filename)
|
||||||
|
end
|
||||||
|
fp:write(blob)
|
||||||
fp:close()
|
fp:close()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
function image_loader.decode_byte(blob)
|
function image_loader.decode_float(blob)
|
||||||
local load_image = function()
|
local load_image = function()
|
||||||
local im = gm.Image()
|
local im = gm.Image()
|
||||||
local alpha = nil
|
local alpha = nil
|
||||||
|
local gamma_lcd = 0.454545
|
||||||
|
|
||||||
im:fromBlob(blob, #blob)
|
im:fromBlob(blob, #blob)
|
||||||
|
|
||||||
|
if im:colorspace() == "CMYK" then
|
||||||
|
im:colorspace("RGB")
|
||||||
|
end
|
||||||
|
local gamma = math.floor(im:gamma() * 1000000) / 1000000
|
||||||
|
if gamma ~= 0 and gamma ~= gamma_lcd then
|
||||||
|
local cg = gamma / gamma_lcd
|
||||||
|
im:gammaCorrection(cg, "Red")
|
||||||
|
im:gammaCorrection(cg, "Blue")
|
||||||
|
im:gammaCorrection(cg, "Green")
|
||||||
|
end
|
||||||
-- FIXME: How to detect that a image has an alpha channel?
|
-- FIXME: How to detect that a image has an alpha channel?
|
||||||
if blob:sub(1, 4) == "\x89PNG" or blob:sub(1, 3) == "GIF" then
|
if blob:sub(1, 4) == "\x89PNG" or blob:sub(1, 3) == "GIF" then
|
||||||
-- split alpha channel
|
-- split alpha channel
|
||||||
im = im:toTensor('float', 'RGBA', 'DHW')
|
im = im:toTensor('float', 'RGBA', 'DHW')
|
||||||
local sum_alpha = (im[4] - 1):sum()
|
local sum_alpha = (im[4] - 1.0):sum()
|
||||||
if sum_alpha > 0 or sum_alpha < 0 then
|
if sum_alpha < 0 then
|
||||||
alpha = im[4]:reshape(1, im:size(2), im:size(3))
|
alpha = im[4]:reshape(1, im:size(2), im:size(3))
|
||||||
|
-- drop full transparent background
|
||||||
|
local mask = torch.le(alpha, 0.0)
|
||||||
|
im[1][mask] = background_color
|
||||||
|
im[2][mask] = background_color
|
||||||
|
im[3][mask] = background_color
|
||||||
end
|
end
|
||||||
local new_im = torch.FloatTensor(3, im:size(2), im:size(3))
|
local new_im = torch.FloatTensor(3, im:size(2), im:size(3))
|
||||||
new_im[1]:copy(im[1])
|
new_im[1]:copy(im[1])
|
||||||
new_im[2]:copy(im[2])
|
new_im[2]:copy(im[2])
|
||||||
new_im[3]:copy(im[3])
|
new_im[3]:copy(im[3])
|
||||||
im = new_im:mul(255):byte()
|
im = new_im
|
||||||
else
|
else
|
||||||
im = im:toTensor('byte', 'RGB', 'DHW')
|
im = im:toTensor('float', 'RGB', 'DHW')
|
||||||
end
|
end
|
||||||
return {im, alpha}
|
return {im, alpha, blob}
|
||||||
end
|
end
|
||||||
local state, ret = pcall(load_image)
|
local state, ret = pcall(load_image)
|
||||||
if state then
|
if state then
|
||||||
return ret[1], ret[2]
|
return ret[1], ret[2], ret[3]
|
||||||
else
|
else
|
||||||
return nil
|
return nil, nil, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function image_loader.decode_byte(blob)
|
||||||
|
local im, alpha
|
||||||
|
im, alpha, blob = image_loader.decode_float(blob)
|
||||||
|
|
||||||
|
if im then
|
||||||
|
im = iproc.float2byte(im)
|
||||||
|
-- hmm, alpha does not convert here
|
||||||
|
return im, alpha, blob
|
||||||
|
else
|
||||||
|
return nil, nil, nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function image_loader.load_float(file)
|
function image_loader.load_float(file)
|
||||||
|
@ -90,18 +124,16 @@ function image_loader.load_byte(file)
|
||||||
return image_loader.decode_byte(buff)
|
return image_loader.decode_byte(buff)
|
||||||
end
|
end
|
||||||
local function test()
|
local function test()
|
||||||
require 'image'
|
torch.setdefaulttensortype("torch.FloatTensor")
|
||||||
local img
|
local a = image_loader.load_float("../images/lena.png")
|
||||||
img = image_loader.load_float("./a.jpg")
|
local blob = image_loader.encode_png(a)
|
||||||
if img then
|
local b = image_loader.decode_float(blob)
|
||||||
print(img:min())
|
assert((b - a):abs():sum() == 0)
|
||||||
print(img:max())
|
|
||||||
image.display(img)
|
a = image_loader.load_byte("../images/lena.png")
|
||||||
end
|
blob = image_loader.encode_png(a)
|
||||||
img = image_loader.load_float("./b.png")
|
b = image_loader.decode_byte(blob)
|
||||||
if img then
|
assert((b:float() - a:float()):abs():sum() == 0)
|
||||||
image.display(img)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
--test()
|
--test()
|
||||||
return image_loader
|
return image_loader
|
||||||
|
|
137
lib/iproc.lua
|
@ -1,16 +1,83 @@
|
||||||
local gm = require 'graphicsmagick'
|
local gm = require 'graphicsmagick'
|
||||||
local image = require 'image'
|
local image = require 'image'
|
||||||
local iproc = {}
|
|
||||||
|
|
||||||
function iproc.scale(src, width, height, filter)
|
local iproc = {}
|
||||||
local t = "float"
|
local clip_eps8 = (1.0 / 255.0) * 0.5 - (1.0e-7 * (1.0 / 255.0) * 0.5)
|
||||||
if src:type() == "torch.ByteTensor" then
|
|
||||||
t = "byte"
|
function iproc.crop_mod4(src)
|
||||||
|
local w = src:size(3) % 4
|
||||||
|
local h = src:size(2) % 4
|
||||||
|
return iproc.crop(src, 0, 0, src:size(3) - w, src:size(2) - h)
|
||||||
end
|
end
|
||||||
|
function iproc.crop(src, w1, h1, w2, h2)
|
||||||
|
local dest
|
||||||
|
if src:dim() == 3 then
|
||||||
|
dest = src[{{}, { h1 + 1, h2 }, { w1 + 1, w2 }}]:clone()
|
||||||
|
else -- dim == 2
|
||||||
|
dest = src[{{ h1 + 1, h2 }, { w1 + 1, w2 }}]:clone()
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
function iproc.crop_nocopy(src, w1, h1, w2, h2)
|
||||||
|
local dest
|
||||||
|
if src:dim() == 3 then
|
||||||
|
dest = src[{{}, { h1 + 1, h2 }, { w1 + 1, w2 }}]
|
||||||
|
else -- dim == 2
|
||||||
|
dest = src[{{ h1 + 1, h2 }, { w1 + 1, w2 }}]
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
function iproc.byte2float(src)
|
||||||
|
local conversion = false
|
||||||
|
local dest = src
|
||||||
|
if src:type() == "torch.ByteTensor" then
|
||||||
|
conversion = true
|
||||||
|
dest = src:float():div(255.0)
|
||||||
|
end
|
||||||
|
return dest, conversion
|
||||||
|
end
|
||||||
|
function iproc.float2byte(src)
|
||||||
|
local conversion = false
|
||||||
|
local dest = src
|
||||||
|
if src:type() == "torch.FloatTensor" then
|
||||||
|
conversion = true
|
||||||
|
dest = (src + clip_eps8):mul(255.0)
|
||||||
|
dest[torch.lt(dest, 0.0)] = 0
|
||||||
|
dest[torch.gt(dest, 255.0)] = 255.0
|
||||||
|
dest = dest:byte()
|
||||||
|
end
|
||||||
|
return dest, conversion
|
||||||
|
end
|
||||||
|
function iproc.scale(src, width, height, filter)
|
||||||
|
local conversion, color
|
||||||
|
src, conversion = iproc.byte2float(src)
|
||||||
|
filter = filter or "Box"
|
||||||
|
if src:size(1) == 3 then
|
||||||
|
color = "RGB"
|
||||||
|
else
|
||||||
|
color = "I"
|
||||||
|
end
|
||||||
|
local im = gm.Image(src, color, "DHW")
|
||||||
|
im:size(math.ceil(width), math.ceil(height), filter)
|
||||||
|
local dest = im:toTensor("float", color, "DHW")
|
||||||
|
if conversion then
|
||||||
|
dest = iproc.float2byte(dest)
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
function iproc.scale_with_gamma22(src, width, height, filter)
|
||||||
|
local conversion
|
||||||
|
src, conversion = iproc.byte2float(src)
|
||||||
filter = filter or "Box"
|
filter = filter or "Box"
|
||||||
local im = gm.Image(src, "RGB", "DHW")
|
local im = gm.Image(src, "RGB", "DHW")
|
||||||
im:size(math.ceil(width), math.ceil(height), filter)
|
im:gammaCorrection(1.0 / 2.2):
|
||||||
return im:toTensor(t, "RGB", "DHW")
|
size(math.ceil(width), math.ceil(height), filter):
|
||||||
|
gammaCorrection(2.2)
|
||||||
|
local dest = im:toTensor("float", "RGB", "DHW")
|
||||||
|
if conversion then
|
||||||
|
dest = iproc.float2byte(dest)
|
||||||
|
end
|
||||||
|
return dest
|
||||||
end
|
end
|
||||||
function iproc.padding(img, w1, w2, h1, h2)
|
function iproc.padding(img, w1, w2, h1, h2)
|
||||||
local dst_height = img:size(2) + h1 + h2
|
local dst_height = img:size(2) + h1 + h2
|
||||||
|
@ -22,5 +89,61 @@ function iproc.padding(img, w1, w2, h1, h2)
|
||||||
flow[2]:add(-w1)
|
flow[2]:add(-w1)
|
||||||
return image.warp(img, flow, "simple", false, "clamp")
|
return image.warp(img, flow, "simple", false, "clamp")
|
||||||
end
|
end
|
||||||
|
function iproc.zero_padding(img, w1, w2, h1, h2)
|
||||||
|
local dst_height = img:size(2) + h1 + h2
|
||||||
|
local dst_width = img:size(3) + w1 + w2
|
||||||
|
local flow = torch.Tensor(2, dst_height, dst_width)
|
||||||
|
flow[1] = torch.ger(torch.linspace(0, dst_height -1, dst_height), torch.ones(dst_width))
|
||||||
|
flow[2] = torch.ger(torch.ones(dst_height), torch.linspace(0, dst_width - 1, dst_width))
|
||||||
|
flow[1]:add(-h1)
|
||||||
|
flow[2]:add(-w1)
|
||||||
|
return image.warp(img, flow, "simple", false, "pad", 0)
|
||||||
|
end
|
||||||
|
function iproc.white_noise(src, std, rgb_weights, gamma)
|
||||||
|
gamma = gamma or 0.454545
|
||||||
|
local conversion
|
||||||
|
src, conversion = iproc.byte2float(src)
|
||||||
|
std = std or 0.01
|
||||||
|
|
||||||
|
local noise = torch.Tensor():resizeAs(src):normal(0, std)
|
||||||
|
if rgb_weights then
|
||||||
|
noise[1]:mul(rgb_weights[1])
|
||||||
|
noise[2]:mul(rgb_weights[2])
|
||||||
|
noise[3]:mul(rgb_weights[3])
|
||||||
|
end
|
||||||
|
|
||||||
|
local dest
|
||||||
|
if gamma ~= 0 then
|
||||||
|
dest = src:clone():pow(gamma):add(noise)
|
||||||
|
dest[torch.lt(dest, 0.0)] = 0.0
|
||||||
|
dest[torch.gt(dest, 1.0)] = 1.0
|
||||||
|
dest:pow(1.0 / gamma)
|
||||||
|
else
|
||||||
|
dest = src + noise
|
||||||
|
end
|
||||||
|
if conversion then
|
||||||
|
dest = iproc.float2byte(dest)
|
||||||
|
end
|
||||||
|
return dest
|
||||||
|
end
|
||||||
|
|
||||||
|
local function test_conversion()
|
||||||
|
local a = torch.linspace(0, 255, 256):float():div(255.0)
|
||||||
|
local b = iproc.float2byte(a)
|
||||||
|
local c = iproc.byte2float(a)
|
||||||
|
local d = torch.linspace(0, 255, 256)
|
||||||
|
assert((a - c):abs():sum() == 0)
|
||||||
|
assert((d:float() - b:float()):abs():sum() == 0)
|
||||||
|
|
||||||
|
a = torch.FloatTensor({256.0, 255.0, 254.999}):div(255.0)
|
||||||
|
b = iproc.float2byte(a)
|
||||||
|
assert(b:float():sum() == 255.0 * 3)
|
||||||
|
|
||||||
|
a = torch.FloatTensor({254.0, 254.499, 253.50001}):div(255.0)
|
||||||
|
b = iproc.float2byte(a)
|
||||||
|
print(b)
|
||||||
|
assert(b:float():sum() == 254.0 * 3)
|
||||||
|
end
|
||||||
|
--test_conversion()
|
||||||
|
|
||||||
return iproc
|
return iproc
|
||||||
|
|
|
@ -3,38 +3,35 @@ require 'cutorch'
|
||||||
require 'xlua'
|
require 'xlua'
|
||||||
|
|
||||||
local function minibatch_adam(model, criterion,
|
local function minibatch_adam(model, criterion,
|
||||||
train_x,
|
train_x, train_y,
|
||||||
config, transformer,
|
config)
|
||||||
input_size, target_size)
|
|
||||||
local parameters, gradParameters = model:getParameters()
|
local parameters, gradParameters = model:getParameters()
|
||||||
config = config or {}
|
config = config or {}
|
||||||
local sum_loss = 0
|
local sum_loss = 0
|
||||||
local count_loss = 0
|
local count_loss = 0
|
||||||
local batch_size = config.xBatchSize or 32
|
local batch_size = config.xBatchSize or 32
|
||||||
local shuffle = torch.randperm(#train_x)
|
local shuffle = torch.randperm(train_x:size(1))
|
||||||
local c = 1
|
local c = 1
|
||||||
local inputs = torch.Tensor(batch_size,
|
|
||||||
input_size[1], input_size[2], input_size[3]):cuda()
|
|
||||||
local targets = torch.Tensor(batch_size,
|
|
||||||
target_size[1] * target_size[2] * target_size[3]):cuda()
|
|
||||||
local inputs_tmp = torch.Tensor(batch_size,
|
local inputs_tmp = torch.Tensor(batch_size,
|
||||||
input_size[1], input_size[2], input_size[3])
|
train_x:size(2), train_x:size(3), train_x:size(4)):zero()
|
||||||
local targets_tmp = torch.Tensor(batch_size,
|
local targets_tmp = torch.Tensor(batch_size,
|
||||||
target_size[1] * target_size[2] * target_size[3])
|
train_y:size(2)):zero()
|
||||||
|
local inputs = inputs_tmp:clone():cuda()
|
||||||
|
local targets = targets_tmp:clone():cuda()
|
||||||
|
|
||||||
for t = 1, #train_x, batch_size do
|
print("## update")
|
||||||
if t + batch_size > #train_x then
|
for t = 1, train_x:size(1), batch_size do
|
||||||
|
if t + batch_size -1 > train_x:size(1) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
xlua.progress(t, #train_x)
|
xlua.progress(t, train_x:size(1))
|
||||||
|
|
||||||
for i = 1, batch_size do
|
for i = 1, batch_size do
|
||||||
local x, y = transformer(train_x[shuffle[t + i - 1]])
|
inputs_tmp[i]:copy(train_x[shuffle[t + i - 1]])
|
||||||
inputs_tmp[i]:copy(x)
|
targets_tmp[i]:copy(train_y[shuffle[t + i - 1]])
|
||||||
targets_tmp[i]:copy(y)
|
|
||||||
end
|
end
|
||||||
inputs:copy(inputs_tmp)
|
inputs:copy(inputs_tmp)
|
||||||
targets:copy(targets_tmp)
|
targets:copy(targets_tmp)
|
||||||
|
|
||||||
local feval = function(x)
|
local feval = function(x)
|
||||||
if x ~= parameters then
|
if x ~= parameters then
|
||||||
parameters:copy(x)
|
parameters:copy(x)
|
||||||
|
@ -48,15 +45,14 @@ local function minibatch_adam(model, criterion,
|
||||||
return f, gradParameters
|
return f, gradParameters
|
||||||
end
|
end
|
||||||
optim.adam(feval, parameters, config)
|
optim.adam(feval, parameters, config)
|
||||||
|
|
||||||
c = c + 1
|
c = c + 1
|
||||||
if c % 10 == 0 then
|
if c % 50 == 0 then
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
xlua.progress(#train_x, #train_x)
|
xlua.progress(train_x:size(1), train_x:size(1))
|
||||||
|
|
||||||
return { mse = sum_loss / count_loss}
|
return { loss = sum_loss / count_loss}
|
||||||
end
|
end
|
||||||
|
|
||||||
return minibatch_adam
|
return minibatch_adam
|
||||||
|
|
|
@ -1,291 +1,263 @@
|
||||||
require 'image'
|
require 'image'
|
||||||
local gm = require 'graphicsmagick'
|
local gm = require 'graphicsmagick'
|
||||||
local iproc = require './iproc'
|
local iproc = require 'iproc'
|
||||||
local reconstruct = require './reconstruct'
|
local data_augmentation = require 'data_augmentation'
|
||||||
|
|
||||||
local pairwise_transform = {}
|
local pairwise_transform = {}
|
||||||
|
|
||||||
local function random_half(src, p, min_size)
|
local function random_half(src, p)
|
||||||
p = p or 0.5
|
if torch.uniform() < p then
|
||||||
local filter = ({"Box","Blackman", "SincFast", "Jinc"})[torch.random(1, 4)]
|
local filter = ({"Box","Box","Blackman","Sinc","Lanczos", "Catrom"})[torch.random(1, 6)]
|
||||||
if p > torch.uniform() then
|
|
||||||
return iproc.scale(src, src:size(3) * 0.5, src:size(2) * 0.5, filter)
|
return iproc.scale(src, src:size(3) * 0.5, src:size(2) * 0.5, filter)
|
||||||
else
|
else
|
||||||
return src
|
return src
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function color_augment(x)
|
local function crop_if_large(src, max_size)
|
||||||
local color_scale = torch.Tensor(3):uniform(0.8, 1.2)
|
local tries = 4
|
||||||
x = x:float():div(255)
|
if src:size(2) > max_size and src:size(3) > max_size then
|
||||||
for i = 1, 3 do
|
local rect
|
||||||
x[i]:mul(color_scale[i])
|
for i = 1, tries do
|
||||||
|
local yi = torch.random(0, src:size(2) - max_size)
|
||||||
|
local xi = torch.random(0, src:size(3) - max_size)
|
||||||
|
rect = iproc.crop(src, xi, yi, xi + max_size, yi + max_size)
|
||||||
|
-- ignore simple background
|
||||||
|
if rect:float():std() >= 0 then
|
||||||
|
break
|
||||||
end
|
end
|
||||||
x[torch.lt(x, 0.0)] = 0.0
|
|
||||||
x[torch.gt(x, 1.0)] = 1.0
|
|
||||||
return x:mul(255):byte()
|
|
||||||
end
|
end
|
||||||
local function flip_augment(x, y)
|
return rect
|
||||||
local flip = torch.random(1, 4)
|
|
||||||
if y then
|
|
||||||
if flip == 1 then
|
|
||||||
x = image.hflip(x)
|
|
||||||
y = image.hflip(y)
|
|
||||||
elseif flip == 2 then
|
|
||||||
x = image.vflip(x)
|
|
||||||
y = image.vflip(y)
|
|
||||||
elseif flip == 3 then
|
|
||||||
x = image.hflip(image.vflip(x))
|
|
||||||
y = image.hflip(image.vflip(y))
|
|
||||||
elseif flip == 4 then
|
|
||||||
end
|
|
||||||
return x, y
|
|
||||||
else
|
else
|
||||||
if flip == 1 then
|
return src
|
||||||
x = image.hflip(x)
|
|
||||||
elseif flip == 2 then
|
|
||||||
x = image.vflip(x)
|
|
||||||
elseif flip == 3 then
|
|
||||||
x = image.hflip(image.vflip(x))
|
|
||||||
elseif flip == 4 then
|
|
||||||
end
|
|
||||||
return x
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local INTERPOLATION_PADDING = 16
|
local function preprocess(src, crop_size, options)
|
||||||
function pairwise_transform.scale(src, scale, size, offset, options)
|
local dest = src
|
||||||
options = options or {color_augment = true, random_half = true, rgb = true}
|
dest = random_half(dest, options.random_half_rate)
|
||||||
if options.random_half then
|
dest = crop_if_large(dest, math.max(crop_size * 2, options.max_size))
|
||||||
src = random_half(src)
|
dest = data_augmentation.flip(dest)
|
||||||
|
dest = data_augmentation.color_noise(dest, options.random_color_noise_rate)
|
||||||
|
dest = data_augmentation.overlay(dest, options.random_overlay_rate)
|
||||||
|
dest = data_augmentation.unsharp_mask(dest, options.random_unsharp_mask_rate)
|
||||||
|
dest = data_augmentation.shift_1px(dest)
|
||||||
|
|
||||||
|
return dest
|
||||||
end
|
end
|
||||||
local yi = torch.random(INTERPOLATION_PADDING, src:size(2) - size - INTERPOLATION_PADDING)
|
local function active_cropping(x, y, size, p, tries)
|
||||||
local xi = torch.random(INTERPOLATION_PADDING, src:size(3) - size - INTERPOLATION_PADDING)
|
assert("x:size == y:size", x:size(2) == y:size(2) and x:size(3) == y:size(3))
|
||||||
local down_scale = 1.0 / scale
|
local r = torch.uniform()
|
||||||
local y = image.crop(src,
|
local t = "float"
|
||||||
xi - INTERPOLATION_PADDING, yi - INTERPOLATION_PADDING,
|
if x:type() == "torch.ByteTensor" then
|
||||||
xi + size + INTERPOLATION_PADDING, yi + size + INTERPOLATION_PADDING)
|
t = "byte"
|
||||||
local filters = {
|
end
|
||||||
"Box", -- 0.012756949974688
|
if p < r then
|
||||||
|
local xi = torch.random(0, y:size(3) - (size + 1))
|
||||||
|
local yi = torch.random(0, y:size(2) - (size + 1))
|
||||||
|
local xc = iproc.crop(x, xi, yi, xi + size, yi + size)
|
||||||
|
local yc = iproc.crop(y, xi, yi, xi + size, yi + size)
|
||||||
|
return xc, yc
|
||||||
|
else
|
||||||
|
local lowres = gm.Image(x, "RGB", "DHW"):
|
||||||
|
size(x:size(3) * 0.5, x:size(2) * 0.5, "Box"):
|
||||||
|
size(x:size(3), x:size(2), "Box"):
|
||||||
|
toTensor(t, "RGB", "DHW")
|
||||||
|
local best_se = 0.0
|
||||||
|
local best_xc, best_yc
|
||||||
|
local m = torch.FloatTensor(x:size(1), size, size)
|
||||||
|
for i = 1, tries do
|
||||||
|
local xi = torch.random(0, y:size(3) - (size + 1))
|
||||||
|
local yi = torch.random(0, y:size(2) - (size + 1))
|
||||||
|
local xc = iproc.crop(x, xi, yi, xi + size, yi + size)
|
||||||
|
local lc = iproc.crop(lowres, xi, yi, xi + size, yi + size)
|
||||||
|
local xcf = iproc.byte2float(xc)
|
||||||
|
local lcf = iproc.byte2float(lc)
|
||||||
|
local se = m:copy(xcf):add(-1.0, lcf):pow(2):sum()
|
||||||
|
if se >= best_se then
|
||||||
|
best_xc = xcf
|
||||||
|
best_yc = iproc.byte2float(iproc.crop(y, xi, yi, xi + size, yi + size))
|
||||||
|
best_se = se
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best_xc, best_yc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function pairwise_transform.scale(src, scale, size, offset, n, options)
|
||||||
|
local filters;
|
||||||
|
|
||||||
|
if options.style == "photo" then
|
||||||
|
filters = {
|
||||||
|
"Box", "lanczos", "Catrom"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filters = {
|
||||||
|
"Box","Box", -- 0.012756949974688
|
||||||
"Blackman", -- 0.013191924552285
|
"Blackman", -- 0.013191924552285
|
||||||
--"Cartom", -- 0.013753536746706
|
--"Catrom", -- 0.013753536746706
|
||||||
--"Hanning", -- 0.013761314529647
|
--"Hanning", -- 0.013761314529647
|
||||||
--"Hermite", -- 0.013850225205266
|
--"Hermite", -- 0.013850225205266
|
||||||
"SincFast", -- 0.014095824314306
|
"Sinc", -- 0.014095824314306
|
||||||
"Jinc", -- 0.014244299255442
|
"Lanczos", -- 0.014244299255442
|
||||||
}
|
}
|
||||||
local downscale_filter = filters[torch.random(1, #filters)]
|
|
||||||
|
|
||||||
y = flip_augment(y)
|
|
||||||
if options.color_augment then
|
|
||||||
y = color_augment(y)
|
|
||||||
end
|
end
|
||||||
local x = iproc.scale(y, y:size(3) * down_scale, y:size(2) * down_scale, downscale_filter)
|
local unstable_region_offset = 8
|
||||||
x = iproc.scale(x, y:size(3), y:size(2))
|
local downscale_filter = filters[torch.random(1, #filters)]
|
||||||
y = y:float():div(255)
|
local y = preprocess(src, size, options)
|
||||||
x = x:float():div(255)
|
assert(y:size(2) % 4 == 0 and y:size(3) % 4 == 0)
|
||||||
|
local down_scale = 1.0 / scale
|
||||||
|
local x = iproc.scale(iproc.scale(y, y:size(3) * down_scale,
|
||||||
|
y:size(2) * down_scale, downscale_filter),
|
||||||
|
y:size(3), y:size(2))
|
||||||
|
x = iproc.crop(x, unstable_region_offset, unstable_region_offset,
|
||||||
|
x:size(3) - unstable_region_offset, x:size(2) - unstable_region_offset)
|
||||||
|
y = iproc.crop(y, unstable_region_offset, unstable_region_offset,
|
||||||
|
y:size(3) - unstable_region_offset, y:size(2) - unstable_region_offset)
|
||||||
|
assert(x:size(2) % 4 == 0 and x:size(3) % 4 == 0)
|
||||||
|
assert(x:size(1) == y:size(1) and x:size(2) == y:size(2) and x:size(3) == y:size(3))
|
||||||
|
|
||||||
|
local batch = {}
|
||||||
|
for i = 1, n do
|
||||||
|
local xc, yc = active_cropping(x, y,
|
||||||
|
size,
|
||||||
|
options.active_cropping_rate,
|
||||||
|
options.active_cropping_tries)
|
||||||
|
xc = iproc.byte2float(xc)
|
||||||
|
yc = iproc.byte2float(yc)
|
||||||
if options.rgb then
|
if options.rgb then
|
||||||
else
|
else
|
||||||
y = image.rgb2yuv(y)[1]:reshape(1, y:size(2), y:size(3))
|
yc = image.rgb2yuv(yc)[1]:reshape(1, yc:size(2), yc:size(3))
|
||||||
x = image.rgb2yuv(x)[1]:reshape(1, x:size(2), x:size(3))
|
xc = image.rgb2yuv(xc)[1]:reshape(1, xc:size(2), xc:size(3))
|
||||||
end
|
end
|
||||||
|
table.insert(batch, {xc, iproc.crop(yc, offset, offset, size - offset, size - offset)})
|
||||||
|
end
|
||||||
|
return batch
|
||||||
|
end
|
||||||
|
function pairwise_transform.jpeg_(src, quality, size, offset, n, options)
|
||||||
|
local unstable_region_offset = 8
|
||||||
|
local y = preprocess(src, size, options)
|
||||||
|
local x = y
|
||||||
|
|
||||||
y = image.crop(y, INTERPOLATION_PADDING + offset, INTERPOLATION_PADDING + offset, y:size(3) - offset - INTERPOLATION_PADDING, y:size(2) - offset - INTERPOLATION_PADDING)
|
|
||||||
x = image.crop(x, INTERPOLATION_PADDING, INTERPOLATION_PADDING, x:size(3) - INTERPOLATION_PADDING, x:size(2) - INTERPOLATION_PADDING)
|
|
||||||
|
|
||||||
return x, y
|
|
||||||
end
|
|
||||||
function pairwise_transform.jpeg_(src, quality, size, offset, options)
|
|
||||||
options = options or {color_augment = true, random_half = true, rgb = true}
|
|
||||||
if options.random_half then
|
|
||||||
src = random_half(src)
|
|
||||||
end
|
|
||||||
local yi = torch.random(0, src:size(2) - size - 1)
|
|
||||||
local xi = torch.random(0, src:size(3) - size - 1)
|
|
||||||
local y = src
|
|
||||||
local x
|
|
||||||
|
|
||||||
if options.color_augment then
|
|
||||||
y = color_augment(y)
|
|
||||||
end
|
|
||||||
x = y
|
|
||||||
for i = 1, #quality do
|
for i = 1, #quality do
|
||||||
x = gm.Image(x, "RGB", "DHW")
|
x = gm.Image(x, "RGB", "DHW")
|
||||||
x:format("jpeg")
|
x:format("jpeg"):depth(8)
|
||||||
|
if torch.uniform() < options.jpeg_chroma_subsampling_rate then
|
||||||
|
-- YUV 420
|
||||||
|
x:samplingFactors({2.0, 1.0, 1.0})
|
||||||
|
else
|
||||||
|
-- YUV 444
|
||||||
x:samplingFactors({1.0, 1.0, 1.0})
|
x:samplingFactors({1.0, 1.0, 1.0})
|
||||||
|
end
|
||||||
local blob, len = x:toBlob(quality[i])
|
local blob, len = x:toBlob(quality[i])
|
||||||
x:fromBlob(blob, len)
|
x:fromBlob(blob, len)
|
||||||
x = x:toTensor("byte", "RGB", "DHW")
|
x = x:toTensor("byte", "RGB", "DHW")
|
||||||
end
|
end
|
||||||
|
x = iproc.crop(x, unstable_region_offset, unstable_region_offset,
|
||||||
|
x:size(3) - unstable_region_offset, x:size(2) - unstable_region_offset)
|
||||||
|
y = iproc.crop(y, unstable_region_offset, unstable_region_offset,
|
||||||
|
y:size(3) - unstable_region_offset, y:size(2) - unstable_region_offset)
|
||||||
|
assert(x:size(2) % 4 == 0 and x:size(3) % 4 == 0)
|
||||||
|
assert(x:size(1) == y:size(1) and x:size(2) == y:size(2) and x:size(3) == y:size(3))
|
||||||
|
|
||||||
y = image.crop(y, xi, yi, xi + size, yi + size)
|
local batch = {}
|
||||||
x = image.crop(x, xi, yi, xi + size, yi + size)
|
for i = 1, n do
|
||||||
y = y:float():div(255)
|
local xc, yc = active_cropping(x, y, size,
|
||||||
x = x:float():div(255)
|
options.active_cropping_rate,
|
||||||
x, y = flip_augment(x, y)
|
options.active_cropping_tries)
|
||||||
|
xc = iproc.byte2float(xc)
|
||||||
|
yc = iproc.byte2float(yc)
|
||||||
if options.rgb then
|
if options.rgb then
|
||||||
else
|
else
|
||||||
y = image.rgb2yuv(y)[1]:reshape(1, y:size(2), y:size(3))
|
yc = image.rgb2yuv(yc)[1]:reshape(1, yc:size(2), yc:size(3))
|
||||||
x = image.rgb2yuv(x)[1]:reshape(1, x:size(2), x:size(3))
|
xc = image.rgb2yuv(xc)[1]:reshape(1, xc:size(2), xc:size(3))
|
||||||
end
|
end
|
||||||
|
if torch.uniform() < options.nr_rate then
|
||||||
return x, image.crop(y, offset, offset, size - offset, size - offset)
|
-- reducing noise
|
||||||
|
table.insert(batch, {xc, iproc.crop(yc, offset, offset, size - offset, size - offset)})
|
||||||
|
else
|
||||||
|
-- ratain useful details
|
||||||
|
table.insert(batch, {yc, iproc.crop(yc, offset, offset, size - offset, size - offset)})
|
||||||
end
|
end
|
||||||
function pairwise_transform.jpeg(src, level, size, offset, options)
|
end
|
||||||
|
return batch
|
||||||
|
end
|
||||||
|
function pairwise_transform.jpeg(src, style, level, size, offset, n, options)
|
||||||
|
if style == "art" then
|
||||||
if level == 1 then
|
if level == 1 then
|
||||||
return pairwise_transform.jpeg_(src, {torch.random(65, 85)},
|
return pairwise_transform.jpeg_(src, {torch.random(65, 85)},
|
||||||
size, offset,
|
size, offset, n, options)
|
||||||
options)
|
|
||||||
elseif level == 2 then
|
elseif level == 2 then
|
||||||
local r = torch.uniform()
|
local r = torch.uniform()
|
||||||
if r > 0.6 then
|
if r > 0.6 then
|
||||||
return pairwise_transform.jpeg_(src, {torch.random(27, 70)},
|
return pairwise_transform.jpeg_(src, {torch.random(27, 70)},
|
||||||
size, offset,
|
size, offset, n, options)
|
||||||
options)
|
|
||||||
elseif r > 0.3 then
|
elseif r > 0.3 then
|
||||||
local quality1 = torch.random(37, 70)
|
local quality1 = torch.random(37, 70)
|
||||||
local quality2 = quality1 - torch.random(5, 10)
|
local quality2 = quality1 - torch.random(5, 10)
|
||||||
return pairwise_transform.jpeg_(src, {quality1, quality2},
|
return pairwise_transform.jpeg_(src, {quality1, quality2},
|
||||||
size, offset,
|
size, offset, n, options)
|
||||||
options)
|
|
||||||
else
|
else
|
||||||
local quality1 = torch.random(52, 70)
|
local quality1 = torch.random(52, 70)
|
||||||
|
local quality2 = quality1 - torch.random(5, 15)
|
||||||
|
local quality3 = quality1 - torch.random(15, 25)
|
||||||
|
|
||||||
return pairwise_transform.jpeg_(src,
|
return pairwise_transform.jpeg_(src,
|
||||||
{quality1,
|
{quality1, quality2, quality3},
|
||||||
quality1 - torch.random(5, 15),
|
size, offset, n, options)
|
||||||
quality1 - torch.random(15, 25)},
|
end
|
||||||
size, offset,
|
else
|
||||||
|
error("unknown noise level: " .. level)
|
||||||
|
end
|
||||||
|
elseif style == "photo" then
|
||||||
|
-- level adjusting by -nr_rate
|
||||||
|
return pairwise_transform.jpeg_(src, {torch.random(30, 70)},
|
||||||
|
size, offset, n,
|
||||||
options)
|
options)
|
||||||
end
|
|
||||||
else
|
else
|
||||||
error("unknown noise level: " .. level)
|
error("unknown style: " .. style)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function pairwise_transform.jpeg_scale_(src, scale, quality, size, offset, options)
|
|
||||||
if options.random_half then
|
function pairwise_transform.test_jpeg(src)
|
||||||
src = random_half(src)
|
torch.setdefaulttensortype("torch.FloatTensor")
|
||||||
end
|
local options = {random_color_noise_rate = 0.5,
|
||||||
local down_scale = 1.0 / scale
|
random_half_rate = 0.5,
|
||||||
local filters = {
|
random_overlay_rate = 0.5,
|
||||||
"Box", -- 0.012756949974688
|
random_unsharp_mask_rate = 0.5,
|
||||||
"Blackman", -- 0.013191924552285
|
jpeg_chroma_subsampling_rate = 0.5,
|
||||||
--"Cartom", -- 0.013753536746706
|
nr_rate = 1.0,
|
||||||
--"Hanning", -- 0.013761314529647
|
active_cropping_rate = 0.5,
|
||||||
--"Hermite", -- 0.013850225205266
|
active_cropping_tries = 10,
|
||||||
"SincFast", -- 0.014095824314306
|
max_size = 256,
|
||||||
"Jinc", -- 0.014244299255442
|
rgb = true
|
||||||
}
|
}
|
||||||
local downscale_filter = filters[torch.random(1, #filters)]
|
local image = require 'image'
|
||||||
local yi = torch.random(INTERPOLATION_PADDING, src:size(2) - size - INTERPOLATION_PADDING)
|
local src = image.lena()
|
||||||
local xi = torch.random(INTERPOLATION_PADDING, src:size(3) - size - INTERPOLATION_PADDING)
|
|
||||||
local y = src
|
|
||||||
local x
|
|
||||||
|
|
||||||
if options.color_augment then
|
|
||||||
y = color_augment(y)
|
|
||||||
end
|
|
||||||
x = y
|
|
||||||
x = iproc.scale(x, y:size(3) * down_scale, y:size(2) * down_scale, downscale_filter)
|
|
||||||
for i = 1, #quality do
|
|
||||||
x = gm.Image(x, "RGB", "DHW")
|
|
||||||
x:format("jpeg")
|
|
||||||
x:samplingFactors({1.0, 1.0, 1.0})
|
|
||||||
local blob, len = x:toBlob(quality[i])
|
|
||||||
x:fromBlob(blob, len)
|
|
||||||
x = x:toTensor("byte", "RGB", "DHW")
|
|
||||||
end
|
|
||||||
x = iproc.scale(x, y:size(3), y:size(2))
|
|
||||||
y = image.crop(y,
|
|
||||||
xi, yi,
|
|
||||||
xi + size, yi + size)
|
|
||||||
x = image.crop(x,
|
|
||||||
xi, yi,
|
|
||||||
xi + size, yi + size)
|
|
||||||
x = x:float():div(255)
|
|
||||||
y = y:float():div(255)
|
|
||||||
x, y = flip_augment(x, y)
|
|
||||||
|
|
||||||
if options.rgb then
|
|
||||||
else
|
|
||||||
y = image.rgb2yuv(y)[1]:reshape(1, y:size(2), y:size(3))
|
|
||||||
x = image.rgb2yuv(x)[1]:reshape(1, x:size(2), x:size(3))
|
|
||||||
end
|
|
||||||
|
|
||||||
return x, image.crop(y, offset, offset, size - offset, size - offset)
|
|
||||||
end
|
|
||||||
function pairwise_transform.jpeg_scale(src, scale, level, size, offset, options)
|
|
||||||
options = options or {color_augment = true, random_half = true}
|
|
||||||
if level == 1 then
|
|
||||||
return pairwise_transform.jpeg_scale_(src, scale, {torch.random(65, 85)},
|
|
||||||
size, offset, options)
|
|
||||||
elseif level == 2 then
|
|
||||||
local r = torch.uniform()
|
|
||||||
if r > 0.6 then
|
|
||||||
return pairwise_transform.jpeg_scale_(src, scale, {torch.random(27, 70)},
|
|
||||||
size, offset, options)
|
|
||||||
elseif r > 0.3 then
|
|
||||||
local quality1 = torch.random(37, 70)
|
|
||||||
local quality2 = quality1 - torch.random(5, 10)
|
|
||||||
return pairwise_transform.jpeg_scale_(src, scale, {quality1, quality2},
|
|
||||||
size, offset, options)
|
|
||||||
else
|
|
||||||
local quality1 = torch.random(52, 70)
|
|
||||||
return pairwise_transform.jpeg_scale_(src, scale,
|
|
||||||
{quality1,
|
|
||||||
quality1 - torch.random(5, 15),
|
|
||||||
quality1 - torch.random(15, 25)},
|
|
||||||
size, offset, options)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
error("unknown noise level: " .. level)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function test_jpeg()
|
|
||||||
local loader = require './image_loader'
|
|
||||||
local src = loader.load_byte("../images/miku_CC_BY-NC.jpg")
|
|
||||||
local y, x = pairwise_transform.jpeg_(src, {}, 128, 0, false)
|
|
||||||
image.display({image = y, legend = "y:0"})
|
|
||||||
image.display({image = x, legend = "x:0"})
|
|
||||||
for i = 2, 9 do
|
|
||||||
local y, x = pairwise_transform.jpeg_(pairwise_transform.random_half(src),
|
|
||||||
{i * 10}, 128, 0, {color_augment = false, random_half = true})
|
|
||||||
image.display({image = y, legend = "y:" .. (i * 10), max=1,min=0})
|
|
||||||
image.display({image = x, legend = "x:" .. (i * 10),max=1,min=0})
|
|
||||||
--print(x:mean(), y:mean())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function test_scale()
|
|
||||||
local loader = require './image_loader'
|
|
||||||
local src = loader.load_byte("../images/miku_CC_BY-NC.jpg")
|
|
||||||
for i = 1, 9 do
|
for i = 1, 9 do
|
||||||
local y, x = pairwise_transform.scale(src, 2.0, 128, 7, {color_augment = true, random_half = true, rgb = true})
|
local xy = pairwise_transform.jpeg(src,
|
||||||
image.display({image = y, legend = "y:" .. (i * 10), min = 0, max = 1})
|
"art",
|
||||||
image.display({image = x, legend = "x:" .. (i * 10), min = 0, max = 1})
|
torch.random(1, 2),
|
||||||
print(y:size(), x:size())
|
128, 7, 1, options)
|
||||||
--print(x:mean(), y:mean())
|
image.display({image = xy[1][1], legend = "y:" .. (i * 10), min=0, max=1})
|
||||||
|
image.display({image = xy[1][2], legend = "x:" .. (i * 10), min=0, max=1})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function test_jpeg_scale()
|
function pairwise_transform.test_scale(src)
|
||||||
local loader = require './image_loader'
|
torch.setdefaulttensortype("torch.FloatTensor")
|
||||||
local src = loader.load_byte("../images/miku_CC_BY-NC.jpg")
|
local options = {random_color_noise_rate = 0.5,
|
||||||
for i = 1, 9 do
|
random_half_rate = 0.5,
|
||||||
local y, x = pairwise_transform.jpeg_scale(src, 2.0, 1, 128, 7, {color_augment = true, random_half = true})
|
random_overlay_rate = 0.5,
|
||||||
image.display({image = y, legend = "y1:" .. (i * 10), min = 0, max = 1})
|
random_unsharp_mask_rate = 0.5,
|
||||||
image.display({image = x, legend = "x1:" .. (i * 10), min = 0, max = 1})
|
active_cropping_rate = 0.5,
|
||||||
print(y:size(), x:size())
|
active_cropping_tries = 10,
|
||||||
--print(x:mean(), y:mean())
|
max_size = 256,
|
||||||
end
|
rgb = true
|
||||||
for i = 1, 9 do
|
}
|
||||||
local y, x = pairwise_transform.jpeg_scale(src, 2.0, 2, 128, 7, {color_augment = true, random_half = true})
|
local image = require 'image'
|
||||||
image.display({image = y, legend = "y2:" .. (i * 10), min = 0, max = 1})
|
local src = image.lena()
|
||||||
image.display({image = x, legend = "x2:" .. (i * 10), min = 0, max = 1})
|
|
||||||
print(y:size(), x:size())
|
|
||||||
--print(x:mean(), y:mean())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--test_scale()
|
|
||||||
--test_jpeg()
|
|
||||||
--test_jpeg_scale()
|
|
||||||
|
|
||||||
|
for i = 1, 10 do
|
||||||
|
local xy = pairwise_transform.scale(src, 2.0, 128, 7, 1, options)
|
||||||
|
image.display({image = xy[1][1], legend = "y:" .. (i * 10), min = 0, max = 1})
|
||||||
|
image.display({image = xy[1][2], legend = "x:" .. (i * 10), min = 0, max = 1})
|
||||||
|
end
|
||||||
|
end
|
||||||
return pairwise_transform
|
return pairwise_transform
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
require 'torch'
|
|
||||||
require 'cutorch'
|
|
||||||
require 'nn'
|
|
||||||
require 'cunn'
|
|
|
@ -1,5 +1,5 @@
|
||||||
require 'image'
|
require 'image'
|
||||||
local iproc = require './iproc'
|
local iproc = require 'iproc'
|
||||||
|
|
||||||
local function reconstruct_y(model, x, offset, block_size)
|
local function reconstruct_y(model, x, offset, block_size)
|
||||||
if x:dim() == 2 then
|
if x:dim() == 2 then
|
||||||
|
@ -48,7 +48,8 @@ local function reconstruct_rgb(model, x, offset, block_size)
|
||||||
end
|
end
|
||||||
return new_x
|
return new_x
|
||||||
end
|
end
|
||||||
function model_is_rgb(model)
|
local reconstruct = {}
|
||||||
|
function reconstruct.is_rgb(model)
|
||||||
if model:get(model:size() - 1).weight:size(1) == 3 then
|
if model:get(model:size() - 1).weight:size(1) == 3 then
|
||||||
-- 3ch RGB
|
-- 3ch RGB
|
||||||
return true
|
return true
|
||||||
|
@ -57,8 +58,23 @@ function model_is_rgb(model)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
function reconstruct.offset_size(model)
|
||||||
local reconstruct = {}
|
local conv = model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
if #conv > 0 then
|
||||||
|
local offset = 0
|
||||||
|
for i = 1, #conv do
|
||||||
|
offset = offset + (conv[i].kW - 1) / 2
|
||||||
|
end
|
||||||
|
return math.floor(offset)
|
||||||
|
else
|
||||||
|
conv = model:findModules("cudnn.SpatialConvolution")
|
||||||
|
local offset = 0
|
||||||
|
for i = 1, #conv do
|
||||||
|
offset = offset + (conv[i].kW - 1) / 2
|
||||||
|
end
|
||||||
|
return math.floor(offset)
|
||||||
|
end
|
||||||
|
end
|
||||||
function reconstruct.image_y(model, x, offset, block_size)
|
function reconstruct.image_y(model, x, offset, block_size)
|
||||||
block_size = block_size or 128
|
block_size = block_size or 128
|
||||||
local output_size = block_size - offset * 2
|
local output_size = block_size - offset * 2
|
||||||
|
@ -78,7 +94,7 @@ function reconstruct.image_y(model, x, offset, block_size)
|
||||||
y[torch.lt(y, 0)] = 0
|
y[torch.lt(y, 0)] = 0
|
||||||
y[torch.gt(y, 1)] = 1
|
y[torch.gt(y, 1)] = 1
|
||||||
yuv[1]:copy(y)
|
yuv[1]:copy(y)
|
||||||
local output = image.yuv2rgb(image.crop(yuv,
|
local output = image.yuv2rgb(iproc.crop(yuv,
|
||||||
pad_w1, pad_h1,
|
pad_w1, pad_h1,
|
||||||
yuv:size(3) - pad_w2, yuv:size(2) - pad_h2))
|
yuv:size(3) - pad_w2, yuv:size(2) - pad_h2))
|
||||||
output[torch.lt(output, 0)] = 0
|
output[torch.lt(output, 0)] = 0
|
||||||
|
@ -89,7 +105,7 @@ function reconstruct.image_y(model, x, offset, block_size)
|
||||||
end
|
end
|
||||||
function reconstruct.scale_y(model, scale, x, offset, block_size)
|
function reconstruct.scale_y(model, scale, x, offset, block_size)
|
||||||
block_size = block_size or 128
|
block_size = block_size or 128
|
||||||
local x_jinc = iproc.scale(x, x:size(3) * scale, x:size(2) * scale, "Jinc")
|
local x_lanczos = iproc.scale(x, x:size(3) * scale, x:size(2) * scale, "Lanczos")
|
||||||
x = iproc.scale(x, x:size(3) * scale, x:size(2) * scale, "Box")
|
x = iproc.scale(x, x:size(3) * scale, x:size(2) * scale, "Box")
|
||||||
|
|
||||||
local output_size = block_size - offset * 2
|
local output_size = block_size - offset * 2
|
||||||
|
@ -105,14 +121,14 @@ function reconstruct.scale_y(model, scale, x, offset, block_size)
|
||||||
local pad_h2 = (h - offset) - x:size(2)
|
local pad_h2 = (h - offset) - x:size(2)
|
||||||
local pad_w2 = (w - offset) - x:size(3)
|
local pad_w2 = (w - offset) - x:size(3)
|
||||||
local yuv_nn = image.rgb2yuv(iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2))
|
local yuv_nn = image.rgb2yuv(iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2))
|
||||||
local yuv_jinc = image.rgb2yuv(iproc.padding(x_jinc, pad_w1, pad_w2, pad_h1, pad_h2))
|
local yuv_lanczos = image.rgb2yuv(iproc.padding(x_lanczos, pad_w1, pad_w2, pad_h1, pad_h2))
|
||||||
local y = reconstruct_y(model, yuv_nn[1], offset, block_size)
|
local y = reconstruct_y(model, yuv_nn[1], offset, block_size)
|
||||||
y[torch.lt(y, 0)] = 0
|
y[torch.lt(y, 0)] = 0
|
||||||
y[torch.gt(y, 1)] = 1
|
y[torch.gt(y, 1)] = 1
|
||||||
yuv_jinc[1]:copy(y)
|
yuv_lanczos[1]:copy(y)
|
||||||
local output = image.yuv2rgb(image.crop(yuv_jinc,
|
local output = image.yuv2rgb(iproc.crop(yuv_lanczos,
|
||||||
pad_w1, pad_h1,
|
pad_w1, pad_h1,
|
||||||
yuv_jinc:size(3) - pad_w2, yuv_jinc:size(2) - pad_h2))
|
yuv_lanczos:size(3) - pad_w2, yuv_lanczos:size(2) - pad_h2))
|
||||||
output[torch.lt(output, 0)] = 0
|
output[torch.lt(output, 0)] = 0
|
||||||
output[torch.gt(output, 1)] = 1
|
output[torch.gt(output, 1)] = 1
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
@ -135,7 +151,7 @@ function reconstruct.image_rgb(model, x, offset, block_size)
|
||||||
local pad_w2 = (w - offset) - x:size(3)
|
local pad_w2 = (w - offset) - x:size(3)
|
||||||
local input = iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2)
|
local input = iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2)
|
||||||
local y = reconstruct_rgb(model, input, offset, block_size)
|
local y = reconstruct_rgb(model, input, offset, block_size)
|
||||||
local output = image.crop(y,
|
local output = iproc.crop(y,
|
||||||
pad_w1, pad_h1,
|
pad_w1, pad_h1,
|
||||||
y:size(3) - pad_w2, y:size(2) - pad_h2)
|
y:size(3) - pad_w2, y:size(2) - pad_h2)
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
@ -162,7 +178,7 @@ function reconstruct.scale_rgb(model, scale, x, offset, block_size)
|
||||||
local pad_w2 = (w - offset) - x:size(3)
|
local pad_w2 = (w - offset) - x:size(3)
|
||||||
local input = iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2)
|
local input = iproc.padding(x, pad_w1, pad_w2, pad_h1, pad_h2)
|
||||||
local y = reconstruct_rgb(model, input, offset, block_size)
|
local y = reconstruct_rgb(model, input, offset, block_size)
|
||||||
local output = image.crop(y,
|
local output = iproc.crop(y,
|
||||||
pad_w1, pad_h1,
|
pad_w1, pad_h1,
|
||||||
y:size(3) - pad_w2, y:size(2) - pad_h2)
|
y:size(3) - pad_w2, y:size(2) - pad_h2)
|
||||||
output[torch.lt(output, 0)] = 0
|
output[torch.lt(output, 0)] = 0
|
||||||
|
@ -172,19 +188,108 @@ function reconstruct.scale_rgb(model, scale, x, offset, block_size)
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
function reconstruct.image(model, x, offset, block_size)
|
function reconstruct.image(model, x, block_size)
|
||||||
if model_is_rgb(model) then
|
local i2rgb = false
|
||||||
return reconstruct.image_rgb(model, x, offset, block_size)
|
if x:size(1) == 1 then
|
||||||
|
local new_x = torch.Tensor(3, x:size(2), x:size(3))
|
||||||
|
new_x[1]:copy(x)
|
||||||
|
new_x[2]:copy(x)
|
||||||
|
new_x[3]:copy(x)
|
||||||
|
x = new_x
|
||||||
|
i2rgb = true
|
||||||
|
end
|
||||||
|
if reconstruct.is_rgb(model) then
|
||||||
|
x = reconstruct.image_rgb(model, x,
|
||||||
|
reconstruct.offset_size(model), block_size)
|
||||||
else
|
else
|
||||||
return reconstruct.image_y(model, x, offset, block_size)
|
x = reconstruct.image_y(model, x,
|
||||||
|
reconstruct.offset_size(model), block_size)
|
||||||
|
end
|
||||||
|
if i2rgb then
|
||||||
|
x = image.rgb2y(x)
|
||||||
|
end
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
function reconstruct.scale(model, scale, x, block_size)
|
||||||
|
local i2rgb = false
|
||||||
|
if x:size(1) == 1 then
|
||||||
|
local new_x = torch.Tensor(3, x:size(2), x:size(3))
|
||||||
|
new_x[1]:copy(x)
|
||||||
|
new_x[2]:copy(x)
|
||||||
|
new_x[3]:copy(x)
|
||||||
|
x = new_x
|
||||||
|
i2rgb = true
|
||||||
|
end
|
||||||
|
if reconstruct.is_rgb(model) then
|
||||||
|
x = reconstruct.scale_rgb(model, scale, x,
|
||||||
|
reconstruct.offset_size(model), block_size)
|
||||||
|
else
|
||||||
|
x = reconstruct.scale_y(model, scale, x,
|
||||||
|
reconstruct.offset_size(model), block_size)
|
||||||
|
end
|
||||||
|
if i2rgb then
|
||||||
|
x = image.rgb2y(x)
|
||||||
|
end
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
local function tta(f, model, x, block_size)
|
||||||
|
local average = nil
|
||||||
|
local offset = reconstruct.offset_size(model)
|
||||||
|
for i = 1, 4 do
|
||||||
|
local flip_f, iflip_f
|
||||||
|
if i == 1 then
|
||||||
|
flip_f = function (a) return a end
|
||||||
|
iflip_f = function (a) return a end
|
||||||
|
elseif i == 2 then
|
||||||
|
flip_f = image.vflip
|
||||||
|
iflip_f = image.vflip
|
||||||
|
elseif i == 3 then
|
||||||
|
flip_f = image.hflip
|
||||||
|
iflip_f = image.hflip
|
||||||
|
elseif i == 4 then
|
||||||
|
flip_f = function (a) return image.hflip(image.vflip(a)) end
|
||||||
|
iflip_f = function (a) return image.vflip(image.hflip(a)) end
|
||||||
|
end
|
||||||
|
for j = 1, 2 do
|
||||||
|
local tr_f, itr_f
|
||||||
|
if j == 1 then
|
||||||
|
tr_f = function (a) return a end
|
||||||
|
itr_f = function (a) return a end
|
||||||
|
elseif j == 2 then
|
||||||
|
tr_f = function(a) return a:transpose(2, 3):contiguous() end
|
||||||
|
itr_f = function(a) return a:transpose(2, 3):contiguous() end
|
||||||
|
end
|
||||||
|
local out = itr_f(iflip_f(f(model, flip_f(tr_f(x)),
|
||||||
|
offset, block_size)))
|
||||||
|
if not average then
|
||||||
|
average = out
|
||||||
|
else
|
||||||
|
average:add(out)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function reconstruct.scale(model, scale, x, offset, block_size)
|
end
|
||||||
if model_is_rgb(model) then
|
return average:div(8.0)
|
||||||
|
end
|
||||||
|
function reconstruct.image_tta(model, x, block_size)
|
||||||
|
if reconstruct.is_rgb(model) then
|
||||||
|
return tta(reconstruct.image_rgb, model, x, block_size)
|
||||||
|
else
|
||||||
|
return tta(reconstruct.image_y, model, x, block_size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function reconstruct.scale_tta(model, scale, x, block_size)
|
||||||
|
if reconstruct.is_rgb(model) then
|
||||||
|
local f = function (model, x, offset, block_size)
|
||||||
return reconstruct.scale_rgb(model, scale, x, offset, block_size)
|
return reconstruct.scale_rgb(model, scale, x, offset, block_size)
|
||||||
|
end
|
||||||
|
return tta(f, model, x, block_size)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
local f = function (model, x, offset, block_size)
|
||||||
return reconstruct.scale_y(model, scale, x, offset, block_size)
|
return reconstruct.scale_y(model, scale, x, offset, block_size)
|
||||||
end
|
end
|
||||||
|
return tta(f, model, x, block_size)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return reconstruct
|
return reconstruct
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'xlua'
|
require 'xlua'
|
||||||
require 'pl'
|
require 'pl'
|
||||||
|
require 'trepl'
|
||||||
|
|
||||||
-- global settings
|
-- global settings
|
||||||
|
|
||||||
|
@ -14,65 +15,85 @@ local settings = {}
|
||||||
|
|
||||||
local cmd = torch.CmdLine()
|
local cmd = torch.CmdLine()
|
||||||
cmd:text()
|
cmd:text()
|
||||||
cmd:text("waifu2x")
|
cmd:text("waifu2x-training")
|
||||||
cmd:text("Options:")
|
cmd:text("Options:")
|
||||||
cmd:option("-seed", 11, 'fixed input seed')
|
cmd:option("-gpu", -1, 'GPU Device ID')
|
||||||
cmd:option("-data_dir", "./data", 'data directory')
|
cmd:option("-seed", 11, 'RNG seed')
|
||||||
cmd:option("-test", "images/miku_small.png", 'test image file')
|
cmd:option("-data_dir", "./data", 'path to data directory')
|
||||||
|
cmd:option("-backend", "cunn", '(cunn|cudnn)')
|
||||||
|
cmd:option("-test", "images/miku_small.png", 'path to test image')
|
||||||
cmd:option("-model_dir", "./models", 'model directory')
|
cmd:option("-model_dir", "./models", 'model directory')
|
||||||
cmd:option("-method", "scale", '(noise|scale|noise_scale)')
|
cmd:option("-method", "scale", 'method to training (noise|scale)')
|
||||||
cmd:option("-noise_level", 1, '(1|2)')
|
cmd:option("-noise_level", 1, '(1|2)')
|
||||||
|
cmd:option("-style", "art", '(art|photo)')
|
||||||
cmd:option("-color", 'rgb', '(y|rgb)')
|
cmd:option("-color", 'rgb', '(y|rgb)')
|
||||||
cmd:option("-scale", 2.0, 'scale')
|
cmd:option("-random_color_noise_rate", 0.0, 'data augmentation using color noise (0.0-1.0)')
|
||||||
cmd:option("-learning_rate", 0.00025, 'learning rate for adam')
|
cmd:option("-random_overlay_rate", 0.0, 'data augmentation using flipped image overlay (0.0-1.0)')
|
||||||
cmd:option("-random_half", 1, 'enable data augmentation using half resolution image')
|
cmd:option("-random_half_rate", 0.0, 'data augmentation using half resolution image (0.0-1.0)')
|
||||||
cmd:option("-crop_size", 128, 'crop size')
|
cmd:option("-random_unsharp_mask_rate", 0.0, 'data augmentation using unsharp mask (0.0-1.0)')
|
||||||
cmd:option("-batch_size", 2, 'mini batch size')
|
cmd:option("-scale", 2.0, 'scale factor (2)')
|
||||||
cmd:option("-epoch", 200, 'epoch')
|
cmd:option("-learning_rate", 0.0005, 'learning rate for adam')
|
||||||
cmd:option("-core", 2, 'cpu core')
|
cmd:option("-crop_size", 46, 'crop size')
|
||||||
|
cmd:option("-max_size", 256, 'if image is larger than max_size, image will be crop to max_size randomly')
|
||||||
|
cmd:option("-batch_size", 8, 'mini batch size')
|
||||||
|
cmd:option("-patches", 16, 'number of patch samples')
|
||||||
|
cmd:option("-inner_epoch", 4, 'number of inner epochs')
|
||||||
|
cmd:option("-epoch", 30, 'number of epochs to run')
|
||||||
|
cmd:option("-thread", -1, 'number of CPU threads')
|
||||||
|
cmd:option("-jpeg_chroma_subsampling_rate", 0.0, 'the rate of YUV 4:2:0/YUV 4:4:4 in denoising training (0.0-1.0)')
|
||||||
|
cmd:option("-validation_rate", 0.05, 'validation-set rate (number_of_training_images * validation_rate > 1)')
|
||||||
|
cmd:option("-validation_crops", 80, 'number of cropping region per image in validation')
|
||||||
|
cmd:option("-active_cropping_rate", 0.5, 'active cropping rate')
|
||||||
|
cmd:option("-active_cropping_tries", 10, 'active cropping tries')
|
||||||
|
cmd:option("-nr_rate", 0.75, 'trade-off between reducing noise and erasing details (0.0-1.0)')
|
||||||
|
cmd:option("-save_history", 0, 'save all model (0|1)')
|
||||||
|
|
||||||
local opt = cmd:parse(arg)
|
local opt = cmd:parse(arg)
|
||||||
for k, v in pairs(opt) do
|
for k, v in pairs(opt) do
|
||||||
settings[k] = v
|
settings[k] = v
|
||||||
end
|
end
|
||||||
|
if settings.save_history == 1 then
|
||||||
|
settings.save_history = true
|
||||||
|
else
|
||||||
|
settings.save_history = false
|
||||||
|
end
|
||||||
|
if settings.save_history then
|
||||||
|
if settings.method == "noise" then
|
||||||
|
settings.model_file = string.format("%s/noise%d_model.%%d-%%d.t7",
|
||||||
|
settings.model_dir, settings.noise_level)
|
||||||
|
elseif settings.method == "scale" then
|
||||||
|
settings.model_file = string.format("%s/scale%.1fx_model.%%d-%%d.t7",
|
||||||
|
settings.model_dir, settings.scale)
|
||||||
|
else
|
||||||
|
error("unknown method: " .. settings.method)
|
||||||
|
end
|
||||||
|
else
|
||||||
if settings.method == "noise" then
|
if settings.method == "noise" then
|
||||||
settings.model_file = string.format("%s/noise%d_model.t7",
|
settings.model_file = string.format("%s/noise%d_model.t7",
|
||||||
settings.model_dir, settings.noise_level)
|
settings.model_dir, settings.noise_level)
|
||||||
elseif settings.method == "scale" then
|
elseif settings.method == "scale" then
|
||||||
settings.model_file = string.format("%s/scale%.1fx_model.t7",
|
settings.model_file = string.format("%s/scale%.1fx_model.t7",
|
||||||
settings.model_dir, settings.scale)
|
settings.model_dir, settings.scale)
|
||||||
elseif settings.method == "noise_scale" then
|
|
||||||
settings.model_file = string.format("%s/noise%d_scale%.1fx_model.t7",
|
|
||||||
settings.model_dir, settings.noise_level, settings.scale)
|
|
||||||
else
|
else
|
||||||
error("unknown method: " .. settings.method)
|
error("unknown method: " .. settings.method)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
if not (settings.color == "rgb" or settings.color == "y") then
|
if not (settings.color == "rgb" or settings.color == "y") then
|
||||||
error("color must be y or rgb")
|
error("color must be y or rgb")
|
||||||
end
|
end
|
||||||
if not (settings.scale == math.floor(settings.scale) and settings.scale % 2 == 0) then
|
if not (settings.scale == math.floor(settings.scale) and settings.scale % 2 == 0) then
|
||||||
error("scale must be mod-2")
|
error("scale must be mod-2")
|
||||||
end
|
end
|
||||||
if settings.random_half == 1 then
|
if not (settings.style == "art" or
|
||||||
settings.random_half = true
|
settings.style == "photo") then
|
||||||
else
|
error(string.format("unknown style: %s", settings.style))
|
||||||
settings.random_half = false
|
end
|
||||||
|
|
||||||
|
if settings.thread > 0 then
|
||||||
|
torch.setnumthreads(tonumber(settings.thread))
|
||||||
end
|
end
|
||||||
torch.setnumthreads(settings.core)
|
|
||||||
|
|
||||||
settings.images = string.format("%s/images.t7", settings.data_dir)
|
settings.images = string.format("%s/images.t7", settings.data_dir)
|
||||||
settings.image_list = string.format("%s/image_list.txt", settings.data_dir)
|
settings.image_list = string.format("%s/image_list.txt", settings.data_dir)
|
||||||
|
|
||||||
settings.validation_ratio = 0.1
|
|
||||||
settings.validation_crops = 40
|
|
||||||
|
|
||||||
local srcnn = require './srcnn'
|
|
||||||
if (settings.method == "scale" or settings.method == "noise_scale") and settings.scale == 4 then
|
|
||||||
settings.create_model = srcnn.waifu4x
|
|
||||||
settings.block_offset = 13
|
|
||||||
else
|
|
||||||
settings.create_model = srcnn.waifu2x
|
|
||||||
settings.block_offset = 7
|
|
||||||
end
|
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
100
lib/srcnn.lua
|
@ -1,74 +1,82 @@
|
||||||
require './LeakyReLU'
|
require 'w2nn'
|
||||||
|
|
||||||
|
-- ref: http://arxiv.org/abs/1502.01852
|
||||||
|
-- ref: http://arxiv.org/abs/1501.00092
|
||||||
|
local srcnn = {}
|
||||||
|
|
||||||
function nn.SpatialConvolutionMM:reset(stdv)
|
function nn.SpatialConvolutionMM:reset(stdv)
|
||||||
stdv = math.sqrt(2 / ( self.kW * self.kH * self.nOutputPlane))
|
stdv = math.sqrt(2 / ((1.0 + 0.1 * 0.1) * self.kW * self.kH * self.nOutputPlane))
|
||||||
self.weight:normal(0, stdv)
|
self.weight:normal(0, stdv)
|
||||||
self.bias:fill(0)
|
self.bias:zero()
|
||||||
end
|
end
|
||||||
local srcnn = {}
|
if cudnn and cudnn.SpatialConvolution then
|
||||||
function srcnn.waifu2x(color)
|
function cudnn.SpatialConvolution:reset(stdv)
|
||||||
local model = nn.Sequential()
|
stdv = math.sqrt(2 / ((1.0 + 0.1 * 0.1) * self.kW * self.kH * self.nOutputPlane))
|
||||||
local ch = nil
|
self.weight:normal(0, stdv)
|
||||||
if color == "rgb" then
|
self.bias:zero()
|
||||||
ch = 3
|
|
||||||
elseif color == "y" then
|
|
||||||
ch = 1
|
|
||||||
else
|
|
||||||
if color then
|
|
||||||
error("unknown color: " .. color)
|
|
||||||
else
|
|
||||||
error("unknown color: nil")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function srcnn.channels(model)
|
||||||
|
return model:get(model:size() - 1).weight:size(1)
|
||||||
|
end
|
||||||
|
function srcnn.waifu2x_cunn(ch)
|
||||||
|
local model = nn.Sequential()
|
||||||
model:add(nn.SpatialConvolutionMM(ch, 32, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(ch, 32, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(32, 32, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(32, 32, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(32, 64, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(32, 64, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(64, 64, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(64, 64, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(64, 128, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(64, 128, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(128, 128, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(128, 128, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.LeakyReLU(0.1))
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
model:add(nn.SpatialConvolutionMM(128, ch, 3, 3, 1, 1, 0, 0))
|
model:add(nn.SpatialConvolutionMM(128, ch, 3, 3, 1, 1, 0, 0))
|
||||||
model:add(nn.View(-1):setNumInputDims(3))
|
model:add(nn.View(-1):setNumInputDims(3))
|
||||||
--model:cuda()
|
--model:cuda()
|
||||||
--print(model:forward(torch.Tensor(32, 1, 92, 92):uniform():cuda()):size())
|
--print(model:forward(torch.Tensor(32, ch, 92, 92):uniform():cuda()):size())
|
||||||
|
|
||||||
return model, 7
|
return model
|
||||||
end
|
end
|
||||||
|
function srcnn.waifu2x_cudnn(ch)
|
||||||
-- current 4x is worse than 2x * 2
|
|
||||||
function srcnn.waifu4x(color)
|
|
||||||
local model = nn.Sequential()
|
local model = nn.Sequential()
|
||||||
|
model:add(cudnn.SpatialConvolution(ch, 32, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(32, 32, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(32, 64, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(64, 64, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(64, 128, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(128, 128, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(w2nn.LeakyReLU(0.1))
|
||||||
|
model:add(cudnn.SpatialConvolution(128, ch, 3, 3, 1, 1, 0, 0))
|
||||||
|
model:add(nn.View(-1):setNumInputDims(3))
|
||||||
|
--model:cuda()
|
||||||
|
--print(model:forward(torch.Tensor(32, ch, 92, 92):uniform():cuda()):size())
|
||||||
|
|
||||||
local ch = nil
|
return model
|
||||||
|
end
|
||||||
|
function srcnn.create(model_name, backend, color)
|
||||||
|
local ch = 3
|
||||||
if color == "rgb" then
|
if color == "rgb" then
|
||||||
ch = 3
|
ch = 3
|
||||||
elseif color == "y" then
|
elseif color == "y" then
|
||||||
ch = 1
|
ch = 1
|
||||||
else
|
else
|
||||||
error("unknown color: " .. color)
|
error("unsupported color: " + color)
|
||||||
|
end
|
||||||
|
if backend == "cunn" then
|
||||||
|
return srcnn.waifu2x_cunn(ch)
|
||||||
|
elseif backend == "cudnn" then
|
||||||
|
return srcnn.waifu2x_cudnn(ch)
|
||||||
|
else
|
||||||
|
error("unsupported backend: " + backend)
|
||||||
end
|
end
|
||||||
|
|
||||||
model:add(nn.SpatialConvolutionMM(ch, 32, 9, 9, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(32, 32, 3, 3, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(32, 64, 5, 5, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(64, 64, 3, 3, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(64, 128, 5, 5, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(128, 128, 3, 3, 1, 1, 0, 0))
|
|
||||||
model:add(nn.LeakyReLU(0.1))
|
|
||||||
model:add(nn.SpatialConvolutionMM(128, ch, 5, 5, 1, 1, 0, 0))
|
|
||||||
model:add(nn.View(-1):setNumInputDims(3))
|
|
||||||
|
|
||||||
return model, 13
|
|
||||||
end
|
end
|
||||||
return srcnn
|
return srcnn
|
||||||
|
|
26
lib/w2nn.lua
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
local function load_nn()
|
||||||
|
require 'torch'
|
||||||
|
require 'nn'
|
||||||
|
end
|
||||||
|
local function load_cunn()
|
||||||
|
require 'cutorch'
|
||||||
|
require 'cunn'
|
||||||
|
end
|
||||||
|
local function load_cudnn()
|
||||||
|
require 'cudnn'
|
||||||
|
cudnn.benchmark = true
|
||||||
|
end
|
||||||
|
if w2nn then
|
||||||
|
return w2nn
|
||||||
|
else
|
||||||
|
pcall(load_cunn)
|
||||||
|
pcall(load_cudnn)
|
||||||
|
w2nn = {}
|
||||||
|
require 'LeakyReLU'
|
||||||
|
require 'LeakyReLU_deprecated'
|
||||||
|
require 'DepthExpand2x'
|
||||||
|
require 'WeightedMSECriterion'
|
||||||
|
require 'ClippedWeightedHuberCriterion'
|
||||||
|
require 'cleanup_model'
|
||||||
|
return w2nn
|
||||||
|
end
|
BIN
models/photo/noise1_model.json
Normal file
BIN
models/photo/noise1_model.t7
Normal file
BIN
models/photo/noise2_model.json
Normal file
BIN
models/photo/noise2_model.t7
Normal file
BIN
models/photo/scale2.0x_model.json
Normal file
BIN
models/photo/scale2.0x_model.t7
Normal file
209
tools/benchmark.lua
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
require 'xlua'
|
||||||
|
require 'w2nn'
|
||||||
|
local iproc = require 'iproc'
|
||||||
|
local reconstruct = require 'reconstruct'
|
||||||
|
local image_loader = require 'image_loader'
|
||||||
|
local gm = require 'graphicsmagick'
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("waifu2x-benchmark")
|
||||||
|
cmd:text("Options:")
|
||||||
|
|
||||||
|
cmd:option("-dir", "./data/test", 'test image directory')
|
||||||
|
cmd:option("-model1_dir", "./models/anime_style_art_rgb", 'model1 directory')
|
||||||
|
cmd:option("-model2_dir", "", 'model2 directory (optional)')
|
||||||
|
cmd:option("-method", "scale", '(scale|noise)')
|
||||||
|
cmd:option("-filter", "Catrom", "downscaling filter (Box|Lanczos|Catrom(Bicubic))")
|
||||||
|
cmd:option("-color", "y", '(rgb|y)')
|
||||||
|
cmd:option("-noise_level", 1, 'model noise level')
|
||||||
|
cmd:option("-jpeg_quality", 75, 'jpeg quality')
|
||||||
|
cmd:option("-jpeg_times", 1, 'jpeg compression times')
|
||||||
|
cmd:option("-jpeg_quality_down", 5, 'value of jpeg quality to decrease each times')
|
||||||
|
cmd:option("-range_bug", 0, 'Reproducing the dynamic range bug that is caused by MATLAB\'s rgb2ycbcr(1|0)')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
torch.setdefaulttensortype('torch.FloatTensor')
|
||||||
|
if cudnn then
|
||||||
|
cudnn.fastest = true
|
||||||
|
cudnn.benchmark = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rgb2y_matlab(x)
|
||||||
|
local y = torch.Tensor(1, x:size(2), x:size(3)):zero()
|
||||||
|
x = iproc.byte2float(x)
|
||||||
|
y:add(x[1] * 65.481)
|
||||||
|
y:add(x[2] * 128.553)
|
||||||
|
y:add(x[3] * 24.966)
|
||||||
|
y:add(16.0)
|
||||||
|
return y:byte():float()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function RGBMSE(x1, x2)
|
||||||
|
x1 = iproc.float2byte(x1):float()
|
||||||
|
x2 = iproc.float2byte(x2):float()
|
||||||
|
return (x1 - x2):pow(2):mean()
|
||||||
|
end
|
||||||
|
local function YMSE(x1, x2)
|
||||||
|
if opt.range_bug == 1 then
|
||||||
|
local x1_2 = rgb2y_matlab(x1)
|
||||||
|
local x2_2 = rgb2y_matlab(x2)
|
||||||
|
return (x1_2 - x2_2):pow(2):mean()
|
||||||
|
else
|
||||||
|
local x1_2 = image.rgb2y(x1):mul(255.0)
|
||||||
|
local x2_2 = image.rgb2y(x2):mul(255.0)
|
||||||
|
return (x1_2 - x2_2):pow(2):mean()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function MSE(x1, x2, color)
|
||||||
|
if color == "y" then
|
||||||
|
return YMSE(x1, x2)
|
||||||
|
else
|
||||||
|
return RGBMSE(x1, x2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function PSNR(x1, x2, color)
|
||||||
|
local mse = MSE(x1, x2, color)
|
||||||
|
return 10 * math.log10((255.0 * 255.0) / mse)
|
||||||
|
end
|
||||||
|
local function transform_jpeg(x, opt)
|
||||||
|
for i = 1, opt.jpeg_times do
|
||||||
|
jpeg = gm.Image(x, "RGB", "DHW")
|
||||||
|
jpeg:format("jpeg")
|
||||||
|
jpeg:samplingFactors({1.0, 1.0, 1.0})
|
||||||
|
blob, len = jpeg:toBlob(opt.jpeg_quality - (i - 1) * opt.jpeg_quality_down)
|
||||||
|
jpeg:fromBlob(blob, len)
|
||||||
|
x = jpeg:toTensor("byte", "RGB", "DHW")
|
||||||
|
end
|
||||||
|
return iproc.byte2float(x)
|
||||||
|
end
|
||||||
|
local function baseline_scale(x, filter)
|
||||||
|
return iproc.scale(x,
|
||||||
|
x:size(3) * 2.0,
|
||||||
|
x:size(2) * 2.0,
|
||||||
|
filter)
|
||||||
|
end
|
||||||
|
local function transform_scale(x, opt)
|
||||||
|
return iproc.scale(x,
|
||||||
|
x:size(3) * 0.5,
|
||||||
|
x:size(2) * 0.5,
|
||||||
|
opt.filter)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function benchmark(opt, x, input_func, model1, model2)
|
||||||
|
local model1_mse = 0
|
||||||
|
local model2_mse = 0
|
||||||
|
local baseline_mse = 0
|
||||||
|
local model1_psnr = 0
|
||||||
|
local model2_psnr = 0
|
||||||
|
local baseline_psnr = 0
|
||||||
|
|
||||||
|
for i = 1, #x do
|
||||||
|
local ground_truth = x[i]
|
||||||
|
local input, model1_output, model2_output, baseline_output
|
||||||
|
|
||||||
|
input = input_func(ground_truth, opt)
|
||||||
|
t = sys.clock()
|
||||||
|
if input:size(3) == ground_truth:size(3) then
|
||||||
|
model1_output = reconstruct.image(model1, input)
|
||||||
|
if model2 then
|
||||||
|
model2_output = reconstruct.image(model2, input)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
model1_output = reconstruct.scale(model1, 2.0, input)
|
||||||
|
if model2 then
|
||||||
|
model2_output = reconstruct.scale(model2, 2.0, input)
|
||||||
|
end
|
||||||
|
baseline_output = baseline_scale(input, opt.filter)
|
||||||
|
end
|
||||||
|
model1_mse = model1_mse + MSE(ground_truth, model1_output, opt.color)
|
||||||
|
model1_psnr = model1_psnr + PSNR(ground_truth, model1_output, opt.color)
|
||||||
|
if model2 then
|
||||||
|
model2_mse = model2_mse + MSE(ground_truth, model2_output, opt.color)
|
||||||
|
model2_psnr = model2_psnr + PSNR(ground_truth, model2_output, opt.color)
|
||||||
|
end
|
||||||
|
if baseline_output then
|
||||||
|
baseline_mse = baseline_mse + MSE(ground_truth, baseline_output, opt.color)
|
||||||
|
baseline_psnr = baseline_psnr + PSNR(ground_truth, baseline_output, opt.color)
|
||||||
|
end
|
||||||
|
if model2 then
|
||||||
|
if baseline_output then
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("%d/%d; baseline_rmse=%f, model1_rmse=%f, model2_rmse=%f, baseline_psnr=%f, model1_psnr=%f, model2_psnr=%f \r",
|
||||||
|
i, #x,
|
||||||
|
math.sqrt(baseline_mse / i),
|
||||||
|
math.sqrt(model1_mse / i), math.sqrt(model2_mse / i),
|
||||||
|
baseline_psnr / i,
|
||||||
|
model1_psnr / i, model2_psnr / i
|
||||||
|
))
|
||||||
|
else
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("%d/%d; model1_rmse=%f, model2_rmse=%f, model1_psnr=%f, model2_psnr=%f \r",
|
||||||
|
i, #x,
|
||||||
|
math.sqrt(model1_mse / i), math.sqrt(model2_mse / i),
|
||||||
|
model1_psnr / i, model2_psnr / i
|
||||||
|
))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if baseline_output then
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("%d/%d; baseline_rmse=%f, model1_rmse=%f, baseline_psnr=%f, model1_psnr=%f \r",
|
||||||
|
i, #x,
|
||||||
|
math.sqrt(baseline_mse / i), math.sqrt(model1_mse / i),
|
||||||
|
baseline_psnr / i, model1_psnr / i
|
||||||
|
))
|
||||||
|
else
|
||||||
|
io.stdout:write(
|
||||||
|
string.format("%d/%d; model1_rmse=%f, model1_psnr=%f \r",
|
||||||
|
i, #x,
|
||||||
|
math.sqrt(model1_mse / i), model1_psnr / i
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
io.stdout:flush()
|
||||||
|
end
|
||||||
|
io.stdout:write("\n")
|
||||||
|
end
|
||||||
|
local function load_data(test_dir)
|
||||||
|
local test_x = {}
|
||||||
|
local files = dir.getfiles(test_dir, "*.*")
|
||||||
|
for i = 1, #files do
|
||||||
|
table.insert(test_x, iproc.crop_mod4(image_loader.load_float(files[i])))
|
||||||
|
xlua.progress(i, #files)
|
||||||
|
end
|
||||||
|
return test_x
|
||||||
|
end
|
||||||
|
function load_model(filename)
|
||||||
|
return torch.load(filename, "ascii")
|
||||||
|
end
|
||||||
|
print(opt)
|
||||||
|
if opt.method == "scale" then
|
||||||
|
local f1 = path.join(opt.model1_dir, "scale2.0x_model.t7")
|
||||||
|
local f2 = path.join(opt.model2_dir, "scale2.0x_model.t7")
|
||||||
|
local s1, model1 = pcall(load_model, f1)
|
||||||
|
local s2, model2 = pcall(load_model, f2)
|
||||||
|
if not s1 then
|
||||||
|
error("Load error: " .. f1)
|
||||||
|
end
|
||||||
|
if not s2 then
|
||||||
|
model2 = nil
|
||||||
|
end
|
||||||
|
local test_x = load_data(opt.dir)
|
||||||
|
benchmark(opt, test_x, transform_scale, model1, model2)
|
||||||
|
elseif opt.method == "noise" then
|
||||||
|
local f1 = path.join(opt.model1_dir, string.format("noise%d_model.t7", opt.noise_level))
|
||||||
|
local f2 = path.join(opt.model2_dir, string.format("noise%d_model.t7", opt.noise_level))
|
||||||
|
local s1, model1 = pcall(load_model, f1)
|
||||||
|
local s2, model2 = pcall(load_model, f2)
|
||||||
|
if not s1 then
|
||||||
|
error("Load error: " .. f1)
|
||||||
|
end
|
||||||
|
if not s2 then
|
||||||
|
model2 = nil
|
||||||
|
end
|
||||||
|
local test_x = load_data(opt.dir)
|
||||||
|
benchmark(opt, test_x, transform_jpeg, model1, model2)
|
||||||
|
end
|
25
tools/cleanup_model.lua
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
|
||||||
|
require 'w2nn'
|
||||||
|
torch.setdefaulttensortype("torch.FloatTensor")
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("cleanup model")
|
||||||
|
cmd:text("Options:")
|
||||||
|
cmd:option("-model", "./model.t7", 'path of model file')
|
||||||
|
cmd:option("-iformat", "binary", 'input format')
|
||||||
|
cmd:option("-oformat", "binary", 'output format')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
local model = torch.load(opt.model, opt.iformat)
|
||||||
|
if model then
|
||||||
|
w2nn.cleanup_model(model)
|
||||||
|
model:cuda()
|
||||||
|
model:evaluate()
|
||||||
|
torch.save(opt.model, model, opt.oformat)
|
||||||
|
else
|
||||||
|
error("model not found")
|
||||||
|
end
|
43
tools/cudnn2cunn.lua
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
require 'os'
|
||||||
|
require 'w2nn'
|
||||||
|
local srcnn = require 'srcnn'
|
||||||
|
|
||||||
|
local function cudnn2cunn(cudnn_model)
|
||||||
|
local cunn_model = srcnn.waifu2x_cunn(srcnn.channels(cudnn_model))
|
||||||
|
local weight_from = cudnn_model:findModules("cudnn.SpatialConvolution")
|
||||||
|
local weight_to = cunn_model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
|
||||||
|
assert(#weight_from == #weight_to)
|
||||||
|
|
||||||
|
for i = 1, #weight_from do
|
||||||
|
local from = weight_from[i]
|
||||||
|
local to = weight_to[i]
|
||||||
|
|
||||||
|
to.weight:copy(from.weight)
|
||||||
|
to.bias:copy(from.bias)
|
||||||
|
end
|
||||||
|
cunn_model:cuda()
|
||||||
|
cunn_model:evaluate()
|
||||||
|
return cunn_model
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("waifu2x cudnn model to cunn model converter")
|
||||||
|
cmd:text("Options:")
|
||||||
|
cmd:option("-i", "", 'Specify the input cunn model')
|
||||||
|
cmd:option("-o", "", 'Specify the output cudnn model')
|
||||||
|
cmd:option("-iformat", "ascii", 'Specify the input format (ascii|binary)')
|
||||||
|
cmd:option("-oformat", "ascii", 'Specify the output format (ascii|binary)')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
if not path.isfile(opt.i) then
|
||||||
|
cmd:help()
|
||||||
|
os.exit(-1)
|
||||||
|
end
|
||||||
|
local cudnn_model = torch.load(opt.i, opt.iformat)
|
||||||
|
local cunn_model = cudnn2cunn(cudnn_model)
|
||||||
|
torch.save(opt.o, cunn_model, opt.oformat)
|
43
tools/cunn2cudnn.lua
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
require 'os'
|
||||||
|
require 'w2nn'
|
||||||
|
local srcnn = require 'srcnn'
|
||||||
|
|
||||||
|
local function cunn2cudnn(cunn_model)
|
||||||
|
local cudnn_model = srcnn.waifu2x_cudnn(srcnn.channels(cunn_model))
|
||||||
|
local weight_from = cunn_model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
local weight_to = cudnn_model:findModules("cudnn.SpatialConvolution")
|
||||||
|
|
||||||
|
assert(#weight_from == #weight_to)
|
||||||
|
|
||||||
|
for i = 1, #weight_from do
|
||||||
|
local from = weight_from[i]
|
||||||
|
local to = weight_to[i]
|
||||||
|
|
||||||
|
to.weight:copy(from.weight)
|
||||||
|
to.bias:copy(from.bias)
|
||||||
|
end
|
||||||
|
cudnn_model:cuda()
|
||||||
|
cudnn_model:evaluate()
|
||||||
|
return cudnn_model
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("waifu2x cunn model to cudnn model converter")
|
||||||
|
cmd:text("Options:")
|
||||||
|
cmd:option("-i", "", 'Specify the input cudnn model')
|
||||||
|
cmd:option("-o", "", 'Specify the output cunn model')
|
||||||
|
cmd:option("-iformat", "ascii", 'Specify the input format (ascii|binary)')
|
||||||
|
cmd:option("-oformat", "ascii", 'Specify the output format (ascii|binary)')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
if not path.isfile(opt.i) then
|
||||||
|
cmd:help()
|
||||||
|
os.exit(-1)
|
||||||
|
end
|
||||||
|
local cunn_model = torch.load(opt.i, opt.iformat)
|
||||||
|
local cudnn_model = cunn2cudnn(cunn_model)
|
||||||
|
torch.save(opt.o, cudnn_model, opt.oformat)
|
54
tools/export_model.lua
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
-- adapted from https://github.com/marcan/cl-waifu2x
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
require 'w2nn'
|
||||||
|
local cjson = require "cjson"
|
||||||
|
|
||||||
|
function export(model, output)
|
||||||
|
local jmodules = {}
|
||||||
|
local modules = model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
if #modules == 0 then
|
||||||
|
-- cudnn model
|
||||||
|
modules = model:findModules("cudnn.SpatialConvolution")
|
||||||
|
end
|
||||||
|
for i = 1, #modules, 1 do
|
||||||
|
local module = modules[i]
|
||||||
|
local jmod = {
|
||||||
|
kW = module.kW,
|
||||||
|
kH = module.kH,
|
||||||
|
nInputPlane = module.nInputPlane,
|
||||||
|
nOutputPlane = module.nOutputPlane,
|
||||||
|
bias = torch.totable(module.bias:float()),
|
||||||
|
weight = torch.totable(module.weight:float():reshape(module.nOutputPlane, module.nInputPlane, module.kW, module.kH))
|
||||||
|
}
|
||||||
|
table.insert(jmodules, jmod)
|
||||||
|
end
|
||||||
|
jmodules[1].color = "RGB"
|
||||||
|
jmodules[1].gamma = 0
|
||||||
|
jmodules[#jmodules].color = "RGB"
|
||||||
|
jmodules[#jmodules].gamma = 0
|
||||||
|
|
||||||
|
local fp = io.open(output, "w")
|
||||||
|
if not fp then
|
||||||
|
error("IO Error: " .. output)
|
||||||
|
end
|
||||||
|
fp:write(cjson.encode(jmodules))
|
||||||
|
fp:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("waifu2x export model")
|
||||||
|
cmd:text("Options:")
|
||||||
|
cmd:option("-i", "input.t7", 'Specify the input torch model')
|
||||||
|
cmd:option("-o", "output.json", 'Specify the output json file')
|
||||||
|
cmd:option("-iformat", "ascii", 'Specify the input format (ascii|binary)')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
if not path.isfile(opt.i) then
|
||||||
|
cmd:help()
|
||||||
|
os.exit(-1)
|
||||||
|
end
|
||||||
|
local model = torch.load(opt.i, opt.iformat)
|
||||||
|
export(model, opt.o)
|
43
tools/rebuild_model.lua
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "..", "lib", "?.lua;") .. package.path
|
||||||
|
require 'os'
|
||||||
|
require 'w2nn'
|
||||||
|
local srcnn = require 'srcnn'
|
||||||
|
|
||||||
|
local function rebuild(old_model)
|
||||||
|
local new_model = srcnn.waifu2x_cunn(srcnn.channels(old_model))
|
||||||
|
local weight_from = old_model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
local weight_to = new_model:findModules("nn.SpatialConvolutionMM")
|
||||||
|
|
||||||
|
assert(#weight_from == #weight_to)
|
||||||
|
|
||||||
|
for i = 1, #weight_from do
|
||||||
|
local from = weight_from[i]
|
||||||
|
local to = weight_to[i]
|
||||||
|
|
||||||
|
to.weight:copy(from.weight)
|
||||||
|
to.bias:copy(from.bias)
|
||||||
|
end
|
||||||
|
new_model:cuda()
|
||||||
|
new_model:evaluate()
|
||||||
|
return new_model
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = torch.CmdLine()
|
||||||
|
cmd:text()
|
||||||
|
cmd:text("waifu2x rebuild cunn model")
|
||||||
|
cmd:text("Options:")
|
||||||
|
cmd:option("-i", "", 'Specify the input model')
|
||||||
|
cmd:option("-o", "", 'Specify the output model')
|
||||||
|
cmd:option("-iformat", "ascii", 'Specify the input format (ascii|binary)')
|
||||||
|
cmd:option("-oformat", "ascii", 'Specify the output format (ascii|binary)')
|
||||||
|
|
||||||
|
local opt = cmd:parse(arg)
|
||||||
|
if not path.isfile(opt.i) then
|
||||||
|
cmd:help()
|
||||||
|
os.exit(-1)
|
||||||
|
end
|
||||||
|
local old_model = torch.load(opt.i, opt.iformat)
|
||||||
|
local new_model = rebuild(old_model)
|
||||||
|
torch.save(opt.o, new_model, opt.oformat)
|
227
train.lua
|
@ -1,21 +1,25 @@
|
||||||
require './lib/portable'
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "lib", "?.lua;") .. package.path
|
||||||
require 'optim'
|
require 'optim'
|
||||||
require 'xlua'
|
require 'xlua'
|
||||||
require 'pl'
|
|
||||||
|
|
||||||
local settings = require './lib/settings'
|
require 'w2nn'
|
||||||
local minibatch_adam = require './lib/minibatch_adam'
|
local settings = require 'settings'
|
||||||
local iproc = require './lib/iproc'
|
local srcnn = require 'srcnn'
|
||||||
local reconstruct = require './lib/reconstruct'
|
local minibatch_adam = require 'minibatch_adam'
|
||||||
local pairwise_transform = require './lib/pairwise_transform'
|
local iproc = require 'iproc'
|
||||||
local image_loader = require './lib/image_loader'
|
local reconstruct = require 'reconstruct'
|
||||||
|
local compression = require 'compression'
|
||||||
|
local pairwise_transform = require 'pairwise_transform'
|
||||||
|
local image_loader = require 'image_loader'
|
||||||
|
|
||||||
local function save_test_scale(model, rgb, file)
|
local function save_test_scale(model, rgb, file)
|
||||||
local up = reconstruct.scale(model, settings.scale, rgb, settings.block_offset)
|
local up = reconstruct.scale(model, settings.scale, rgb)
|
||||||
image.save(file, up)
|
image.save(file, up)
|
||||||
end
|
end
|
||||||
local function save_test_jpeg(model, rgb, file)
|
local function save_test_jpeg(model, rgb, file)
|
||||||
local im, count = reconstruct.image(model, rgb, settings.block_offset)
|
local im, count = reconstruct.image(model, rgb)
|
||||||
image.save(file, im)
|
image.save(file, im)
|
||||||
end
|
end
|
||||||
local function split_data(x, test_size)
|
local function split_data(x, test_size)
|
||||||
|
@ -31,14 +35,19 @@ local function split_data(x, test_size)
|
||||||
end
|
end
|
||||||
return train_x, valid_x
|
return train_x, valid_x
|
||||||
end
|
end
|
||||||
local function make_validation_set(x, transformer, n)
|
local function make_validation_set(x, transformer, n, patches)
|
||||||
n = n or 4
|
n = n or 4
|
||||||
local data = {}
|
local data = {}
|
||||||
for i = 1, #x do
|
for i = 1, #x do
|
||||||
for k = 1, n do
|
for k = 1, math.max(n / patches, 1) do
|
||||||
local x, y = transformer(x[i], true)
|
local xy = transformer(x[i], true, patches)
|
||||||
table.insert(data, {x = x:reshape(1, x:size(1), x:size(2), x:size(3)),
|
local tx = torch.Tensor(patches, xy[1][1]:size(1), xy[1][1]:size(2), xy[1][1]:size(3))
|
||||||
y = y:reshape(1, y:size(1), y:size(2), y:size(3))})
|
local ty = torch.Tensor(patches, xy[1][2]:size(1), xy[1][2]:size(2), xy[1][2]:size(3))
|
||||||
|
for j = 1, #xy do
|
||||||
|
tx[j]:copy(xy[j][1])
|
||||||
|
ty[j]:copy(xy[j][2])
|
||||||
|
end
|
||||||
|
table.insert(data, {x = tx, y = ty})
|
||||||
end
|
end
|
||||||
xlua.progress(i, #x)
|
xlua.progress(i, #x)
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
@ -50,113 +59,195 @@ local function validate(model, criterion, data)
|
||||||
for i = 1, #data do
|
for i = 1, #data do
|
||||||
local z = model:forward(data[i].x:cuda())
|
local z = model:forward(data[i].x:cuda())
|
||||||
loss = loss + criterion:forward(z, data[i].y:cuda())
|
loss = loss + criterion:forward(z, data[i].y:cuda())
|
||||||
|
if i % 100 == 0 then
|
||||||
xlua.progress(i, #data)
|
xlua.progress(i, #data)
|
||||||
if i % 10 == 0 then
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
xlua.progress(#data, #data)
|
||||||
return loss / #data
|
return loss / #data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function create_criterion(model)
|
||||||
|
if reconstruct.is_rgb(model) then
|
||||||
|
local offset = reconstruct.offset_size(model)
|
||||||
|
local output_w = settings.crop_size - offset * 2
|
||||||
|
local weight = torch.Tensor(3, output_w * output_w)
|
||||||
|
weight[1]:fill(0.29891 * 3) -- R
|
||||||
|
weight[2]:fill(0.58661 * 3) -- G
|
||||||
|
weight[3]:fill(0.11448 * 3) -- B
|
||||||
|
return w2nn.ClippedWeightedHuberCriterion(weight, 0.1, {0.0, 1.0}):cuda()
|
||||||
|
else
|
||||||
|
return nn.MSECriterion():cuda()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function transformer(x, is_validation, n, offset)
|
||||||
|
x = compression.decompress(x)
|
||||||
|
n = n or settings.patches
|
||||||
|
|
||||||
|
if is_validation == nil then is_validation = false end
|
||||||
|
local random_color_noise_rate = nil
|
||||||
|
local random_overlay_rate = nil
|
||||||
|
local active_cropping_rate = nil
|
||||||
|
local active_cropping_tries = nil
|
||||||
|
if is_validation then
|
||||||
|
active_cropping_rate = 0
|
||||||
|
active_cropping_tries = 0
|
||||||
|
random_color_noise_rate = 0.0
|
||||||
|
random_overlay_rate = 0.0
|
||||||
|
else
|
||||||
|
active_cropping_rate = settings.active_cropping_rate
|
||||||
|
active_cropping_tries = settings.active_cropping_tries
|
||||||
|
random_color_noise_rate = settings.random_color_noise_rate
|
||||||
|
random_overlay_rate = settings.random_overlay_rate
|
||||||
|
end
|
||||||
|
|
||||||
|
if settings.method == "scale" then
|
||||||
|
return pairwise_transform.scale(x,
|
||||||
|
settings.scale,
|
||||||
|
settings.crop_size, offset,
|
||||||
|
n,
|
||||||
|
{
|
||||||
|
random_half_rate = settings.random_half_rate,
|
||||||
|
random_color_noise_rate = random_color_noise_rate,
|
||||||
|
random_overlay_rate = random_overlay_rate,
|
||||||
|
random_unsharp_mask_rate = settings.random_unsharp_mask_rate,
|
||||||
|
max_size = settings.max_size,
|
||||||
|
active_cropping_rate = active_cropping_rate,
|
||||||
|
active_cropping_tries = active_cropping_tries,
|
||||||
|
rgb = (settings.color == "rgb")
|
||||||
|
})
|
||||||
|
elseif settings.method == "noise" then
|
||||||
|
return pairwise_transform.jpeg(x,
|
||||||
|
settings.style,
|
||||||
|
settings.noise_level,
|
||||||
|
settings.crop_size, offset,
|
||||||
|
n,
|
||||||
|
{
|
||||||
|
random_half_rate = settings.random_half_rate,
|
||||||
|
random_color_noise_rate = random_color_noise_rate,
|
||||||
|
random_overlay_rate = random_overlay_rate,
|
||||||
|
random_unsharp_mask_rate = settings.random_unsharp_mask_rate,
|
||||||
|
max_size = settings.max_size,
|
||||||
|
jpeg_chroma_subsampling_rate = settings.jpeg_chroma_subsampling_rate,
|
||||||
|
active_cropping_rate = active_cropping_rate,
|
||||||
|
active_cropping_tries = active_cropping_tries,
|
||||||
|
nr_rate = settings.nr_rate,
|
||||||
|
rgb = (settings.color == "rgb")
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resampling(x, y, train_x, transformer, input_size, target_size)
|
||||||
|
print("## resampling")
|
||||||
|
for t = 1, #train_x do
|
||||||
|
xlua.progress(t, #train_x)
|
||||||
|
local xy = transformer(train_x[t], false, settings.patches)
|
||||||
|
for i = 1, #xy do
|
||||||
|
local index = (t - 1) * settings.patches + i
|
||||||
|
x[index]:copy(xy[i][1])
|
||||||
|
y[index]:copy(xy[i][2])
|
||||||
|
end
|
||||||
|
if t % 50 == 0 then
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function train()
|
local function train()
|
||||||
local model, offset = settings.create_model(settings.color)
|
local LR_MIN = 1.0e-5
|
||||||
assert(offset == settings.block_offset)
|
local model = srcnn.create(settings.method, settings.backend, settings.color)
|
||||||
local criterion = nn.MSECriterion():cuda()
|
local offset = reconstruct.offset_size(model)
|
||||||
|
local pairwise_func = function(x, is_validation, n)
|
||||||
|
return transformer(x, is_validation, n, offset)
|
||||||
|
end
|
||||||
|
local criterion = create_criterion(model)
|
||||||
local x = torch.load(settings.images)
|
local x = torch.load(settings.images)
|
||||||
local lrd_count = 0
|
local train_x, valid_x = split_data(x, math.floor(settings.validation_rate * #x))
|
||||||
local train_x, valid_x = split_data(x,
|
|
||||||
math.floor(settings.validation_ratio * #x),
|
|
||||||
settings.validation_crops)
|
|
||||||
local test = image_loader.load_float(settings.test)
|
|
||||||
local adam_config = {
|
local adam_config = {
|
||||||
learningRate = settings.learning_rate,
|
learningRate = settings.learning_rate,
|
||||||
xBatchSize = settings.batch_size,
|
xBatchSize = settings.batch_size,
|
||||||
}
|
}
|
||||||
|
local lrd_count = 0
|
||||||
local ch = nil
|
local ch = nil
|
||||||
if settings.color == "y" then
|
if settings.color == "y" then
|
||||||
ch = 1
|
ch = 1
|
||||||
elseif settings.color == "rgb" then
|
elseif settings.color == "rgb" then
|
||||||
ch = 3
|
ch = 3
|
||||||
end
|
end
|
||||||
local transformer = function(x, is_validation)
|
|
||||||
if is_validation == nil then is_validation = false end
|
|
||||||
if settings.method == "scale" then
|
|
||||||
return pairwise_transform.scale(x,
|
|
||||||
settings.scale,
|
|
||||||
settings.crop_size, offset,
|
|
||||||
{ color_augment = not is_validation,
|
|
||||||
random_half = settings.random_half,
|
|
||||||
rgb = (settings.color == "rgb")
|
|
||||||
})
|
|
||||||
elseif settings.method == "noise" then
|
|
||||||
return pairwise_transform.jpeg(x,
|
|
||||||
settings.noise_level,
|
|
||||||
settings.crop_size, offset,
|
|
||||||
{ color_augment = not is_validation,
|
|
||||||
random_half = settings.random_half,
|
|
||||||
rgb = (settings.color == "rgb")
|
|
||||||
})
|
|
||||||
elseif settings.method == "noise_scale" then
|
|
||||||
return pairwise_transform.jpeg_scale(x,
|
|
||||||
settings.scale,
|
|
||||||
settings.noise_level,
|
|
||||||
settings.crop_size, offset,
|
|
||||||
{ color_augment = not is_validation,
|
|
||||||
random_half = settings.random_half,
|
|
||||||
rgb = (settings.color == "rgb")
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local best_score = 100000.0
|
local best_score = 100000.0
|
||||||
print("# make validation-set")
|
print("# make validation-set")
|
||||||
local valid_xy = make_validation_set(valid_x, transformer, 20)
|
local valid_xy = make_validation_set(valid_x, pairwise_func,
|
||||||
|
settings.validation_crops,
|
||||||
|
settings.patches)
|
||||||
valid_x = nil
|
valid_x = nil
|
||||||
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
model:cuda()
|
model:cuda()
|
||||||
print("load .. " .. #train_x)
|
print("load .. " .. #train_x)
|
||||||
|
|
||||||
|
local x = torch.Tensor(settings.patches * #train_x,
|
||||||
|
ch, settings.crop_size, settings.crop_size)
|
||||||
|
local y = torch.Tensor(settings.patches * #train_x,
|
||||||
|
ch * (settings.crop_size - offset * 2) * (settings.crop_size - offset * 2)):zero()
|
||||||
|
|
||||||
for epoch = 1, settings.epoch do
|
for epoch = 1, settings.epoch do
|
||||||
model:training()
|
model:training()
|
||||||
print("# " .. epoch)
|
print("# " .. epoch)
|
||||||
print(minibatch_adam(model, criterion, train_x, adam_config,
|
resampling(x, y, train_x, pairwise_func)
|
||||||
transformer,
|
for i = 1, settings.inner_epoch do
|
||||||
{ch, settings.crop_size, settings.crop_size},
|
print(minibatch_adam(model, criterion, x, y, adam_config))
|
||||||
{ch, settings.crop_size - offset * 2, settings.crop_size - offset * 2}
|
|
||||||
))
|
|
||||||
model:evaluate()
|
model:evaluate()
|
||||||
print("# validation")
|
print("# validation")
|
||||||
local score = validate(model, criterion, valid_xy)
|
local score = validate(model, criterion, valid_xy)
|
||||||
if score < best_score then
|
if score < best_score then
|
||||||
|
local test_image = image_loader.load_float(settings.test) -- reload
|
||||||
lrd_count = 0
|
lrd_count = 0
|
||||||
best_score = score
|
best_score = score
|
||||||
print("* update best model")
|
print("* update best model")
|
||||||
|
if settings.save_history then
|
||||||
|
local model_clone = model:clone()
|
||||||
|
w2nn.cleanup_model(model_clone)
|
||||||
|
torch.save(string.format(settings.model_file, epoch, i), model_clone)
|
||||||
|
if settings.method == "noise" then
|
||||||
|
local log = path.join(settings.model_dir,
|
||||||
|
("noise%d_best.%d-%d.png"):format(settings.noise_level,
|
||||||
|
epoch, i))
|
||||||
|
save_test_jpeg(model, test_image, log)
|
||||||
|
elseif settings.method == "scale" then
|
||||||
|
local log = path.join(settings.model_dir,
|
||||||
|
("scale%.1f_best.%d-%d.png"):format(settings.scale,
|
||||||
|
epoch, i))
|
||||||
|
save_test_scale(model, test_image, log)
|
||||||
|
end
|
||||||
|
else
|
||||||
torch.save(settings.model_file, model)
|
torch.save(settings.model_file, model)
|
||||||
if settings.method == "noise" then
|
if settings.method == "noise" then
|
||||||
local log = path.join(settings.model_dir,
|
local log = path.join(settings.model_dir,
|
||||||
("noise%d_best.png"):format(settings.noise_level))
|
("noise%d_best.png"):format(settings.noise_level))
|
||||||
save_test_jpeg(model, test, log)
|
save_test_jpeg(model, test_image, log)
|
||||||
elseif settings.method == "scale" then
|
elseif settings.method == "scale" then
|
||||||
local log = path.join(settings.model_dir,
|
local log = path.join(settings.model_dir,
|
||||||
("scale%.1f_best.png"):format(settings.scale))
|
("scale%.1f_best.png"):format(settings.scale))
|
||||||
save_test_scale(model, test, log)
|
save_test_scale(model, test_image, log)
|
||||||
elseif settings.method == "noise_scale" then
|
end
|
||||||
local log = path.join(settings.model_dir,
|
|
||||||
("noise%d_scale%.1f_best.png"):format(settings.noise_level,
|
|
||||||
settings.scale))
|
|
||||||
save_test_scale(model, test, log)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
lrd_count = lrd_count + 1
|
lrd_count = lrd_count + 1
|
||||||
if lrd_count > 5 then
|
if lrd_count > 2 and adam_config.learningRate > LR_MIN then
|
||||||
lrd_count = 0
|
|
||||||
adam_config.learningRate = adam_config.learningRate * 0.8
|
adam_config.learningRate = adam_config.learningRate * 0.8
|
||||||
print("* learning rate decay: " .. adam_config.learningRate)
|
print("* learning rate decay: " .. adam_config.learningRate)
|
||||||
|
lrd_count = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print("current: " .. score .. ", best: " .. best_score)
|
print("current: " .. score .. ", best: " .. best_score)
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
if settings.gpu > 0 then
|
||||||
|
cutorch.setDevice(settings.gpu)
|
||||||
|
end
|
||||||
torch.manualSeed(settings.seed)
|
torch.manualSeed(settings.seed)
|
||||||
cutorch.manualSeed(settings.seed)
|
cutorch.manualSeed(settings.seed)
|
||||||
print(settings)
|
print(settings)
|
||||||
|
|
14
train.sh
|
@ -1,10 +1,12 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
th train.lua -color rgb -method noise -noise_level 1 -model_dir models/anime_style_art_rgb -test images/miku_noisy.png
|
th convert_data.lua
|
||||||
th cleanup_model.lua -model models/anime_style_art_rgb/noise1_model.t7 -oformat ascii
|
|
||||||
|
|
||||||
th train.lua -color rgb -method noise -noise_level 2 -model_dir models/anime_style_art_rgb -test images/miku_noisy.png
|
th train.lua -method scale -model_dir models/anime_style_art_rgb -test images/miku_small.png -thread 4
|
||||||
th cleanup_model.lua -model models/anime_style_art_rgb/noise2_model.t7 -oformat ascii
|
th tools/cleanup_model.lua -model models/anime_style_art_rgb/scale2.0x_model.t7 -oformat ascii
|
||||||
|
|
||||||
th train.lua -color rgb -method scale -scale 2 -model_dir models/anime_style_art_rgb -test images/miku_small.png
|
th train.lua -method noise -noise_level 1 -style art -model_dir models/anime_style_art_rgb -test images/miku_noisy.png -thread 4
|
||||||
th cleanup_model.lua -model models/anime_style_art_rgb/scale2.0x_model.t7 -oformat ascii
|
th tools/cleanup_model.lua -model models/anime_style_art_rgb/noise1_model.t7 -oformat ascii
|
||||||
|
|
||||||
|
th train.lua -method noise -noise_level 2 -style art -model_dir models/anime_style_art_rgb -test images/miku_noisy.png -thread 4
|
||||||
|
th tools/cleanup_model.lua -model models/anime_style_art_rgb/noise2_model.t7 -oformat ascii
|
||||||
|
|
12
train_photo.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
th convert_data.lua -style photo -data_dir ./data/photo -model_dir models/photo
|
||||||
|
|
||||||
|
th train.lua -style photo -method scale -data_dir ./data/photo -model_dir models/photo_uk -test work/scale_test_photo.png -color rgb -thread 4 -backend cudnn -random_unsharp_mask_rate 0.1 -validation_crops 160
|
||||||
|
th tools/cleanup_model.lua -model models/photo/scale2.0x_model.t7 -oformat ascii
|
||||||
|
|
||||||
|
th train.lua -style photo -method noise -noise_level 1 -data_dir ./data/photo -model_dir models/photo -test work/noise_test_photo.jpg -color rgb -thread 4 -backend cudnn -random_unsharp_mask_rate 0.5 -validation_crops 160 -nr_rate 0.6 -epoch 33
|
||||||
|
th tools/cleanup_model.lua -model models/photo/noise1_model.t7 -oformat ascii
|
||||||
|
|
||||||
|
th train.lua -style photo -method noise -noise_level 2 -data_dir ./data/photo -model_dir models/photo -test work/noise_test_photo.jpg -color rgb -thread 4 -backend cudnn -random_unsharp_mask_rate 0.5 -validation_crops 160 -nr_rate 0.8 -epoch 38
|
||||||
|
th tools/cleanup_model.lua -model models/photo/noise2_model.t7 -oformat ascii
|
9
train_ukbench.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
th convert_data.lua -data_dir ./data/ukbench
|
||||||
|
|
||||||
|
#th train.lua -style photo -method noise -noise_level 2 -data_dir ./data/ukbench -model_dir models/ukbench -test images/lena.png -nr_rate 0.9 -jpeg_sampling_factors 420 # -thread 4 -backend cudnn
|
||||||
|
#th tools/cleanup_model.lua -model models/ukbench/noise2_model.t7 -oformat ascii
|
||||||
|
|
||||||
|
th train.lua -method scale -data_dir ./data/ukbench -model_dir models/ukbench -test images/lena.jpg # -thread 4 -backend cudnn
|
||||||
|
th tools/cleanup_model.lua -model models/ukbench/scale2.0x_model.t7 -oformat ascii
|
172
waifu2x.lua
|
@ -1,12 +1,12 @@
|
||||||
require './lib/portable'
|
|
||||||
require 'sys'
|
|
||||||
require 'pl'
|
require 'pl'
|
||||||
require './lib/LeakyReLU'
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
package.path = path.join(path.dirname(__FILE__), "lib", "?.lua;") .. package.path
|
||||||
local iproc = require './lib/iproc'
|
require 'sys'
|
||||||
local reconstruct = require './lib/reconstruct'
|
require 'w2nn'
|
||||||
local image_loader = require './lib/image_loader'
|
local iproc = require 'iproc'
|
||||||
local BLOCK_OFFSET = 7
|
local reconstruct = require 'reconstruct'
|
||||||
|
local image_loader = require 'image_loader'
|
||||||
|
local alpha_util = require 'alpha_util'
|
||||||
|
|
||||||
torch.setdefaulttensortype('torch.FloatTensor')
|
torch.setdefaulttensortype('torch.FloatTensor')
|
||||||
|
|
||||||
|
@ -14,43 +14,112 @@ local function convert_image(opt)
|
||||||
local x, alpha = image_loader.load_float(opt.i)
|
local x, alpha = image_loader.load_float(opt.i)
|
||||||
local new_x = nil
|
local new_x = nil
|
||||||
local t = sys.clock()
|
local t = sys.clock()
|
||||||
|
local scale_f, image_f
|
||||||
|
|
||||||
|
if opt.tta == 1 then
|
||||||
|
scale_f = reconstruct.scale_tta
|
||||||
|
image_f = reconstruct.image_tta
|
||||||
|
else
|
||||||
|
scale_f = reconstruct.scale
|
||||||
|
image_f = reconstruct.image
|
||||||
|
end
|
||||||
if opt.o == "(auto)" then
|
if opt.o == "(auto)" then
|
||||||
local name = path.basename(opt.i)
|
local name = path.basename(opt.i)
|
||||||
local e = path.extension(name)
|
local e = path.extension(name)
|
||||||
local base = name:sub(0, name:len() - e:len())
|
local base = name:sub(0, name:len() - e:len())
|
||||||
opt.o = path.join(path.dirname(opt.i), string.format("%s(%s).png", base, opt.m))
|
opt.o = path.join(path.dirname(opt.i), string.format("%s_%s.png", base, opt.m))
|
||||||
end
|
end
|
||||||
if opt.m == "noise" then
|
if opt.m == "noise" then
|
||||||
local model = torch.load(path.join(opt.model_dir, ("noise%d_model.t7"):format(opt.noise_level)), "ascii")
|
local model_path = path.join(opt.model_dir, ("noise%d_model.t7"):format(opt.noise_level))
|
||||||
model:evaluate()
|
local model = torch.load(model_path, "ascii")
|
||||||
new_x = reconstruct.image(model, x, BLOCK_OFFSET, opt.crop_size)
|
if not model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
new_x = image_f(model, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha)
|
||||||
elseif opt.m == "scale" then
|
elseif opt.m == "scale" then
|
||||||
local model = torch.load(path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale)), "ascii")
|
local model_path = path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale))
|
||||||
model:evaluate()
|
local model = torch.load(model_path, "ascii")
|
||||||
new_x = reconstruct.scale(model, opt.scale, x, BLOCK_OFFSET, opt.crop_size)
|
if not model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(model))
|
||||||
|
new_x = scale_f(model, opt.scale, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha, model)
|
||||||
elseif opt.m == "noise_scale" then
|
elseif opt.m == "noise_scale" then
|
||||||
local noise_model = torch.load(path.join(opt.model_dir, ("noise%d_model.t7"):format(opt.noise_level)), "ascii")
|
local noise_model_path = path.join(opt.model_dir, ("noise%d_model.t7"):format(opt.noise_level))
|
||||||
local scale_model = torch.load(path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale)), "ascii")
|
local noise_model = torch.load(noise_model_path, "ascii")
|
||||||
noise_model:evaluate()
|
local scale_model_path = path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale))
|
||||||
scale_model:evaluate()
|
local scale_model = torch.load(scale_model_path, "ascii")
|
||||||
x = reconstruct.image(noise_model, x, BLOCK_OFFSET)
|
|
||||||
new_x = reconstruct.scale(scale_model, opt.scale, x, BLOCK_OFFSET, opt.crop_size)
|
if not noise_model then
|
||||||
|
error("Load Error: " .. noise_model_path)
|
||||||
|
end
|
||||||
|
if not scale_model then
|
||||||
|
error("Load Error: " .. scale_model_path)
|
||||||
|
end
|
||||||
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(scale_model))
|
||||||
|
x = image_f(noise_model, x, opt.crop_size)
|
||||||
|
new_x = scale_f(scale_model, opt.scale, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha, scale_model)
|
||||||
else
|
else
|
||||||
error("undefined method:" .. opt.method)
|
error("undefined method:" .. opt.method)
|
||||||
end
|
end
|
||||||
image_loader.save_png(opt.o, new_x, alpha)
|
image_loader.save_png(opt.o, new_x, opt.depth)
|
||||||
print(opt.o .. ": " .. (sys.clock() - t) .. " sec")
|
print(opt.o .. ": " .. (sys.clock() - t) .. " sec")
|
||||||
end
|
end
|
||||||
local function convert_frames(opt)
|
local function convert_frames(opt)
|
||||||
local noise1_model = torch.load(path.join(opt.model_dir, "noise1_model.t7"), "ascii")
|
local model_path, noise1_model, noise2_model, scale_model
|
||||||
local noise2_model = torch.load(path.join(opt.model_dir, "noise2_model.t7"), "ascii")
|
local scale_f, image_f
|
||||||
local scale_model = torch.load(path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale)), "ascii")
|
if opt.tta == 1 then
|
||||||
|
scale_f = reconstruct.scale_tta
|
||||||
noise1_model:evaluate()
|
image_f = reconstruct.image_tta
|
||||||
noise2_model:evaluate()
|
else
|
||||||
scale_model:evaluate()
|
scale_f = reconstruct.scale
|
||||||
|
image_f = reconstruct.image
|
||||||
|
end
|
||||||
|
if opt.m == "scale" then
|
||||||
|
model_path = path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale))
|
||||||
|
scale_model = torch.load(model_path, "ascii")
|
||||||
|
if not scale_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
elseif opt.m == "noise" and opt.noise_level == 1 then
|
||||||
|
model_path = path.join(opt.model_dir, "noise1_model.t7")
|
||||||
|
noise1_model = torch.load(model_path, "ascii")
|
||||||
|
if not noise1_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
elseif opt.m == "noise" and opt.noise_level == 2 then
|
||||||
|
model_path = path.join(opt.model_dir, "noise2_model.t7")
|
||||||
|
noise2_model = torch.load(model_path, "ascii")
|
||||||
|
if not noise2_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
elseif opt.m == "noise_scale" then
|
||||||
|
model_path = path.join(opt.model_dir, ("scale%.1fx_model.t7"):format(opt.scale))
|
||||||
|
scale_model = torch.load(model_path, "ascii")
|
||||||
|
if not scale_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
if opt.noise_level == 1 then
|
||||||
|
model_path = path.join(opt.model_dir, "noise1_model.t7")
|
||||||
|
noise1_model = torch.load(model_path, "ascii")
|
||||||
|
if not noise1_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
elseif opt.noise_level == 2 then
|
||||||
|
model_path = path.join(opt.model_dir, "noise2_model.t7")
|
||||||
|
noise2_model = torch.load(model_path, "ascii")
|
||||||
|
if not noise2_model then
|
||||||
|
error("Load Error: " .. model_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
local fp = io.open(opt.l)
|
local fp = io.open(opt.l)
|
||||||
|
if not fp then
|
||||||
|
error("Open Error: " .. opt.l)
|
||||||
|
end
|
||||||
local count = 0
|
local count = 0
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for line in fp:lines() do
|
for line in fp:lines() do
|
||||||
|
@ -62,17 +131,25 @@ local function convert_frames(opt)
|
||||||
local x, alpha = image_loader.load_float(lines[i])
|
local x, alpha = image_loader.load_float(lines[i])
|
||||||
local new_x = nil
|
local new_x = nil
|
||||||
if opt.m == "noise" and opt.noise_level == 1 then
|
if opt.m == "noise" and opt.noise_level == 1 then
|
||||||
new_x = reconstruct.image(noise1_model, x, BLOCK_OFFSET, opt.crop_size)
|
new_x = image_f(noise1_model, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha)
|
||||||
elseif opt.m == "noise" and opt.noise_level == 2 then
|
elseif opt.m == "noise" and opt.noise_level == 2 then
|
||||||
new_x = reconstruct.image(noise2_model, x, BLOCK_OFFSET)
|
new_x = image_f(noise2_model, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha)
|
||||||
elseif opt.m == "scale" then
|
elseif opt.m == "scale" then
|
||||||
new_x = reconstruct.scale(scale_model, opt.scale, x, BLOCK_OFFSET, opt.crop_size)
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(scale_model))
|
||||||
|
new_x = scale_f(scale_model, opt.scale, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha, scale_model)
|
||||||
elseif opt.m == "noise_scale" and opt.noise_level == 1 then
|
elseif opt.m == "noise_scale" and opt.noise_level == 1 then
|
||||||
x = reconstruct.image(noise1_model, x, BLOCK_OFFSET)
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(scale_model))
|
||||||
new_x = reconstruct.scale(scale_model, opt.scale, x, BLOCK_OFFSET, opt.crop_size)
|
x = image_f(noise1_model, x, opt.crop_size)
|
||||||
|
new_x = scale_f(scale_model, opt.scale, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha, scale_model)
|
||||||
elseif opt.m == "noise_scale" and opt.noise_level == 2 then
|
elseif opt.m == "noise_scale" and opt.noise_level == 2 then
|
||||||
x = reconstruct.image(noise2_model, x, BLOCK_OFFSET)
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(scale_model))
|
||||||
new_x = reconstruct.scale(scale_model, opt.scale, x, BLOCK_OFFSET, opt.crop_size)
|
x = image_f(noise2_model, x, opt.crop_size)
|
||||||
|
new_x = scale_f(scale_model, opt.scale, x, opt.crop_size)
|
||||||
|
new_x = alpha_util.composite(new_x, alpha, scale_model)
|
||||||
else
|
else
|
||||||
error("undefined method:" .. opt.method)
|
error("undefined method:" .. opt.method)
|
||||||
end
|
end
|
||||||
|
@ -85,7 +162,7 @@ local function convert_frames(opt)
|
||||||
else
|
else
|
||||||
output = string.format(opt.o, i)
|
output = string.format(opt.o, i)
|
||||||
end
|
end
|
||||||
image_loader.save_png(output, new_x, alpha)
|
image_loader.save_png(output, new_x, opt.depth)
|
||||||
xlua.progress(i, #lines)
|
xlua.progress(i, #lines)
|
||||||
if i % 10 == 0 then
|
if i % 10 == 0 then
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
@ -101,17 +178,28 @@ local function waifu2x()
|
||||||
cmd:text()
|
cmd:text()
|
||||||
cmd:text("waifu2x")
|
cmd:text("waifu2x")
|
||||||
cmd:text("Options:")
|
cmd:text("Options:")
|
||||||
cmd:option("-i", "images/miku_small.png", 'path of the input image')
|
cmd:option("-i", "images/miku_small.png", 'path to input image')
|
||||||
cmd:option("-l", "", 'path of the image-list')
|
cmd:option("-l", "", 'path to image-list.txt')
|
||||||
cmd:option("-scale", 2, 'scale factor')
|
cmd:option("-scale", 2, 'scale factor')
|
||||||
cmd:option("-o", "(auto)", 'path of the output file')
|
cmd:option("-o", "(auto)", 'path to output file')
|
||||||
cmd:option("-model_dir", "./models/anime_style_art_rgb", 'model directory')
|
cmd:option("-depth", 8, 'bit-depth of the output image (8|16)')
|
||||||
|
cmd:option("-model_dir", "./models/anime_style_art_rgb", 'path to model directory')
|
||||||
cmd:option("-m", "noise_scale", 'method (noise|scale|noise_scale)')
|
cmd:option("-m", "noise_scale", 'method (noise|scale|noise_scale)')
|
||||||
cmd:option("-noise_level", 1, '(1|2)')
|
cmd:option("-noise_level", 1, '(1|2)')
|
||||||
cmd:option("-crop_size", 128, 'patch size per process')
|
cmd:option("-crop_size", 128, 'patch size per process')
|
||||||
cmd:option("-resume", 0, "skip existing files (0|1)")
|
cmd:option("-resume", 0, "skip existing files (0|1)")
|
||||||
|
cmd:option("-thread", -1, "number of CPU threads")
|
||||||
|
cmd:option("-tta", 0, '8x slower and slightly high quality (0|1)')
|
||||||
|
|
||||||
local opt = cmd:parse(arg)
|
local opt = cmd:parse(arg)
|
||||||
|
if opt.thread > 0 then
|
||||||
|
torch.setnumthreads(opt.thread)
|
||||||
|
end
|
||||||
|
if cudnn then
|
||||||
|
cudnn.fastest = true
|
||||||
|
cudnn.benchmark = false
|
||||||
|
end
|
||||||
|
|
||||||
if string.len(opt.l) == 0 then
|
if string.len(opt.l) == 0 then
|
||||||
convert_image(opt)
|
convert_image(opt)
|
||||||
else
|
else
|
||||||
|
|
253
web.lua
|
@ -1,11 +1,23 @@
|
||||||
|
require 'pl'
|
||||||
|
local __FILE__ = (function() return string.gsub(debug.getinfo(2, 'S').source, "^@", "") end)()
|
||||||
|
local ROOT = path.dirname(__FILE__)
|
||||||
|
package.path = path.join(ROOT, "lib", "?.lua;") .. package.path
|
||||||
_G.TURBO_SSL = true
|
_G.TURBO_SSL = true
|
||||||
local turbo = require 'turbo'
|
|
||||||
|
require 'w2nn'
|
||||||
local uuid = require 'uuid'
|
local uuid = require 'uuid'
|
||||||
local ffi = require 'ffi'
|
local ffi = require 'ffi'
|
||||||
local md5 = require 'md5'
|
local md5 = require 'md5'
|
||||||
require 'pl'
|
local iproc = require 'iproc'
|
||||||
require './lib/portable'
|
local reconstruct = require 'reconstruct'
|
||||||
require './lib/LeakyReLU'
|
local image_loader = require 'image_loader'
|
||||||
|
local alpha_util = require 'alpha_util'
|
||||||
|
local gm = require 'graphicsmagick'
|
||||||
|
|
||||||
|
-- Note: turbo and xlua has different implementation of string:split().
|
||||||
|
-- Therefore, string:split() has conflict issue.
|
||||||
|
-- In this script, use turbo's string:split().
|
||||||
|
local turbo = require 'turbo'
|
||||||
|
|
||||||
local cmd = torch.CmdLine()
|
local cmd = torch.CmdLine()
|
||||||
cmd:text()
|
cmd:text()
|
||||||
|
@ -13,34 +25,36 @@ cmd:text("waifu2x-api")
|
||||||
cmd:text("Options:")
|
cmd:text("Options:")
|
||||||
cmd:option("-port", 8812, 'listen port')
|
cmd:option("-port", 8812, 'listen port')
|
||||||
cmd:option("-gpu", 1, 'Device ID')
|
cmd:option("-gpu", 1, 'Device ID')
|
||||||
cmd:option("-core", 2, 'number of CPU cores')
|
cmd:option("-thread", -1, 'number of CPU threads')
|
||||||
local opt = cmd:parse(arg)
|
local opt = cmd:parse(arg)
|
||||||
cutorch.setDevice(opt.gpu)
|
cutorch.setDevice(opt.gpu)
|
||||||
torch.setdefaulttensortype('torch.FloatTensor')
|
torch.setdefaulttensortype('torch.FloatTensor')
|
||||||
torch.setnumthreads(opt.core)
|
if opt.thread > 0 then
|
||||||
|
torch.setnumthreads(opt.thread)
|
||||||
local iproc = require './lib/iproc'
|
end
|
||||||
local reconstruct = require './lib/reconstruct'
|
if cudnn then
|
||||||
local image_loader = require './lib/image_loader'
|
cudnn.fastest = true
|
||||||
|
cudnn.benchmark = false
|
||||||
local MODEL_DIR = "./models/anime_style_art_rgb"
|
end
|
||||||
|
local ART_MODEL_DIR = path.join(ROOT, "models", "anime_style_art_rgb")
|
||||||
local noise1_model = torch.load(path.join(MODEL_DIR, "noise1_model.t7"), "ascii")
|
local PHOTO_MODEL_DIR = path.join(ROOT, "models", "photo")
|
||||||
local noise2_model = torch.load(path.join(MODEL_DIR, "noise2_model.t7"), "ascii")
|
local art_noise1_model = torch.load(path.join(ART_MODEL_DIR, "noise1_model.t7"), "ascii")
|
||||||
local scale20_model = torch.load(path.join(MODEL_DIR, "scale2.0x_model.t7"), "ascii")
|
local art_noise2_model = torch.load(path.join(ART_MODEL_DIR, "noise2_model.t7"), "ascii")
|
||||||
|
local art_scale2_model = torch.load(path.join(ART_MODEL_DIR, "scale2.0x_model.t7"), "ascii")
|
||||||
local USE_CACHE = true
|
local photo_scale2_model = torch.load(path.join(PHOTO_MODEL_DIR, "scale2.0x_model.t7"), "ascii")
|
||||||
local CACHE_DIR = "./cache"
|
local photo_noise1_model = torch.load(path.join(PHOTO_MODEL_DIR, "noise1_model.t7"), "ascii")
|
||||||
|
local photo_noise2_model = torch.load(path.join(PHOTO_MODEL_DIR, "noise2_model.t7"), "ascii")
|
||||||
|
local CLEANUP_MODEL = false -- if you are using the low memory GPU, you could use this flag.
|
||||||
|
local CACHE_DIR = path.join(ROOT, "cache")
|
||||||
local MAX_NOISE_IMAGE = 2560 * 2560
|
local MAX_NOISE_IMAGE = 2560 * 2560
|
||||||
local MAX_SCALE_IMAGE = 1280 * 1280
|
local MAX_SCALE_IMAGE = 1280 * 1280
|
||||||
local CURL_OPTIONS = {
|
local CURL_OPTIONS = {
|
||||||
request_timeout = 15,
|
request_timeout = 60,
|
||||||
connect_timeout = 10,
|
connect_timeout = 60,
|
||||||
allow_redirects = true,
|
allow_redirects = true,
|
||||||
max_redirects = 2
|
max_redirects = 2
|
||||||
}
|
}
|
||||||
local CURL_MAX_SIZE = 2 * 1024 * 1024
|
local CURL_MAX_SIZE = 3 * 1024 * 1024
|
||||||
local BLOCK_OFFSET = 7 -- see srcnn.lua
|
|
||||||
|
|
||||||
local function valid_size(x, scale)
|
local function valid_size(x, scale)
|
||||||
if scale == 0 then
|
if scale == 0 then
|
||||||
|
@ -50,16 +64,12 @@ local function valid_size(x, scale)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_image(req)
|
local function cache_url(url)
|
||||||
local file = req:get_argument("file", "")
|
local hash = md5.sumhexa(url)
|
||||||
local url = req:get_argument("url", "")
|
local cache_file = path.join(CACHE_DIR, "url_" .. hash)
|
||||||
local blob = nil
|
if path.exists(cache_file) then
|
||||||
local img = nil
|
return image_loader.load_float(cache_file)
|
||||||
local alpha = nil
|
else
|
||||||
if file and file:len() > 0 then
|
|
||||||
blob = file
|
|
||||||
img, alpha = image_loader.decode_float(blob)
|
|
||||||
elseif url and url:len() > 0 then
|
|
||||||
local res = coroutine.yield(
|
local res = coroutine.yield(
|
||||||
turbo.async.HTTPClient({verify_ca=false},
|
turbo.async.HTTPClient({verify_ca=false},
|
||||||
nil,
|
nil,
|
||||||
|
@ -71,33 +81,95 @@ local function get_image(req)
|
||||||
content_type = content_type[1]
|
content_type = content_type[1]
|
||||||
end
|
end
|
||||||
if content_type and content_type:find("image") then
|
if content_type and content_type:find("image") then
|
||||||
blob = res.body
|
local fp = io.open(cache_file, "wb")
|
||||||
img, alpha = image_loader.decode_float(blob)
|
local blob = res.body
|
||||||
|
fp:write(blob)
|
||||||
|
fp:close()
|
||||||
|
return image_loader.decode_float(blob)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return img, blob, alpha
|
return nil, nil, nil
|
||||||
end
|
end
|
||||||
|
local function get_image(req)
|
||||||
|
local file = req:get_argument("file", "")
|
||||||
|
local url = req:get_argument("url", "")
|
||||||
|
if file and file:len() > 0 then
|
||||||
|
return image_loader.decode_float(file)
|
||||||
|
elseif url and url:len() > 0 then
|
||||||
|
return cache_url(url)
|
||||||
|
end
|
||||||
|
return nil, nil, nil
|
||||||
|
end
|
||||||
|
local function cleanup_model(model)
|
||||||
|
if CLEANUP_MODEL then
|
||||||
|
w2nn.cleanup_model(model) -- release GPU memory
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function convert(x, alpha, options)
|
||||||
|
local cache_file = path.join(CACHE_DIR, options.prefix .. ".png")
|
||||||
|
local alpha_cache_file = path.join(CACHE_DIR, options.alpha_prefix .. ".png")
|
||||||
|
local alpha_orig = alpha
|
||||||
|
|
||||||
local function apply_denoise1(x)
|
if path.exists(alpha_cache_file) then
|
||||||
return reconstruct.image(noise1_model, x, BLOCK_OFFSET)
|
alpha = image_loader.load_float(alpha_cache_file)
|
||||||
|
if alpha:dim() == 2 then
|
||||||
|
alpha = alpha:reshape(1, alpha:size(1), alpha:size(2))
|
||||||
end
|
end
|
||||||
local function apply_denoise2(x)
|
if alpha:size(1) == 3 then
|
||||||
return reconstruct.image(noise2_model, x, BLOCK_OFFSET)
|
alpha = image.rgb2y(alpha)
|
||||||
end
|
end
|
||||||
local function apply_scale2x(x)
|
|
||||||
return reconstruct.scale(scale20_model, 2.0, x, BLOCK_OFFSET)
|
|
||||||
end
|
end
|
||||||
local function cache_do(cache, x, func)
|
if path.exists(cache_file) then
|
||||||
if path.exists(cache) then
|
x = image_loader.load_float(cache_file)
|
||||||
return image.load(cache)
|
return x, alpha
|
||||||
else
|
else
|
||||||
x = func(x)
|
if options.style == "art" then
|
||||||
image.save(cache, x)
|
if options.border then
|
||||||
return x
|
x = alpha_util.make_border(x, alpha_orig, reconstruct.offset_size(art_scale2_model))
|
||||||
|
end
|
||||||
|
if options.method == "scale" then
|
||||||
|
x = reconstruct.scale(art_scale2_model, 2.0, x)
|
||||||
|
if alpha then
|
||||||
|
if not (alpha:size(2) == x:size(2) and alpha:size(3) == x:size(3)) then
|
||||||
|
alpha = reconstruct.scale(art_scale2_model, 2.0, alpha)
|
||||||
|
image_loader.save_png(alpha_cache_file, alpha)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
cleanup_model(art_scale2_model)
|
||||||
|
elseif options.method == "noise1" then
|
||||||
|
x = reconstruct.image(art_noise1_model, x)
|
||||||
|
cleanup_model(art_noise1_model)
|
||||||
|
else -- options.method == "noise2"
|
||||||
|
x = reconstruct.image(art_noise2_model, x)
|
||||||
|
cleanup_model(art_noise2_model)
|
||||||
|
end
|
||||||
|
else -- photo
|
||||||
|
if options.border then
|
||||||
|
x = alpha_util.make_border(x, alpha, reconstruct.offset_size(photo_scale2_model))
|
||||||
|
end
|
||||||
|
if options.method == "scale" then
|
||||||
|
x = reconstruct.scale(photo_scale2_model, 2.0, x)
|
||||||
|
if alpha then
|
||||||
|
if not (alpha:size(2) == x:size(2) and alpha:size(3) == x:size(3)) then
|
||||||
|
alpha = reconstruct.scale(photo_scale2_model, 2.0, alpha)
|
||||||
|
image_loader.save_png(alpha_cache_file, alpha)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cleanup_model(photo_scale2_model)
|
||||||
|
elseif options.method == "noise1" then
|
||||||
|
x = reconstruct.image(photo_noise1_model, x)
|
||||||
|
cleanup_model(photo_noise1_model)
|
||||||
|
elseif options.method == "noise2" then
|
||||||
|
x = reconstruct.image(photo_noise2_model, x)
|
||||||
|
cleanup_model(photo_noise2_model)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
image_loader.save_png(cache_file, x)
|
||||||
|
|
||||||
|
return x, alpha
|
||||||
|
end
|
||||||
|
end
|
||||||
local function client_disconnected(handler)
|
local function client_disconnected(handler)
|
||||||
return not(handler.request and
|
return not(handler.request and
|
||||||
handler.request.connection and
|
handler.request.connection and
|
||||||
|
@ -112,63 +184,63 @@ function APIHandler:post()
|
||||||
self:write("client disconnected")
|
self:write("client disconnected")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local x, src, alpha = get_image(self)
|
local x, alpha, blob = get_image(self)
|
||||||
local scale = tonumber(self:get_argument("scale", "0"))
|
local scale = tonumber(self:get_argument("scale", "0"))
|
||||||
local noise = tonumber(self:get_argument("noise", "0"))
|
local noise = tonumber(self:get_argument("noise", "0"))
|
||||||
if x and valid_size(x, scale) then
|
local style = self:get_argument("style", "art")
|
||||||
if USE_CACHE and (noise ~= 0 or scale ~= 0) then
|
local download = (self:get_argument("download", "")):len()
|
||||||
local hash = md5.sumhexa(src)
|
|
||||||
local cache_noise1 = path.join(CACHE_DIR, hash .. "_noise1.png")
|
|
||||||
local cache_noise2 = path.join(CACHE_DIR, hash .. "_noise2.png")
|
|
||||||
local cache_scale = path.join(CACHE_DIR, hash .. "_scale.png")
|
|
||||||
local cache_noise1_scale = path.join(CACHE_DIR, hash .. "_noise1_scale.png")
|
|
||||||
local cache_noise2_scale = path.join(CACHE_DIR, hash .. "_noise2_scale.png")
|
|
||||||
|
|
||||||
|
if style ~= "art" then
|
||||||
|
style = "photo" -- style must be art or photo
|
||||||
|
end
|
||||||
|
if x and valid_size(x, scale) then
|
||||||
|
if (noise ~= 0 or scale ~= 0) then
|
||||||
|
local hash = md5.sumhexa(blob)
|
||||||
|
local alpha_prefix = style .. "_" .. hash .. "_alpha"
|
||||||
|
local border = false
|
||||||
|
if scale ~= 0 and alpha then
|
||||||
|
border = true
|
||||||
|
end
|
||||||
if noise == 1 then
|
if noise == 1 then
|
||||||
x = cache_do(cache_noise1, x, apply_denoise1)
|
x = convert(x, alpha, {method = "noise1", style = style,
|
||||||
|
prefix = style .. "_noise1_" .. hash,
|
||||||
|
alpha_prefix = alpha_prefix, border = border})
|
||||||
|
border = false
|
||||||
elseif noise == 2 then
|
elseif noise == 2 then
|
||||||
x = cache_do(cache_noise2, x, apply_denoise2)
|
x = convert(x, alpha, {method = "noise2", style = style,
|
||||||
|
prefix = style .. "_noise2_" .. hash,
|
||||||
|
alpha_prefix = alpha_prefix, border = border})
|
||||||
|
border = false
|
||||||
end
|
end
|
||||||
if scale == 1 or scale == 2 then
|
if scale == 1 or scale == 2 then
|
||||||
|
local prefix
|
||||||
if noise == 1 then
|
if noise == 1 then
|
||||||
x = cache_do(cache_noise1_scale, x, apply_scale2x)
|
prefix = style .. "_noise1_scale_" .. hash
|
||||||
elseif noise == 2 then
|
elseif noise == 2 then
|
||||||
x = cache_do(cache_noise2_scale, x, apply_scale2x)
|
prefix = style .. "_noise2_scale_" .. hash
|
||||||
else
|
else
|
||||||
x = cache_do(cache_scale, x, apply_scale2x)
|
prefix = style .. "_scale_" .. hash
|
||||||
end
|
end
|
||||||
|
x, alpha = convert(x, alpha, {method = "scale", style = style, prefix = prefix, alpha_prefix = alpha_prefix, border = border})
|
||||||
if scale == 1 then
|
if scale == 1 then
|
||||||
x = iproc.scale(x,
|
x = iproc.scale(x, x:size(3) * (1.6 / 2.0), x:size(2) * (1.6 / 2.0), "Sinc")
|
||||||
math.floor(x:size(3) * (1.6 / 2.0) + 0.5),
|
|
||||||
math.floor(x:size(2) * (1.6 / 2.0) + 0.5),
|
|
||||||
"Jinc")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif noise ~= 0 or scale ~= 0 then
|
|
||||||
if noise == 1 then
|
|
||||||
x = apply_denoise1(x)
|
|
||||||
elseif noise == 2 then
|
|
||||||
x = apply_denoise2(x)
|
|
||||||
end
|
|
||||||
if scale == 1 then
|
|
||||||
local x16 = {math.floor(x:size(3) * 1.6 + 0.5), math.floor(x:size(2) * 1.6 + 0.5)}
|
|
||||||
x = apply_scale2x(x)
|
|
||||||
x = iproc.scale(x, x16[1], x16[2], "Jinc")
|
|
||||||
elseif scale == 2 then
|
|
||||||
x = apply_scale2x(x)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
local name = uuid() .. ".png"
|
local name = uuid() .. ".png"
|
||||||
local blob, len = image_loader.encode_png(x, alpha)
|
local blob = image_loader.encode_png(alpha_util.composite(x, alpha))
|
||||||
|
|
||||||
self:set_header("Content-Disposition", string.format('filename="%s"', name))
|
self:set_header("Content-Disposition", string.format('filename="%s"', name))
|
||||||
|
self:set_header("Content-Length", string.format("%d", #blob))
|
||||||
|
if download > 0 then
|
||||||
|
self:set_header("Content-Type", "application/octet-stream")
|
||||||
|
else
|
||||||
self:set_header("Content-Type", "image/png")
|
self:set_header("Content-Type", "image/png")
|
||||||
self:set_header("Content-Length", string.format("%d", len))
|
end
|
||||||
self:write(ffi.string(blob, len))
|
self:write(blob)
|
||||||
else
|
else
|
||||||
if not x then
|
if not x then
|
||||||
self:set_status(400)
|
self:set_status(400)
|
||||||
self:write("ERROR: unsupported image format.")
|
self:write("ERROR: An error occurred. (unsupported image format/connection timeout/file is too large)")
|
||||||
else
|
else
|
||||||
self:set_status(400)
|
self:set_status(400)
|
||||||
self:write("ERROR: image size exceeds maximum allowable size.")
|
self:write("ERROR: image size exceeds maximum allowable size.")
|
||||||
|
@ -177,9 +249,10 @@ function APIHandler:post()
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
local FormHandler = class("FormHandler", turbo.web.RequestHandler)
|
local FormHandler = class("FormHandler", turbo.web.RequestHandler)
|
||||||
local index_ja = file.read("./assets/index.ja.html")
|
local index_ja = file.read(path.join(ROOT, "assets", "index.ja.html"))
|
||||||
local index_ru = file.read("./assets/index.ru.html")
|
local index_ru = file.read(path.join(ROOT, "assets", "index.ru.html"))
|
||||||
local index_en = file.read("./assets/index.html")
|
local index_pt = file.read(path.join(ROOT, "assets", "index.pt.html"))
|
||||||
|
local index_en = file.read(path.join(ROOT, "assets", "index.html"))
|
||||||
function FormHandler:get()
|
function FormHandler:get()
|
||||||
local lang = self.request.headers:get("Accept-Language")
|
local lang = self.request.headers:get("Accept-Language")
|
||||||
if lang then
|
if lang then
|
||||||
|
@ -191,6 +264,8 @@ function FormHandler:get()
|
||||||
self:write(index_ja)
|
self:write(index_ja)
|
||||||
elseif langs[1] == "ru" then
|
elseif langs[1] == "ru" then
|
||||||
self:write(index_ru)
|
self:write(index_ru)
|
||||||
|
elseif langs[1] == "pt" or langs[1] == "pt-BR" then
|
||||||
|
self:write(index_pt)
|
||||||
else
|
else
|
||||||
self:write(index_en)
|
self:write(index_en)
|
||||||
end
|
end
|
||||||
|
@ -209,10 +284,8 @@ turbo.log.categories = {
|
||||||
local app = turbo.web.Application:new(
|
local app = turbo.web.Application:new(
|
||||||
{
|
{
|
||||||
{"^/$", FormHandler},
|
{"^/$", FormHandler},
|
||||||
{"^/index.html", turbo.web.StaticFileHandler, path.join("./assets", "index.html")},
|
|
||||||
{"^/index.ja.html", turbo.web.StaticFileHandler, path.join("./assets", "index.ja.html")},
|
|
||||||
{"^/index.ru.html", turbo.web.StaticFileHandler, path.join("./assets", "index.ru.html")},
|
|
||||||
{"^/api$", APIHandler},
|
{"^/api$", APIHandler},
|
||||||
|
{"^/([%a%d%.%-_]+)$", turbo.web.StaticFileHandler, path.join(ROOT, "assets/")},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
app:listen(opt.port, "0.0.0.0", {max_body_size = CURL_MAX_SIZE})
|
app:listen(opt.port, "0.0.0.0", {max_body_size = CURL_MAX_SIZE})
|
||||||
|
|
14
webgen/README.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# webgen
|
||||||
|
|
||||||
|
## Generating web pages
|
||||||
|
|
||||||
|
```
|
||||||
|
ruby gen.rb
|
||||||
|
```
|
||||||
|
|
||||||
|
View at `../assets`.
|
||||||
|
|
||||||
|
## Adding a translation file
|
||||||
|
|
||||||
|
1. Adding a translation file to `./locales`
|
||||||
|
2. Run `./gen.rb`
|
BIN
webgen/assets/bg.png
Normal file
After Width: | Height: | Size: 198 KiB |
BIN
webgen/assets/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
28
webgen/assets/mobile.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
body {
|
||||||
|
width: 98%;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
.all-page {
|
||||||
|
width: auto;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
.main-title {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.option-left {
|
||||||
|
width: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#url {
|
||||||
|
width: 100%;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
.option-right {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
min-width: 10px;
|
||||||
|
width: 100%;
|
||||||
|
height: 3em;
|
||||||
|
}
|
BIN
webgen/assets/src/chibi_20162765420.png
Executable file
After Width: | Height: | Size: 64 KiB |
1
webgen/assets/src/chibi_20162765420.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
chibi_20162765420.png was generated by http://tetrabo.com/chibichara/
|
BIN
webgen/assets/src/chibi_2x.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
webgen/assets/src/favicon.psd
Normal file
191
webgen/assets/style.css
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input[type="reset"]::-moz-focus-inner,
|
||||||
|
input[type="button"]::-moz-focus-inner,
|
||||||
|
input[type="submit"]::-moz-focus-inner,
|
||||||
|
input[type="submit"]::-moz-focus-inner,
|
||||||
|
input[type="file"] > input[type="button"]::-moz-focus-inner
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
input[type="checkbox"]:focus {
|
||||||
|
-moz-outline-offset: -1px !important;
|
||||||
|
-moz-outline: 1px solid #000 !important;
|
||||||
|
}
|
||||||
|
:focus {
|
||||||
|
outline: none;
|
||||||
|
} /*Remove a dotted line around 1) buttons, 2) checkboxes, 3) links*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
div, span, a, input {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 782px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #ccc url(bg.png) no-repeat center bottom;
|
||||||
|
color: #000;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: Tahoma, Arial, Verdana, Meiryo, "MS Gothic", sans-serif, Lucida Sans;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.all-page {
|
||||||
|
position: relative;
|
||||||
|
width: 690px;
|
||||||
|
margin: 15px auto;
|
||||||
|
padding: 10px 30px 15px 30px;
|
||||||
|
background: #eee;
|
||||||
|
border: 2px solid #999;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.all-page:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: -1px;
|
||||||
|
top: -1px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
height: calc(100% - 2px);
|
||||||
|
padding: 0 1px;
|
||||||
|
box-shadow: 0px 5px 8px #bbb;
|
||||||
|
z-index: -1;
|
||||||
|
} /*for crop shadow bottom for 4px (2px from border and 2px from calc)*/
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0.6em 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choose-lang {
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin: 0 5px;
|
||||||
|
opacity: 0.9;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
|
p.margin1 { margin: 0.9em 0; }
|
||||||
|
|
||||||
|
.links-box {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example {
|
||||||
|
width: 445px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue-link {
|
||||||
|
color: #36b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gray-link {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.second-title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 1em 0 1em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-box {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-left {
|
||||||
|
display: inline-block;
|
||||||
|
width: 180px;
|
||||||
|
color: #707070;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-left-small {
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: normal;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right-small {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-hint {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#url {
|
||||||
|
width: 300px;
|
||||||
|
height: 23px;
|
||||||
|
padding: 0 3px;
|
||||||
|
border: 1px solid #b0b0b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0 5px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r-text {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio:checked + .r-text { color: #494; }
|
||||||
|
|
||||||
|
.button {
|
||||||
|
min-width: 160px;
|
||||||
|
height: 26px;
|
||||||
|
margin: 0 10px 3px 0;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
background: #f2f2f2;
|
||||||
|
background-image: linear-gradient(to bottom, #f9f9f9, #dadada);
|
||||||
|
border: 1px solid #999;
|
||||||
|
border-radius: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background: #f7f7f7;
|
||||||
|
background-image: linear-gradient(to bottom, #fefefe, #e2e2e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-hint {
|
||||||
|
margin: 0.85em 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
52
webgen/assets/ui.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
$(function (){
|
||||||
|
var expires = 365;
|
||||||
|
function clear_file() {
|
||||||
|
var new_file = $("#file").clone();
|
||||||
|
new_file.change(clear_url);
|
||||||
|
$("#file").replaceWith(new_file);
|
||||||
|
}
|
||||||
|
function clear_url() {
|
||||||
|
$("#url").val("")
|
||||||
|
}
|
||||||
|
function on_change_style(e) {
|
||||||
|
var checked = $("input[name=style]:checked");
|
||||||
|
if (checked.val() == "art") {
|
||||||
|
$(".main-title").text("waifu2x");
|
||||||
|
} else {
|
||||||
|
$(".main-title").html("w<s>/a/</s>ifu2x");
|
||||||
|
}
|
||||||
|
$.cookie("style", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function on_change_noise_level(e)
|
||||||
|
{
|
||||||
|
var checked = $("input[name=noise]:checked");
|
||||||
|
$.cookie("noise", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function on_change_scale_factor(e)
|
||||||
|
{
|
||||||
|
var checked = $("input[name=scale]:checked");
|
||||||
|
$.cookie("scale", checked.val(), {expires: expires});
|
||||||
|
}
|
||||||
|
function restore_from_cookie()
|
||||||
|
{
|
||||||
|
if ($.cookie("style")) {
|
||||||
|
$("input[name=style]").filter("[value=" + $.cookie("style") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
if ($.cookie("noise")) {
|
||||||
|
$("input[name=noise]").filter("[value=" + $.cookie("noise") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
if ($.cookie("scale")) {
|
||||||
|
$("input[name=scale]").filter("[value=" + $.cookie("scale") + "]").prop("checked", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("#url").change(clear_file);
|
||||||
|
$("#file").change(clear_url);
|
||||||
|
$("input[name=style]").change(on_change_style);
|
||||||
|
$("input[name=noise]").change(on_change_noise_level);
|
||||||
|
$("input[name=scale]").change(on_change_scale_factor);
|
||||||
|
|
||||||
|
restore_from_cookie();
|
||||||
|
on_change_style();
|
||||||
|
on_change_scale_factor();
|
||||||
|
on_change_noise_level();
|
||||||
|
})
|
60
webgen/gen.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'erb'
|
||||||
|
require 'yaml'
|
||||||
|
require 'optparse'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
def to_h(a)
|
||||||
|
Hash[a]
|
||||||
|
end
|
||||||
|
|
||||||
|
def symbolize_keys(val)
|
||||||
|
if val.is_a?(Hash)
|
||||||
|
to_h(val.map{|k,v|
|
||||||
|
[k.to_sym, symbolize_keys(v)]
|
||||||
|
})
|
||||||
|
elsif val.is_a?(Array)
|
||||||
|
val = val.map{|v| symbolize_keys(v)}
|
||||||
|
else
|
||||||
|
val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def load_locales(dir)
|
||||||
|
locales = {}
|
||||||
|
Dir.entries(dir).each do |ent|
|
||||||
|
if ent =~ /^\w\w.yml$/
|
||||||
|
lang = File.basename(ent, ".yml")
|
||||||
|
yml = YAML.load_file(File.join(dir, ent))
|
||||||
|
if yml
|
||||||
|
locales[lang.to_sym] = symbolize_keys(yml)
|
||||||
|
else
|
||||||
|
locales[lang.to_sym] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
locales
|
||||||
|
end
|
||||||
|
def copy(indir, outdir)
|
||||||
|
files = Dir.entries(indir).to_a.map{|ent|
|
||||||
|
File.join(indir, ent)
|
||||||
|
}.select{|ent|
|
||||||
|
File.file?(ent)
|
||||||
|
}
|
||||||
|
FileUtils.copy(files, outdir, preserve: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
DIR = File.dirname(__FILE__)
|
||||||
|
DEFAULT_LANG = :en
|
||||||
|
LANG_DIR = File.join(DIR, "locales")
|
||||||
|
OUTPUT_DIR = File.join(DIR, "..", "assets")
|
||||||
|
DONT_MAKE_CHANGE = "This file was automatically generated by webgen/gen.rb. Do not make changes to this file manually."
|
||||||
|
locales = load_locales(LANG_DIR)
|
||||||
|
template = File.read(File.join(DIR, "templates", "index.html.erb"))
|
||||||
|
erb = ERB.new(template)
|
||||||
|
locales.each do |lang, locale|
|
||||||
|
output_path = File.join(OUTPUT_DIR, lang == DEFAULT_LANG ? "index.html" : "index.#{lang}.html")
|
||||||
|
t = locales[DEFAULT_LANG].merge(locale)
|
||||||
|
t[:dont_make_change] = DONT_MAKE_CHANGE
|
||||||
|
t[:lang] = lang.to_s
|
||||||
|
File.write(output_path, erb.result(binding))
|
||||||
|
end
|
||||||
|
copy(File.join(DIR, "assets"), OUTPUT_DIR)
|
24
webgen/locales/en.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
description: Single-Image Super-Resolution for Anime-Style Art using Deep Convolutional Neural Networks. And it supports photo.
|
||||||
|
show_demonstration: Show full demonstration
|
||||||
|
go_to_github: Go to GitHub
|
||||||
|
image_choosing: Image choosing
|
||||||
|
type_url: Type URL
|
||||||
|
choose_file: Or choose a file
|
||||||
|
file_limits: "Limits: Size: 3MB, Noise Reduction: 2560x2560px, Upscaling: 1280x1280px."
|
||||||
|
style: Style
|
||||||
|
artwork: Artwork
|
||||||
|
photo: Photo
|
||||||
|
noise_reduction: Noise Reduction
|
||||||
|
expect_jpeg: expect JPEG artifact
|
||||||
|
nr_none: None
|
||||||
|
nr_medium: Medium
|
||||||
|
nr_high: High
|
||||||
|
nr_hint: "You need use noise reduction if image actually has noise or it may cause opposite effect."
|
||||||
|
upscaling: Upscaling
|
||||||
|
up_none: None
|
||||||
|
button_convert: Convert
|
||||||
|
button_download: Download
|
||||||
|
hints:
|
||||||
|
- "If you are using Firefox, Please press the CTRL+S key to save image. \"Save Image\" option doesn't work."
|
||||||
|
|
23
webgen/locales/ja.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
description: 深層畳み込みニューラルネットワークによる二次元画像のための超解像システム。 写真にも対応。
|
||||||
|
show_demonstration: 実行例を表示
|
||||||
|
go_to_github: プロジェクトページ(GitHub)
|
||||||
|
image_choosing: 画像を選択
|
||||||
|
type_url: URLを入力
|
||||||
|
choose_file: ファイルを選択
|
||||||
|
file_limits: "制限: サイズ: 3MB, ノイズ除去: 2560x2560px, 拡大(前): 1280x1280px."
|
||||||
|
style: スタイル
|
||||||
|
artwork: イラスト
|
||||||
|
photo: 写真
|
||||||
|
noise_reduction: ノイズ除去
|
||||||
|
expect_jpeg: JPEGノイズを想定
|
||||||
|
nr_none: なし
|
||||||
|
nr_medium: 中
|
||||||
|
nr_high: 高
|
||||||
|
nr_hint: "ノイズ除去は細部が消えることがあります。JPEGノイズがある場合に使用します。"
|
||||||
|
upscaling: 拡大
|
||||||
|
up_none: なし
|
||||||
|
button_convert: 実行
|
||||||
|
button_download: 実行結果を保存
|
||||||
|
hints:
|
||||||
|
- "Firefoxの方は、右クリから画像が保存できないようなので、CTRL+SキーかALTキー後 ファイル - ページを保存 で画像を保存してください。"
|
23
webgen/locales/pt.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
description: Single-Image Super-Resolution for Anime-Style Art using Deep Convolutional Neural Networks. And it supports photo.
|
||||||
|
show_demonstration: Sobre
|
||||||
|
go_to_github: Ir para Github
|
||||||
|
image_choosing: Imagem
|
||||||
|
type_url: URL
|
||||||
|
choose_file: ARQUIVO
|
||||||
|
file_limits: "Limites: Tamanho: 3MB, Redução de ruído: 2560x2560px, Aumento de escala: 1280x1280px."
|
||||||
|
style: Estilo
|
||||||
|
artwork: Arte
|
||||||
|
photo: Foto
|
||||||
|
noise_reduction: Redução de ruído
|
||||||
|
expect_jpeg: Exceto artefato JPEG
|
||||||
|
nr_none: Nenhuma
|
||||||
|
nr_medium: Média
|
||||||
|
nr_high: Alta
|
||||||
|
nr_hint: "Quando usando a escala 2x, Nós nunca recomendamos usar um nível alto de redução de ruído, quase sempre deixa a imagem pior, faz sentido apenas para casos raros quando a imagem tinha uma qualidade muito má desde o começo."
|
||||||
|
upscaling: Aumento de escala
|
||||||
|
up_none: Nenhum
|
||||||
|
button_convert: Converter
|
||||||
|
button_download: Baixar
|
||||||
|
hints:
|
||||||
|
- "Se Você estiver usando o Firefox, por favor, aperte CTRL+S para salvar a imagem. A opção \"Salvar Imagem\" não funciona"
|
23
webgen/locales/ru.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
description: "Waifu2x позволяет увеличивать в 4 раза рисованные изображения, например аниме или арт, а также устранять шум на изображении (преимущественно артефакты сжатия JPEG). Теперь также поддерживаются фотографии."
|
||||||
|
show_demonstration: Посмотреть полную демонстрацию
|
||||||
|
go_to_github: Перейти на GitHub
|
||||||
|
image_choosing: Выбор изображения
|
||||||
|
type_url: Укажите URL
|
||||||
|
choose_file: Либо выберите файл
|
||||||
|
file_limits: "Макс. размер файла — 3MB, устранение шума — макс. 2560x2560px, апскейл — 1280x1280px."
|
||||||
|
style: Тип изображения
|
||||||
|
artwork: Арт
|
||||||
|
photo: Фотография
|
||||||
|
noise_reduction: Устранение шума
|
||||||
|
expect_jpeg: артефактов JPEG
|
||||||
|
nr_none: Нет
|
||||||
|
nr_medium: Средне
|
||||||
|
nr_high: Сильно
|
||||||
|
nr_hint: "Устранение шума нужно использовать, если на картинке действительно есть шум, иначе это даст противоположный эффект."
|
||||||
|
upscaling: Апскейл
|
||||||
|
up_none: Нет
|
||||||
|
button_convert: Преобразовать
|
||||||
|
button_download: Скачать
|
||||||
|
hints:
|
||||||
|
- "Если Вы используете Firefox, для сохранения изображения нажмите Ctrl+S (перетаскивание изображения и опция \"Сохранить изображение\" работать не будут)."
|
148
webgen/templates/index.html.erb
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<%= t[:lang] %>">
|
||||||
|
<!-- <%= t[:dont_make_change] %> -->
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"/>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0,width=device-width">
|
||||||
|
<link href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="mobile.css" rel="stylesheet" type="text/css" media="screen and (max-width: 768px) and (min-width: 0px)">
|
||||||
|
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
|
<title>waifu2x</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="all-page">
|
||||||
|
<h1 class="main-title">waifu2x</h1>
|
||||||
|
<div class="choose-lang">
|
||||||
|
<a href="index.html">
|
||||||
|
English
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ja.html">
|
||||||
|
日本語
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.ru.html">
|
||||||
|
Русский
|
||||||
|
</a>
|
||||||
|
/
|
||||||
|
<a href="index.pt.html">
|
||||||
|
Português
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p><%= t[:description] %></p>
|
||||||
|
<p class="margin1 link-box">
|
||||||
|
<a href="https://raw.githubusercontent.com/nagadomi/waifu2x/master/images/slide.png" class="blue-link" target="_blank">
|
||||||
|
<%= t[:show_demonstration] %>
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="blue-link" target="_blank">
|
||||||
|
<%= t[:go_to_github] %>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<form action="/api" method="POST" enctype="multipart/form-data" target="_blank">
|
||||||
|
<div class="option-box first">
|
||||||
|
<div class="option-left"><%= t[:image_choosing] %>:</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<input type="text" id="url" name="url" placeholder="<%= t[:type_url] %>">
|
||||||
|
<div class="option-right-small">
|
||||||
|
<%= t[:choose_file] %>:
|
||||||
|
<input type="file" id="file" name="file"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
<%= t[:file_limits] %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
<%= t[:style] %>:
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="style" class="radio" value="art" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:artwork] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="style" class="radio" value="photo">
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:photo] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
<%= t[:noise_reduction] %>:
|
||||||
|
<div class="option-left-small">
|
||||||
|
(<%= t[:expect_jpeg] %>)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="0">
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:nr_none] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="noise" class="radio" value="1" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:nr_medium] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="noise" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:nr_high] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="option-hint">
|
||||||
|
<%= t[:nr_hint] %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="option-box">
|
||||||
|
<div class="option-left">
|
||||||
|
<%= t[:upscaling] %>:
|
||||||
|
<div class="option-left-small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="option-right">
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="0" checked>
|
||||||
|
<span class="r-text">
|
||||||
|
<%= t[:up_none] %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="1">
|
||||||
|
<span class="r-text">
|
||||||
|
1.6x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="scale" class="radio" value="2">
|
||||||
|
<span class="r-text">
|
||||||
|
2x
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% if t[:button_convert] && !t[:button_convert].empty? %>
|
||||||
|
<input type="submit" class="button" value="<%= t[:button_convert] %>">
|
||||||
|
<% else %>
|
||||||
|
<input type="submit" class="button">
|
||||||
|
<% end %>
|
||||||
|
<input type="submit" name="download" value="<%= t[:button_download]%>" class="button">
|
||||||
|
<div class="bottom-hint">
|
||||||
|
<ul>
|
||||||
|
<% t[:hints].each do |hint| %>
|
||||||
|
<li><%= hint %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-info">
|
||||||
|
<a href="https://github.com/nagadomi/waifu2x" class="gray-link" target="_blank">waifu2x</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|