Last active
April 11, 2023 20:46
-
-
Save diramazioni/bb45705e00006e594e7fa5165377feb1 to your computer and use it in GitHub Desktop.
Find out the "bluriness" of each extracted image-frame to select the best image out of N frames, copy the resulting frames in a new dir mantaining exif info
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
### Find out the "bluriness" of each extracted image-frame to select the best image out of N frames, | |
### copy the resulting frames in a new dir mantaining exif info | |
### Arguments: image_directory best_of_n_frames method (1 or 2) | |
### python blurCV_detect.py /home/SfM/footage 30 1 | |
""" | |
import sys, glob, os, shutil | |
# sys.argv[1] image directory to operate | |
best_every = int(sys.argv[2]) # choose the best image every X frames | |
method = int(sys.argv[3]) # choose the method to compute the "bluriness" (1 or 2 value) | |
ok = 'ok_' + sys.argv[3] # dir name for selected images | |
from PIL import Image | |
import numpy | |
import cv2 | |
import piexif | |
from collections import OrderedDict | |
import math | |
def median(values): | |
half = len(values) // 2 | |
svalues = sorted(values) | |
if not len(svalues) % 2: | |
return (svalues[half - 1] + svalues[half]) / 2.0 | |
return svalues[half] | |
def mean(values): | |
return sum(values)/len(values) | |
def stanDevPop(values): | |
length = len(values) | |
total_sum = 0 | |
for i in range(length): | |
total_sum += (values[i]-mean(values))**2 | |
return math.sqrt(total_sum/length) | |
def computeBlurSimple(filePath): | |
img = cv2.imread(filePath) | |
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
return numpy.max(cv2.convertScaleAbs(cv2.Laplacian(gray,3))) | |
def computeBlur(filePath): | |
thresh = 35 | |
MinZero = 0.02 | |
im=Image.open(filePath).convert('F') | |
x=numpy.asarray(im) | |
x_cropped=x[0:(numpy.shape(x)[0]/16)*16 - 1,0:(numpy.shape(x)[1]/16)*16 - 1] | |
LL1,(LH1,HL1,HH1)=pywt.dwt2(x_cropped,'haar') | |
LL2,(LH2,HL2,HH2)=pywt.dwt2(LL1 ,'haar') | |
LL3,(LH3,HL3,HH3)=pywt.dwt2(LL2 ,'haar') | |
Emap1=numpy.square(LH1) + numpy.square(HL1) + numpy.square(HH1) | |
Emap2=numpy.square(LH2) + numpy.square(HL2) + numpy.square(HH2) | |
Emap3=numpy.square(LH3) + numpy.square(HL3) + numpy.square(HH3) | |
dimx=numpy.shape(Emap1)[0]/8 | |
dimy=numpy.shape(Emap1)[1]/8 | |
Emax1=[] | |
vert=1 | |
for j in range(0,dimx - 2): | |
horz=1; | |
Emax1.append([]) | |
for k in range(0,dimy - 2): | |
Emax1[j].append(numpy.max(numpy.max(Emap1[vert:vert+7,horz:horz+7]))) | |
horz=horz+8 | |
vert=vert+8 | |
dimx=numpy.shape(Emap2)[0]/4 | |
dimy=numpy.shape(Emap2)[1]/4 | |
Emax2=[] | |
vert=1 | |
for j in range(0,dimx - 2): | |
horz=1; | |
Emax2.append([]) | |
for k in range(0,dimy - 2): | |
Emax2[j].append(numpy.max(numpy.max(Emap2[vert:vert+3,horz:horz+3]))) | |
horz=horz+4 | |
vert=vert+4 | |
dimx=numpy.shape(Emap3)[0]/2 | |
dimy=numpy.shape(Emap3)[1]/2 | |
Emax3=[] | |
vert=1 | |
for j in range(0,dimx - 2): | |
horz=1; | |
Emax3.append([]) | |
for k in range(0,dimy - 2): | |
Emax3[j].append(numpy.max(numpy.max(Emap3[vert:vert+1,horz:horz+1]))) | |
horz=horz+2 | |
vert=vert+2 | |
N_edge=0 | |
N_da=0 | |
N_rg=0 | |
N_brg=0 | |
EdgeMap = [] | |
for j in range(0, dimx - 2): | |
EdgeMap.append([]) | |
for k in range(0, dimy - 2): | |
if (Emax1[j][k]>thresh) or (Emax2[j][k]>thresh) or (Emax3[j][k]>thresh): | |
EdgeMap[j].append(1) | |
N_edge = N_edge + 1 | |
rg = 0 | |
if (Emax1[j][k]>Emax2[j][k]) and (Emax2[j][k]>Emax3[j][k]): | |
N_da=N_da+1 | |
elif (Emax1[j][k]<Emax2[j][k]) and (Emax2[j][k]<Emax3[j][k]): | |
rg = 1 | |
N_rg=N_rg+1 | |
elif (Emax2[j][k]>Emax1[j][k]) and (Emax2[j][k]>Emax3[j][k]): | |
rg = 1 | |
N_rg=N_rg+1 | |
if rg and (Emax1[j][k]<thresh): | |
N_brg=N_brg+1 | |
else: | |
EdgeMap[j].append(0) | |
return float(N_da)/N_edge | |
def computeCanny(filePath): | |
img = cv2.imread(filePath) | |
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
imgCanny = cv2.Canny(gray, 225, 175) | |
nCountCanny = cv2.countNonZero(imgCanny) | |
dSharpness = nCountCanny * 1000.0 / (imgCanny.shape[0] * imgCanny.shape[1]) | |
return dSharpness | |
def write_exif(filePath): | |
im = Image.open(filePath) | |
#exif_dict = piexif.load(im.info["exif"]) | |
exif_dict = piexif.load(filePath) | |
if piexif.ImageIFD.XResolution not in exif_dict["0th"]: | |
print("adding exif %s" % filePath) | |
w, h = im.size | |
exif_dict["0th"][piexif.ImageIFD.XResolution] = (w, 1) | |
exif_dict["0th"][piexif.ImageIFD.YResolution] = (h, 1) | |
exif_bytes = piexif.dump(exif_dict) | |
im.save(filePath, "jpeg", exif=exif_bytes) | |
def chunks(odict, n): | |
"""Yield successive n-sized chunks from odict.""" | |
for i in range(0, len(odict), n): | |
yield list(odict.keys())[i:i + n] | |
os.chdir(sys.argv[1]) | |
if not os.path.exists(ok): | |
os.mkdir(ok) | |
oresult = OrderedDict() | |
values = [] | |
for f in sorted(glob.glob('*.jpg')): | |
if method == 1: | |
val = computeCanny(f) | |
else: | |
val = computeBlur(f) | |
values.append(val) | |
oresult[f] = val | |
print(f, val) | |
#oresult = OrderedDict(result.items()) | |
stdev = stanDevPop(values) | |
mean = mean(values) | |
median = median(values) | |
print('mean:', mean) | |
print('median:', median) | |
print('stdev:', stdev) | |
for ckeys in list(chunks(oresult, best_every)): | |
print('----') | |
chunkd = {key: oresult[key] for key in ckeys} | |
f, maximum = max(chunkd.items(), key=lambda k: k[1]) | |
write_exif(f) | |
print(f, maximum) | |
shutil.copyfile(f, '%s/%s'% (ok,f)) | |
''' This is an implementation of a post found here http://stackoverflow.com/a/20198278/1911518 ''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment