Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save typoman/764606b11eff4b5cc13ad2d15082477b to your computer and use it in GitHub Desktop.
Save typoman/764606b11eff4b5cc13ad2d15082477b to your computer and use it in GitHub Desktop.
This python script is written as a part of research for contextual spacing tools to ease extracting letterform shapes from manuscripts
import cv2
import numpy as np
import os
from pathlib import Path
import argparse
"""
Usage:
Install prerequisite python packages:
pip3 install numpy
pip3 install opencv-python
Run the script:
1- cd to the folder that contains the images opened in Mac os Finder:
cd "$(osascript -e 'tell application "Finder" to get the POSIX path of (target of front Finder window as alias)')"
2- python3 'path/to/extract-contours-from-image.py'
"""
expand_2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2))
def extractAndSaveContoursFromImage(imagePath, minImageWidth=50, minImageHeight=50, minImageSize=1000,
cropmargin=10, expand_contours=3):
fpo = Path(imagePath)
fileName = fpo.stem
fileRoot = fpo.parent
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(
gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
thresh = cv2.dilate(thresh, expand_2, iterations=1) # connects more shapes that are closer to another (like sarksh kaf)
output_folder = f"{fileRoot}/contours"
os.makedirs(output_folder, exist_ok=True)
output_path = f"{output_folder}/threshold.jpg"
cv2.imwrite(output_path, thresh) # debug mask
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
expand_3 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (expand_contours, expand_contours))
for i, c in enumerate(cnts):
x, y, w, h = cv2.boundingRect(c)
if w < minImageWidth or h < minImageWidth or w * h < minImageSize:
continue
# increase w and h first to account for cropmargin
h = h + (cropmargin * 2)
w = w + (cropmargin * 2)
x = max(x - cropmargin, 0)
y = max(y - cropmargin, 0)
cropped = gray[y: y + h, x: x + w]
mask_image = np.ones((h, w, 3), np.uint8) * 0
# translate contours by -(x,y) to move it into the top-left direction
c = c - [x, y]
# draw contour on mask_image and expand (grow) the contour edge
cv2.drawContours(mask_image, [c], -1, (255, 255, 255), thickness=cv2.FILLED)
mask_image = cv2.dilate(mask_image, expand_3, iterations=3) # expands the contours
mask_image = cv2.GaussianBlur(mask_image, (expand_contours, expand_contours), 0)
mask_image = cv2.cvtColor(mask_image, cv2.COLOR_BGR2GRAY)
mask_image = cv2.bitwise_not(mask_image)
result = cv2.add(mask_image, cropped)
output_path = os.path.join(output_folder, f'm_{fileName}_{i}.jpg')
cv2.imwrite(output_path, result)
def main(args=None):
if args is None:
args = sys.argv[1:]
parser = argparse.ArgumentParser(
description='This script extracts all the contuors from images in a dir. It will crop the contours so they will all have 3 extra pixels around them. You can use it to extract glyphs from a document.')
parser.add_argument('Images Folder', nargs='?',
help='Path to the folder that contains the images.', default=os.getcwd())
args = parser.parse_args(args)
# access positional argument using this syntax
input_dir = vars(args)['Images Folder']
for fileName in os.listdir(input_dir):
if fileName.endswith((".jpg", ".jpeg")):
extractAndSaveContoursFromImage(os.path.abspath(
os.path.join(input_dir, fileName)), 10, 10, 100)
if __name__ == "__main__":
import sys
sys.exit(main())
@typoman
Copy link
Author

typoman commented Jul 8, 2024

The research for contextual spacing fonts was partially funded by the stimuleringsfonds.

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