summaryrefslogtreecommitdiff
path: root/utils/raspberrypi/ctt/ctt_macbeth_locator.py
diff options
context:
space:
mode:
authorNaushir Patuck <naush@raspberrypi.com>2020-05-03 16:49:53 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2020-05-11 23:54:45 +0300
commitc01cfe14f5540ba96b458088185ac7ae90bb3534 (patch)
treef9112e0195de83ea1b20cf81cb62144cd50174f9 /utils/raspberrypi/ctt/ctt_macbeth_locator.py
parent0db2c8dc75e466e7648dc1b95380495c6a126349 (diff)
libcamera: utils: Raspberry Pi Camera Tuning Tool
Initial implementation of the Raspberry Pi (BCM2835) Camera Tuning Tool. All code is licensed under the BSD-2-Clause terms. Copyright (c) 2019-2020 Raspberry Pi Trading Ltd. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'utils/raspberrypi/ctt/ctt_macbeth_locator.py')
-rw-r--r--utils/raspberrypi/ctt/ctt_macbeth_locator.py748
1 files changed, 748 insertions, 0 deletions
diff --git a/utils/raspberrypi/ctt/ctt_macbeth_locator.py b/utils/raspberrypi/ctt/ctt_macbeth_locator.py
new file mode 100644
index 00000000..583d5a69
--- /dev/null
+++ b/utils/raspberrypi/ctt/ctt_macbeth_locator.py
@@ -0,0 +1,748 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2019, Raspberry Pi (Trading) Limited
+#
+# ctt_macbeth_locator.py - camera tuning tool Macbeth chart locator
+
+from ctt_ransac import *
+from ctt_tools import *
+import warnings
+
+"""
+NOTE: some custom functions have been used here to make the code more readable.
+These are defined in tools.py if they are needed for reference.
+"""
+
+"""
+Some inconsistencies between packages cause runtime warnings when running
+the clustering algorithm. This catches these warnings so they don't flood the
+output to the console
+"""
+def fxn():
+ warnings.warn("runtime",RuntimeWarning)
+
+"""
+Define the success message
+"""
+success_msg = 'Macbeth chart located successfully'
+
+def find_macbeth(Cam,img,mac_config=(0,0)):
+ small_chart,show = mac_config
+ print('Locating macbeth chart')
+ Cam.log += '\nLocating macbeth chart'
+ """
+ catch the warnings
+ """
+ warnings.simplefilter("ignore")
+ fxn()
+
+ """
+ Reference macbeth chart is created that will be correlated with the located
+ macbeth chart guess to produce a confidence value for the match.
+ """
+ ref = cv2.imread(Cam.path +'ctt_ref.pgm',flags=cv2.IMREAD_GRAYSCALE)
+ ref_w = 120
+ ref_h = 80
+ rc1 = (0,0)
+ rc2 = (0,ref_h)
+ rc3 = (ref_w,ref_h)
+ rc4 = (ref_w,0)
+ ref_corns = np.array((rc1,rc2,rc3,rc4),np.float32)
+ ref_data = (ref,ref_w,ref_h,ref_corns)
+
+ """
+ locate macbeth chart
+ """
+ cor,mac,coords,msg = get_macbeth_chart(img,ref_data)
+
+ """
+ following bits of code tries to fix common problems with simple
+ techniques.
+ If now or at any point the best correlation is of above 0.75, then
+ nothing more is tried as this is a high enough confidence to ensure
+ reliable macbeth square centre placement.
+ """
+
+ """
+ brighten image 2x
+ """
+ if cor < 0.75:
+ a = 2
+ img_br = cv2.convertScaleAbs(img,alpha=a,beta=0)
+ cor_b,mac_b,coords_b,msg_b = get_macbeth_chart(img_br,ref_data)
+ if cor_b > cor:
+ cor,mac,coords,msg = cor_b,mac_b,coords_b,msg_b
+
+ """
+ brighten image 4x
+ """
+ if cor < 0.75:
+ a = 4
+ img_br = cv2.convertScaleAbs(img,alpha=a,beta=0)
+ cor_b,mac_b,coords_b,msg_b = get_macbeth_chart(img_br,ref_data)
+ if cor_b > cor:
+ cor,mac,coords,msg = cor_b,mac_b,coords_b,msg_b
+
+ """
+ In case macbeth chart is too small, take a selection of the image and
+ attempt to locate macbeth chart within that. The scale increment is
+ root 2
+ """
+ """
+ These variables will be used to transform the found coordinates at smaller
+ scales back into the original. If ii is still -1 after this section that
+ means it was not successful
+ """
+ ii = -1
+ w_best = 0
+ h_best = 0
+ d_best = 100
+ """
+ d_best records the scale of the best match. Macbeth charts are only looked
+ for at one scale increment smaller than the current best match in order to avoid
+ unecessarily searching for macbeth charts at small scales.
+ If a macbeth chart ha already been found then set d_best to 0
+ """
+ if cor != 0:
+ d_best = 0
+
+ """
+ scale 3/2 (approx root2)
+ """
+ if cor < 0.75:
+ imgs = []
+ """
+ get size of image
+ """
+ shape = list(img.shape[:2])
+ w,h = shape
+ """
+ set dimensions of the subselection and the step along each axis between
+ selections
+ """
+ w_sel = int(2*w/3)
+ h_sel = int(2*h/3)
+ w_inc = int(w/6)
+ h_inc = int(h/6)
+ """
+ for each subselection, look for a macbeth chart
+ """
+ for i in range(3):
+ for j in range(3):
+ w_s,h_s = i*w_inc,j*h_inc
+ img_sel = img[w_s:w_s+w_sel,h_s:h_s+h_sel]
+ cor_ij,mac_ij,coords_ij,msg_ij = get_macbeth_chart(img_sel,ref_data)
+ """
+ if the correlation is better than the best then record the
+ scale and current subselection at which macbeth chart was
+ found. Also record the coordinates, macbeth chart and message.
+ """
+ if cor_ij > cor:
+ cor = cor_ij
+ mac,coords,msg = mac_ij,coords_ij,msg_ij
+ ii,jj = i,j
+ w_best,h_best = w_inc,h_inc
+ d_best = 1
+
+
+ """
+ scale 2
+ """
+ if cor < 0.75:
+ imgs = []
+ shape = list(img.shape[:2])
+ w,h = shape
+ w_sel = int(w/2)
+ h_sel = int(h/2)
+ w_inc = int(w/8)
+ h_inc = int(h/8)
+ for i in range(5):
+ for j in range(5):
+ w_s,h_s = i*w_inc,j*h_inc
+ img_sel = img[w_s:w_s+w_sel,h_s:h_s+h_sel]
+ cor_ij,mac_ij,coords_ij,msg_ij = get_macbeth_chart(img_sel,ref_data)
+ if cor_ij > cor:
+ cor = cor_ij
+ mac,coords,msg = mac_ij,coords_ij,msg_ij
+ ii,jj = i,j
+ w_best,h_best = w_inc,h_inc
+ d_best = 2
+
+ """
+ The following code checks for macbeth charts at even smaller scales. This
+ slows the code down significantly and has therefore been omitted by default,
+ however it is not unusably slow so might be useful if the macbeth chart
+ is too small to be picked up to by the current subselections.
+ Use this for macbeth charts with side lengths around 1/5 image dimensions
+ (and smaller...?) it is, however, recommended that macbeth charts take up as
+ large as possible a proportion of the image.
+ """
+
+ if small_chart:
+
+ if cor < 0.75 and d_best > 1 :
+ imgs = []
+ shape = list(img.shape[:2])
+ w,h = shape
+ w_sel = int(w/3)
+ h_sel = int(h/3)
+ w_inc = int(w/12)
+ h_inc = int(h/12)
+ for i in range(9):
+ for j in range(9):
+ w_s,h_s = i*w_inc,j*h_inc
+ img_sel = img[w_s:w_s+w_sel,h_s:h_s+h_sel]
+ cor_ij,mac_ij,coords_ij,msg_ij = get_macbeth_chart(img_sel,ref_data)
+ if cor_ij > cor:
+ cor = cor_ij
+ mac,coords,msg = mac_ij,coords_ij,msg_ij
+ ii,jj = i,j
+ w_best,h_best = w_inc,h_inc
+ d_best = 3
+
+ if cor < 0.75 and d_best > 2:
+ imgs = []
+ shape = list(img.shape[:2])
+ w,h = shape
+ w_sel = int(w/4)
+ h_sel = int(h/4)
+ w_inc = int(w/16)
+ h_inc = int(h/16)
+ for i in range(13):
+ for j in range(13):
+ w_s,h_s = i*w_inc,j*h_inc
+ img_sel = img[w_s:w_s+w_sel,h_s:h_s+h_sel]
+ cor_ij,mac_ij,coords_ij,msg_ij = get_macbeth_chart(img_sel,ref_data)
+ if cor_ij > cor:
+ cor = cor_ij
+ mac,coords,msg = mac_ij,coords_ij,msg_ij
+ ii,jj = i,j
+ w_best,h_best = w_inc,h_inc
+
+ """
+ Transform coordinates from subselection to original image
+ """
+ if ii != -1:
+ for a in range(len(coords)):
+ for b in range(len(coords[a][0])):
+ coords[a][0][b][1] += ii*w_best
+ coords[a][0][b][0] += jj*h_best
+
+ """
+ initialise coords_fit variable
+ """
+ coords_fit = None
+ # print('correlation: {}'.format(cor))
+ """
+ print error or success message
+ """
+ print(msg)
+ Cam.log += '\n' + msg
+ if msg == success_msg:
+ coords_fit = coords
+ Cam.log += '\nMacbeth chart vertices:\n'
+ Cam.log += '{}'.format(2*np.round(coords_fit[0][0]),0)
+ """
+ if correlation is lower than 0.75 there may be a risk of macbeth chart
+ corners not having been located properly. It might be worth running
+ with show set to true to check where the macbeth chart centres have
+ been located.
+ """
+ print('Confidence: {:.3f}'.format(cor))
+ Cam.log += '\nConfidence: {:.3f}'.format(cor)
+ if cor < 0.75:
+ print('Caution: Low confidence guess!')
+ Cam.log += 'WARNING: Low confidence guess!'
+ # cv2.imshow('MacBeth',mac)
+ # represent(mac,'MacBeth chart')
+
+ """
+ extract data from coords_fit and plot on original image
+ """
+ if show and coords_fit != None:
+ copy = img.copy()
+ verts = coords_fit[0][0]
+ cents = coords_fit[1][0]
+
+ """
+ draw circles at vertices of macbeth chart
+ """
+ for vert in verts:
+ p = tuple(np.round(vert).astype(np.int32))
+ cv2.circle(copy,p,10,1,-1)
+ """
+ draw circles at centres of squares
+ """
+ for i in range(len(cents)):
+ cent = cents[i]
+ p = tuple(np.round(cent).astype(np.int32))
+ """
+ draw black circle on white square, white circle on black square an
+ grey circle everywhere else.
+ """
+ if i == 3:
+ cv2.circle(copy,p,8,0,-1)
+ elif i == 23:
+ cv2.circle(copy,p,8,1,-1)
+ else:
+ cv2.circle(copy,p,8,0.5,-1)
+ copy,_ = reshape(copy,400)
+ represent(copy)
+
+ return(coords_fit)
+
+def get_macbeth_chart(img,ref_data):
+ """
+ function returns coordinates of macbeth chart vertices and square centres,
+ along with an error/success message for debugging purposes. Additionally,
+ it scores the match with a confidence value.
+
+ Brief explanation of the macbeth chart locating algorithm:
+ - Find rectangles within image
+ - Take rectangles within percentage offset of median perimeter. The
+ assumption is that these will be the macbeth squares
+ - For each potential square, find the 24 possible macbeth centre locations
+ that would produce a square in that location
+ - Find clusters of potential macbeth chart centres to find the potential
+ macbeth centres with the most votes, i.e. the most likely ones
+ - For each potential macbeth centre, use the centres of the squares that
+ voted for it to find macbeth chart corners
+ - For each set of corners, transform the possible match into normalised
+ space and correlate with a reference chart to evaluate the match
+ - Select the highest correlation as the macbeth chart match, returning the
+ correlation as the confidence score
+ """
+
+ """
+ get reference macbeth chart data
+ """
+ (ref,ref_w,ref_h,ref_corns) = ref_data
+
+ """
+ the code will raise and catch a MacbethError in case of a problem, trying
+ to give some likely reasons why the problem occred, hence the try/except
+ """
+ try:
+ """
+ obtain image, convert to grayscale and normalise
+ """
+ src = img
+ src,factor = reshape(src,200)
+ original=src.copy()
+ a = 125/np.average(src)
+ src_norm = cv2.convertScaleAbs(src,alpha=a,beta=0)
+ """
+ This code checks if there are seperate colour channels. In the past the
+ macbeth locator ran on jpgs and this makes it robust to different
+ filetypes. Note that running it on a jpg has 4x the pixels of the
+ average bayer channel so coordinates must be doubled.
+
+ This is best done in img_load.py in the get_patches method. The
+ coordinates and image width,height must be divided by two if the
+ macbeth locator has been run on a demosaicked image.
+ """
+ if len(src_norm.shape) == 3:
+ src_bw = cv2.cvtColor(src_norm,cv2.COLOR_BGR2GRAY)
+ else:
+ src_bw = src_norm
+ original_bw = src_bw.copy()
+ """
+ obtain image edges
+ """
+ sigma=2
+ src_bw = cv2.GaussianBlur(src_bw,(0,0),sigma)
+ t1,t2 = 50,100
+ edges = cv2.Canny(src_bw,t1,t2)
+ """
+ dilate edges to prevent self-intersections in contours
+ """
+ k_size = 2
+ kernel = np.ones((k_size,k_size))
+ its = 1
+ edges = cv2.dilate(edges,kernel,iterations=its)
+ """
+ find Contours in image
+ """
+ conts,_ = cv2.findContours(edges,
+ cv2.RETR_TREE,
+ cv2.CHAIN_APPROX_NONE)
+ if len(conts) == 0:
+ raise MacbethError(
+ '\nWARNING: No macbeth chart found!'
+ '\nNo contours found in image\n'
+ 'Possible problems:\n'
+ '- Macbeth chart is too dark or bright\n'
+ '- Macbeth chart is occluded\n'
+ )
+ """
+ find quadrilateral contours
+ """
+ epsilon = 0.07
+ conts_per = []
+ for i in range(len(conts)):
+ per = cv2.arcLength(conts[i],True)
+ poly = cv2.approxPolyDP(conts[i],
+ epsilon*per,True)
+ if len(poly) == 4 and cv2.isContourConvex(poly):
+ conts_per.append((poly,per))
+
+ if len(conts_per) == 0:
+ raise MacbethError(
+ '\nWARNING: No macbeth chart found!'
+ '\nNo quadrilateral contours found'
+ '\nPossible problems:\n'
+ '- Macbeth chart is too dark or bright\n'
+ '- Macbeth chart is occluded\n'
+ '- Macbeth chart is out of camera plane\n'
+ )
+
+ """
+ sort contours by perimeter and get perimeters within percent of median
+ """
+ conts_per = sorted(conts_per,key=lambda x:x[1])
+ med_per = conts_per[int(len(conts_per)/2)][1]
+ side = med_per/4
+ perc = 0.1
+ med_low,med_high = med_per*(1-perc),med_per*(1+perc)
+ squares = []
+ for i in conts_per:
+ if med_low <= i[1] and med_high >= i[1]:
+ squares.append(i[0])
+
+ """
+ obtain coordinates of nomralised macbeth and squares
+ """
+ square_verts,mac_norm = get_square_verts(0.06)
+ """
+ for each square guess, find 24 possible macbeth chart centres
+ """
+ mac_mids = []
+ squares_raw = []
+ for i in range(len(squares)):
+ square = squares[i]
+ squares_raw.append(square)
+ """
+ convert quads to rotated rectangles. This is required as the
+ 'squares' are usually quite irregular quadrilaterls, so performing
+ a transform would result in exaggerated warping and inaccurate
+ macbeth chart centre placement
+ """
+ rect = cv2.minAreaRect(square)
+ square = cv2.boxPoints(rect).astype(np.float32)
+ """
+ reorder vertices to prevent 'hourglass shape'
+ """
+ square = sorted(square,key=lambda x:x[0])
+ square_1 = sorted(square[:2],key=lambda x:x[1])
+ square_2 = sorted(square[2:],key=lambda x:-x[1])
+ square = np.array(np.concatenate((square_1,square_2)),np.float32)
+ square = np.reshape(square,(4,2)).astype(np.float32)
+ squares[i] = square
+ """
+ find 24 possible macbeth chart centres by trasnforming normalised
+ macbeth square vertices onto candidate square vertices found in image
+ """
+ for j in range(len(square_verts)):
+ verts = square_verts[j]
+ p_mat = cv2.getPerspectiveTransform(verts,square)
+ mac_guess = cv2.perspectiveTransform(mac_norm,p_mat)
+ mac_guess = np.round(mac_guess).astype(np.int32)
+ """
+ keep only if candidate macbeth is within image border
+ (deprecated)
+ """
+ in_border = True
+ # for p in mac_guess[0]:
+ # pptest = cv2.pointPolygonTest(
+ # img_con,
+ # tuple(p),
+ # False
+ # )
+ # if pptest == -1:
+ # in_border = False
+ # break
+
+ if in_border:
+ mac_mid = np.mean(mac_guess,
+ axis=1)
+ mac_mids.append([mac_mid,(i,j)])
+
+ if len(mac_mids) == 0:
+ raise MacbethError(
+ '\nWARNING: No macbeth chart found!'
+ '\nNo possible macbeth charts found within image'
+ '\nPossible problems:\n'
+ '- Part of the macbeth chart is outside the image\n'
+ '- Quadrilaterals in image background\n'
+ )
+
+ """
+ reshape data
+ """
+ for i in range(len(mac_mids)):
+ mac_mids[i][0] = mac_mids[i][0][0]
+
+ """
+ find where midpoints cluster to identify most likely macbeth centres
+ """
+ clustering = cluster.AgglomerativeClustering(
+ n_clusters=None,
+ compute_full_tree = True,
+ distance_threshold = side*2
+ )
+ mac_mids_list = [x[0] for x in mac_mids]
+
+ if len(mac_mids_list)==1:
+ """
+ special case of only one valid centre found (probably not needed)
+ """
+ clus_list = []
+ clus_list.append([mac_mids,len(mac_mids)])
+
+ else:
+ clustering.fit(mac_mids_list)
+ # try:
+ # clustering.fit(mac_mids_list)
+ # except RuntimeWarning as error:
+ # return(0,None,None,error)
+
+ """
+ create list of all clusters
+ """
+ clus_list = []
+ if clustering.n_clusters_ >1:
+ for i in range(clustering.labels_.max()+1):
+ indices = [j for j,x in enumerate(clustering.labels_) if x == i]
+ clus = []
+ for index in indices:
+ clus.append(mac_mids[index])
+ clus_list.append([clus,len(clus)])
+ clus_list.sort(key=lambda x:-x[1])
+
+ elif clustering.n_clusters_ == 1:
+ """
+ special case of only one cluster found
+ """
+ # print('only 1 cluster')
+ clus_list.append([mac_mids,len(mac_mids)])
+ else:
+ raise MacbethError(
+ '\nWARNING: No macebth chart found!'
+ '\nNo clusters found'
+ '\nPossible problems:\n'
+ '- NA\n'
+ )
+
+ """
+ keep only clusters with enough votes
+ """
+ clus_len_max = clus_list[0][1]
+ clus_tol= 0.7
+ for i in range(len(clus_list)):
+ if clus_list[i][1] < clus_len_max * clus_tol:
+ clus_list = clus_list[:i]
+ break
+ cent = np.mean(clus_list[i][0],axis=0)[0]
+ clus_list[i].append(cent)
+
+ """
+ represent most popular cluster centroids
+ """
+ # copy = original_bw.copy()
+ # copy = cv2.cvtColor(copy,cv2.COLOR_GRAY2RGB)
+ # copy = cv2.resize(copy,None,fx=2,fy=2)
+ # for clus in clus_list:
+ # centroid = tuple(2*np.round(clus[2]).astype(np.int32))
+ # cv2.circle(copy,centroid,7,(255,0,0),-1)
+ # cv2.circle(copy,centroid,2,(0,0,255),-1)
+ # represent(copy)
+
+ """
+ get centres of each normalised square
+ """
+ reference = get_square_centres(0.06)
+
+ """
+ for each possible macbeth chart, transform image into
+ normalised space and find correlation with reference
+ """
+ max_cor = 0
+ best_map = None
+ best_fit = None
+ best_cen_fit = None
+ best_ref_mat = None
+
+ for clus in clus_list:
+ clus = clus[0]
+ sq_cents = []
+ ref_cents = []
+ i_list = [p[1][0] for p in clus]
+ for point in clus:
+ i,j = point[1]
+ """
+ remove any square that voted for two different points within
+ the same cluster. This causes the same point in the image to be
+ mapped to two different reference square centres, resulting in
+ a very distorted perspective transform since cv2.findHomography
+ simply minimises error.
+ This phenomenon is not particularly likely to occur due to the
+ enforced distance threshold in the clustering fit but it is
+ best to keep this in just in case.
+ """
+ if i_list.count(i) == 1:
+ square = squares_raw[i]
+ sq_cent = np.mean(square,axis=0)
+ ref_cent = reference[j]
+ sq_cents.append(sq_cent)
+ ref_cents.append(ref_cent)
+
+ """
+ At least three squares need to have voted for a centre in
+ order for a transform to be found
+ """
+ if len(sq_cents) < 3:
+ raise MacbethError(
+ '\nWARNING: No macbeth chart found!'
+ '\nNot enough squares found'
+ '\nPossible problems:\n'
+ '- Macbeth chart is occluded\n'
+ '- Macbeth chart is too dark of bright\n'
+ )
+
+ ref_cents = np.array(ref_cents)
+ sq_cents = np.array(sq_cents)
+ """
+ find best fit transform from normalised centres to image
+ """
+ h_mat,mask = cv2.findHomography(ref_cents,sq_cents)
+ if 'None' in str(type(h_mat)):
+ raise MacbethError(
+ '\nERROR\n'
+ )
+
+ """
+ transform normalised corners and centres into image space
+ """
+ mac_fit = cv2.perspectiveTransform(mac_norm,h_mat)
+ mac_cen_fit = cv2.perspectiveTransform(np.array([reference]),h_mat)
+ """
+ transform located corners into reference space
+ """
+ ref_mat = cv2.getPerspectiveTransform(
+ mac_fit,
+ np.array([ref_corns])
+ )
+ map_to_ref = cv2.warpPerspective(
+ original_bw,ref_mat,
+ (ref_w,ref_h)
+ )
+ """
+ normalise brigthness
+ """
+ a = 125/np.average(map_to_ref)
+ map_to_ref = cv2.convertScaleAbs(map_to_ref,alpha=a,beta=0)
+ """
+ find correlation with bw reference macbeth
+ """
+ cor = correlate(map_to_ref,ref)
+ """
+ keep only if best correlation
+ """
+ if cor > max_cor:
+ max_cor = cor
+ best_map = map_to_ref
+ best_fit = mac_fit
+ best_cen_fit = mac_cen_fit
+ best_ref_mat = ref_mat
+
+ """
+ rotate macbeth by pi and recorrelate in case macbeth chart is
+ upside-down
+ """
+ mac_fit_inv = np.array(
+ ([[mac_fit[0][2],mac_fit[0][3],
+ mac_fit[0][0],mac_fit[0][1]]])
+ )
+ mac_cen_fit_inv = np.flip(mac_cen_fit,axis=1)
+ ref_mat = cv2.getPerspectiveTransform(
+ mac_fit_inv,
+ np.array([ref_corns])
+ )
+ map_to_ref = cv2.warpPerspective(
+ original_bw,ref_mat,
+ (ref_w,ref_h)
+ )
+ a = 125/np.average(map_to_ref)
+ map_to_ref = cv2.convertScaleAbs(map_to_ref,alpha=a,beta=0)
+ cor = correlate(map_to_ref,ref)
+ if cor > max_cor:
+ max_cor = cor
+ best_map = map_to_ref
+ best_fit = mac_fit_inv
+ best_cen_fit = mac_cen_fit_inv
+ best_ref_mat = ref_mat
+
+ """
+ Check best match is above threshold
+ """
+ cor_thresh = 0.6
+ if max_cor < cor_thresh:
+ raise MacbethError(
+ '\nWARNING: Correlation too low'
+ '\nPossible problems:\n'
+ '- Bad lighting conditions\n'
+ '- Macbeth chart is occluded\n'
+ '- Background is too noisy\n'
+ '- Macbeth chart is out of camera plane\n'
+ )
+ """
+ Following code is mostly representation for debugging purposes
+ """
+
+
+ """
+ draw macbeth corners and centres on image
+ """
+ copy = original.copy()
+ copy = cv2.resize(original,None,fx=2,fy=2)
+ # print('correlation = {}'.format(round(max_cor,2)))
+ for point in best_fit[0]:
+ point = np.array(point,np.float32)
+ point = tuple(2*np.round(point).astype(np.int32))
+ cv2.circle(copy,point,4,(255,0,0),-1)
+ for point in best_cen_fit[0]:
+ point = np.array(point,np.float32)
+ point = tuple(2*np.round(point).astype(np.int32))
+ cv2.circle(copy,point,4,(0,0,255),-1)
+ copy = copy.copy()
+ cv2.circle(copy,point,4,(0,0,255),-1)
+
+ """
+ represent coloured macbeth in reference space
+ """
+ best_map_col = cv2.warpPerspective(
+ original,best_ref_mat,(ref_w,ref_h)
+ )
+ best_map_col = cv2.resize(
+ best_map_col,None,fx=4,fy=4
+ )
+ a = 125/np.average(best_map_col)
+ best_map_col_norm = cv2.convertScaleAbs(
+ best_map_col,alpha=a,beta=0
+ )
+ # cv2.imshow('Macbeth',best_map_col)
+ # represent(copy)
+
+
+ """
+ rescale coordinates to original image size
+ """
+ fit_coords = (best_fit/factor,best_cen_fit/factor)
+
+ return(max_cor,best_map_col_norm,fit_coords,success_msg)
+
+ """
+ catch macbeth errors and continue with code
+ """
+ except MacbethError as error:
+ return(0,None,None,error)