├── Jacobian_inner_product_noisevsimg.ipynb ├── Jacobian_multi_layer_deep_decoder.ipynb ├── README.md ├── denoising_MSE_curves.ipynb ├── denoising_bm3d_example.ipynb ├── denoising_bm3d_imagenet_selected100_paper.ipynb ├── denoising_imagenet_selected100_paper.ipynb ├── denoising_performance_example.ipynb ├── image_fitted_faster_than_noise_on_imgnet.ipynb ├── include ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── compression.cpython-36.pyc │ ├── compression.cpython-37.pyc │ ├── decoder.cpython-36.pyc │ ├── decoder.cpython-37.pyc │ ├── fit.cpython-36.pyc │ ├── fit.cpython-37.pyc │ ├── helpers.cpython-36.pyc │ ├── helpers.cpython-37.pyc │ ├── transforms.cpython-36.pyc │ ├── transforms.cpython-37.pyc │ ├── visualize.cpython-36.pyc │ ├── visualize.cpython-37.pyc │ ├── wavelet.cpython-36.pyc │ └── wavelet.cpython-37.pyc ├── compression.py ├── decoder.py ├── denoise.py ├── fit.py ├── helpers.py ├── onedim.py ├── transforms.py ├── visualize.py └── wavelet.py ├── kernels_and_associated_dual_kernels.ipynb ├── linear_least_squares_selective_fitting_warmup.ipynb ├── noise_vs_img_fitting_different_architectures.ipynb ├── test_data ├── astronaut.png └── phantom256.png └── visualization_linear_approximation.ipynb /Jacobian_inner_product_noisevsimg.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Jacobian at initialization\n", 8 | "\n", 9 | "Here, we compute the norm of the product of the Jacobian at initialization with a signal and noise. For both deep decoder and deep image prior, this quantity is significantly smaller for noise than for a natural image. Thus, a natural image is better aligned with the leading singular vectors of the Jacobian than noise. This demonstrates that the Jacobian of the networks is approximately low-rank, with natural images lying in the space spanned by the leading singularvectors.\n", 10 | "\n", 11 | "Running the DIP part requires the models from [https://github.com/DmitryUlyanov/deep-image-prior](https://github.com/DmitryUlyanov/deep-image-prior)." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "num GPUs 1\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "from __future__ import print_function\n", 29 | "import matplotlib.pyplot as plt\n", 30 | "#%matplotlib notebook\n", 31 | "\n", 32 | "import os\n", 33 | "\n", 34 | "import warnings\n", 35 | "warnings.filterwarnings('ignore')\n", 36 | "\n", 37 | "from include import *\n", 38 | "from PIL import Image\n", 39 | "import PIL\n", 40 | "\n", 41 | "import numpy as np\n", 42 | "import torch\n", 43 | "import torch.optim\n", 44 | "from torch.autograd import Variable\n", 45 | "from models import *\n", 46 | "\n", 47 | "GPU = True\n", 48 | "if GPU == True:\n", 49 | " torch.backends.cudnn.enabled = True\n", 50 | " torch.backends.cudnn.benchmark = True\n", 51 | " dtype = torch.cuda.FloatTensor\n", 52 | " os.environ['CUDA_VISIBLE_DEVICES'] = '0'\n", 53 | " print(\"num GPUs\",torch.cuda.device_count())\n", 54 | "else:\n", 55 | " dtype = torch.FloatTensor\n" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Load image" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 2, 68 | "metadata": { 69 | "scrolled": true 70 | }, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "torch.Size([1, 1, 256, 256])\n", 77 | "1.0\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "path = './test_data/'\n", 83 | "#img_name = \"astronaut\"\n", 84 | "#img_name = \"mri\"\n", 85 | "img_name = \"phantom256\"\n", 86 | "\n", 87 | "img_path = path + img_name + \".png\"\n", 88 | "img_pil = Image.open(img_path)\n", 89 | "\n", 90 | "#img_pil = load_and_crop(img_path,target_width=256,target_height=256)\n", 91 | "\n", 92 | "img_np = pil_to_np(img_pil)\n", 93 | "img_np = img_np / np.max(img_np)\n", 94 | "img_clean_var = np_to_var(img_np).type(dtype)\n", 95 | "print(img_clean_var.shape)\n", 96 | "print(np.max(img_np))" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "## Functions to generate noisy image and noise" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 3, 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "Image size: (1, 256, 256)\n" 116 | ] 117 | }, 118 | { 119 | "data": { 120 | "text/plain": [ 121 | "(tensor(85.7806, device='cuda:0'), tensor(85.7806, device='cuda:0'))" 122 | ] 123 | }, 124 | "execution_count": 3, 125 | "metadata": {}, 126 | "output_type": "execute_result" 127 | } 128 | ], 129 | "source": [ 130 | "def get_noisy_img(sig=30,noise_same = False):\n", 131 | " sigma = sig/255.\n", 132 | " if noise_same: # add the same noise in each channel\n", 133 | " noise = np.random.normal(scale=sigma, size=img_np.shape[1:])\n", 134 | " noise = np.array( [noise]*img_np.shape[0] )\n", 135 | " else: # add independent noise in each channel\n", 136 | " noise = np.random.normal(scale=sigma, size=img_np.shape)\n", 137 | "\n", 138 | " img_noisy_np = np.clip( img_np + noise , 0, 1).astype(np.float32)\n", 139 | " img_noisy_var = np_to_var(img_noisy_np).type(dtype)\n", 140 | " return img_noisy_np,img_noisy_var\n", 141 | "\n", 142 | "def get_noise(sig=30,noise_same = False,sh=None):\n", 143 | " sigma = sig/255.\n", 144 | " if noise_same: # add the same noise in each channel\n", 145 | " if sh is None:\n", 146 | " sh = img_np.shape[1:]\n", 147 | " noise = np.random.rand(sh[0],sh[1]) #np.random.normal(scale=sigma, size=img_np.shape[1:])\n", 148 | " noise = np.array( [noise]*img_np.shape[0] )\n", 149 | " else: # add independent noise in each channel\n", 150 | " if sh is None:\n", 151 | " sh = img_np.shape\n", 152 | " noise = np.random.rand(sh[0],sh[1],sh[2]) # np.random.normal(scale=sigma, size=img_np.shape)\n", 153 | "\n", 154 | " img_noisy_np = np.clip( noise , 0, 1).astype(np.float32)\n", 155 | " img_noisy_var = np_to_var(img_noisy_np).type(dtype)\n", 156 | " return img_noisy_np,img_noisy_var\n", 157 | "\n", 158 | "img_noisy_np,img_noisy_var = get_noisy_img() \n", 159 | "output_depth = img_np.shape[0] \n", 160 | "print(\"Image size: \", img_np.shape)\n", 161 | "\n", 162 | "img_np, img_var = get_noise(sig=30,noise_same = False,sh=None)\n", 163 | "# make sure the norm is the same\n", 164 | "img_var *= torch.norm(img_clean_var)/torch.norm(img_var) \n", 165 | "\n", 166 | "torch.norm(img_var), torch.norm(img_clean_var)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 4, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "numit = 5000" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 5, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "def tikz_hist(res,bins=100,filename=\"data.csv\"):\n", 185 | " hist = plt.hist(res,normed=True, bins=bins)\n", 186 | " plt.show()\n", 187 | " x = np.array([ (hist[1][i] + hist[1][i+1])/2 for i in range(bins) ])\n", 188 | " y = np.array(hist[0])\n", 189 | " np.savetxt(filename, np.vstack([ x , y ]).T , delimiter=\"\\t\")" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "## Jacobian" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 6, 202 | "metadata": {}, 203 | "outputs": [ 204 | { 205 | "name": "stdout", 206 | "output_type": "stream", 207 | "text": [ 208 | "input shape: [1, 256, 16, 16]\n" 209 | ] 210 | } 211 | ], 212 | "source": [ 213 | "num_channels = [256]*4\n", 214 | "\n", 215 | "def get_net_input(num_channels=[256]*4,upsample=True):\n", 216 | " if upsample:\n", 217 | " totalupsample = 2**len(num_channels)\n", 218 | " else:\n", 219 | " totalupsample = 1\n", 220 | " width = int(img_clean_var.data.shape[2]/totalupsample)\n", 221 | " height = int(img_clean_var.data.shape[3]/totalupsample)\n", 222 | " shape = [1,num_channels[0], width, height]\n", 223 | " print(\"input shape: \", shape)\n", 224 | " net_input = Variable(torch.zeros(shape)).type(dtype)\n", 225 | " net_input.data.uniform_()\n", 226 | " net_input.data *= 1./10\n", 227 | " return net_input\n", 228 | "\n", 229 | "net_input = get_net_input(num_channels)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 7, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "def get_jacobian(net, x, noutputs):\n", 239 | " x = x.squeeze()\n", 240 | " n = x.size()[0]\n", 241 | " x = x.repeat(noutputs, 1)\n", 242 | " x.requires_grad_(True)\n", 243 | " y = net(x)\n", 244 | " y.backward(torch.eye(noutputs))\n", 245 | " return x.grad.data" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 8, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "def grad_norm(net): \n", 255 | " # returns the norm of the gradient corresponding to the convolutional parameters\n", 256 | " \n", 257 | " # count number of convolutional layers\n", 258 | " nconvnets = 0\n", 259 | " for p in list(filter(lambda p: len(p.data.shape)>2, net.parameters())):\n", 260 | " nconvnets += 1\n", 261 | " \n", 262 | " out_grads = np.zeros(nconvnets)\n", 263 | " p = [x for x in net.parameters() ]\n", 264 | " for ind,p in enumerate(list(filter(lambda p: p.grad is not None and len(p.data.shape)>2, net.parameters()))):\n", 265 | " out_grads[ind] = p.grad.data.norm(2).item()\n", 266 | "\n", 267 | " return np.linalg.norm( out_grads ) \n", 268 | "\n", 269 | "def get_jacobinanprodsDD(img_var,numit=100):\n", 270 | " res = []\n", 271 | " for i in range(numit):\n", 272 | " net = decodernw(1,num_channels_up=num_channels).type(dtype)\n", 273 | " out = net(net_input).type(dtype)\n", 274 | " out.backward( img_var )\n", 275 | " res += [grad_norm(net)]\n", 276 | " return res" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 9, 282 | "metadata": {}, 283 | "outputs": [ 284 | { 285 | "data": { 286 | "image/png": "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\n", 287 | "text/plain": [ 288 | "
" 289 | ] 290 | }, 291 | "metadata": { 292 | "needs_background": "light" 293 | }, 294 | "output_type": "display_data" 295 | } 296 | ], 297 | "source": [ 298 | "imgjprods = get_jacobinanprodsDD(img_clean_var,numit=numit)\n", 299 | "\n", 300 | "tikz_hist(imgjprods,bins=50,filename=\"JacobianCleanImg.csv\")" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 10, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "data": { 310 | "image/png": "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\n", 311 | "text/plain": [ 312 | "
" 313 | ] 314 | }, 315 | "metadata": { 316 | "needs_background": "light" 317 | }, 318 | "output_type": "display_data" 319 | } 320 | ], 321 | "source": [ 322 | "imgjprods = get_jacobinanprodsDD(img_var,numit=numit)\n", 323 | "\n", 324 | "tikz_hist(imgjprods,bins=50,filename=\"JacobianNoiseImg.csv\")" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "## DIP" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 11, 337 | "metadata": {}, 338 | "outputs": [ 339 | { 340 | "name": "stdout", 341 | "output_type": "stream", 342 | "text": [ 343 | "input shape: [1, 32, 256, 256]\n" 344 | ] 345 | } 346 | ], 347 | "source": [ 348 | "num_channels = [32]*5\n", 349 | "net_input = get_net_input(num_channels,False)\n", 350 | "\n", 351 | "def get_jacobinanprodsDIP(img_var,numit=numit):\n", 352 | " res = []\n", 353 | " for i in range(numit): \n", 354 | " net = get_net(32, 'skip', 'reflection',n_channels=output_depth,skip_n33d=128,\n", 355 | " skip_n33u=128,skip_n11=4,num_scales=5,upsample_mode='bilinear').type(dtype) \n", 356 | " out = net(net_input).type(dtype)\n", 357 | " out.backward( img_var )\n", 358 | " res += [grad_norm(net)]\n", 359 | " return res" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": 12, 365 | "metadata": {}, 366 | "outputs": [ 367 | { 368 | "data": { 369 | "image/png": "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\n", 370 | "text/plain": [ 371 | "
" 372 | ] 373 | }, 374 | "metadata": { 375 | "needs_background": "light" 376 | }, 377 | "output_type": "display_data" 378 | } 379 | ], 380 | "source": [ 381 | "imgjprods = get_jacobinanprodsDIP(img_clean_var,numit=numit)\n", 382 | "\n", 383 | "tikz_hist(imgjprods,bins=50,filename=\"JacobianCleanImgDIP.csv\")" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 13, 389 | "metadata": {}, 390 | "outputs": [ 391 | { 392 | "data": { 393 | "image/png": "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\n", 394 | "text/plain": [ 395 | "
" 396 | ] 397 | }, 398 | "metadata": { 399 | "needs_background": "light" 400 | }, 401 | "output_type": "display_data" 402 | } 403 | ], 404 | "source": [ 405 | "imgjprods = get_jacobinanprodsDIP(img_var,numit=numit)\n", 406 | "\n", 407 | "tikz_hist(imgjprods,bins=50,filename=\"JacobianNoiseImgDIP.csv\")" 408 | ] 409 | } 410 | ], 411 | "metadata": { 412 | "kernelspec": { 413 | "display_name": "Python 3", 414 | "language": "python", 415 | "name": "python3" 416 | }, 417 | "language_info": { 418 | "codemirror_mode": { 419 | "name": "ipython", 420 | "version": 3 421 | }, 422 | "file_extension": ".py", 423 | "mimetype": "text/x-python", 424 | "name": "python", 425 | "nbconvert_exporter": "python", 426 | "pygments_lexer": "ipython3", 427 | "version": "3.6.7" 428 | } 429 | }, 430 | "nbformat": 4, 431 | "nbformat_minor": 2 432 | } 433 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Denoising and Regularization via Exploiting the Structural Bias of Convolutional Generators 2 | 3 | This repository provides code for reproducing the figures in the paper: 4 | 5 | **``Denoising and Regularization via Exploiting the Structural Bias of Convolutional Generators''**, by Reinhard Heckel and Mahdi Soltanolkotabi. Contact: [reinhard.heckel@gmail.com](reinhard.heckel@gmail.com) 6 | 7 | The paper is available online [here](http://www.reinhardheckel.com/papers/overparameterized_convolutional_generators.pdf). 8 | 9 | ## Organization 10 | 11 | - Figure 1: denoising_MSE_curves.ipynb 12 | - Figure 2: denoising_performance_example.ipynb, denoising_bm3d_example.ipynb 13 | - Figure 4,8: noise_vs_img_fitting_different_architectures.ipynb 14 | - Figure 5: linear_least_squares_selective_fitting_warmup.ipynb 15 | - Figure 6: kernels_and_associated_dual_kernels.ipynb 16 | - Figure 7: Jacobian_multi_layer_deep_decoder.ipynb 17 | - Figure 10: image_fitted_faster_than_noise_on_imgnet.ipynb 18 | - Figure 12: Jacobian_inner_product_noisevsimg.ipynb 19 | - Table 1: denoising_imagenet_selected100_paper.ipynb, denoising_bm3d_imagenet_selected100_paper.ipynb 20 | 21 | ## Installation 22 | 23 | The code is written in python and relies on pytorch. The following libraries are required: 24 | - python 3 25 | - pytorch 26 | - numpy 27 | - skimage 28 | - matplotlib 29 | - scikit-image 30 | - jupyter 31 | 32 | The libraries can be installed via: 33 | ``` 34 | conda install jupyter 35 | ``` 36 | 37 | A small part of the code compares performance to the deep image prior. This part requires downloading the models folder from [https://github.com/DmitryUlyanov/deep-image-prior](https://github.com/DmitryUlyanov/deep-image-prior). 38 | 39 | 40 | ## Citation 41 | ``` 42 | @article{heckel_denoising_2019, 43 | author = {Reinhard Heckel and Mahdi Soltanolkotabi}, 44 | title = {Denoising and Regularization via Exploiting the Structural Bias of Convolutional Generators}, 45 | journal = {arXiv:1910.14634 [cs.LG]}, 46 | year = {2019} 47 | } 48 | ``` 49 | 50 | ## Licence 51 | 52 | All files are provided under the terms of the Apache License, Version 2.0. 53 | -------------------------------------------------------------------------------- /denoising_bm3d_imagenet_selected100_paper.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Denoising with BM3D\n", 8 | "\n", 9 | "The code below demonstrates the denoising performance on an example image." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "#from __future__ import print_function\n", 19 | "#import matplotlib.pyplot as plt\n", 20 | "#%matplotlib notebook\n", 21 | "\n", 22 | "import os\n", 23 | "\n", 24 | "from os import *\n", 25 | "from os.path import *\n", 26 | "\n", 27 | "import warnings\n", 28 | "warnings.filterwarnings('ignore')\n", 29 | "\n", 30 | "#from include import *\n", 31 | "from PIL import Image\n", 32 | "import PIL\n", 33 | "\n", 34 | "import numpy as np\n", 35 | "import pybm3d" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "## Load images" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "got 100 images\n" 55 | ] 56 | } 57 | ], 58 | "source": [ 59 | "def pil_to_np(img_PIL):\n", 60 | " '''Converts image in PIL format to np.array.\n", 61 | " \n", 62 | " From W x H x C [0...255] to C x W x H [0..1]\n", 63 | " '''\n", 64 | " ar = np.array(img_PIL)\n", 65 | "\n", 66 | " if len(ar.shape) == 3:\n", 67 | " ar = ar.transpose(2,0,1)\n", 68 | " else:\n", 69 | " ar = ar[None, ...]\n", 70 | "\n", 71 | " return ar.astype(np.float32) / 255.\n", 72 | "\n", 73 | "def load_and_crop(imgname,target_width=512,target_height=512):\n", 74 | " '''\n", 75 | " imgname: string of image location\n", 76 | " load an image, and center-crop if the image is large enough, else return none\n", 77 | " '''\n", 78 | " img = Image.open(imgname)\n", 79 | " width, height = img.size\n", 80 | " if width <= target_width or height <= target_height:\n", 81 | " return None\t\n", 82 | " \n", 83 | " left = (width - target_width)/2\n", 84 | " top = (height - target_height)/2\n", 85 | " right = (width + target_width)/2\n", 86 | " bottom = (height + target_height)/2\n", 87 | " \n", 88 | " return img.crop((left, top, right, bottom))\n", 89 | "\n", 90 | "def get_imgnet_imgs(path = './imgs/'):\n", 91 | " siz = 512\n", 92 | " imgs = []\n", 93 | " imgnames = [f for f in listdir(path) if isfile(join(path, f))] \n", 94 | " for imgname in imgnames:\n", 95 | " # prepare and select image\n", 96 | " imgname = path + imgname\n", 97 | "\n", 98 | " img = load_and_crop(imgname,target_width=512,target_height=512)\n", 99 | " if img is None: # then the image could not be croped to 512x512\n", 100 | " continue\n", 101 | " \n", 102 | " img_np = pil_to_np(img)\n", 103 | "\n", 104 | " if img_np.shape[0] != 3: # we only want to consider color images\n", 105 | " continue\n", 106 | " imgs += [img_np]\n", 107 | " print(\"got \", len(imgs), \" images\")\n", 108 | " return imgs\n", 109 | "\n", 110 | "imgs = get_imgnet_imgs()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 1, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "## to greyscale\n", 120 | "def rgb2gray(rgb):\n", 121 | " return 0.2989*rgb[0] + 0.5870*rgb[1] + 0.1140*rgb[2]\n", 122 | "\n", 123 | "#gimg = np.array([rgb2gray(imgs[0])])\n", 124 | "#gimg = gimg.transpose(1,2,0)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 11, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "def get_noisy_img(img_np,sig=30,noise_same = False):\n", 134 | " sigma = sig/255.\n", 135 | " if noise_same: # add the same noise in each channel\n", 136 | " noise1 = np.random.normal(scale=sigma, size=img_np.shape[:2])\n", 137 | " noise = np.zeros(img_np.shape)\n", 138 | " noise[:,:,0] = noise1\n", 139 | " noise[:,:,1] = noise1\n", 140 | " noise[:,:,2] = noise1\n", 141 | " else: # add independent noise in each channel\n", 142 | " noise = np.random.normal(scale=sigma, size=img_np.shape)\n", 143 | "\n", 144 | " img_noisy_np = np.clip( img_np + noise , 0, 1).astype(np.float32)\n", 145 | " #img_noisy_var = np_to_var(img_noisy_np).type(dtype)\n", 146 | " return img_noisy_np #,img_noisy_var" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 6, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "def psnr(x_hat,x_true,maxv=1.):\n", 156 | " x_hat = x_hat.flatten()\n", 157 | " x_true = x_true.flatten()\n", 158 | " mse=np.mean(np.square(x_hat-x_true))\n", 159 | " psnr_ = 10.*np.log(maxv**2/mse)/np.log(10.)\n", 160 | " return psnr_" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "## Denoise noisy image" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 12, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "sig = 25.0\n", 177 | "noise_same = True" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 13, 183 | "metadata": {}, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "(512, 512, 3) 1.0 0.0\n", 190 | "Noise PSNR: 20.212181200072777\n", 191 | "Recov PSNR: 23.63727431083024\n", 192 | "(512, 512, 3) 0.972549 0.0\n", 193 | "Noise PSNR: 20.504885441401665\n", 194 | "Recov PSNR: 23.940315558364137\n", 195 | "(512, 512, 3) 1.0 0.0\n", 196 | "Noise PSNR: 20.301638870226878\n", 197 | "Recov PSNR: 23.696178870942163\n", 198 | "(512, 512, 3) 1.0 0.0\n", 199 | "Noise PSNR: 20.768846524713283\n", 200 | "Recov PSNR: 28.750837924533748\n", 201 | "(512, 512, 3) 1.0 0.0\n", 202 | "Noise PSNR: 20.594251079013212\n", 203 | "Recov PSNR: 25.36551000419403\n", 204 | "(512, 512, 3) 1.0 0.0\n", 205 | "Noise PSNR: 20.464093268981927\n", 206 | "Recov PSNR: 23.997677276501193\n", 207 | "(512, 512, 3) 1.0 0.0\n", 208 | "Noise PSNR: 20.57955428958745\n", 209 | "Recov PSNR: 24.469921477217806\n", 210 | "(512, 512, 3) 1.0 0.0\n", 211 | "Noise PSNR: 20.54248374191298\n", 212 | "Recov PSNR: 26.705066584818994\n", 213 | "(512, 512, 3) 1.0 0.0\n", 214 | "Noise PSNR: 20.59146513458877\n", 215 | "Recov PSNR: 24.70622667488254\n", 216 | "(512, 512, 3) 0.99215686 0.0\n", 217 | "Noise PSNR: 20.480707008180083\n", 218 | "Recov PSNR: 25.4927768415609\n", 219 | "(512, 512, 3) 1.0 0.0\n", 220 | "Noise PSNR: 20.56513601853171\n", 221 | "Recov PSNR: 27.63120525150335\n", 222 | "(512, 512, 3) 1.0 0.0\n", 223 | "Noise PSNR: 20.85872056313074\n", 224 | "Recov PSNR: 27.272297774312243\n", 225 | "(512, 512, 3) 1.0 0.0\n", 226 | "Noise PSNR: 20.765993136637388\n", 227 | "Recov PSNR: 27.338659874856795\n", 228 | "(512, 512, 3) 1.0 0.0\n", 229 | "Noise PSNR: 21.306242258803405\n", 230 | "Recov PSNR: 25.446193654517028\n", 231 | "(512, 512, 3) 1.0 0.0\n", 232 | "Noise PSNR: 20.54850148581473\n", 233 | "Recov PSNR: 27.01530978394026\n", 234 | "(512, 512, 3) 1.0 0.0\n", 235 | "Noise PSNR: 20.398360202163094\n", 236 | "Recov PSNR: 26.389982539269145\n", 237 | "(512, 512, 3) 1.0 0.05882353\n", 238 | "Noise PSNR: 20.181425964893922\n", 239 | "Recov PSNR: 25.42297513517164\n", 240 | "(512, 512, 3) 1.0 0.0\n", 241 | "Noise PSNR: 20.236747114864038\n", 242 | "Recov PSNR: 26.119894603915196\n", 243 | "(512, 512, 3) 1.0 0.0\n", 244 | "Noise PSNR: 20.549344599038925\n", 245 | "Recov PSNR: 24.745598223981585\n", 246 | "(512, 512, 3) 1.0 0.0\n", 247 | "Noise PSNR: 20.406044023408143\n", 248 | "Recov PSNR: 23.02123809966171\n", 249 | "(512, 512, 3) 0.88235295 0.0\n", 250 | "Noise PSNR: 21.07100899874531\n", 251 | "Recov PSNR: 27.966267242110685\n", 252 | "(512, 512, 3) 1.0 0.0\n", 253 | "Noise PSNR: 20.266211966933977\n", 254 | "Recov PSNR: 26.026880683704405\n", 255 | "(512, 512, 3) 1.0 0.0\n", 256 | "Noise PSNR: 20.28534942506627\n", 257 | "Recov PSNR: 24.028501285463516\n", 258 | "(512, 512, 3) 1.0 0.0\n", 259 | "Noise PSNR: 20.37162074821613\n", 260 | "Recov PSNR: 23.20894817892436\n", 261 | "(512, 512, 3) 1.0 0.0\n", 262 | "Noise PSNR: 20.819537047306984\n", 263 | "Recov PSNR: 24.955001645791075\n", 264 | "(512, 512, 3) 1.0 0.011764706\n", 265 | "Noise PSNR: 20.36794988194004\n", 266 | "Recov PSNR: 25.01484370885203\n", 267 | "(512, 512, 3) 1.0 0.0\n", 268 | "Noise PSNR: 20.859252924913807\n", 269 | "Recov PSNR: 25.41711012187948\n", 270 | "(512, 512, 3) 1.0 0.07058824\n", 271 | "Noise PSNR: 20.391379042510795\n", 272 | "Recov PSNR: 25.847262037706383\n", 273 | "(512, 512, 3) 1.0 0.0\n", 274 | "Noise PSNR: 20.33835437136609\n", 275 | "Recov PSNR: 23.321868702283826\n", 276 | "(512, 512, 3) 1.0 0.0\n", 277 | "Noise PSNR: 20.43070507108709\n", 278 | "Recov PSNR: 24.873822391942387\n", 279 | "(512, 512, 3) 1.0 0.0\n", 280 | "Noise PSNR: 20.481937911063778\n", 281 | "Recov PSNR: 23.8057189559377\n", 282 | "(512, 512, 3) 1.0 0.0\n", 283 | "Noise PSNR: 21.362199887623458\n", 284 | "Recov PSNR: 28.620436302966986\n", 285 | "(512, 512, 3) 1.0 0.0\n", 286 | "Noise PSNR: 21.195583777328547\n", 287 | "Recov PSNR: 28.785344603958727\n", 288 | "(512, 512, 3) 1.0 0.0\n", 289 | "Noise PSNR: 20.362385421825444\n", 290 | "Recov PSNR: 24.592646298984757\n", 291 | "(512, 512, 3) 1.0 0.0\n", 292 | "Noise PSNR: 20.260951503499502\n", 293 | "Recov PSNR: 26.27547029013389\n", 294 | "(512, 512, 3) 1.0 0.0\n", 295 | "Noise PSNR: 21.437002162190304\n", 296 | "Recov PSNR: 25.784020736052643\n", 297 | "(512, 512, 3) 1.0 0.0\n", 298 | "Noise PSNR: 20.448009174943806\n", 299 | "Recov PSNR: 28.338362719443378\n", 300 | "(512, 512, 3) 1.0 0.0\n", 301 | "Noise PSNR: 20.29782132626301\n", 302 | "Recov PSNR: 23.440980909381945\n", 303 | "(512, 512, 3) 1.0 0.0\n", 304 | "Noise PSNR: 20.394585615905946\n", 305 | "Recov PSNR: 25.44805373031625\n", 306 | "(512, 512, 3) 1.0 0.0\n", 307 | "Noise PSNR: 20.39641311987079\n", 308 | "Recov PSNR: 24.20596256704499\n", 309 | "(512, 512, 3) 1.0 0.078431375\n", 310 | "Noise PSNR: 20.88329087810746\n", 311 | "Recov PSNR: 29.331612323118552\n", 312 | "(512, 512, 3) 1.0 0.0\n", 313 | "Noise PSNR: 20.701236714965223\n", 314 | "Recov PSNR: 24.088279746783332\n", 315 | "(512, 512, 3) 1.0 0.0\n", 316 | "Noise PSNR: 20.351789215955613\n", 317 | "Recov PSNR: 24.565497444521036\n", 318 | "(512, 512, 3) 1.0 0.0\n", 319 | "Noise PSNR: 20.365233633265184\n", 320 | "Recov PSNR: 25.345188304122615\n", 321 | "(512, 512, 3) 1.0 0.0\n", 322 | "Noise PSNR: 20.620843698072637\n", 323 | "Recov PSNR: 25.397858996489905\n", 324 | "(512, 512, 3) 1.0 0.0\n", 325 | "Noise PSNR: 20.772502391644636\n", 326 | "Recov PSNR: 26.214549685021296\n", 327 | "(512, 512, 3) 1.0 0.0\n", 328 | "Noise PSNR: 20.89921246786762\n", 329 | "Recov PSNR: 27.732481393578652\n", 330 | "(512, 512, 3) 1.0 0.0\n", 331 | "Noise PSNR: 20.503894657113488\n", 332 | "Recov PSNR: 25.033410148687754\n", 333 | "(512, 512, 3) 1.0 0.0\n", 334 | "Noise PSNR: 20.746347428212257\n", 335 | "Recov PSNR: 27.42911953759399\n", 336 | "(512, 512, 3) 1.0 0.0\n", 337 | "Noise PSNR: 20.60218659863753\n", 338 | "Recov PSNR: 27.84133661305435\n", 339 | "(512, 512, 3) 1.0 0.050980393\n", 340 | "Noise PSNR: 20.21157092052578\n", 341 | "Recov PSNR: 25.681543134704302\n", 342 | "(512, 512, 3) 1.0 0.007843138\n", 343 | "Noise PSNR: 20.371621188818207\n", 344 | "Recov PSNR: 24.442689973454986\n", 345 | "(512, 512, 3) 1.0 0.0\n", 346 | "Noise PSNR: 20.88919016991864\n", 347 | "Recov PSNR: 26.802588862825644\n", 348 | "(512, 512, 3) 1.0 0.0\n", 349 | "Noise PSNR: 21.505021422178842\n", 350 | "Recov PSNR: 26.751418943139534\n", 351 | "(512, 512, 3) 0.8784314 0.078431375\n", 352 | "Noise PSNR: 20.201139366298737\n", 353 | "Recov PSNR: 25.979236079533912\n", 354 | "(512, 512, 3) 1.0 0.0\n", 355 | "Noise PSNR: 20.229492836552055\n", 356 | "Recov PSNR: 26.556030467778225\n", 357 | "(512, 512, 3) 1.0 0.0\n", 358 | "Noise PSNR: 22.663572833508585\n", 359 | "Recov PSNR: 26.00644769518003\n", 360 | "(512, 512, 3) 1.0 0.0\n", 361 | "Noise PSNR: 20.55470772886831\n", 362 | "Recov PSNR: 26.25484391889147\n", 363 | "(512, 512, 3) 1.0 0.0\n", 364 | "Noise PSNR: 20.791949108181264\n", 365 | "Recov PSNR: 26.210043053906254\n", 366 | "(512, 512, 3) 1.0 0.0\n", 367 | "Noise PSNR: 20.489994383272126\n", 368 | "Recov PSNR: 24.281132877707673\n", 369 | "(512, 512, 3) 1.0 0.0\n", 370 | "Noise PSNR: 20.381062090222596\n", 371 | "Recov PSNR: 24.325705124929762\n", 372 | "(512, 512, 3) 1.0 0.0\n", 373 | "Noise PSNR: 20.438834326532188\n", 374 | "Recov PSNR: 24.384656944078717\n", 375 | "(512, 512, 3) 1.0 0.003921569\n", 376 | "Noise PSNR: 20.360608654940275\n", 377 | "Recov PSNR: 25.71720661048374\n", 378 | "(512, 512, 3) 1.0 0.0\n", 379 | "Noise PSNR: 20.28177360376858\n", 380 | "Recov PSNR: 25.5587857585238\n", 381 | "(512, 512, 3) 1.0 0.0\n", 382 | "Noise PSNR: 20.732083177945896\n", 383 | "Recov PSNR: 24.246224953118094\n", 384 | "(512, 512, 3) 1.0 0.0\n", 385 | "Noise PSNR: 20.547831517988826\n", 386 | "Recov PSNR: 27.15494658403775\n", 387 | "(512, 512, 3) 0.85882354 0.0\n", 388 | "Noise PSNR: 20.253141334835455\n", 389 | "Recov PSNR: 24.61338703868632\n", 390 | "(512, 512, 3) 1.0 0.0\n", 391 | "Noise PSNR: 20.381955459886118\n", 392 | "Recov PSNR: 25.90159621176429\n", 393 | "(512, 512, 3) 1.0 0.0\n", 394 | "Noise PSNR: 20.601218891409445\n", 395 | "Recov PSNR: 26.883687277248278\n", 396 | "(512, 512, 3) 1.0 0.0\n", 397 | "Noise PSNR: 20.496299796191835\n", 398 | "Recov PSNR: 24.530463374261938\n", 399 | "(512, 512, 3) 1.0 0.0\n", 400 | "Noise PSNR: 20.669437860597373\n", 401 | "Recov PSNR: 22.3111797760357\n", 402 | "(512, 512, 3) 1.0 0.0\n", 403 | "Noise PSNR: 20.52946515058389\n", 404 | "Recov PSNR: 24.8521365367373\n", 405 | "(512, 512, 3) 1.0 0.0\n", 406 | "Noise PSNR: 20.418582850846928\n", 407 | "Recov PSNR: 25.872974047164654\n", 408 | "(512, 512, 3) 1.0 0.0\n", 409 | "Noise PSNR: 20.545457635330962\n", 410 | "Recov PSNR: 23.28932483116338\n", 411 | "(512, 512, 3) 0.93333334 0.0\n", 412 | "Noise PSNR: 20.400763235295166\n", 413 | "Recov PSNR: 27.486010211875254\n", 414 | "(512, 512, 3) 1.0 0.0\n", 415 | "Noise PSNR: 20.595904760121005\n", 416 | "Recov PSNR: 25.171128316539246\n", 417 | "(512, 512, 3) 1.0 0.0\n", 418 | "Noise PSNR: 20.649539190001782\n", 419 | "Recov PSNR: 24.285987221176573\n", 420 | "(512, 512, 3) 1.0 0.0\n", 421 | "Noise PSNR: 20.66221849343832\n", 422 | "Recov PSNR: 25.907687691067967\n", 423 | "(512, 512, 3) 1.0 0.0\n", 424 | "Noise PSNR: 20.44171253833379\n", 425 | "Recov PSNR: 24.372830839819954\n", 426 | "(512, 512, 3) 1.0 0.0\n", 427 | "Noise PSNR: 20.665001186594473\n", 428 | "Recov PSNR: 25.678229193479474\n", 429 | "(512, 512, 3) 1.0 0.0\n", 430 | "Noise PSNR: 20.194153633915025\n", 431 | "Recov PSNR: 25.16241731506566\n", 432 | "(512, 512, 3) 1.0 0.0\n", 433 | "Noise PSNR: 20.241951382784187\n", 434 | "Recov PSNR: 24.872739451710075\n", 435 | "(512, 512, 3) 1.0 0.0\n", 436 | "Noise PSNR: 21.15028872426424\n", 437 | "Recov PSNR: 26.21962158905324\n", 438 | "(512, 512, 3) 1.0 0.0\n", 439 | "Noise PSNR: 20.946377291946877\n", 440 | "Recov PSNR: 24.956474403494262\n", 441 | "(512, 512, 3) 1.0 0.0\n", 442 | "Noise PSNR: 20.42800509504013\n", 443 | "Recov PSNR: 26.085611432453454\n", 444 | "(512, 512, 3) 1.0 0.0\n", 445 | "Noise PSNR: 20.579051433603397\n", 446 | "Recov PSNR: 26.471694868140176\n", 447 | "(512, 512, 3) 1.0 0.0\n", 448 | "Noise PSNR: 20.273315961656948\n", 449 | "Recov PSNR: 23.70575463385746\n", 450 | "(512, 512, 3) 1.0 0.007843138\n", 451 | "Noise PSNR: 21.33651026880801\n", 452 | "Recov PSNR: 26.93139751777845\n", 453 | "(512, 512, 3) 1.0 0.0\n", 454 | "Noise PSNR: 20.89228910571425\n", 455 | "Recov PSNR: 24.759780544364784\n", 456 | "(512, 512, 3) 1.0 0.0\n", 457 | "Noise PSNR: 20.824624513111274\n", 458 | "Recov PSNR: 27.26841616666023\n", 459 | "25.522569088039877\n", 460 | "25.522569088039877\n" 461 | ] 462 | } 463 | ], 464 | "source": [ 465 | "psnrs = []\n", 466 | "for img in imgs:\n", 467 | " # get noisy img\n", 468 | " \n", 469 | " # make grayscale\n", 470 | " #gimg = np.array([rgb2gray(imgs[0])])\n", 471 | " #img = gimg.transpose(1,2,0)\n", 472 | " img = img.transpose(1,2,0)\n", 473 | " \n", 474 | " img_noisy_np = get_noisy_img(img,sig=sig,noise_same=noise_same)\n", 475 | " output_depth = img.shape[0] \n", 476 | " print(img.shape, np.max(img), np.min(img))\n", 477 | " \n", 478 | " # denoise\n", 479 | " sigma = sig/255.\n", 480 | " out_img_np = pybm3d.bm3d.bm3d(img_noisy_np, sigma)\n", 481 | " #img = img.transpose(2,1,0)\n", 482 | " \n", 483 | " print(\"Noise PSNR: \",psnr(img,img_noisy_np) )\n", 484 | " print(\"Recov PSNR: \",psnr(img,out_img_np) )\n", 485 | " psnrv = psnr(img,out_img_np)\n", 486 | " psnrs.append(psnrv)\n", 487 | "print(np.mean(psnrs))\n", 488 | "print(np.mean(psnrs))" 489 | ] 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": null, 494 | "metadata": {}, 495 | "outputs": [], 496 | "source": [] 497 | } 498 | ], 499 | "metadata": { 500 | "kernelspec": { 501 | "display_name": "Python 3", 502 | "language": "python", 503 | "name": "python3" 504 | }, 505 | "language_info": { 506 | "codemirror_mode": { 507 | "name": "ipython", 508 | "version": 3 509 | }, 510 | "file_extension": ".py", 511 | "mimetype": "text/x-python", 512 | "name": "python", 513 | "nbconvert_exporter": "python", 514 | "pygments_lexer": "ipython3", 515 | "version": "3.6.7" 516 | } 517 | }, 518 | "nbformat": 4, 519 | "nbformat_minor": 2 520 | } 521 | -------------------------------------------------------------------------------- /include/__init__.py: -------------------------------------------------------------------------------- 1 | from .transforms import * 2 | from .wavelet import * 3 | from .decoder import * 4 | from .visualize import * 5 | from .fit import * 6 | from .helpers import * 7 | from .compression import * -------------------------------------------------------------------------------- /include/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/compression.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/compression.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/compression.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/compression.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/decoder.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/decoder.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/decoder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/decoder.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/fit.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/fit.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/fit.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/fit.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/helpers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/helpers.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/helpers.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/helpers.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/transforms.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/transforms.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/transforms.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/transforms.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/visualize.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/visualize.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/visualize.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/visualize.cpython-37.pyc -------------------------------------------------------------------------------- /include/__pycache__/wavelet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/wavelet.cpython-36.pyc -------------------------------------------------------------------------------- /include/__pycache__/wavelet.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/include/__pycache__/wavelet.cpython-37.pyc -------------------------------------------------------------------------------- /include/compression.py: -------------------------------------------------------------------------------- 1 | from torch.autograd import Variable 2 | import torch 3 | import torch.optim 4 | import copy 5 | import numpy as np 6 | 7 | from .helpers import * 8 | from .decoder import * 9 | from .fit import * 10 | from .wavelet import * 11 | 12 | def rep_error_deep_decoder(img_np,k=128,convert2ycbcr=False): 13 | ''' 14 | mse obtained by representing img_np with the deep decoder 15 | ''' 16 | output_depth = img_np.shape[0] 17 | if output_depth == 3 and convert2ycbcr: 18 | img = rgb2ycbcr(img_np) 19 | else: 20 | img = img_np 21 | img_var = np_to_var(img).type(dtype) 22 | 23 | num_channels = [k]*5 24 | net = decodernwv2(output_depth,num_channels_up=num_channels,bn_before_act=True).type(dtype) 25 | rnd = 500 26 | numit = 15000 27 | rn = 0.005 28 | mse_n, mse_t, ni, net = fit( num_channels=num_channels, 29 | reg_noise_std=rn, 30 | reg_noise_decayevery = rnd, 31 | num_iter=numit, 32 | LR=0.004, 33 | img_noisy_var=img_var, 34 | net=net, 35 | img_clean_var=img_var, 36 | find_best=True, 37 | ) 38 | out_img = net(ni.type(dtype)).data.cpu().numpy()[0] 39 | if output_depth == 3 and convert2ycbcr: 40 | out_img = ycbcr2rgb(out_img) 41 | return psnr(out_img,img_np), out_img, num_param(net) 42 | 43 | def rep_error_wavelet(img_np,ncoeff=300): 44 | ''' 45 | mse obtained by representing img_np with wavelet thresholding 46 | ncoff coefficients are retained per color channel 47 | ''' 48 | if img_np.shape[0] == 1: 49 | img_np = img_np[0,:,:] 50 | out_img_np = denoise_wavelet(img_np, ncoeff=ncoeff, multichannel=False, convert2ycbcr=True, mode='hard') 51 | else: 52 | img_np = np.transpose(img_np) 53 | out_img_np = denoise_wavelet(img_np, ncoeff=ncoeff, multichannel=True, convert2ycbcr=True, mode='hard') 54 | # img_np = np.array([img_np[:,:,0],img_np[:,:,1],img_np[:,:,2]]) 55 | return psnr(out_img_np,img_np), out_img_np 56 | 57 | def myimgshow(plt,img): 58 | if(img.shape[0] == 1): 59 | plt.imshow(np.clip(img[0],0,1),cmap='Greys',interpolation='none') 60 | else: 61 | plt.imshow(np.clip(img.transpose(1, 2, 0),0,1),interpolation='none') 62 | 63 | -------------------------------------------------------------------------------- /include/decoder.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | 5 | def add_module(self, module): 6 | self.add_module(str(len(self) + 1), module) 7 | 8 | torch.nn.Module.add = add_module 9 | 10 | 11 | def conv(in_f, out_f, kernel_size, stride=1, pad='zero',bias=False): 12 | padder = None 13 | to_pad = int((kernel_size - 1) / 2) 14 | if pad == 'reflection': 15 | padder = nn.ReflectionPad2d(to_pad) 16 | to_pad = 0 17 | 18 | convolver = nn.Conv2d(in_f, out_f, kernel_size, stride, padding=to_pad, bias=bias) 19 | 20 | layers = filter(lambda x: x is not None, [padder, convolver]) 21 | return nn.Sequential(*layers) 22 | 23 | def decodernw( 24 | num_output_channels=3, 25 | num_channels_up=[128]*5, 26 | filter_size_up=1, 27 | need_sigmoid=True, 28 | pad ='reflection', 29 | upsample_mode='bilinear', 30 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 31 | bn_before_act = False, 32 | bn_affine = True, 33 | bn = True, 34 | upsample_first = True, 35 | bias=False 36 | ): 37 | 38 | num_channels_up = num_channels_up + [num_channels_up[-1],num_channels_up[-1]] 39 | n_scales = len(num_channels_up) 40 | 41 | if not (isinstance(filter_size_up, list) or isinstance(filter_size_up, tuple)) : 42 | filter_size_up = [filter_size_up]*n_scales 43 | model = nn.Sequential() 44 | 45 | 46 | for i in range(len(num_channels_up)-1): 47 | 48 | if upsample_first: 49 | model.add(conv( num_channels_up[i], num_channels_up[i+1], filter_size_up[i], 1, pad=pad, bias=bias)) 50 | if upsample_mode!='none' and i != len(num_channels_up)-2: 51 | model.add(nn.Upsample(scale_factor=2, mode=upsample_mode)) 52 | #model.add(nn.functional.interpolate(size=None,scale_factor=2, mode=upsample_mode)) 53 | else: 54 | if upsample_mode!='none' and i!=0: 55 | model.add(nn.Upsample(scale_factor=2, mode=upsample_mode)) 56 | #model.add(nn.functional.interpolate(size=None,scale_factor=2, mode=upsample_mode)) 57 | model.add(conv( num_channels_up[i], num_channels_up[i+1], filter_size_up[i], 1, pad=pad,bias=bias)) 58 | 59 | if i != len(num_channels_up)-1: 60 | if(bn_before_act and bn): 61 | model.add(nn.BatchNorm2d( num_channels_up[i+1] ,affine=bn_affine)) 62 | if act_fun is not None: 63 | model.add(act_fun) 64 | if( (not bn_before_act) and bn): 65 | model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 66 | 67 | model.add(conv( num_channels_up[-1], num_output_channels, 1, pad=pad,bias=bias)) 68 | if need_sigmoid: 69 | model.add(nn.Sigmoid()) 70 | 71 | return model 72 | 73 | 74 | 75 | # Residual block 76 | class ResidualBlock(nn.Module): 77 | def __init__(self, in_f, out_f): 78 | super(ResidualBlock, self).__init__() 79 | self.conv = nn.Conv2d(in_f, out_f, 1, 1, padding=0, bias=False) 80 | 81 | def forward(self, x): 82 | residual = x 83 | out = self.conv(x) 84 | out += residual 85 | return out 86 | 87 | def resdecoder( 88 | num_output_channels=3, 89 | num_channels_up=[128]*5, 90 | filter_size_up=1, 91 | need_sigmoid=True, 92 | pad='reflection', 93 | upsample_mode='bilinear', 94 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 95 | bn_before_act = False, 96 | bn_affine = True, 97 | ): 98 | 99 | num_channels_up = num_channels_up + [num_channels_up[-1],num_channels_up[-1]] 100 | n_scales = len(num_channels_up) 101 | 102 | if not (isinstance(filter_size_up, list) or isinstance(filter_size_up, tuple)) : 103 | filter_size_up = [filter_size_up]*n_scales 104 | 105 | model = nn.Sequential() 106 | 107 | for i in range(len(num_channels_up)-2): 108 | 109 | model.add( ResidualBlock( num_channels_up[i], num_channels_up[i+1]) ) 110 | 111 | if upsample_mode!='none': 112 | model.add(nn.Upsample(scale_factor=2, mode=upsample_mode)) 113 | #model.add(nn.functional.interpolate(size=None,scale_factor=2, mode=upsample_mode)) 114 | 115 | if i != len(num_channels_up)-1: 116 | model.add(act_fun) 117 | #model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 118 | 119 | # new 120 | model.add(ResidualBlock( num_channels_up[-1], num_channels_up[-1])) 121 | #model.add(nn.BatchNorm2d( num_channels_up[-1] ,affine=bn_affine)) 122 | model.add(act_fun) 123 | # end new 124 | 125 | model.add(conv( num_channels_up[-1], num_output_channels, 1, pad=pad)) 126 | 127 | if need_sigmoid: 128 | model.add(nn.Sigmoid()) 129 | 130 | return model 131 | 132 | ########################## 133 | 134 | 135 | def np_to_tensor(img_np): 136 | '''Converts image in numpy.array to torch.Tensor. 137 | 138 | From C x W x H [0..1] to C x W x H [0..1] 139 | ''' 140 | return torch.from_numpy(img_np) 141 | 142 | def set_to(tensor,mtx): 143 | if not len(tensor.shape)==4: 144 | raise Exception("assumes a 4D tensor") 145 | num_kernels = tensor.shape[0] 146 | for i in range(tensor.shape[0]): 147 | for j in range(tensor.shape[1]): 148 | if i == j: 149 | tensor[i,j] = np_to_tensor(mtx) 150 | else: 151 | tensor[i,j] = np_to_tensor(np.zeros(mtx.shape)) 152 | return tensor 153 | 154 | def conv2(in_f, out_f, kernel_size, stride=1, pad='zero',bias=False): 155 | padder = None 156 | to_pad = int((kernel_size - 1) / 2) 157 | 158 | if kernel_size != 4: 159 | convolver = nn.Conv2d(in_f, out_f, kernel_size, stride, padding=to_pad, bias=bias) 160 | else: 161 | padder = nn.ReflectionPad2d( (1,0,1,0) ) 162 | convolver = nn.Conv2d(in_f, out_f, kernel_size, stride, padding=1, bias=bias) 163 | layers = filter(lambda x: x is not None, [padder, convolver]) 164 | return nn.Sequential(*layers) 165 | 166 | def fixed_decodernw( 167 | num_output_channels=3, 168 | num_channels_up=[128]*5, 169 | need_sigmoid=True, 170 | pad ='reflection', 171 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 172 | bn_affine = True, 173 | bn = True, 174 | mtx = np.array( [[1,3,3,1] , [3,9,9,3], [3,9,9,3], [1,3,3,1] ] )*1/16., 175 | output_padding = 0,padding=1, 176 | ): 177 | 178 | num_channels_up = num_channels_up + [num_channels_up[-1],num_channels_up[-1]] 179 | n_scales = len(num_channels_up) 180 | 181 | model = nn.Sequential() 182 | 183 | for i in range(len(num_channels_up)-2): 184 | 185 | # those will be fixed 186 | model.add(conv2( num_channels_up[i], num_channels_up[i], 4, 1, pad=pad)) 187 | # those will be learned 188 | model.add(conv( num_channels_up[i], num_channels_up[i+1], 1, 1, pad=pad)) 189 | 190 | if i != len(num_channels_up)-1: 191 | if act_fun is not None: 192 | model.add(act_fun) 193 | model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 194 | 195 | model.add(conv( num_channels_up[-1], num_output_channels, 1, pad=pad)) 196 | if need_sigmoid: 197 | model.add(nn.Sigmoid()) 198 | 199 | ### 200 | # this is a Gaussian kernel 201 | 202 | # set filters to fixed and then set the gradients to zero 203 | for m in model.modules(): 204 | if isinstance(m, nn.Conv2d): 205 | if(m.kernel_size == mtx.shape): 206 | m.weight.data = set_to(m.weight.data,mtx) 207 | for param in m.parameters(): 208 | param.requires_grad = False 209 | ### 210 | 211 | return model 212 | 213 | 214 | #### 215 | 216 | def deconv_decoder( 217 | num_output_channels=3, 218 | num_channels_up=[128]*5, 219 | filter_size=1, 220 | pad ='reflection', 221 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 222 | bn_affine = True, 223 | stride=2, 224 | padding=0, 225 | output_padding=0, 226 | final_conv=False, 227 | ): 228 | 229 | n_scales = len(num_channels_up) 230 | 231 | model = nn.Sequential() 232 | 233 | for i in range(len(num_channels_up)-1): 234 | 235 | model.add( 236 | nn.ConvTranspose2d(num_channels_up[i], num_channels_up[i+1], filter_size, stride=stride, padding=padding, output_padding=output_padding, groups=1, bias=False, dilation=1) 237 | ) 238 | #model.add(deconv(num_channels_up[i], num_channels_up[i+1], filter_size, stride,pad)) 239 | 240 | if i != len(num_channels_up)-1: 241 | model.add(act_fun) 242 | model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 243 | 244 | if final_conv: 245 | model.add(conv( num_channels_up[-1], num_channels_up[-1], 1, 1, pad=pad)) 246 | model.add(act_fun) 247 | model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 248 | 249 | model.add(conv( num_channels_up[-1], num_output_channels, 1, pad=pad)) 250 | model.add(nn.Sigmoid()) 251 | 252 | return model 253 | 254 | 255 | ##### 256 | 257 | 258 | def fixed_deconv_decoder( 259 | num_output_channels=3, 260 | num_channels_up=[128]*5, 261 | filter_size=1, 262 | pad ='reflection', 263 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 264 | bn_affine = True, 265 | mtx = np.array( [[1,4,7,4,1] , [4,16,26,16,4], [7,26,41,26,7], [4,16,26,16,4], [1,4,7,4,1]] ), 266 | output_padding=1, 267 | padding=2, 268 | ): 269 | 270 | num_channels_up = num_channels_up + [num_channels_up[-1]] 271 | n_scales = len(num_channels_up) 272 | 273 | model = nn.Sequential() 274 | 275 | for i in range(len(num_channels_up)-1): 276 | 277 | # those will be learned - conv 278 | model.add(conv( num_channels_up[i], num_channels_up[i+1], 1, 1, pad=pad)) 279 | 280 | # those will be fixed - upsample 281 | model.add( nn.ConvTranspose2d( 282 | num_channels_up[i], 283 | num_channels_up[i+1], 284 | kernel_size=4, 285 | stride=2, 286 | padding=padding, 287 | output_padding=output_padding, groups=1, bias=False, dilation=1) ) 288 | 289 | if i != len(num_channels_up)-1: 290 | model.add(act_fun) 291 | model.add(nn.BatchNorm2d( num_channels_up[i+1], affine=bn_affine)) 292 | 293 | model.add(conv( num_channels_up[-1], num_output_channels, 1, pad=pad)) 294 | model.add(nn.Sigmoid()) 295 | 296 | ### 297 | # this is a Gaussian kernel 298 | # set filters to fixed and then set the gradients to zero 299 | for m in model.modules(): 300 | if isinstance(m, nn.ConvTranspose2d): 301 | if(m.kernel_size == mtx.shape): 302 | m.weight.data = set_to(m.weight.data,mtx) 303 | for param in m.parameters(): 304 | param.requires_grad = False 305 | ### 306 | 307 | return model 308 | 309 | 310 | -------------------------------------------------------------------------------- /include/denoise.py: -------------------------------------------------------------------------------- 1 | from torch.autograd import Variable 2 | import torch 3 | import torch.optim 4 | import copy 5 | import numpy as np 6 | from scipy.linalg import hadamard 7 | 8 | from .helpers import * 9 | 10 | dtype = torch.cuda.FloatTensor 11 | #dtype = torch.FloatTensor 12 | 13 | 14 | def exp_lr_scheduler(optimizer, epoch, init_lr=0.001, lr_decay_epoch=7): 15 | """Decay learning rate by a factor of 0.1 every lr_decay_epoch epochs.""" 16 | lr = init_lr * (0.5**(epoch // lr_decay_epoch)) 17 | 18 | if epoch % lr_decay_epoch == 0: 19 | print('LR is set to {}'.format(lr)) 20 | 21 | for param_group in optimizer.param_groups: 22 | param_group['lr'] = lr 23 | 24 | return optimizer 25 | 26 | 27 | def DIPdenoise( net, 28 | img_noisy_var, 29 | num_channels, 30 | img_clean_var, 31 | net_type="decoder", 32 | num_iter = 5000, 33 | LR = 0.01, 34 | OPTIMIZER='adam', 35 | opt_input = False, 36 | reg_noise_std = 0, 37 | reg_noise_decayevery = 100000, 38 | mask_var = None, 39 | apply_f = None, 40 | decaylr = False, 41 | net_input = None, 42 | net_input_gen = "random", 43 | ): 44 | 45 | if net_input is not None: 46 | print("input provided") 47 | else: 48 | # feed noise into the network 49 | if net_type == "decoder": 50 | totalupsample = 2**len(num_channels) 51 | width = int(img_clean_var.data.shape[2]/totalupsample) 52 | height = int(img_clean_var.data.shape[3]/totalupsample) 53 | shape = [1,num_channels[0], width, height] 54 | print("shape: ", shape) 55 | if(net_input_gen == "random"): 56 | net_input = Variable(torch.zeros(shape)) 57 | net_input.data.uniform_() 58 | net_input.data *= 1./10 59 | elif(net_input_gen == "hadamard"): 60 | H = hadamard(width*height) 61 | ni = np.zeros(shape) 62 | for i in range(shape[1]): 63 | ni[0,i] = np.reshape(H[i],(width,height)) 64 | net_input = np_to_var(ni[0]) 65 | net_input.data *= 1./20 66 | elif(net_input_gen == "rademacher"): 67 | ni = np.random.randint(2, size = shape) - 0.5 68 | net_input = np_to_var(ni[0]) 69 | net_input.data *= 1./10 70 | print(net_input.data.cpu().numpy()[0]) 71 | 72 | elif net_type == "hourglass": 73 | print("hourglass mode") 74 | input_depth = 32 75 | shape = [1, input_depth, img_clean_var.data.shape[2], img_clean_var.data.shape[3]] 76 | net_input = Variable(torch.zeros(shape)) 77 | net_input.data.uniform_() 78 | net_input.data *= 1./10 79 | elif net_type == "noup": 80 | print("no upsampling mode") 81 | shape = [1, num_channels[0], img_clean_var.data.shape[2], img_clean_var.data.shape[3]] 82 | net_input = Variable(torch.zeros(shape)) 83 | net_input.data.uniform_() 84 | net_input.data *= 1./10 85 | #print(net_input.data.cpu().numpy() ) 86 | net_input_saved = net_input.data.clone() 87 | noise = net_input.data.clone() 88 | p = [x for x in net.parameters() ] 89 | 90 | if(opt_input == True): 91 | net_input.requires_grad = True 92 | p += [net_input] 93 | 94 | mse_wrt_noisy = np.zeros(num_iter) 95 | mse_wrt_truth = np.zeros(num_iter) 96 | if OPTIMIZER == 'SGD': 97 | print("optimize with SGD", LR) 98 | optimizer = torch.optim.SGD(p, lr=LR,momentum=0.9) 99 | elif OPTIMIZER == 'adam': 100 | print("optimize with adam", LR) 101 | optimizer = torch.optim.Adam(p, lr=LR) 102 | elif OPTIMIZER == 'adadelta': 103 | print("optimize with adadelta", LR) 104 | optimizer = torch.optim.Adadelta(p, lr=LR, rho=0.9, eps=1e-06, weight_decay=0) 105 | 106 | mse = torch.nn.MSELoss().type(dtype) 107 | if apply_f is None: 108 | noise_energy = mse(img_noisy_var, img_clean_var) 109 | else: 110 | noise_energy = mse(img_noisy_var, img_noisy_var) 111 | 112 | for i in range(num_iter): 113 | if decaylr is True: 114 | optimizer = exp_lr_scheduler(optimizer, i, init_lr=LR, lr_decay_epoch=100) 115 | if reg_noise_std > 0: 116 | if i % reg_noise_decayevery == 0: 117 | reg_noise_std *= 0.7 118 | net_input = Variable(net_input_saved + (noise.normal_() * reg_noise_std)) 119 | optimizer.zero_grad() 120 | out = net(net_input.type(dtype)) 121 | # training loss 122 | if mask_var is not None: 123 | loss = mse( out * mask_var , img_noisy_var * mask_var ) 124 | elif apply_f: 125 | loss = mse( apply_f(out) , img_noisy_var ) 126 | else: 127 | loss = mse(out, img_noisy_var) 128 | loss.backward() 129 | mse_wrt_noisy[i] = var_to_np(loss) 130 | # the actual loss 131 | true_loss = mse(Variable(out.data, requires_grad=False), img_clean_var) 132 | mse_wrt_truth[i] = var_to_np(true_loss) 133 | if i % 10 == 0: 134 | out2 = net(Variable(net_input_saved).type(dtype)) 135 | loss2 = mse(out2, img_clean_var) 136 | print ('Iteration %05d Train loss %f Actual loss %f Actual loss orig %f Noise Energy %f' % (i, loss.data[0],true_loss.data[0],loss2.data[0],noise_energy.data[0]), '\r', end='') 137 | optimizer.step() 138 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved 139 | 140 | 141 | 142 | ''' 143 | 144 | def fit(net, 145 | img_noisy_var, 146 | num_channels, 147 | img_clean_var, 148 | num_iter = 5000, 149 | LR = 0.01, 150 | OPTIMIZER='adam', 151 | opt_input = False, 152 | reg_noise_std = 0, 153 | reg_noise_decayevery = 100000, 154 | mask_var = None, 155 | apply_f = None, 156 | decaylr = False, 157 | net_input = None, 158 | net_input_gen = "random", 159 | ): 160 | 161 | if net_input is not None: 162 | print("input provided") 163 | else: 164 | # feed uniform noise into the network 165 | totalupsample = 2**len(num_channels) 166 | width = int(img_clean_var.data.shape[2]/totalupsample) 167 | height = int(img_clean_var.data.shape[3]/totalupsample) 168 | shape = [1,num_channels[0], width, height] 169 | print("shape: ", shape) 170 | net_input = Variable(torch.zeros(shape)) 171 | net_input.data.uniform_() 172 | net_input.data *= 1./10 173 | 174 | net_input_saved = net_input.data.clone() 175 | noise = net_input.data.clone() 176 | p = [x for x in net.parameters() ] 177 | 178 | if(opt_input == True): 179 | net_input.requires_grad = True 180 | p += [net_input] 181 | 182 | mse_wrt_noisy = np.zeros(num_iter) 183 | mse_wrt_truth = np.zeros(num_iter) 184 | 185 | if OPTIMIZER == 'SGD': 186 | print("optimize with SGD", LR) 187 | optimizer = torch.optim.SGD(p, lr=LR,momentum=0.9) 188 | elif OPTIMIZER == 'adam': 189 | print("optimize with adam", LR) 190 | optimizer = torch.optim.Adam(p, lr=LR) 191 | 192 | mse = torch.nn.MSELoss() #.type(dtype) 193 | noise_energy = mse(img_noisy_var, img_clean_var) 194 | 195 | best_net = copy.deepcopy(net) 196 | best_mse = 1000000.0 197 | 198 | for i in range(num_iter): 199 | if decaylr is True: 200 | optimizer = exp_lr_scheduler(optimizer, i, init_lr=LR, lr_decay_epoch=100) 201 | if reg_noise_std > 0: 202 | if i % reg_noise_decayevery == 0: 203 | reg_noise_std *= 0.7 204 | net_input = Variable(net_input_saved + (noise.normal_() * reg_noise_std)) 205 | optimizer.zero_grad() 206 | out = net(net_input.type(dtype)) 207 | 208 | # training loss 209 | if mask_var is not None: 210 | loss = mse( out * mask_var , img_noisy_var * mask_var ) 211 | elif apply_f: 212 | loss = mse( apply_f(out) , img_noisy_var ) 213 | else: 214 | loss = mse(out, img_noisy_var) 215 | loss.backward() 216 | mse_wrt_noisy[i] = loss.data.cpu().numpy() 217 | 218 | # the actual loss 219 | true_loss = mse(Variable(out.data, requires_grad=False), img_clean_var) 220 | mse_wrt_truth[i] = true_loss.data.cpu().numpy() 221 | if i % 10 == 0: 222 | out2 = net(Variable(net_input_saved).type(dtype)) 223 | loss2 = mse(out2, img_clean_var) 224 | #print ('Iteration %05d Train loss %f Actual loss %f Actual loss orig %f Noise Energy %f' 225 | # % (i, loss.data.item(),true_loss.data.item(),loss2.data.item(),noise_energy.data.item()), '\r', end='') 226 | print ('Iteration %05d Train loss %f Actual loss %f Actual loss orig %f Noise Energy %f' % (i, loss.data[0],true_loss.data[0],loss2.data[0],noise_energy.data[0]), '\r', end='') 227 | 228 | # if training loss improves by at least one percent, we found a new best net 229 | if best_mse > 1.005*loss.data[0]: 230 | best_mse = loss.data[0] 231 | best_net = copy.deepcopy(net) 232 | 233 | optimizer.step() 234 | 235 | net = best_net 236 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved 237 | 238 | ''' 239 | 240 | -------------------------------------------------------------------------------- /include/fit.py: -------------------------------------------------------------------------------- 1 | from torch.autograd import Variable 2 | import torch 3 | import torch.optim 4 | import copy 5 | import numpy as np 6 | from scipy.linalg import hadamard 7 | 8 | from .helpers import * 9 | 10 | dtype = torch.cuda.FloatTensor 11 | #dtype = torch.FloatTensor 12 | 13 | 14 | def exp_lr_scheduler(optimizer, epoch, init_lr=0.001, lr_decay_epoch=500): 15 | """Decay learning rate by a factor of 0.1 every lr_decay_epoch epochs.""" 16 | lr = init_lr * (0.65**(epoch // lr_decay_epoch)) 17 | 18 | if epoch % lr_decay_epoch == 0: 19 | print('LR is set to {}'.format(lr)) 20 | 21 | for param_group in optimizer.param_groups: 22 | param_group['lr'] = lr 23 | 24 | return optimizer 25 | 26 | def sqnorm(a): 27 | return np.sum( a*a ) 28 | 29 | def get_distances(initial_maps,final_maps): 30 | results = [] 31 | for a,b in zip(initial_maps,final_maps): 32 | res = sqnorm(a-b)/(sqnorm(a) + sqnorm(b)) 33 | results += [res] 34 | return(results) 35 | 36 | def get_weights(net): 37 | weights = [] 38 | for m in net.modules(): 39 | if isinstance(m, nn.Conv2d): 40 | weights += [m.weight.data.cpu().numpy()] 41 | return weights 42 | 43 | def fit(net, 44 | img_noisy_var, 45 | num_channels, 46 | img_clean_var, 47 | num_iter = 5000, 48 | LR = 0.01, 49 | OPTIMIZER='adam', 50 | opt_input = False, 51 | reg_noise_std = 0, 52 | reg_noise_decayevery = 100000, 53 | mask_var = None, 54 | apply_f = None, 55 | lr_decay_epoch = 0, 56 | net_input = None, 57 | net_input_gen = "random", 58 | find_best=False, 59 | weight_decay=0, 60 | upsample_mode = "bilinear", 61 | totalupsample = 1, 62 | loss_type="MSE", 63 | output_gradients=False, 64 | output_weights=False, 65 | show_images=False, 66 | plot_after=None, 67 | ): 68 | 69 | if net_input is not None: 70 | print("input provided") 71 | else: 72 | if upsample_mode=="bilinear": 73 | # feed uniform noise into the network 74 | totalupsample = 2**len(num_channels) 75 | elif upsample_mode=="deconv": 76 | # feed uniform noise into the network 77 | totalupsample = 2**(len(num_channels)-1) 78 | width = int(img_clean_var.data.shape[2]/totalupsample) 79 | height = int(img_clean_var.data.shape[3]/totalupsample) 80 | shape = [1,num_channels[0], width, height] 81 | print("input shape: ", shape) 82 | net_input = Variable(torch.zeros(shape)).type(dtype) 83 | net_input.data.uniform_() 84 | net_input.data *= 1./10 85 | 86 | net_input = net_input.type(dtype) 87 | net_input_saved = net_input.data.clone() 88 | noise = net_input.data.clone() 89 | p = [x for x in net.parameters() ] 90 | 91 | if(opt_input == True): # optimizer over the input as well 92 | net_input.requires_grad = True 93 | p += [net_input] 94 | 95 | mse_wrt_noisy = np.zeros(num_iter) 96 | mse_wrt_truth = np.zeros(num_iter) 97 | 98 | 99 | if OPTIMIZER == 'SGD': 100 | print("optimize with SGD", LR) 101 | optimizer = torch.optim.SGD(p, lr=LR,momentum=0.9,weight_decay=weight_decay) 102 | elif OPTIMIZER == 'adam': 103 | print("optimize with adam", LR) 104 | optimizer = torch.optim.Adam(p, lr=LR,weight_decay=weight_decay) 105 | elif OPTIMIZER == 'LBFGS': 106 | print("optimize with LBFGS", LR) 107 | optimizer = torch.optim.LBFGS(p, lr=LR) 108 | 109 | if loss_type=="MSE": 110 | mse = torch.nn.MSELoss() #.type(dtype) 111 | if loss_type=="L1": 112 | mse = nn.L1Loss() 113 | 114 | if find_best: 115 | best_net = copy.deepcopy(net) 116 | best_mse = 1000000.0 117 | 118 | nconvnets = 0 119 | for p in list(filter(lambda p: len(p.data.shape)>2, net.parameters())): 120 | nconvnets += 1 121 | 122 | out_grads = np.zeros((nconvnets,num_iter)) 123 | 124 | init_weights = get_weights(net) 125 | out_weights = np.zeros(( len(init_weights) ,num_iter)) 126 | 127 | out_imgs = np.zeros((1,1)) 128 | 129 | if plot_after is not None: 130 | out_img_np = net( net_input_saved.type(dtype) ).data.cpu().numpy()[0] 131 | out_imgs = np.zeros( (len(plot_after),) + out_img_np.shape ) 132 | 133 | for i in range(num_iter): 134 | 135 | if lr_decay_epoch is not 0: 136 | optimizer = exp_lr_scheduler(optimizer, i, init_lr=LR, lr_decay_epoch=lr_decay_epoch) 137 | if reg_noise_std > 0: 138 | if i % reg_noise_decayevery == 0: 139 | reg_noise_std *= 0.7 140 | net_input = Variable(net_input_saved + (noise.normal_() * reg_noise_std)) 141 | 142 | def closure(): 143 | optimizer.zero_grad() 144 | out = net(net_input.type(dtype)) 145 | 146 | # training loss 147 | if mask_var is not None: 148 | loss = mse( out * mask_var , img_noisy_var * mask_var ) 149 | elif apply_f: 150 | loss = mse( apply_f(out) , img_noisy_var ) 151 | else: 152 | loss = mse(out, img_noisy_var) 153 | 154 | loss.backward() 155 | mse_wrt_noisy[i] = loss.data.cpu().numpy() 156 | 157 | 158 | # the actual loss 159 | true_loss = mse( Variable(out.data, requires_grad=False).type(dtype), img_clean_var.type(dtype) ) 160 | mse_wrt_truth[i] = true_loss.data.cpu().numpy() 161 | 162 | if output_gradients: 163 | for ind,p in enumerate(list(filter(lambda p: p.grad is not None and len(p.data.shape)>2, net.parameters()))): 164 | out_grads[ind,i] = p.grad.data.norm(2).item() 165 | #print(p.grad.data.norm(2).item()) 166 | #su += p.grad.data.norm(2).item() 167 | #mse_wrt_noisy[i] = su 168 | 169 | if i % 10 == 0: 170 | out2 = net(Variable(net_input_saved).type(dtype)) 171 | loss2 = mse(out2, img_clean_var) 172 | print ('Iteration %05d Train loss %f Actual loss %f Actual loss orig %f' % (i, loss.data,true_loss.data,loss2.data), '\r', end='') 173 | 174 | if show_images: 175 | if i % 50 == 0: 176 | print(i) 177 | out_img_np = net( ni.type(dtype) ).data.cpu().numpy()[0] 178 | myimgshow(plt,out_img_np) 179 | plt.show() 180 | 181 | if plot_after is not None: 182 | if i in plot_after: 183 | out_imgs[ plot_after.index(i) ,:] = net( net_input_saved.type(dtype) ).data.cpu().numpy()[0] 184 | 185 | if output_weights: 186 | out_weights[:,i] = np.array( get_distances( init_weights, get_weights(net) ) ) 187 | 188 | return loss 189 | 190 | loss = optimizer.step(closure) 191 | 192 | if find_best: 193 | # if training loss improves by at least one percent, we found a new best net 194 | if best_mse > 1.005*loss.data: 195 | best_mse = loss.data 196 | best_net = copy.deepcopy(net) 197 | 198 | 199 | if find_best: 200 | net = best_net 201 | if output_gradients and output_weights: 202 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, net, out_grads 203 | elif output_gradients: 204 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, net, out_grads 205 | elif output_weights: 206 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, net, out_weights 207 | elif plot_after is not None: 208 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, net, out_imgs 209 | else: 210 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, net 211 | 212 | 213 | 214 | 215 | 216 | 217 | ### weight regularization 218 | #if orth_reg > 0: 219 | # for name, param in net.named_parameters(): 220 | # consider all the conv weights, but the last one which only combines colors 221 | # if '.1.weight' in name and str( len(net)-1 ) not in name: 222 | # param_flat = param.view(param.shape[0], -1) 223 | # sym = torch.mm(param_flat, torch.t(param_flat)) 224 | # sym -= Variable(torch.eye(param_flat.shape[0])).type(dtype) 225 | # loss = loss + (orth_reg * sym.sum().type(dtype) ) 226 | ### 227 | 228 | def fit_multiple(net, 229 | imgs, # list of images [ [1, color channels, W, H] ] 230 | num_channels, 231 | num_iter = 5000, 232 | LR = 0.01, 233 | find_best=False, 234 | upsample_mode="bilinear", 235 | ): 236 | # generate netinputs 237 | # feed uniform noise into the network 238 | nis = [] 239 | for i in range(len(imgs)): 240 | if upsample_mode=="bilinear": 241 | # feed uniform noise into the network 242 | totalupsample = 2**len(num_channels) 243 | elif upsample_mode=="deconv": 244 | # feed uniform noise into the network 245 | totalupsample = 2**(len(num_channels)-1) 246 | #totalupsample = 2**len(num_channels) 247 | width = int(imgs[0].data.shape[2]/totalupsample) 248 | height = int(imgs[0].data.shape[3]/totalupsample) 249 | shape = [1 ,num_channels[0], width, height] 250 | print("shape: ", shape) 251 | net_input = Variable(torch.zeros(shape)) 252 | net_input.data.uniform_() 253 | net_input.data *= 1./10 254 | nis.append(net_input) 255 | 256 | # learnable parameters are the weights 257 | p = [x for x in net.parameters() ] 258 | 259 | mse_wrt_noisy = np.zeros(num_iter) 260 | 261 | optimizer = torch.optim.Adam(p, lr=LR) 262 | 263 | mse = torch.nn.MSELoss() #.type(dtype) 264 | 265 | if find_best: 266 | best_net = copy.deepcopy(net) 267 | best_mse = 1000000.0 268 | 269 | for i in range(num_iter): 270 | 271 | def closure(): 272 | optimizer.zero_grad() 273 | 274 | #loss = np_to_var(np.array([0.0])) 275 | out = net(nis[0].type(dtype)) 276 | loss = mse(out, imgs[0].type(dtype)) 277 | #for img,ni in zip(imgs,nis): 278 | for j in range(1,len(imgs)): 279 | #out = net(ni.type(dtype)) 280 | #loss += mse(out, img.type(dtype)) 281 | out = net(nis[j].type(dtype)) 282 | loss += mse(out, imgs[j].type(dtype)) 283 | 284 | #out = net(nis[0].type(dtype)) 285 | #out2 = net(nis[1].type(dtype)) 286 | #loss = mse(out, imgs[0].type(dtype)) + mse(out2, imgs[1].type(dtype)) 287 | 288 | loss.backward() 289 | mse_wrt_noisy[i] = loss.data.cpu().numpy() 290 | 291 | if i % 10 == 0: 292 | print ('Iteration %05d Train loss %f' % (i, loss.data), '\r', end='') 293 | return loss 294 | 295 | loss = optimizer.step(closure) 296 | 297 | if find_best: 298 | # if training loss improves by at least one percent, we found a new best net 299 | if best_mse > 1.005*loss.data: 300 | best_mse = loss.data 301 | best_net = copy.deepcopy(net) 302 | 303 | if find_best: 304 | net = best_net 305 | return mse_wrt_noisy, nis, net 306 | 307 | -------------------------------------------------------------------------------- /include/helpers.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torchvision 4 | import sys 5 | 6 | import numpy as np 7 | from PIL import Image 8 | import PIL 9 | import numpy as np 10 | 11 | from torch.autograd import Variable 12 | 13 | import random 14 | import numpy as np 15 | import torch 16 | import matplotlib.pyplot as plt 17 | 18 | from PIL import Image 19 | import PIL 20 | 21 | from torch.autograd import Variable 22 | 23 | def myimgshow(plt,img): 24 | if(img.shape[0] == 1): 25 | plt.imshow(np.clip(img[0],0,1),cmap='Greys',interpolation='none') 26 | else: 27 | plt.imshow(np.clip(img.transpose(1, 2, 0),0,1),interpolation='none') 28 | 29 | def load_and_crop(imgname,target_width=512,target_height=512): 30 | ''' 31 | imgname: string of image location 32 | load an image, and center-crop if the image is large enough, else return none 33 | ''' 34 | img = Image.open(imgname) 35 | width, height = img.size 36 | if width <= target_width or height <= target_height: 37 | return None 38 | 39 | left = (width - target_width)/2 40 | top = (height - target_height)/2 41 | right = (width + target_width)/2 42 | bottom = (height + target_height)/2 43 | 44 | return img.crop((left, top, right, bottom)) 45 | 46 | def save_np_img(img,filename): 47 | if(img.shape[0] == 1): 48 | plt.imshow(np.clip(img[0],0,1),cmap='Greys',interpolation='nearest') 49 | else: 50 | plt.imshow(np.clip(img.transpose(1, 2, 0),0,1)) 51 | plt.axis('off') 52 | plt.savefig(filename, bbox_inches='tight') 53 | plt.close() 54 | 55 | def np_to_tensor(img_np): 56 | '''Converts image in numpy.array to torch.Tensor. 57 | 58 | From C x W x H [0..1] to C x W x H [0..1] 59 | ''' 60 | return torch.from_numpy(img_np) 61 | 62 | def np_to_var(img_np, dtype = torch.cuda.FloatTensor): 63 | '''Converts image in numpy.array to torch.Variable. 64 | 65 | From C x W x H [0..1] to 1 x C x W x H [0..1] 66 | ''' 67 | return Variable(np_to_tensor(img_np)[None, :]) 68 | 69 | def var_to_np(img_var): 70 | '''Converts an image in torch.Variable format to np.array. 71 | 72 | From 1 x C x W x H [0..1] to C x W x H [0..1] 73 | ''' 74 | return img_var.data.cpu().numpy()[0] 75 | 76 | 77 | def pil_to_np(img_PIL): 78 | '''Converts image in PIL format to np.array. 79 | 80 | From W x H x C [0...255] to C x W x H [0..1] 81 | ''' 82 | ar = np.array(img_PIL) 83 | 84 | if len(ar.shape) == 3: 85 | ar = ar.transpose(2,0,1) 86 | else: 87 | ar = ar[None, ...] 88 | 89 | return ar.astype(np.float32) / 255. 90 | 91 | 92 | def rgb2ycbcr(img): 93 | #out = color.rgb2ycbcr( img.transpose(1, 2, 0) ) 94 | #return out.transpose(2,0,1)/256. 95 | r,g,b = img[0],img[1],img[2] 96 | y = 0.299*r+0.587*g+0.114*b 97 | cb = 0.5 - 0.168736*r - 0.331264*g + 0.5*b 98 | cr = 0.5 + 0.5*r - 0.418588*g - 0.081312*b 99 | return np.array([y,cb,cr]) 100 | 101 | def ycbcr2rgb(img): 102 | #out = color.ycbcr2rgb( 256.*img.transpose(1, 2, 0) ) 103 | #return (out.transpose(2,0,1) - np.min(out))/(np.max(out)-np.min(out)) 104 | y,cb,cr = img[0],img[1],img[2] 105 | r = y + 1.402*(cr-0.5) 106 | g = y - 0.344136*(cb-0.5) - 0.714136*(cr-0.5) 107 | b = y + 1.772*(cb - 0.5) 108 | return np.array([r,g,b]) 109 | 110 | 111 | 112 | def mse(x_hat,x_true,maxv=1.): 113 | x_hat = x_hat.flatten() 114 | x_true = x_true.flatten() 115 | mse = np.mean(np.square(x_hat-x_true)) 116 | energy = np.mean(np.square(x_true)) 117 | return mse/energy 118 | 119 | def psnr(x_hat,x_true,maxv=1.): 120 | x_hat = x_hat.flatten() 121 | x_true = x_true.flatten() 122 | mse=np.mean(np.square(x_hat-x_true)) 123 | psnr_ = 10.*np.log(maxv**2/mse)/np.log(10.) 124 | return psnr_ 125 | 126 | def num_param(net): 127 | s = sum([np.prod(list(p.size())) for p in net.parameters()]); 128 | return s 129 | #print('Number of params: %d' % s) 130 | 131 | def num_trainable_param(net): 132 | s = sum([np.prod(list(p.size())) for p in net.parameters() if p.requires_grad==True]); 133 | return s 134 | 135 | def rgb2gray(rgb): 136 | r, g, b = rgb[0,:,:], rgb[1,:,:], rgb[2,:,:] 137 | gray = 0.2989 * r + 0.5870 * g + 0.1140 * b 138 | return np.array([gray]) 139 | 140 | def savemtx_for_logplot(A,filename = "exp.dat"): 141 | ind = sorted(list(set([int(i) for i in np.geomspace(1, len(A[0])-1 ,num=700)]))) 142 | A = [ [a[i] for i in ind] for a in A] 143 | X = np.array([ind] + A) 144 | np.savetxt(filename, X.T, delimiter=' ') 145 | 146 | 147 | def get_imgnet_imgs(num_samples = 100, path = '../imagenet/',verbose=False): 148 | perm = [i for i in range(1,50000)] 149 | random.Random(4).shuffle(perm) 150 | siz = 512 151 | file = open("exp_imgnet_imgs.txt","w") 152 | 153 | imgs = [] 154 | sampled = 0 155 | imgslist = [] 156 | for imgnr in perm: 157 | # prepare and select image 158 | # Format is: ILSVRC2012_val_00024995.JPEG 159 | imgnr_str = str(imgnr).zfill(8) 160 | imgname = path + 'ILSVRC2012_val_' + imgnr_str + ".JPEG" 161 | img = load_and_crop(imgname,target_width=512,target_height=512) 162 | if img is None: # then the image could not be croped to 512x512 163 | continue 164 | 165 | img_np = pil_to_np(img) 166 | 167 | if img_np.shape[0] != 3: # we only want to consider color images 168 | continue 169 | if verbose: 170 | imgslist += ['ILSVRC2012_val_' + imgnr_str + ".JPEG"] 171 | print("cp ", imgname, "./imgs") 172 | imgs += [img_np] 173 | sampled += 1 174 | if sampled >= num_samples: 175 | break 176 | if verbose: 177 | print(imgslist) 178 | return imgs 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /include/onedim.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn as nn 4 | import torch.optim 5 | from torch.autograd import Variable 6 | import matplotlib.pyplot as plt 7 | import copy 8 | 9 | 10 | 11 | dtype = torch.FloatTensor # This code is meant for CPU 12 | 13 | def add_module(self, module): 14 | self.add_module(str(len(self) + 1), module) 15 | 16 | torch.nn.Module.add = add_module 17 | 18 | 19 | def conv1(in_f, out_f, kernel_size, stride=1, pad='zero'): 20 | padder = None 21 | to_pad = int((kernel_size - 1) / 2) 22 | if pad == 'reflection': 23 | padder = nn.ReflectionPad2d(to_pad) 24 | to_pad = 0 25 | 26 | convolver = nn.Conv1d(in_f, out_f, kernel_size, stride, padding=to_pad, bias=False) 27 | 28 | layers = filter(lambda x: x is not None, [padder, convolver]) 29 | return nn.Sequential(*layers) 30 | 31 | 32 | 33 | # Define the upsampling matrices 34 | def get_upsample_matrix(k, identity=False, upsample_mode='linear'): 35 | # Returns a 2*k-1 x k numpy array corresponding to an upsampling matrix 36 | 37 | if identity: 38 | return np.eye(k) 39 | U = np.zeros((2*k-1, k)) 40 | for i in range(k): 41 | U[2*i, i] = 1 42 | 43 | if i < k-1: 44 | if upsample_mode=='linear': 45 | U[2*i+1, [i, (i+1) % k]] = [1./2, 1./2] 46 | elif upsample_mode=='convex0.7-0.3': 47 | U[2*i+1, [i, (i+1) % k]] = [0.7, 0.3] 48 | elif upsample_mode=='convex0.75-0.25': 49 | U[2*i+1, [i, (i+1) % k]] = [0.75, 0.25] 50 | return U 51 | 52 | 53 | 54 | class Upsample_Module(nn.Module): 55 | # Only works for batch size 1. Works for any number of channels 56 | 57 | def __init__(self, upsample_mode='linear'): 58 | super(Upsample_Module,self).__init__() 59 | self.upsample_mode=upsample_mode 60 | 61 | def forward(self, x): 62 | n = x.shape[2] 63 | U = Variable(torch.Tensor(get_upsample_matrix(n, upsample_mode=self.upsample_mode))) 64 | return torch.stack([torch.t(U.matmul(torch.t(x[0,...])))], 0) 65 | 66 | 67 | 68 | 69 | def decoder_1d( 70 | num_output_channels=1, 71 | num_channels_up=[128]*5, 72 | filter_size_up=1, 73 | need_sigmoid=False, 74 | pad='zero', 75 | upsample_mode='linear', 76 | act_fun=nn.ReLU(), # nn.LeakyReLU(0.2, inplace=True) 77 | need_bn=True, 78 | ): 79 | 80 | num_channels_up = num_channels_up + [num_channels_up[-1],num_channels_up[-1]] 81 | n_scales = len(num_channels_up) 82 | #print('n_scales = %d' %n_scales) 83 | 84 | if not (isinstance(filter_size_up, list) or isinstance(filter_size_up, tuple)) : 85 | filter_size_up = [filter_size_up]*n_scales 86 | 87 | model = nn.Sequential() 88 | 89 | for i in range(len(num_channels_up)-1): 90 | 91 | if upsample_mode!='none' and i!=0: 92 | if upsample_mode=='MatrixUpsample': 93 | model.add(Upsample_Module()) 94 | elif upsample_mode=='MatrixUpsampleConvex0.7-0.3': 95 | model.add(Upsample_Module(upsample_mode='convex0.7-0.3')) 96 | elif upsample_mode=='MatrixUpsampleConvex0.75-0.25': 97 | model.add(Upsample_Module(upsample_mode='convex0.75-0.25')) 98 | elif upsample_mode=='nnUpsampleDouble': 99 | model.add(nn.Upsample(scale_factor=2.0, mode='linear', align_corners=False)) 100 | elif upsample_mode=='nearest': 101 | model.add(nn.Upsample(scale_factor=2.0, mode='nearest')) 102 | 103 | model.add(conv1( num_channels_up[i], num_channels_up[i+1], filter_size_up[i], 1, pad=pad)) 104 | if i != len(num_channels_up)-1: 105 | if need_bn: 106 | model.add(nn.BatchNorm1d( num_channels_up[i+1] )) 107 | model.add(act_fun) 108 | 109 | model.add(conv1( num_channels_up[-1], num_output_channels, 1, pad=pad)) 110 | 111 | if need_sigmoid: 112 | model.add(nn.Sigmoid()) 113 | 114 | return model 115 | 116 | 117 | def fit_1d(net, 118 | img_noisy_var, 119 | num_channels, 120 | img_clean_var, 121 | net_input, # Passing in the net_input is required 122 | num_iter = 5000, 123 | LR = 0.01, 124 | OPTIMIZER='adam', 125 | opt_input = False, 126 | reg_noise_std = 0, 127 | reg_noise_decayevery = 100000, 128 | mask_var = None, 129 | apply_f = None, 130 | decaylr = False, 131 | net_input_gen = "random", 132 | plot_output_every = None, 133 | ): 134 | 135 | net_input_saved = net_input.data.clone() 136 | noise = net_input.data.clone() 137 | p = [x for x in net.parameters() ] 138 | 139 | if(opt_input == True): 140 | net_input.requires_grad = True 141 | p += [net_input] 142 | 143 | mse_wrt_noisy = np.zeros(num_iter) 144 | mse_wrt_truth = np.zeros(num_iter) 145 | 146 | if OPTIMIZER == 'SGD': 147 | print("optimize with SGD", LR) 148 | optimizer = torch.optim.SGD(p, lr=LR,momentum=0.9) 149 | elif OPTIMIZER == 'adam': 150 | print("optimize with adam", LR) 151 | optimizer = torch.optim.Adam(p, lr=LR) 152 | 153 | mse = torch.nn.MSELoss() #.type(dtype) 154 | noise_energy = mse(img_noisy_var, img_clean_var) 155 | 156 | 157 | 158 | for i in range(num_iter): 159 | if decaylr is True: 160 | optimizer = exp_lr_scheduler(optimizer, i, init_lr=LR, lr_decay_epoch=100) 161 | if reg_noise_std > 0: 162 | if i % reg_noise_decayevery == 0: 163 | reg_noise_std *= 0.7 164 | net_input = Variable(net_input_saved + (noise.normal_() * reg_noise_std)) 165 | optimizer.zero_grad() 166 | out = net(net_input.type(dtype)) 167 | 168 | 169 | 170 | # training loss 171 | if mask_var is not None: 172 | loss = mse( out * mask_var , img_noisy_var * mask_var ) 173 | elif apply_f: 174 | loss = mse( apply_f(out) , img_noisy_var ) 175 | else: 176 | loss = mse(out, img_noisy_var) 177 | loss.backward() 178 | mse_wrt_noisy[i] = loss.data.cpu().numpy() 179 | if mse_wrt_noisy[i] == np.min(mse_wrt_noisy[:i+1]): 180 | best_net = copy.deepcopy(net) 181 | best_mse_wrt_noisy = mse_wrt_noisy[i] 182 | 183 | # the actual loss 184 | true_loss = mse(Variable(out.data, requires_grad=False), img_clean_var) 185 | mse_wrt_truth[i] = true_loss.data.cpu().numpy() 186 | if i % 10 == 0: 187 | out2 = net(Variable(net_input_saved).type(dtype)) 188 | loss2 = mse(out2, img_clean_var) 189 | print ('Iteration %05d Train loss %f Actual loss %f Actual loss orig %f Noise Energy %f' 190 | % (i, loss.data.item(),true_loss.data.item(),loss2.data.item(),noise_energy.data.item()), '\r', end='') 191 | if plot_output_every and (i % plot_output_every==1): 192 | out3 = net(Variable(net_input_saved).type(dtype)) 193 | ax = plt.figure(figsize=(12,5)) 194 | plt.plot(out3[0,0,:].data.numpy(), '.b') 195 | plt.plot(img_clean_var[0,0,:].data.numpy(), '-r') 196 | plt.show() 197 | optimizer.step() 198 | return mse_wrt_noisy, mse_wrt_truth,net_input_saved, best_net, best_mse_wrt_noisy # Didn't implement case wehere there is noise in signal 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /include/transforms.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) Facebook, Inc. and its affiliates. 3 | 4 | This source code is licensed under the MIT license found in the 5 | LICENSE file in the root directory of this source tree. 6 | """ 7 | 8 | import numpy as np 9 | import torch 10 | 11 | 12 | def to_tensor(data): 13 | """ 14 | Convert numpy array to PyTorch tensor. For complex arrays, the real and imaginary parts 15 | are stacked along the last dimension. 16 | 17 | Args: 18 | data (np.array): Input numpy array 19 | 20 | Returns: 21 | torch.Tensor: PyTorch version of data 22 | """ 23 | if np.iscomplexobj(data): 24 | data = np.stack((data.real, data.imag), axis=-1) 25 | return torch.from_numpy(data) 26 | 27 | 28 | def apply_mask(data, mask_func, seed=None): 29 | """ 30 | Subsample given k-space by multiplying with a mask. 31 | 32 | Args: 33 | data (torch.Tensor): The input k-space data. This should have at least 3 dimensions, where 34 | dimensions -3 and -2 are the spatial dimensions, and the final dimension has size 35 | 2 (for complex values). 36 | mask_func (callable): A function that takes a shape (tuple of ints) and a random 37 | number seed and returns a mask. 38 | seed (int or 1-d array_like, optional): Seed for the random number generator. 39 | 40 | Returns: 41 | (tuple): tuple containing: 42 | masked data (torch.Tensor): Subsampled k-space data 43 | mask (torch.Tensor): The generated mask 44 | """ 45 | shape = np.array(data.shape) 46 | shape[:-3] = 1 47 | mask = mask_func(shape, seed) 48 | return data * mask, mask 49 | 50 | 51 | def fft2(data): 52 | """ 53 | Apply centered 2 dimensional Fast Fourier Transform. 54 | 55 | Args: 56 | data (torch.Tensor): Complex valued input data containing at least 3 dimensions: dimensions 57 | -3 & -2 are spatial dimensions and dimension -1 has size 2. All other dimensions are 58 | assumed to be batch dimensions. 59 | 60 | Returns: 61 | torch.Tensor: The FFT of the input. 62 | """ 63 | assert data.size(-1) == 2 64 | data = ifftshift(data, dim=(-3, -2)) 65 | data = torch.fft(data, 2, normalized=True) 66 | data = fftshift(data, dim=(-3, -2)) 67 | return data 68 | 69 | 70 | def ifft2(data): 71 | """ 72 | Apply centered 2-dimensional Inverse Fast Fourier Transform. 73 | 74 | Args: 75 | data (torch.Tensor): Complex valued input data containing at least 3 dimensions: dimensions 76 | -3 & -2 are spatial dimensions and dimension -1 has size 2. All other dimensions are 77 | assumed to be batch dimensions. 78 | 79 | Returns: 80 | torch.Tensor: The IFFT of the input. 81 | """ 82 | assert data.size(-1) == 2 83 | data = ifftshift(data, dim=(-3, -2)) 84 | data = torch.ifft(data, 2, normalized=True) 85 | data = fftshift(data, dim=(-3, -2)) 86 | return data 87 | 88 | 89 | def complex_abs(data): 90 | """ 91 | Compute the absolute value of a complex valued input tensor. 92 | 93 | Args: 94 | data (torch.Tensor): A complex valued tensor, where the size of the final dimension 95 | should be 2. 96 | 97 | Returns: 98 | torch.Tensor: Absolute value of data 99 | """ 100 | assert data.size(-1) == 2 101 | return (data ** 2).sum(dim=-1).sqrt() 102 | 103 | 104 | def root_sum_of_squares(data, dim=0): 105 | """ 106 | Compute the Root Sum of Squares (RSS) transform along a given dimension of a tensor. 107 | 108 | Args: 109 | data (torch.Tensor): The input tensor 110 | dim (int): The dimensions along which to apply the RSS transform 111 | 112 | Returns: 113 | torch.Tensor: The RSS value 114 | """ 115 | return torch.sqrt((data ** 2).sum(dim)) 116 | 117 | 118 | def center_crop(data, shape): 119 | """ 120 | Apply a center crop to the input real image or batch of real images. 121 | 122 | Args: 123 | data (torch.Tensor): The input tensor to be center cropped. It should have at 124 | least 2 dimensions and the cropping is applied along the last two dimensions. 125 | shape (int, int): The output shape. The shape should be smaller than the 126 | corresponding dimensions of data. 127 | 128 | Returns: 129 | torch.Tensor: The center cropped image 130 | """ 131 | assert 0 < shape[0] <= data.shape[-2] 132 | assert 0 < shape[1] <= data.shape[-1] 133 | w_from = (data.shape[-2] - shape[0]) // 2 134 | h_from = (data.shape[-1] - shape[1]) // 2 135 | w_to = w_from + shape[0] 136 | h_to = h_from + shape[1] 137 | return data[..., w_from:w_to, h_from:h_to] 138 | 139 | 140 | def complex_center_crop(data, shape): 141 | """ 142 | Apply a center crop to the input image or batch of complex images. 143 | 144 | Args: 145 | data (torch.Tensor): The complex input tensor to be center cropped. It should 146 | have at least 3 dimensions and the cropping is applied along dimensions 147 | -3 and -2 and the last dimensions should have a size of 2. 148 | shape (int, int): The output shape. The shape should be smaller than the 149 | corresponding dimensions of data. 150 | 151 | Returns: 152 | torch.Tensor: The center cropped image 153 | """ 154 | assert 0 < shape[0] <= data.shape[-3] 155 | assert 0 < shape[1] <= data.shape[-2] 156 | w_from = (data.shape[-3] - shape[0]) // 2 157 | h_from = (data.shape[-2] - shape[1]) // 2 158 | w_to = w_from + shape[0] 159 | h_to = h_from + shape[1] 160 | return data[..., w_from:w_to, h_from:h_to, :] 161 | 162 | 163 | def normalize(data, mean, stddev, eps=0.): 164 | """ 165 | Normalize the given tensor using: 166 | (data - mean) / (stddev + eps) 167 | 168 | Args: 169 | data (torch.Tensor): Input data to be normalized 170 | mean (float): Mean value 171 | stddev (float): Standard deviation 172 | eps (float): Added to stddev to prevent dividing by zero 173 | 174 | Returns: 175 | torch.Tensor: Normalized tensor 176 | """ 177 | return (data - mean) / (stddev + eps) 178 | 179 | 180 | def normalize_instance(data, eps=0.): 181 | """ 182 | Normalize the given tensor using: 183 | (data - mean) / (stddev + eps) 184 | where mean and stddev are computed from the data itself. 185 | 186 | Args: 187 | data (torch.Tensor): Input data to be normalized 188 | eps (float): Added to stddev to prevent dividing by zero 189 | 190 | Returns: 191 | torch.Tensor: Normalized tensor 192 | """ 193 | mean = data.mean() 194 | std = data.std() 195 | return normalize(data, mean, std, eps), mean, std 196 | 197 | 198 | # Helper functions 199 | 200 | def roll(x, shift, dim): 201 | """ 202 | Similar to np.roll but applies to PyTorch Tensors 203 | """ 204 | if isinstance(shift, (tuple, list)): 205 | assert len(shift) == len(dim) 206 | for s, d in zip(shift, dim): 207 | x = roll(x, s, d) 208 | return x 209 | shift = shift % x.size(dim) 210 | if shift == 0: 211 | return x 212 | left = x.narrow(dim, 0, x.size(dim) - shift) 213 | right = x.narrow(dim, x.size(dim) - shift, shift) 214 | return torch.cat((right, left), dim=dim) 215 | 216 | 217 | def fftshift(x, dim=None): 218 | """ 219 | Similar to np.fft.fftshift but applies to PyTorch Tensors 220 | """ 221 | if dim is None: 222 | dim = tuple(range(x.dim())) 223 | shift = [dim // 2 for dim in x.shape] 224 | elif isinstance(dim, int): 225 | shift = x.shape[dim] // 2 226 | else: 227 | shift = [x.shape[i] // 2 for i in dim] 228 | return roll(x, shift, dim) 229 | 230 | 231 | def ifftshift(x, dim=None): 232 | """ 233 | Similar to np.fft.ifftshift but applies to PyTorch Tensors 234 | """ 235 | if dim is None: 236 | dim = tuple(range(x.dim())) 237 | shift = [(dim + 1) // 2 for dim in x.shape] 238 | elif isinstance(dim, int): 239 | shift = (x.shape[dim] + 1) // 2 240 | else: 241 | shift = [(x.shape[i] + 1) // 2 for i in dim] 242 | return roll(x, shift, dim) 243 | -------------------------------------------------------------------------------- /include/visualize.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from torch.autograd import Variable 3 | import torch 4 | import torch.optim 5 | import numpy as np 6 | from collections import Iterable 7 | 8 | 9 | dtype = torch.cuda.FloatTensor 10 | #dtype = torch.FloatTensor 11 | 12 | def save_np_img(img,filename): 13 | if(img.shape[0] == 1): 14 | plt.imshow(np.clip(img[0],0,1)) 15 | else: 16 | plt.imshow(np.clip(img.transpose(1, 2, 0),0,1)) 17 | plt.axis('off') 18 | plt.savefig(filename, bbox_inches='tight') 19 | plt.close() 20 | 21 | def apply_until(net_input,net,n = 100): 22 | # applies function by funtion of a network 23 | for i,fun in enumerate(net): 24 | if i>=n: 25 | break 26 | if i==0: 27 | out = fun(net_input.type(dtype)) 28 | else: 29 | out = fun(out) 30 | print(i, "last func. applied:", net[i-1]) 31 | if n == 0: 32 | return net_input 33 | else: 34 | return out 35 | 36 | 37 | from math import ceil 38 | 39 | 40 | # given a lists of images as np-arrays, plot them as a row# given 41 | def plot_image_grid(imgs,nrows=10): 42 | ncols = ceil( len(imgs)/nrows ) 43 | nrows = min(nrows,len(imgs)) 44 | fig, axes = plt.subplots(nrows=nrows, ncols=ncols, sharex=True, sharey=True,figsize=(ncols,nrows),squeeze=False) 45 | for i, row in enumerate(axes): 46 | for j, ax in enumerate(row): 47 | ax.imshow(imgs[j*nrows+i], cmap='Greys_r', interpolation='none') 48 | ax.get_xaxis().set_visible(False) 49 | ax.get_yaxis().set_visible(False) 50 | fig.tight_layout(pad=0.1) 51 | return fig 52 | 53 | def save_tensor(out,filename,nrows=8): 54 | imgs = [img for img in out.data.cpu().numpy()[0]] 55 | fig = plot_image_grid(imgs,nrows=nrows) 56 | plt.savefig(filename) 57 | plt.close() 58 | 59 | def plot_kernels(tensor): 60 | if not len(tensor.shape)==4: 61 | raise Exception("assumes a 4D tensor") 62 | num_kernels = tensor.shape[0] 63 | fig = plt.figure(figsize=(tensor.shape[0],tensor.shape[1])) 64 | for i in range(tensor.shape[0]): 65 | for j in range(tensor.shape[1]): 66 | ax1 = fig.add_subplot(tensor.shape[0],tensor.shape[1],1+i*tensor.shape[0]+j) 67 | ax1.imshow(tensor[i][j]) 68 | ax1.axis('off') 69 | ax1.set_xticklabels([]) 70 | ax1.set_yticklabels([]) 71 | 72 | plt.subplots_adjust(wspace=0.1, hspace=0.1) 73 | plt.show() 74 | 75 | def plot_tensor(out,nrows=8): 76 | imgs = [img for img in out.data.cpu().numpy()[0]] 77 | fig = plot_image_grid(imgs,nrows=nrows) 78 | plt.show() 79 | -------------------------------------------------------------------------------- /include/wavelet.py: -------------------------------------------------------------------------------- 1 | #import matplotlib.pyplot as plt 2 | import numpy as np 3 | import numbers 4 | import pywt 5 | import scipy 6 | import skimage.color as color 7 | from skimage.restoration import (denoise_wavelet, estimate_sigma) 8 | from skimage import data, img_as_float 9 | from skimage.util import random_noise 10 | from skimage.measure import compare_psnr 11 | from include import * 12 | 13 | def _wavelet_threshold(image, wavelet, ncoeff = None, threshold=None, mode='soft', wavelet_levels=None): 14 | 15 | wavelet = pywt.Wavelet(wavelet) 16 | 17 | # original_extent is used to workaround PyWavelets issue #80 18 | # odd-sized input results in an image with 1 extra sample after waverecn 19 | original_extent = [slice(s) for s in image.shape] 20 | 21 | # Determine the number of wavelet decomposition levels 22 | if wavelet_levels is None: 23 | # Determine the maximum number of possible levels for image 24 | dlen = wavelet.dec_len 25 | wavelet_levels = np.min( 26 | [pywt.dwt_max_level(s, dlen) for s in image.shape]) 27 | 28 | # Skip coarsest wavelet scales (see Notes in docstring). 29 | wavelet_levels = max(wavelet_levels - 3, 1) 30 | 31 | coeffs = pywt.wavedecn(image, wavelet=wavelet, level=wavelet_levels) 32 | # Detail coefficients at each decomposition level 33 | dcoeffs = coeffs[1:] 34 | 35 | a = [] 36 | for level in dcoeffs: 37 | for key in level: 38 | a += [np.ndarray.flatten(level[key])] 39 | a = np.concatenate(a) 40 | a = np.sort( np.abs(a) ) 41 | 42 | sh = coeffs[0].shape 43 | basecoeffs = sh[0]*sh[1] 44 | threshold = a[- (ncoeff - basecoeffs)] 45 | 46 | # A single threshold for all coefficient arrays 47 | denoised_detail = [{key: pywt.threshold(level[key],value=threshold, 48 | mode=mode) for key in level} for level in dcoeffs] 49 | 50 | denoised_coeffs = [coeffs[0]] + denoised_detail 51 | return pywt.waverecn(denoised_coeffs, wavelet)[original_extent] 52 | 53 | 54 | def denoise_wavelet(image, ncoeff=None, wavelet='db1', mode='hard', 55 | wavelet_levels=None, multichannel=False, 56 | convert2ycbcr=False): 57 | 58 | image = img_as_float(image) 59 | 60 | 61 | if multichannel: 62 | if convert2ycbcr: 63 | out = color.rgb2ycbcr(image) 64 | for i in range(3): 65 | # renormalizing this color channel to live in [0, 1] 66 | min, max = out[..., i].min(), out[..., i].max() 67 | channel = out[..., i] - min 68 | channel /= max - min 69 | out[..., i] = denoise_wavelet(channel, wavelet=wavelet,ncoeff=ncoeff, 70 | mode=mode, 71 | wavelet_levels=wavelet_levels) 72 | 73 | out[..., i] = out[..., i] * (max - min) 74 | out[..., i] += min 75 | out = color.ycbcr2rgb(out) 76 | else: 77 | out = np.empty_like(image) 78 | for c in range(image.shape[-1]): 79 | out[..., c] = _wavelet_threshold(image[..., c],ncoeff=ncoeff, 80 | wavelet=wavelet, mode=mode, 81 | wavelet_levels=wavelet_levels) 82 | else: 83 | out = _wavelet_threshold(image, wavelet=wavelet, mode=mode,ncoeff=ncoeff, 84 | wavelet_levels=wavelet_levels) 85 | 86 | clip_range = (-1, 1) if image.min() < 0 else (0, 1) 87 | return np.clip(out, *clip_range) 88 | 89 | 90 | -------------------------------------------------------------------------------- /linear_least_squares_selective_fitting_warmup.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Linear regression fitting noise versus structure\n", 8 | "\n", 9 | "Let \n", 10 | "$$\n", 11 | "y = y^\\ast + e, \\quad y^\\ast = X \\theta^\\ast\n", 12 | "$$\n", 13 | "where $X$ is a fat matrix. Consider optimizing the least squares objective \n", 14 | "$$\n", 15 | "\\frac{1}{2}\n", 16 | "\\| X \\theta - y \\|^2.\n", 17 | "$$\n", 18 | "The gradient is \n", 19 | "$$\n", 20 | "\\nabla f(\\theta) = X^T X \\theta - X^T y.\n", 21 | "$$\n", 22 | "We have\n", 23 | "$$\n", 24 | "\\begin{align}\n", 25 | "\\theta_{k+1} - \\theta^\\ast\n", 26 | "&=\n", 27 | "\\theta_k - \\alpha(X^T X \\theta - X^T y) - \\theta^\\ast \\\\\n", 28 | "&=\n", 29 | "\\theta_k - \\alpha(X^T X \\theta - X^T X \\theta^\\ast - X^T e) - \\theta^\\ast \\\\\n", 30 | "&=\n", 31 | "(I - \\alpha X^T X) (\\theta_k - \\theta^\\ast) + \\alpha X^T e\n", 32 | "\\end{align}\n", 33 | "$$\n", 34 | "The difference in terms of residual therefore becomes\n", 35 | "$$\n", 36 | "X\\theta_{k+1} - X\\theta^\\ast\n", 37 | "=\n", 38 | "(I - \\alpha XX^T) (X\\theta_k - X\\theta^\\ast) + \\alpha X X^T e \\\\\n", 39 | "$$" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 1, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "import matplotlib.pyplot as plt\n", 49 | "#%matplotlib notebook\n", 50 | "#import matplotlib.pyplot as plt\n", 51 | "from numpy import *\n", 52 | "import numpy as np" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 2, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "def gradient_descent(A,b,niter = 10000,ytarget=None,stepsize=None):\n", 62 | " \n", 63 | " def f(x):\n", 64 | " return linalg.norm(dot(A,x) - b)**2\n", 65 | "\n", 66 | " def gradf(x):\n", 67 | " return 0.5*(dot(Q,x) - dot(b,A))\n", 68 | "\n", 69 | " Q = dot(A.T,A)\n", 70 | " eigenvalues = linalg.eigvals(Q)\n", 71 | " M = max(eigenvalues)\n", 72 | " m = min(eigenvalues)\n", 73 | "\n", 74 | " xopt = dot( linalg.inv( dot(A.T,A) ), dot( A.T , b ) ) \n", 75 | " \n", 76 | " print(\"optimal errors: \", linalg.norm( y - dot(A,xopt) ), linalg.norm( ytarget - dot(A,xopt) ) )\n", 77 | " \n", 78 | " if stepsize==None:\n", 79 | " stepsize = 2/(M+m)\n", 80 | " \n", 81 | " print(\"minimal and maximal eigenvalues: \", m, M)\n", 82 | " print(\"stepsize: \", stepsize)\n", 83 | " \n", 84 | " residuals = []\n", 85 | " gradients = []\n", 86 | " residual_target = []\n", 87 | " distances = []\n", 88 | " xk = zeros(n) #random.randn(n) # random initializer\n", 89 | " for k in range(niter):\n", 90 | " xk = xk - stepsize*gradf(xk)\n", 91 | " residuals.append( linalg.norm( y - dot(A,xk) ) )\n", 92 | " gradients.append( linalg.norm(gradf(xk)) )\n", 93 | " residual_target.append( linalg.norm( ytarget - dot(A,xk) ) )\n", 94 | " distances.append( linalg.norm(xk) )\n", 95 | " return array(residuals), array(gradients), array(residual_target),array(distances)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 3, 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "data": { 105 | "image/png": "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\n", 106 | "text/plain": [ 107 | "" 108 | ] 109 | }, 110 | "metadata": {}, 111 | "output_type": "display_data" 112 | }, 113 | { 114 | "name": "stdout", 115 | "output_type": "stream", 116 | "text": [ 117 | "1.5072614647736406 1.0000000000000009 1.0 0.13591856159578958\n", 118 | "optimal errors: 0.5198674927030573 1.052776280346125\n", 119 | "minimal and maximal eigenvalues: 3.3195518835241045e-18 1.0000000000000047\n", 120 | "stepsize: 0.25\n", 121 | "logarithmic\n" 122 | ] 123 | }, 124 | { 125 | "data": { 126 | "image/png": "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\n", 127 | "text/plain": [ 128 | "" 129 | ] 130 | }, 131 | "metadata": {}, 132 | "output_type": "display_data" 133 | }, 134 | { 135 | "data": { 136 | "image/png": "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\n", 137 | "text/plain": [ 138 | "" 139 | ] 140 | }, 141 | "metadata": {}, 142 | "output_type": "display_data" 143 | }, 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "non logarithmic\n" 149 | ] 150 | }, 151 | { 152 | "data": { 153 | "image/png": "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\n", 154 | "text/plain": [ 155 | "" 156 | ] 157 | }, 158 | "metadata": {}, 159 | "output_type": "display_data" 160 | }, 161 | { 162 | "data": { 163 | "image/png": "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\n", 164 | "text/plain": [ 165 | "" 166 | ] 167 | }, 168 | "metadata": {}, 169 | "output_type": "display_data" 170 | } 171 | ], 172 | "source": [ 173 | "# generate a problem instance\n", 174 | "n = 100\n", 175 | "A = random.randn(n,n)\n", 176 | "U,S,VT = linalg.svd(A)\n", 177 | "\n", 178 | "ytarget = U[:, int(0)]\n", 179 | "perturbation = random.randn(n) #U[:,n-10]\n", 180 | "perturbation = perturbation/linalg.norm(perturbation)\n", 181 | "#perturbation = U[:,n-10]\n", 182 | "\n", 183 | "newS = np.array([s**3 for s in S])\n", 184 | "newS = newS/np.max(newS)\n", 185 | "plt.plot(newS)\n", 186 | "plt.title(\"spectrum\")\n", 187 | "plt.show()\n", 188 | "\n", 189 | "S = np.diag(newS)\n", 190 | "A = U @ S @ VT\n", 191 | "\n", 192 | "y = ytarget + perturbation\n", 193 | "\n", 194 | "print(linalg.norm(y), linalg.norm(ytarget), linalg.norm(perturbation), dot(ytarget,perturbation) )\n", 195 | "\n", 196 | "steps = 1000\n", 197 | "residuals,gradients,residual_target,distances = gradient_descent(A,y,niter=steps,ytarget=ytarget,stepsize=0.25)\n", 198 | "\n", 199 | "print(\"logarithmic\")\n", 200 | "plt.plot( log(residuals) )\n", 201 | "plt.show()\n", 202 | "plt.plot( log(residual_target) )\n", 203 | "plt.show()\n", 204 | "\n", 205 | "print(\"non logarithmic\")\n", 206 | "plt.plot( residuals )\n", 207 | "plt.show()\n", 208 | "plt.plot( residual_target )\n", 209 | "plt.show()\n", 210 | "\n", 211 | "\n", 212 | "ks = np.array( [i for i in range(steps)] )\n", 213 | "np.savetxt(\"ls_residuals.dat\", np.vstack([ ks ,np.array(residuals),np.array(residual_target) ] ).T , delimiter=\"\\t\")\n", 214 | "\n", 215 | "ns = np.array( [i for i in range(n)] )\n", 216 | "np.savetxt(\"ls_spectrum.dat\", np.vstack([ ns ,np.array(newS)] ).T , delimiter=\"\\t\")\n" 217 | ] 218 | } 219 | ], 220 | "metadata": { 221 | "kernelspec": { 222 | "display_name": "Python 3", 223 | "language": "python", 224 | "name": "python3" 225 | }, 226 | "language_info": { 227 | "codemirror_mode": { 228 | "name": "ipython", 229 | "version": 3 230 | }, 231 | "file_extension": ".py", 232 | "mimetype": "text/x-python", 233 | "name": "python", 234 | "nbconvert_exporter": "python", 235 | "pygments_lexer": "ipython3", 236 | "version": "3.6.4" 237 | } 238 | }, 239 | "nbformat": 4, 240 | "nbformat_minor": 2 241 | } 242 | -------------------------------------------------------------------------------- /test_data/astronaut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/test_data/astronaut.png -------------------------------------------------------------------------------- /test_data/phantom256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLI-lab/overparameterized_convolutional_generators/ef2fae85768f1954dbd1ead75b9ba8e214c13230/test_data/phantom256.png -------------------------------------------------------------------------------- /visualization_linear_approximation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Approximation with a linear model\n", 8 | "\n", 9 | "Here, we visualy demonstrate that an overparameterized network can be well approximated around a random inital point with a linearized model." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import matplotlib.pyplot as plt\n", 19 | "#%matplotlib notebook\n", 20 | "#import matplotlib.pyplot as plt\n", 21 | "from numpy import *\n", 22 | "import numpy as np" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 46, 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "image/png": "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\n", 33 | "text/plain": [ 34 | "" 35 | ] 36 | }, 37 | "metadata": {}, 38 | "output_type": "display_data" 39 | } 40 | ], 41 | "source": [ 42 | "# generator network\n", 43 | "\n", 44 | "n = 10\n", 45 | "k = 100 \n", 46 | "v = np.ones(k)\n", 47 | "v[:int(k/2)] = -np.ones( int(k/2) )\n", 48 | "v = v/np.sqrt(k)\n", 49 | "U = np.eye(n)\n", 50 | "\n", 51 | "def G(C): \n", 52 | " return np.maximum( U @ C , 0 ) @ v\n", 53 | "\n", 54 | "# Jaccobian\n", 55 | "def J(C):\n", 56 | " return np.vstack( [ve * (U.T @ np.diag(c > 0)) for ve,c in zip(v,C.T)] ).T\n", 57 | " \n", 58 | "# original loss\n", 59 | "def loss(y,C):\n", 60 | " return np.linalg.norm( y - G(C) )**2\n", 61 | "\n", 62 | "# associated linearized loss\n", 63 | "def losslin(y,C,C0):\n", 64 | " return np.linalg.norm( G(C0) + J(C0) @ np.hstack((C-C0).T) - y )**2\n", 65 | "\n", 66 | "\n", 67 | "\n", 68 | "y = np.random.randn(n)\n", 69 | "\n", 70 | "# initial vector\n", 71 | "C0 = np.random.randn(n,k)\n", 72 | "\n", 73 | "# random direction\n", 74 | "Crand = np.random.randn(n,k)\n", 75 | "\n", 76 | "R = 3\n", 77 | "epsilons = np.linspace(-R,R,100)\n", 78 | "\n", 79 | "\n", 80 | "losses = [loss(y, C0+ep*Crand) for ep in epsilons]\n", 81 | "linlosses = [losslin(y, C0+ep*Crand,C0) for ep in epsilons]\n", 82 | "\n", 83 | "\n", 84 | "plt.plot(losses)\n", 85 | "plt.plot(linlosses)\n", 86 | "plt.show()" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [] 95 | } 96 | ], 97 | "metadata": { 98 | "kernelspec": { 99 | "display_name": "Python 3", 100 | "language": "python", 101 | "name": "python3" 102 | }, 103 | "language_info": { 104 | "codemirror_mode": { 105 | "name": "ipython", 106 | "version": 3 107 | }, 108 | "file_extension": ".py", 109 | "mimetype": "text/x-python", 110 | "name": "python", 111 | "nbconvert_exporter": "python", 112 | "pygments_lexer": "ipython3", 113 | "version": "3.6.4" 114 | } 115 | }, 116 | "nbformat": 4, 117 | "nbformat_minor": 2 118 | } 119 | --------------------------------------------------------------------------------