Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 03af4fd

Browse files
Add autocrop as a standalone
1 parent aeac256 commit 03af4fd

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

‎autocrop.py‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from imageio import imread, imsave
2+
from skimage.measure import moments
3+
from skimage.filters import threshold_otsu, gaussian
4+
from skimage.color import rgb2gray
5+
from skimage.morphology import remove_small_objects
6+
import numpy as np
7+
import matplotlib.pyplot as plt
8+
import argparse
9+
10+
11+
def _parseargs():
12+
parser = argparse.ArgumentParser(
13+
description="Autocrop image with obvious frontground and background\n\n",
14+
epilog="Developed by Huy Nguyen, Gruebele-Lyding Groups\n"
15+
"University of Illinois at Urbana-Champaign\n",
16+
)
17+
parser.add_argument(
18+
"input", nargs="*", type=str, help="Put in your images"
19+
)
20+
parser.add_argument(
21+
"--convert-gray",
22+
"-g",
23+
action="store_true",
24+
help="Whether to convert to gray and save",
25+
)
26+
parser.add_argument(
27+
"--minsize",
28+
"-m",
29+
type=int,
30+
help="minsize in pixels of objects",
31+
default=30,
32+
)
33+
parser.add_argument(
34+
"--extra-space", "-x", type=float, help="extra rim space", default=0.2
35+
)
36+
args = parser.parse_args()
37+
return args
38+
39+
40+
def autocrop(img, min_size=30, convert_gray=False, extra_space=0.2):
41+
"""Separate front ground and background. Choose only largest objects as front ground.
42+
43+
min_size: minimum size in pixels of objects to include.
44+
extra_space: fraction of the front ground size
45+
"""
46+
img = np.array(img).copy()
47+
# Convert to grayscale if RGB
48+
if len(img.shape) > 2:
49+
img_gr = rgb2gray(img)
50+
else:
51+
img_gr = img.copy()
52+
53+
# Filter image
54+
img_gr = gaussian(img_gr)
55+
56+
# Convert to binary
57+
thr = threshold_otsu(img_gr)
58+
binary = img_gr > thr
59+
60+
# Remove small spots
61+
binary = remove_small_objects(binary, min_size=min_size)
62+
63+
# Find centroid
64+
M = moments(binary)
65+
centroid = (M[1, 0] / M[0, 0], M[0, 1] / M[0, 0])
66+
67+
# Find edges
68+
fg_indices = np.argwhere(binary > 0)
69+
mincol = np.min(fg_indices[:, 1])
70+
maxcol = np.max(fg_indices[:, 1])
71+
minrow = np.min(fg_indices[:, 0])
72+
maxrow = np.max(fg_indices[:, 0])
73+
extension = extra_space * np.array([maxrow - minrow, maxcol - mincol])
74+
75+
# Crop to 100% + extra_space length each dimension
76+
framerow = np.clip(
77+
[int(minrow - extension[0]), int(maxrow + extension[0])],
78+
a_min=0,
79+
a_max=img.shape[0],
80+
)
81+
framecol = np.clip(
82+
[int(mincol - extension[1]), int(maxcol + extension[1])],
83+
a_min=0,
84+
a_max=img.shape[1],
85+
)
86+
87+
if convert_gray:
88+
return rgb2gray(img)[
89+
framerow[0] : framerow[1], framecol[0] : framecol[1]
90+
]
91+
92+
else:
93+
return img[framerow[0] : framerow[1], framecol[0] : framecol[1]]
94+
95+
96+
def main():
97+
args = _parseargs()
98+
for f in args.input:
99+
img = imread(f)
100+
cropped_img = autocrop(
101+
img,
102+
min_size=args.minsize,
103+
convert_gray=args.convert_gray,
104+
extra_space=args.extra_space,
105+
)
106+
fn = f.split(".")
107+
imsave(
108+
"".join(fn[:-1]) + f"_cropped.{fn[-1]}",
109+
cropped_img,
110+
format="TIFF-PIL",
111+
# compression="tiff_deflate",
112+
)
113+
114+
115+
if __name__ == "__main__":
116+
main()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /