Skip to content

Instantly share code, notes, and snippets.

@bhive01
Last active November 6, 2018 22:21
Show Gist options
  • Select an option

  • Save bhive01/492101637c0c81f4bbc889901857a377 to your computer and use it in GitHub Desktop.

Select an option

Save bhive01/492101637c0c81f4bbc889901857a377 to your computer and use it in GitHub Desktop.
# Brandon Hurr
# Assumptions
# 1. Color card is Xrite Passport
# 2. Card is not mirrored
# 3. Only color calibration half of card is visible
# 4. When remove edges is selected at command line, the card is not at the edge of the image.
# 3. The scale factor is based upon median width of found objects (contours) that should be xrite squares, but could not be
# could be problematic with lots of squares objects in image other than color card
import argparse
import sys
import os.path
import trans #pip install trans
import time
import cv2
import math
import pandas as pd
import numpy as np
import statsmodels.formula.api as smf
from itertools import product
from skimage import color, restoration
from scipy.signal import convolve2d as conv2
from scipy.spatial.distance import squareform, pdist
from sklearn import linear_model
from play2_funcs import *
# known passport card values
xritePassportR = [105.0,193.0,90.0,74.0,132.0,115.0,204.0,67.0,199.0,77.0,144.0,201.0,33.0,60.0,176.0,205.0,183.0,47.0,235.0,206.0,167.0,120.0,75.0,34.0]
xritePassportG = [68.0,142.0,124.0,104.0,135.0,202.0,105.0,93.0,73.0,52.0,196.0,161.0,58.0,160.0,44.0,198.0,82.0,143.0,235.0,206.0,169.0,120.0,76.0,36.0]
xritePassportB = [54.0,122.0,170.0,46.0,186.0,188.0,17.0,195.0,87.0,109.0,35.0,11.0,168.0,62.0,42.0,16.0,156.0,192.0,231.0,204.0,166.0,116.0,75.0,35.0]
# set up parser to take input of folder with images to loop through
ap = argparse.ArgumentParser()
ap.add_argument("--path", help = "Path to folder containing images")
ap.add_argument("--cardsquare", help = "Draw square around color card if found, default is 0 (none)", nargs='?', const=0, type=int)
ap.add_argument("--blurfactor", help = "Laplacian Fourier Transform detection of blurriness in image from http://www.pyimagesearch.com/2015/09/07/blur-detection-with-opencv/", nargs='?', const=40, type=int)
ap.add_argument("--squaresimilarity", help = "How similar in size should the squares be (integer percentage, default is 90%)", nargs='?', const=90, type=int)
ap.add_argument('--expandhist', help = 'In darker samples, the expansion of the histogram hinders finding the squares due to problems with the otsu thresholding. If your image has a bright background, expandhist, otherwise, do not.', action='store_true', default=False)
ap.add_argument('--removeedges', help = 'If there are artifacts around the edge that are complicating finding the card, this will remove things within X% of the borders.', nargs='?', const=0, type=int)
ap.add_argument('--threshold', help = 'Allows choice of different methods for segmentation. otsu, normal and adaptguass', choices = ["otsu","adaptgauss","normal"], nargs='?', default = "normal", type=str.lower)
ap.add_argument('--threshvalue', help = 'If there are artifacts around the edge that are complicating finding the card, this will remove things within X% of the borders.', nargs='?', const=127, type=int)
ap.add_argument('--debug', action='store_true', default=False)
args = ap.parse_args()
# create new folder for storing images (resolution to minute so can overwrite if you start two processes quickly after each other)
currentTime = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
foldername = '{0}_Results'.format(currentTime)
savedir = os.path.join(args.path, foldername)
os.makedirs(savedir)
#write out extra tables and images if having issues using the --debug flag
if args.debug:
savecolcardsdir = os.path.join(savedir, 'colcards')
savefaildir = os.path.join(savedir, 'failures')
saveedgesdir = os.path.join(savedir, 'edges')
savemaskdir = os.path.join(savedir, 'masks')
saveclahedir = os.path.join(savedir, 'clahe')
saveblurdir = os.path.join(savedir, 'gaussblur')
savethreshdir = os.path.join(savedir, 'threshold')
os.makedirs(savecolcardsdir)
os.makedirs(savefaildir)
os.makedirs(saveedgesdir)
os.makedirs(savemaskdir)
os.makedirs(saveclahedir)
os.makedirs(saveblurdir)
os.makedirs(savethreshdir)
#define types of tiles we're interested in
filetypes = tuple([".JPG", ".jpg", ".JPEG", ".jpeg"])
# read the filelist from the path directory
filelist = [f for f in os.listdir(args.path) if f.endswith(filetypes)]
# how long is the list of files for progress reporting
listlength = len(filelist)
for fileindex, filename in enumerate(filelist):
# progress bar
pctdone = int(float(fileindex)/float(listlength) * 100)
sys.stdout.write("\rPercentDone: %d%%" % pctdone)
sys.stdout.flush()
# read in image
img = cv2.imread(os.path.join(args.path, filename))
# get image attributes
height, width, channels = img.shape
totalpx = float(height*width) # float for further processing
# minimum and maximum square size based upon 12 MP image
minarea = 1000./12000000. * totalpx # 1000px^2/12MP smallest size acceptable square
maxarea = 150000./12000000. * totalpx #150kpx^2/12MP largest size acceptable square
# create gray image for further processing
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Laplacian Fourier Transform detection of blurriness in image from http://www.pyimagesearch.com/2015/09/07/blur-detection-with-opencv/
blurfactor = cv2.Laplacian(gray_img, cv2.CV_64F).var()
# if image is more blurry than blurfactor then try and deblur using kernel
if blurfactor < args.blurfactor:
# kernel from https://www.packtpub.com/mapt/book/Application+Development/9781785283932/2/ch02lvl1sec22/Sharpening
kernel = np.array([[-1,-1,-1,-1,-1],
[-1,2,2,2,-1],
[-1,2,8,2,-1],
[-1,2,2,2,-1],
[-1,-1,-1,-1,-1]]) / 8.0
# store result back out for further processing
gray_img = cv2.filter2D(gray_img, -1, kernel)
if args.expandhist:
# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=3.25, tileGridSize=(4,4))
# apply CLAHE histogram expansion to find squares better with canny edge detection
gray_img = clahe.apply(gray_img)
# thresholding
if args.threshold == "otsu" :
# blur slightly so defects on card squares and background patterns are less likely to be picked up
gaussian = cv2.GaussianBlur(gray_img,(5,5),0)
ret, threshold = cv2.threshold(gaussian, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) #
elif args.threshold == "normal":
# blur slightly so defects on card squares and background patterns are less likely to be picked up
gaussian = cv2.GaussianBlur(gray_img,(5,5),0)
ret,threshold = cv2.threshold(gaussian,args.threshvalue,255,cv2.THRESH_BINARY)
elif args.threshold == "adaptgauss":
# blur slightly so defects on card squares and background patterns are less likely to be picked up
gaussian = cv2.GaussianBlur(gray_img,(11,11),0)
threshold = cv2.adaptiveThreshold(gaussian,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,2)
# canny edge detection with median-based thresholding
edges = auto_canny(threshold)
# save out files along the way
if args.debug:
if args.expandhist:
savefilename = '{0}clahe.jpg'.format(filename)
savefilecomplete = os.path.join(saveclahedir, savefilename)
cv2.imwrite(savefilecomplete, gray_img, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
savefilename = '{0}blur.jpg'.format(filename)
savefilecomplete = os.path.join(saveblurdir, savefilename)
cv2.imwrite(savefilecomplete, gaussian, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
savefilename = '{0}threshold.jpg'.format(filename)
savefilecomplete = os.path.join(savethreshdir, savefilename)
cv2.imwrite(savefilecomplete, threshold, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
savefilename = '{0}edges.jpg'.format(filename)
savefilecomplete = os.path.join(saveedgesdir, savefilename)
cv2.imwrite(savefilecomplete, edges, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
# compute contours to find the squares of the card
_, contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
mindex = [] # variable of which contour is which
mu = [] # variable to store moments
mc = [] # variable to x,y coordinates in tuples
mx = [] # variable to x coordinate as integer
my = [] # variable to y coordinate as integer
marea = [] # variable to store area
msquare = [] # variable to store whether something is a square (1) or not (0)
msquarecoords = [] # variable to store square approximation coordinates
mchild = [] # variable to store child hierarchy element
mheight = [] # fitted rectangle height
mwidth = [] # fitted rectangle width
mwhratio = [] # ratio of height/width
# extract moments from contour image
for x in range(0, len(contours)):
mu.append(cv2.moments(contours[x]))
marea.append(cv2.contourArea(contours[x]))
mchild.append(int(hierarchy[0][x][2]))
mindex.append(x)
# cycle through moment data and compute location for each moment
for m in mu:
if m['m00'] != 0: # this is the area term for a moment
mc.append((int(m['m10']/m['m00']), int(m['m01']/m['m00'])))
mx.append(int(m['m10']/m['m00']))
my.append(int(m['m01']/m['m00']))
else:
mc.append((0,0))
mx.append(0)
my.append(0)
# loop over our contours and extract data about them
for index, c in enumerate(contours):
# area isn't 0 and is greater than minarea and less than maxarea
if (marea[index] != 0 and marea[index] > minarea and marea[index] < maxarea):
# finding squares
# from http://www.pyimagesearch.com/2014/04/21/building-pokedex-python-finding-game-boy-screen-step-4-6/
# approximate the contour
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.15 * peri, True)
center, wh, angle = cv2.minAreaRect(c) #rotated rectangle
mwidth.append(wh[0])
mheight.append(wh[1])
mwhratio.append(wh[0]/wh[1])
msquare.append(len(approx))
# if our approximated contour has four points, then
# we can assume that we have found 4-sided objects (possibly squares)
# 2016-10-05 added 5 to increase finding of card in blurred situations
if len(approx) == 4 or 5:
msquarecoords.append(approx)
# if it's four pointed construct a mask for the contour, then compute mean RGB
# creates a mask equal in size to img
mask = np.zeros(img.shape[:2], dtype="uint8")
else: # it's not a square
# need to append these to keep array's the same length
msquare.append(0)
msquarecoords.append(0)
else: # contour has an area of zero, not interesting
# need to append these to keep array's the same length
msquare.append(0)
msquarecoords.append(0)
mwidth.append(0)
mheight.append(0)
mwhratio.append(0)
#make a pandas df from data for filtering out junk
locarea = {'index' : mindex, 'X' : mx, 'Y' : my, 'width' : mwidth, 'height' : mheight, 'WHratio': mwhratio, 'Area' : marea, 'square' : msquare, 'child' : mchild}
df = pd.DataFrame(locarea)
# add filename and blurfactor to output
df['File'] = filename
df['blurriness'] = blurfactor
# no ratio cutoff for debugging
dfnoratio = df[(df['Area'] > minarea) & (df['Area'] < maxarea) & (df['child'] != -1) & (df['square'].isin([4,5]))]
# filter df for attributes that would isolate squares of reasonable size
df = df[(df['Area'] > minarea) & (df['Area'] < maxarea) & (df['child'] != -1) & (df['square'].isin([4,5])) & (df['WHratio'] < 1.2) & (df['WHratio'] > 0.85)]
# filter nested squares from dataframe, was having issues with median being towards smaller nested squares
df = df[~(df['index'].isin(df['index']+1))]
# if you want to remove edges, this code selects the middle 96% of the image in both directions
# card should not be right at edge if you select this
if args.removeedges > 0:
percentremove = float(args.removeedges) / 100.
minwidth = percentremove * float(width)
maxwidth = (1. - percentremove) * float(width)
minheight = percentremove * float(height)
maxheight = (1. - percentremove) * float(height)
df = df[(df['X'] > minwidth) & (df['X'] < maxwidth) & (df['Y'] > minheight) & (df['Y'] < maxheight)]
###### find and count up squares that are within a given radius, more squares = more likelihood of them being the card
# Median width of square time 2.5 gives proximity radius for searching for similar squares
medianSqwidthpx = df["width"].median()
#squares that are within 6 widths of the current square
pixeldist = medianSqwidthpx*6
# computes euclidian distance matrix for the x and y contour centroids
distmatrix = pd.DataFrame(squareform(pdist(df[['X', 'Y']])))
# add up distances that are less than ones have distance less than pixeldist pixels
distmatrixflat = distmatrix.apply(lambda x: x[x<=pixeldist].count() - 1, axis=1)
#
# append distprox summary to dataframe
df = df.assign(distprox = distmatrixflat.values)
##### Compute how similar in area the squares are. lots of similar values indicates card
# isolate area measurements
filteredArea = df['Area']
# create empty matrix for storing comparisons
sizecomp = np.zeros((len(filteredArea), len(filteredArea)))
# double loop through all areas to compare to each other
for p in xrange(0, len(filteredArea)):
for o in xrange(0, len(filteredArea)):
big = max(filteredArea.iloc[p], filteredArea.iloc[o])
small = min(filteredArea.iloc[p], filteredArea.iloc[o])
pct = 100. * (small/big)
sizecomp[p][o] = pct
# how many comparisons for a given square are above squaresimilarity input (default 90%)
sizematrix = pd.DataFrame(sizecomp).apply(lambda x: x[x>=args.squaresimilarity].count() - 1, axis=1)
# append sizeprox summary to dataframe
df = df.assign(sizeprox = sizematrix.values)
# reorder dataframe for better printing
df= df[['File', 'index', 'X', 'Y', 'width', 'height', 'WHratio', 'Area', 'square', 'child', 'blurriness', 'distprox', 'sizeprox']]
####### loosely filter for size and distance as well as square size relative to median
minsqwidth = medianSqwidthpx * 0.80
maxsqwidth = medianSqwidthpx * 1.2
df = df[(df['distprox'] >= 5) & (df['sizeprox'] >= 5) & (df['width'] > minsqwidth) & (df['width'] < maxsqwidth)]
# filter for proximity again to root out stragglers
###### find and count up squares that are within a given radius, more squares = more likelihood of them being the card
# Median width of square time 2.5 gives proximity radius for searching for similar squares
medianSqwidthpx = df["width"].median()
#squares that are within 6 widths of the current square
pixeldist = medianSqwidthpx*5
# computes euclidian distance matrix for the x and y contour centroids
distmatrix = pd.DataFrame(squareform(pdist(df[['X', 'Y']])))
# add up distances that are less than ones have distance less than pixeldist pixels
distmatrixflat = distmatrix.apply(lambda x: x[x<=pixeldist].count() - 1, axis=1)
#
# append distprox summary to dataframe
df = df.assign(distprox = distmatrixflat.values)
####### filter results for distance proximity to other squares
df = df[(df['distprox'] >= 4)]
###### Create composite bounding rectangle of found squares
###### Then transform the perspective
# creates a completely black (zeros) mask equal in size to img
mask = np.zeros(img.shape[:2], dtype="uint8")
# fills in found squares in mask with white
if len(df.index) > 1:
for index in df['index']:
cv2.drawContours(mask, contours, index, 255, -1)
#save out mask file if debugging
if args.debug:
savefilename = '{0}mask.jpg'.format(filename)
savefilecomplete = os.path.join(savemaskdir, savefilename)
cv2.imwrite(savefilecomplete, mask, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
if cv2.countNonZero(mask) != 0:
# creates a composite selection of found squares
compositesquares = cv2.findNonZero(mask)
# finds minimum area rectangle around squares
rect = cv2.minAreaRect(compositesquares)
box = cv2.boxPoints(rect) #extracts points from rectangle
box = np.int0(box) #rounds points to exact pixel locations
src = np.asarray(box, np.float32) #make points 32bit floats for further math
#transform the card, expand area around card is 0
warped = four_point_transform(img, src, 0)
# rotate 90 degrees if width is less than height (card is upright)
cardheight, cardwidth = warped.shape[:2]
if cardwidth < cardheight:
warped = cv2.transpose(warped)
warped = cv2.flip(warped, 1)
# get parameters after rotation
rotatedcardheight, rotatedcardwidth = warped.shape[:2]
hwratio = float(rotatedcardheight)/float(rotatedcardwidth)
# if only a partial card was found the ratio will be significantly out of whack
# best to skip these for now and figure out why I can't find the squares later
if hwratio > 0.6 and hwratio < 0.7:
# creating scale as each card can be different sizes
scale = float(rotatedcardwidth)/8.2 #card is 8.2 cm from square edge to opposite edge across
fudgefactor = int(0.17 * scale) # 1.5 mm border around square in case of misalignment
startcenter = int(0.6 * scale) # 0.6 cm in from top and left edge
centerwidth = int(1.434 * scale) # square centers are ~1.5 cm apart
# compute list of x position centers
xpos = []
calc = startcenter
for i in xrange(0,6):
xpos.append(calc)
calc = calc + centerwidth
# compute list of y position centers
ypos = []
calc = startcenter
for i in xrange(0,4):
ypos.append(calc)
calc = calc + centerwidth
# create tuples from combinations of x and y coordinates
centers = list(product(xpos, ypos))
# sort the list of centers by y and then x
centers = sorted(centers, key=lambda x: (x[1], x[0]))
# set up arrays for storing color
cardcolorR = [] # variable to store mean of R
cardcolorG = [] # variable to store mean of G
cardcolorB = [] # variable to store mean of B
cardcolorRstddev = [] # variable to store stddev of R
cardcolorGstddev = [] # variable to store stddev of G
cardcolorBstddev = [] # variable to store stddev of B
# loop through centers and take color data
for c in centers:
# creates a mask equal in size to img
mask = np.zeros(warped.shape[:2], dtype="uint8")
# draw and fill contour
cv2.circle(mask, c, (startcenter-fudgefactor), 255, -1)
# extract median BGR value
mean, stddev = cv2.meanStdDev(warped, mask=mask)[:3]
# append color data to mcolor array
cardcolorR.append(int(mean[2]))
cardcolorG.append(int(mean[1]))
cardcolorB.append(int(mean[0]))
cardcolorRstddev.append(int(stddev[2]))
cardcolorGstddev.append(int(stddev[1]))
cardcolorBstddev.append(int(stddev[0]))
# check mirroring left and right middle squares regardless of orientation
leftDark = cardcolorR[6] + cardcolorG[6] + cardcolorB[6] + cardcolorR[12] + cardcolorG[12] + cardcolorB[21]
rightLight = cardcolorR[11] + cardcolorG[11] + cardcolorB[11] + cardcolorR[17] + cardcolorG[17] + cardcolorB[17]
# need combination of left-right/up-down to get mirror and orientation
# check card orientation: only two possibilities if not mirrored
orangeR = cardcolorR[6]
blueR = cardcolorR[12]
yellowR = cardcolorR[11]
lightblueR = cardcolorR[17]
# card is not in expected orientation
# testing both sides of the card in case something is covered or blotchy
# if mirrored, this will be true and skipped
if blueR > orangeR or lightblueR > yellowR:
# reverse order of square
cardcolorR = cardcolorR[::-1]
cardcolorG = cardcolorG[::-1]
cardcolorB = cardcolorB[::-1]
cardcolorRstddev = cardcolorRstddev[::-1]
cardcolorGstddev = cardcolorGstddev[::-1]
cardcolorBstddev = cardcolorBstddev[::-1]
# create dataframe with color results from transformed card
colorcard = {'File' : filename, 'X' : [x[0] for x in centers], 'Y' : [x[1] for x in centers], 'R' : cardcolorR, 'G' : cardcolorG, 'B' : cardcolorB, 'Rsd' : cardcolorRstddev, 'Gsd' : cardcolorGstddev, 'Bsd' : cardcolorBstddev}
colorcardDF = pd.DataFrame(colorcard)
colorcardDF= colorcardDF[['File', 'X', 'Y', 'R', 'G', 'B', 'Rsd', 'Gsd', 'Bsd']]
# bring in standard color values
colorcardDF = colorcardDF.assign(xritePassportR = xritePassportR)
colorcardDF = colorcardDF.assign(xritePassportG = xritePassportG)
colorcardDF = colorcardDF.assign(xritePassportB = xritePassportB)
#filter model data for bad SDs
# bad SDs would indicate junk on the card squares
filteredCCDF = colorcardDF[(colorcardDF['Rsd'] < 45) & (colorcardDF['Gsd'] < 45) & (colorcardDF['Bsd'] < 45)]
# create a fitted model in one line
lm = smf.ols(formula='xritePassportR ~ R', data=filteredCCDF).fit()
redintercept = lm.params.Intercept
redslope = lm.params.R
# create a fitted model in one line
lm = smf.ols(formula='xritePassportG ~ G', data=filteredCCDF).fit()
greenintercept = lm.params.Intercept
greenslope = lm.params.G
# create a fitted model in one line
lm = smf.ols(formula='xritePassportB ~ B', data=filteredCCDF).fit()
blueintercept = lm.params.Intercept
blueslope = lm.params.B
##### correct image for color imbalance
# numpy referencing (row, col, ch) http://scikit-image.org/docs/dev/user_guide/numpy_images.html
# correct red channel
img[:,:,2] = cv2.add(cv2.multiply(img[:,:,2],np.array([redslope])),np.array([redintercept]))
# correct green channel
img[:,:,1] = cv2.add(cv2.multiply(img[:,:,1],np.array([greenslope])),np.array([greenintercept]))
# correct blue channel
img[:,:,0] = cv2.add(cv2.multiply(img[:,:,0],np.array([blueslope])),np.array([blueintercept]))
# save out image
if args.cardsquare > 0: # draw box around card
cv2.drawContours(img,[box],0,(255,0,0), args.cardsquare)
savefilename = '{0}colcor.jpg'.format(filename)
savefilecomplete = os.path.join(savedir, savefilename)
cv2.imwrite(savefilecomplete, img, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
# compute number of rows remaining in table, this is number of squares used in correction
fCCDFrowcol = filteredCCDF.shape
numsquares = fCCDFrowcol[0]
# compute scale from median width, square is 1.2 cm across
scale = medianSqwidthpx/1.2
ColorCorrected = "Success"
outdata = [filename, ColorCorrected, numsquares, scale, redslope, redintercept, greenslope, greenintercept, blueslope, blueintercept]
# if ratio of card is wrong, probably a truncated card
else:
ColorCorrected = "Fail Bad Card Ratio"
numsquares, scale, redslope, redintercept, greenslope, greenintercept, blueslope, blueintercept = ("",)*8
#outdata = [filename, ColorCorrected, numsquares, scale, redslope, redintercept, greenslope, greenintercept, blueslope, blueintercept]
else: # cv2.countNonZero(mask) == 0:
ColorCorrected = "Fail No Mask"
numsquares, scale, redslope, redintercept, greenslope, greenintercept, blueslope, blueintercept = ("",)*8
# save out calibration data and scale
colorout = {'File' : filename, 'ColorCorrected' : ColorCorrected, 'numberofSquares4correction' : [numsquares], 'SetScalepx': [scale], 'RedSlope' : [redslope], 'RedIntercept' : [redintercept], 'GreenSlope' : [greenslope], 'GreenIntercept' : [greenintercept], 'BlueSlope' : [blueslope], 'BlueIntercept' : [blueintercept]}
coloroutDF = pd.DataFrame(colorout)
#reorder columns for prettier output
coloroutDF= coloroutDF[['File','ColorCorrected','numberofSquares4correction','SetScalepx','RedSlope','RedIntercept','GreenSlope','GreenIntercept','BlueSlope','BlueIntercept']]
# write out results
if fileindex == 0:
coloroutDF.to_csv(os.path.join(savedir, 'Results.csv'), mode = 'w', header=True)
else:
coloroutDF.to_csv(os.path.join(savedir, 'Results.csv'), mode = 'a', header=False)
#write out extra tables and images if having issues using the --debug flag
if args.debug:
if fileindex == 0:
df.to_csv(os.path.join(savedir, 'Contours.csv'), mode = 'w', header=True)
else:
df.to_csv(os.path.join(savedir, 'Contours.csv'), mode = 'a', header=False)
try:
filteredCCDF
except NameError:
print('No filtered CCDF')
else:
if fileindex == 0:
filteredCCDF.to_csv(os.path.join(savedir, 'Squares.csv'), mode = 'w', header=True)
else:
filteredCCDF.to_csv(os.path.join(savedir, 'Squares.csv'), mode = 'a', header=False)
# if only a partial card was found the ratio will be significantly out of whack
# want to only print out those that fail ratio check
if hwratio > 0.6 and hwratio < 0.7:
# loop through centers and draw circles
for row in filteredCCDF.itertuples():
cv2.circle(warped, (row.X, row.Y), (startcenter-fudgefactor), (0,0,255), 3)
savefilename = '{0}colcard.jpg'.format(filename)
savecolcardcomplete = os.path.join(savecolcardsdir, savefilename)
cv2.imwrite(savecolcardcomplete, warped, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
# save out image with false color labeling for partially filtered contours (blue) and filtered contours (red)
if args.cardsquare > 0 and ColorCorrected == 'Success': # draw box around card
cv2.drawContours(img,[box],0,(255,0,0), args.cardsquare)
for index in dfnoratio['index']:
cv2.drawContours(img, contours, index, (255,0,0), -1)
for index in df['index']:
cv2.drawContours(img, contours, index, (0,0,255), -1)
savefilename = '{0}colcor.jpg'.format(filename)
savefilecomplete = os.path.join(savefaildir, savefilename)
cv2.imwrite(savefilecomplete, img, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment