2

I'm making a toy code to detect and fix scanline glitches on images.

I pick Lenna image from Wikipedia, and add horizontal scanline glitches, by multiplying the rows with random values between 0.9 and 1.1

Adding scanline glitches

To identify the horizontal scanline glitch, I calculate a moving average for each column, which should be close to the original value, and get the ratio between the moving average, and the glitchy value.

Because each row has the same glitch percentage, I collect the median ratio of each row, and multiply the glitchy row by that ratio to make the correction.

The result looks close to the original:

Original and corrected image

But when I calculate the difference between the corrected image and the original, I get color differences up to 255.

This doesn't makes sense. What I'm doing wrong?

Animated difference

import matplotlib.pyplot as plt
import cv2
import numpy as np

URL = "https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png"

def verticalMovingAvg(imgOrig, kernel_size=30):
    '''Returns the vertical moving average of an image,
    for each column, using a kernel of size kernel_size.
    
    Also returns, for each row, the median of the 
    ratio moving average/image.'''
    verticMeanImg=imgOrig*0
    halfSize = kernel_size // 2
    for col in range(imgOrig.shape[1]):
        for row in range(imgOrig.shape[0]):
            verticMeanImg[row, col] = np.mean(imgOrig[max(0,row-halfSize):min(imgOrig.shape[0],row+halfSize), col],axis=0)
    return verticMeanImg, np.median(verticMeanImg/imgOrig, axis=1)
                
def downloadImage(URL):
    '''Downloads the image on the URL, and convers to cv2 BGR format'''
    from io import BytesIO
    from PIL import Image as PIL_Image
    import requests
    response = requests.get(URL)
    image = PIL_Image.open(BytesIO(response.content))
    return cv2.cvtColor(np.array(image), cv2.COLOR_BGR2RGB)

def createScanlineGlithcedImage(img):
    '''Returns an image with the size of the original image,
        but with an horizontal scanline glith effect
        on rows.'''
    # Scanline deviation for each row
    rowDeviation = np.random.uniform(.9, 1.1, (img.shape[0], 1))
    
    #Multiplies each row by the deviation
    imgGlitched = img*0
    for row in range(img.shape[0]):
        imgGlitched[row, :, :] = np.clip(img[row, :, :]*rowDeviation[row], 0, 255)

    cv2.imshow('Glitched image', imgGlitched)
    cv2.waitKey(10)
    return imgGlitched, rowDeviation

img = downloadImage(URL)
cv2.imshow('Original image', img)
cv2.waitKey(10)

imgGlitched, rowDeviation = createScanlineGlithcedImage(img)

#Calculate correction
verticMobileAvgImage , medianCorrection= verticalMovingAvg(imgGlitched)

cv2.imshow('Vertical moving average', verticMobileAvgImage)
cv2.waitKey(10)

#Correct image
imgCorrected = imgGlitched*0
for row in range(imgGlitched.shape[0]):
    imgCorrected[row, :, :] = np.clip(imgGlitched[row, :, :]*medianCorrection[row], 0, 255)

cv2.imshow("image corrected", imgCorrected)
cv2.waitKey(10)

#Plot error
# plt.plot(np.mean(imgCorrected-img, axis=1))
plt.imshow(imgCorrected-img)
plt.title("Error = image corrected-image original")
plt.show()

#animation of original and corrected
while True:
    cv2.imshow("Image comparison", img)
    cv2.waitKey(100)
    cv2.imshow("Image comparison", imgCorrected)
    cv2.waitKey(100)
Colim
  • 1,283
  • 3
  • 11
  • 4
    You are doing arithmetic on 8-bit unsigned integers. What is 180-190 in unsigned arithmetic? – Cris Luengo Jul 02 '22 at 06:18
  • 1
    those aren't differences of 255, those are differences of -1, which wraps around. you calculated with unsigned integers (uint8). cast to int16 or so before subtracting. – Christoph Rackwitz Jul 02 '22 at 09:18

1 Answers1

0

As Cris Luengo and Christoph Rackwitz pointed, this is a problem of math overflow of uint8, so the solution is to convert to signed type

instead of

plt.imshow(imgCorrected-img)

it can be done

difference=imgCorrected.astype(int)-img.astype(int)
plt.imshow((np.abs(difference)*255/np.max(difference)).astype('uint8'))

It displays negative errors, at the cost of losing information about the sign.

enter image description here

Colim
  • 1,283
  • 3
  • 11