Skip to content

Instantly share code, notes, and snippets.

@ES-Alexander
Last active August 13, 2022 03:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ES-Alexander/1cd2f32d3bd379faffd3b881c0e3ba47 to your computer and use it in GitHub Desktop.
Save ES-Alexander/1cd2f32d3bd379faffd3b881c0e3ba47 to your computer and use it in GitHub Desktop.
Clean up a drawing and add a transparent background
'''
Cleans up a drawing, and creates grey, blue, and red variants.
Blue and red are saved with a transparent background.
Grey is saved as single channel.
Author: ES-Alexander
License: MIT (a.k.a. free use, but not my problem if it doesn't work for you)
Created in response to:
https://www.reddit.com/r/opencv/comments/wmyuyh/question_remove_paper_background_from_sketches/
'''
from pathlib import Path
import numpy as np
import cv2
UINT8_MAX = 2**8 - 1
# must be odd - use a bigger number for larger / more rough textures, but also adds more blur
BACKGROUND_TEXTURE_SIZE = 3
# value between 0 and 1, for the saturation of the variants
SATURATION = 0.7
path = Path('path/to/image.jpg')
image = cv2.imread(str(path))
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(grey, (BACKGROUND_TEXTURE_SIZE,BACKGROUND_TEXTURE_SIZE), 0)
# threshold, and choose the least lossy result
tri, thresh_tri = cv2.threshold(blur, 0, UINT8_MAX,
cv2.THRESH_TRUNC+cv2.THRESH_TRIANGLE)
otsu, thresh_otsu = cv2.threshold(blur, 0, UINT8_MAX,
cv2.THRESH_TRUNC+cv2.THRESH_OTSU)
thresh = thresh_otsu if otsu > tri else thresh_tri
norm = cv2.normalize(thresh, None, 0, UINT8_MAX, cv2.NORM_MINMAX)
opacity = UINT8_MAX - norm
# light variants
full = np.ones_like(opacity) * UINT8_MAX
bright = np.uint8(full * SATURATION)
light_norm = np.uint8(norm * SATURATION)
light_blue = cv2.merge((bright, light_norm, light_norm, opacity))
light_red = cv2.merge((light_norm, light_norm, bright, opacity))
# dark variants
black = np.zeros_like(opacity)
dark = np.uint8(opacity * SATURATION)
dark_blue = cv2.merge((dark, black, black, opacity))
dark_red = cv2.merge((black, black, dark, opacity))
image_display = cv2.merge(list(cv2.split(image))+[full])
grey_display = cv2.merge([norm]*3+[full])
display = cv2.vconcat((
cv2.hconcat((image_display, grey_display)),
cv2.hconcat((light_blue, light_red)),
cv2.hconcat((dark_blue, dark_red)),
))
cv2.imshow('display', display)
cv2.waitKey(0)
cv2.imwrite('results.png', display)
output_path = path.with_stem(path.stem+'_processed')
cv2.imwrite(str(output_path.with_stem(path.stem+'_grey')), norm)
cv2.imwrite(str(path.with_stem(path.stem+'_light_blue').with_suffix('.png')), light_blue)
cv2.imwrite(str(path.with_stem(path.stem+'_light_red').with_suffix('.png')), light_red)
cv2.imwrite(str(path.with_stem(path.stem+'_dark_blue').with_suffix('.png')), dark_blue)
cv2.imwrite(str(path.with_stem(path.stem+'_dark_red').with_suffix('.png')), dark_red)
@ES-Alexander
Copy link
Author

ES-Alexander commented Aug 13, 2022

Indicative results

Good performance

results

OK performance (uneven lighting + large variation in pencil stroke darkness)

results

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment