MCMC Applications: Denoising Images#

References and credits

Image to Text#

import numpy as np

url = "https://www.cosy.sbg.ac.at/~pmeerw/Watermarking/lena_gray.gif"

from urllib.request import urlopen
from PIL import Image

imgX = Image.open(urlopen(url))
imgX = imgX.convert('L')

def image_to_text(img, outputfile="whatever.txt"):

  img = np.asarray(img)
  img = 2*(img > 128).astype(int)-1

  #file = open(sys.argv[2], "w")
  file = open(outputfile, "w")

  txt = list(img)
  for i in txt:
      for j in i:
          file.write(str(j) + " ")
      file.write('\n')
  file.close()

  return img 

outputfile = "outO.txt" 
img_new = image_to_text(imgX,outputfile)

imgX
_images/mod3_part3_extra_MCMC_denoise_2_0.png

Make noise#

import sys
import numpy as np


def make_noise(txtfile, outputfile):

  #img = np.loadtxt(sys.argv[1])
  #img = np.loadtxt("outO.txt")
  img = np.loadtxt(txtfile)

  #pi = float(sys.argv[2])
  pi = float(0.15)

  I,J = img.shape
  flip = np.random.rand(I,J) < pi
  X = img * (-1)**flip

  #file = open(sys.argv[3], "w")
  file = open(outputfile, "w")

  txt = list(X)
  for i in txt:
      for j in i:
          file.write(str(int(j)) + " ")
      file.write('\n')
  file.close()

  return X,I,J

txtfile = "outO.txt"
outputfile = "outN.txt"
X,I,J = make_noise(txtfile,outputfile)

Text to image#

import sys
import numpy as np
import warnings
warnings.filterwarnings("ignore")

from matplotlib import pyplot as plt
import imageio

def text_to_image(filepath,outimage):

  img = np.loadtxt(filepath)

  #imageio.imwrite(sys.argv[2], img)
  imageio.imwrite(outimage, img)

  plt.imshow(img,cmap='gray',vmin=-1,vmax=1)
  plt.show()

  I,J = img.shape
  return I,J

filepath = "outN.txt"
outimage = "outI.png"
In,Jn = text_to_image(filepath,outimage) 
WARNING:imageio:Lossy conversion from float64 to uint8. Range [-1.0, 1.0]. Convert image to uint8 prior to saving to suppress this warning.
_images/mod3_part3_extra_MCMC_denoise_6_1.png

Image Denoising#

We will sample a \(Z\) from the posterior distribution. Hopefully, it will appear as denoised.

beta, pi  = 0.8, 0.15 # for arbitrary parameters that reflects our prior belief
gamma = 0.5*np.log((1-pi)/pi)

T = 500000 # sufficient number of steps to converge stationary distribution
Z = X.copy() # Initialize Z from X 
for t in range(T):
    if(t%10000==0):
      #print("...{:}".format(t))
      print('\r', 'Iteration', t, end='')
    i,j = np.random.choice(I), np.random.choice(J) # randomly choose a coordinate (i,j)
    
    # Calculate acceptance propability
    # We don't take exponential. Instead, we take the log of random number. This is more stable.
    delta_E = -2*gamma*X[i,j]*Z[i,j] -2*beta*Z[i,j]*(np.sum(Z[max(i-1,0):i+2,max(j-1,0):j+2]) - Z[i,j])
    
    # We also don't deal with minimum function for acceptance probability
    #   because we don't need to cast it to 1 if it is higher.
    
    # Flip the pixel if accepted
    if np.log(np.random.rand()) < delta_E:
        Z[i,j] = - Z[i,j] # Update the image
 Iteration 490000

Result#

fig, ax = plt.subplots(1,3, figsize=(16,8))
ax[0].imshow(img_new,cmap='gray',vmin=-1,vmax=1)
ax[0].set_title("Original", fontsize=16)
ax[1].imshow(X,cmap='gray',vmin=-1,vmax=1)
ax[1].set_title("Noisy", fontsize=16)
ax[2].imshow(Z,cmap='gray',vmin=-1,vmax=1)
ax[2].set_title("Denoised", fontsize=16)
plt.show()
_images/mod3_part3_extra_MCMC_denoise_11_0.png

Apply to Medical Imaging#

#url2 = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjnOaoY1-_poLbinbtmsa51yvFC40OfVJOEKEvekj0svNFxX8l5vEst5dkf3veORPK3Gg&usqp=CAU"
url2 = "https://raw.githubusercontent.com/cfteach/brds/main/datasets/brain_image.png" 

imgX2 = Image.open(urlopen(url2))
imgX2 = imgX2.convert('L')

imgX2


outputfile2 = "out_brain.txt" 
img_new2 = image_to_text(imgX2,outputfile2)

outimage2 = "out_brain.png"
I,J = text_to_image(outputfile2,outimage2) 
WARNING:imageio:Lossy conversion from float64 to uint8. Range [-1.0, 1.0]. Convert image to uint8 prior to saving to suppress this warning.
_images/mod3_part3_extra_MCMC_denoise_13_1.png
beta, pi  = 0.8, 0.15 # for arbitrary parameters that reflects our prior belief
gamma = 0.5*np.log((1-pi)/pi)

T = 5000000 # sufficient number of steps to converge stationary distribution
img = np.loadtxt(outputfile2)
X = img 
Z = X.copy() # Initialize Z from X 

for t in range(T):
    if(t%10000==0):
      #print("...{:}".format(t))
      print('\r', 'Iteration', t, end='')
    i,j = np.random.choice(I), np.random.choice(J) # randomly choose a coordinate (i,j)
    
    # Calculate acceptance propability
    # We don't take exponential. Instead, we take the log of random number. This is more stable.
    delta_E = -2*gamma*X[i,j]*Z[i,j] -2*beta*Z[i,j]*(np.sum(Z[max(i-1,0):i+2,max(j-1,0):j+2]) - Z[i,j])
    
    # We also don't deal with minimum function for acceptance probability
    #   because we don't need to cast it to 1 if it is higher.
    
    # Flip the pixel if accepted
    if np.log(np.random.rand()) < delta_E:
        Z[i,j] = - Z[i,j] # Update the image
 Iteration 4990000
fig, ax = plt.subplots(1,2, figsize=(16,8))
ax[0].imshow(img,cmap='gray',vmin=-1,vmax=1)
ax[0].set_title("Original", fontsize=16)
ax[1].imshow(Z,cmap='gray',vmin=-1,vmax=1)
ax[1].set_title("Denoised", fontsize=16)
plt.show()
_images/mod3_part3_extra_MCMC_denoise_16_0.png
txtfile = "out_brain.txt"
outputfile = "out_brain_noisy.txt"
X_n,I_N,J_n = make_noise(txtfile,outputfile)
filepath = "out_brain_noisy.txt"
outimage = "out_brain_noisy.png"
I_n,J_n = text_to_image(filepath,outimage) 
WARNING:imageio:Lossy conversion from float64 to uint8. Range [-1.0, 1.0]. Convert image to uint8 prior to saving to suppress this warning.
_images/mod3_part3_extra_MCMC_denoise_18_1.png
beta, pi  = 0.8, 0.15 # for arbitrary parameters that reflects our prior belief
gamma = 0.5*np.log((1-pi)/pi)

T = 500000 # sufficient number of steps to converge stationary distribution
img = np.loadtxt(outputfile)
X = img 
Z = X.copy() # Initialize Z from X 

for t in range(T):
    if(t%10000==0):
      #print("...{:}".format(t))
      print('\r', 'Iteration', t, end='')
    i,j = np.random.choice(I_n), np.random.choice(J_n) # randomly choose a coordinate (i,j)
    
    # Calculate acceptance propability
    # We don't take exponential. Instead, we take the log of random number. This is more stable.
    delta_E = -2*gamma*X[i,j]*Z[i,j] -2*beta*Z[i,j]*(np.sum(Z[max(i-1,0):i+2,max(j-1,0):j+2]) - Z[i,j])
    
    # We also don't deal with minimum function for acceptance probability
    #   because we don't need to cast it to 1 if it is higher.
    
    # Flip the pixel if accepted
    if np.log(np.random.rand()) < delta_E:
        Z[i,j] = - Z[i,j] # Update the image
 Iteration 490000
fig, ax = plt.subplots(1,3, figsize=(16,8))
ax[0].imshow(np.loadtxt(outputfile2),cmap='gray',vmin=-1,vmax=1)
ax[0].set_title("Original", fontsize=16)
ax[1].imshow(X,cmap='gray',vmin=-1,vmax=1)
ax[1].set_title("Noisy", fontsize=16)
ax[2].imshow(Z,cmap='gray',vmin=-1,vmax=1)
ax[2].set_title("Denoised", fontsize=16)
plt.show()
_images/mod3_part3_extra_MCMC_denoise_21_0.png