You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1136 lines
39 KiB
1136 lines
39 KiB
{ |
|
"cells": [ |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"import cv2, math, os, json, traceback, io, time\n", |
|
"import numpy as np\n", |
|
"import matplotlib.pyplot as plt\n", |
|
"import scipy.signal\n", |
|
"\n", |
|
"print(cv2.__version__)\n", |
|
"\n", |
|
"IMAGE_DIR = '/home/johanv/nextcloud/projects/boggle2.0/cascademan/categories/5x5/images'\n", |
|
"IMAGE_FILE = '00170.jpg'\n", |
|
"\n", |
|
"RED = (0, 0, 255)\n", |
|
"BLUE = (255, 0, 0)\n", |
|
"GREEN = (0, 255, 0)\n", |
|
"YELLOW = (0, 255, 255)\n", |
|
"CONTOUR_THICKNESS = 2\n", |
|
"MAX_DISP_DIM = 500" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"#a generic error\n", |
|
"class BoggleError(Exception):\n", |
|
" def __init__(self, arg):\n", |
|
" self.strerror = arg\n", |
|
" self.args = {arg}\n", |
|
"\n", |
|
"def four_point_transform(image, pts, size):\n", |
|
" w = size\n", |
|
" h = size\n", |
|
" ntl = [0, 0]\n", |
|
" ntr = [w, 0]\n", |
|
" nbr = [w, h]\n", |
|
" nbl = [0, h]\n", |
|
" location = np.float32(pts)\n", |
|
" x0 = pts[0][0]\n", |
|
" x2 = pts[2][0]\n", |
|
" #make sure the board ends up rotated the correct way\n", |
|
" if x0 < x2:\n", |
|
" newLocation = np.float32([ntl, nbl, nbr, ntr])\n", |
|
" else:\n", |
|
" newLocation = np.float32([ntr, ntl, nbl, nbr])\n", |
|
" M = cv2.getPerspectiveTransform(location, newLocation)\n", |
|
" warpedimage = cv2.warpPerspective(image, M, (w, h))\n", |
|
" return warpedimage\n", |
|
"\n", |
|
"def resizeWithAspectRatio(image, maxDispDim, inter=cv2.INTER_AREA):\n", |
|
" w = image.shape[0] #TODO width and height are swapped here\n", |
|
" h = image.shape[1]\n", |
|
"\n", |
|
" ar = w / h\n", |
|
" #print(ar)\n", |
|
" if w > h:\n", |
|
" nw = maxDispDim\n", |
|
" nh = int(maxDispDim / ar)\n", |
|
" elif h > w:\n", |
|
" nh = maxDispDim\n", |
|
" nw = int(maxDispDim / ar)\n", |
|
" else:\n", |
|
" nh = maxDispDim\n", |
|
" nw = maxDispDim\n", |
|
"\n", |
|
" dim = None\n", |
|
" (h, w) = image.shape[:2]\n", |
|
"\n", |
|
" r = nw / float(w)\n", |
|
" dim = (nw, int(h * r))\n", |
|
"\n", |
|
" return cv2.resize(image, dim, interpolation=inter)\n", |
|
"\n", |
|
"def imshow_fit(name, img, maxDispDim=MAX_DISP_DIM):\n", |
|
" if maxDispDim is not None:\n", |
|
" img = resizeWithAspectRatio(img, maxDispDim)\n", |
|
" cv2.imshow(name, img)\n", |
|
"\n", |
|
"def findBestCtr(contours):\n", |
|
" bestArea = 0\n", |
|
" bestCtr = None\n", |
|
" for i, ctr in enumerate(contours):\n", |
|
" area = cv2.contourArea(ctr)\n", |
|
" # perimeter = cv2.arcLength(ctr, True)\n", |
|
" if area > bestArea:\n", |
|
" bestArea = area\n", |
|
" bestCtr = ctr\n", |
|
" return bestCtr\n", |
|
"\n", |
|
"\n", |
|
"def angleEveryFew(ctr, step):\n", |
|
" angles = []\n", |
|
" # dists = []\n", |
|
" prev = ctr[-1]\n", |
|
" for i in range(0, len(ctr), step):\n", |
|
" curr = ctr[i]\n", |
|
" px, py = prev[0]\n", |
|
" cx, cy = curr[0]\n", |
|
" # angle = (px-cx)/(py-cy)\n", |
|
" angle = math.atan2(cy - py, cx - px)\n", |
|
" angles.append(angle)\n", |
|
" # dist = math.hypot(cx-px, cy-py)\n", |
|
" # dists.append(dist)\n", |
|
" prev = curr\n", |
|
" return angles\n", |
|
"\n", |
|
"\n", |
|
"def angleAvg(angles):\n", |
|
" x = 0\n", |
|
" y = 0\n", |
|
" for angle in angles:\n", |
|
" x += math.cos(angle)\n", |
|
" y += math.sin(angle)\n", |
|
" return math.atan2(y, x)\n", |
|
"\n", |
|
"\n", |
|
"def angleDiffAbs(angle1, angle2):\n", |
|
" return abs(((angle1 - angle2 + math.pi) % (2 * math.pi)) - math.pi)\n", |
|
"\n", |
|
"\n", |
|
"def runningAvg(angles, history):\n", |
|
" result = []\n", |
|
" for i in range(len(angles)):\n", |
|
" # avg = 0\n", |
|
" vals2 = []\n", |
|
" for j in range(history):\n", |
|
" val = angles[int(i + j - history / 2) % len(angles)]\n", |
|
" vals2.append(val)\n", |
|
" # avg += val\n", |
|
" # avg /= history\n", |
|
" avg = angleAvg(vals2)\n", |
|
" result.append(avg)\n", |
|
" return result\n", |
|
"\n", |
|
"\n", |
|
"def diffAbs(vals):\n", |
|
" diffs = []\n", |
|
" prev = vals[-1]\n", |
|
" for i in range(0, len(vals), 1):\n", |
|
" curr = vals[i]\n", |
|
" diff = angleDiffAbs(prev, curr) * 10\n", |
|
" diffs.append(diff)\n", |
|
" prev = curr\n", |
|
" return diffs\n", |
|
"\n", |
|
"\n", |
|
"def debounce(bools, history):\n", |
|
" result = []\n", |
|
" for i in range(len(bools)):\n", |
|
" total = 0\n", |
|
" for j in range(history):\n", |
|
" val = bools[int(i + j - history / 2) % len(bools)]\n", |
|
" total += val\n", |
|
" result.append(int(total >= history / 2))\n", |
|
" return result\n", |
|
"\n", |
|
"\n", |
|
"def findGaps(diffs):\n", |
|
" wasGap = True\n", |
|
" seamI = 0\n", |
|
" for i, diff in enumerate(diffs):\n", |
|
" isGap = diff\n", |
|
" if not isGap and not wasGap:\n", |
|
" seamI = i\n", |
|
" break\n", |
|
" wasGap = isGap\n", |
|
"\n", |
|
" xs = range(len(diffs))\n", |
|
" xs = [x for x in xs]\n", |
|
" xs_a = xs[seamI:]\n", |
|
" xs_a.extend(xs[:seamI])\n", |
|
" diffs_a = diffs[seamI:]\n", |
|
" diffs_a.extend(diffs[:seamI])\n", |
|
"\n", |
|
" xs2 = []\n", |
|
" diffs2 = []\n", |
|
" gapsStart = []\n", |
|
" gapsStartY = []\n", |
|
" gapsEnd = []\n", |
|
" gapsEndY = []\n", |
|
" wasGap = None\n", |
|
" gapwidth = 0\n", |
|
" for x, diff in zip(xs_a, diffs_a):\n", |
|
" isGap = diff\n", |
|
" if wasGap is None:\n", |
|
" wasGap = isGap\n", |
|
" if isGap:\n", |
|
" xs2.append(x)\n", |
|
" diffs2.append(diff)\n", |
|
" gapwidth += 1\n", |
|
" if isGap and not wasGap:\n", |
|
" gapsStart.append(x)\n", |
|
" gapsStartY.append(.5)\n", |
|
" if not isGap and wasGap:\n", |
|
" gapsEnd.append(x)\n", |
|
" gapsEndY.append(gapwidth)\n", |
|
" gapwidth = 0\n", |
|
" wasGap = isGap\n", |
|
" return gapsStart, gapsStartY, gapsEnd, gapsEndY, diffs2, xs2\n", |
|
" # gaps = [i for i in zip(gapsStart, gapsEnd, gapsEndY)]\n", |
|
" # return gaps, diffs, xs2\n", |
|
"\n", |
|
"\n", |
|
"def top4gaps(gaps):\n", |
|
" def length_sort(gap):\n", |
|
" return -gap[2]\n", |
|
"\n", |
|
" gaps2 = sorted(gaps, key=length_sort)\n", |
|
" gaps2 = gaps2[:4]\n", |
|
" return [i for i in gaps if i in gaps2]\n", |
|
"\n", |
|
"\n", |
|
"def invertGaps(gaps):\n", |
|
" segments = []\n", |
|
" prev = gaps[-1]\n", |
|
" for curr in gaps:\n", |
|
" segments.append((prev[1], curr[0]))\n", |
|
" prev = curr\n", |
|
" return segments\n", |
|
"\n", |
|
"\n", |
|
"def findSidePoints(segments, ctr, step):\n", |
|
" sidePoints = []\n", |
|
" for seg in segments:\n", |
|
" if seg[1] > seg[0]:\n", |
|
" sidePoints.append(ctr[seg[0] * step:seg[1] * step])\n", |
|
" else:\n", |
|
" sidePoints.append(ctr[seg[0] * step:, :seg[1] * step])\n", |
|
" return sidePoints\n", |
|
"\n", |
|
"\n", |
|
"def getEndVals(arr, fraction):\n", |
|
" if fraction >= 0.5: return arr\n", |
|
" l = len(arr)\n", |
|
" keep = int(fraction * l)\n", |
|
" keep = max(keep, 1)\n", |
|
" return np.concatenate((arr[:keep], arr[l - keep:]))\n", |
|
"\n", |
|
"\n", |
|
"def fitSidePointsToLines(sidePoints):\n", |
|
" lines = []\n", |
|
" for sp in sidePoints:\n", |
|
" xs = np.zeros(len(sp), int)\n", |
|
" ys = np.zeros(len(sp), int)\n", |
|
" for i, pt in enumerate(sp):\n", |
|
" x, y = pt[0] #TODO if pt is empty\n", |
|
" xs[i] = x\n", |
|
" ys[i] = y\n", |
|
" lines.append(np.polyfit(xs, ys, 1))\n", |
|
" return lines\n", |
|
"\n", |
|
"\n", |
|
"def findCorners(lines):\n", |
|
" points = []\n", |
|
"\n", |
|
" prev = lines[-1]\n", |
|
" for curr in lines:\n", |
|
" a1, b1 = prev\n", |
|
" a2, b2 = curr\n", |
|
"\n", |
|
" x = (b2 - b1) / (a1 - a2)\n", |
|
" y = np.polyval(curr, x)\n", |
|
" #if math.isnan(x): x = 0 #TODO\n", |
|
" #if math.isnan(y): y = 0\n", |
|
" points.append((int(x), int(y)))\n", |
|
" prev = curr\n", |
|
" return points\n", |
|
"\n", |
|
"\n", |
|
"def drawLinesAndPoints(image, lines, points):\n", |
|
" width = image.shape[1]\n", |
|
"\n", |
|
" for l in lines:\n", |
|
" y1 = int(np.polyval(l, 0))\n", |
|
" y2 = int(np.polyval(l, width - 1))\n", |
|
" cv2.line(image, (0, y1), (width - 1, y2), RED, CONTOUR_THICKNESS)\n", |
|
"\n", |
|
" for point in points:\n", |
|
" cv2.circle(image, point, 4, BLUE, 3)\n", |
|
"\n", |
|
"\n", |
|
"def contourPlot(xs, xs2, angles, anglesAvg, diffs, diffs2, gapsStart, gapsStartY, gapsEnd, gapsEndY2, normalPlots):\n", |
|
" fig = plt.figure(figsize=(8,10))\n", |
|
" ax1 = fig.add_subplot(111)\n", |
|
"\n", |
|
" # ax1.scatter(xs, dists, s=10, c='b', marker=\"s\", label=\"dists\")\n", |
|
" ax1.scatter(xs, angles, s=10, c='r', marker=\"o\", label=\"angles\")\n", |
|
" ax1.scatter(xs, anglesAvg, s=10, c='b', marker=\"o\", label=\"anglesAvg\")\n", |
|
" ax1.scatter(xs, diffs, s=10, c='g', marker=\"o\", label=\"diffs\")\n", |
|
" ax1.scatter(xs2, diffs2, s=10, c='m', marker=\"s\", label=\"diffs2\")\n", |
|
" ax1.scatter(gapsStart, gapsStartY, s=10, c='c', marker=\"o\", label=\"gapsStart\")\n", |
|
" if gapsEndY2 is not None:\n", |
|
" ax1.scatter(gapsEnd, gapsEndY2, s=10, c='y', marker=\"o\", label=\"gapsEnd\")\n", |
|
" plt.legend(loc='best')\n", |
|
" if normalPlots:\n", |
|
" plt.show(block=False)\n", |
|
" return None\n", |
|
" else:\n", |
|
" return plotToImg()\n", |
|
"\n", |
|
"def waitForKey():\n", |
|
" while True:\n", |
|
" key = cv2.waitKey(0)\n", |
|
" print(\"key\", key)\n", |
|
" if key == 27: # esc\n", |
|
" cv2.destroyAllWindows()\n", |
|
" quit()\n", |
|
" if key == ord(\" \") or key == ord(\"q\"):\n", |
|
" break\n", |
|
" cv2.destroyAllWindows()\n", |
|
" plt.close('all')\n", |
|
"\n", |
|
"def waitForConsoleEnter():\n", |
|
" print(\"=== hit enter to continue ===\")\n", |
|
" input()\n", |
|
" print(\"=== continuing ===\")\n", |
|
" cv2.destroyAllWindows()\n", |
|
" plt.close('all')\n", |
|
"\n", |
|
"#https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html\n", |
|
"#window: np.ones (flat), np.hanning, np.hamming, np.bartlett, np.blackman\n", |
|
"def smooth(x, window_len=11, window=np.hanning):\n", |
|
" if x.ndim != 1:\n", |
|
" raise ValueError(\"smooth only accepts 1 dimension arrays.\")\n", |
|
"\n", |
|
" if x.size < window_len:\n", |
|
" raise ValueError(\"Input vector needs to be bigger than window size.\")\n", |
|
"\n", |
|
" if window_len<3:\n", |
|
" return x\n", |
|
"\n", |
|
" s=np.r_[x[window_len-1:0:-1], x, x[-2:-window_len-1:-1]]\n", |
|
" w = window(window_len)\n", |
|
" y = np.convolve(w / w.sum(), s, mode='valid')\n", |
|
" return y\n", |
|
"\n", |
|
"\n", |
|
"def findRowsOrCols(img, doCols, smoothFactor, ax):\n", |
|
" smoothFactor = int(smoothFactor * img.shape[0])\n", |
|
" #print(\"smoothFactor\", smoothFactor)\n", |
|
" \n", |
|
" if doCols:\n", |
|
" title = \"colSum\"\n", |
|
" imgSum = cv2.reduce(img, 0, cv2.REDUCE_AVG, dtype=cv2.CV_32S)\n", |
|
" imgSum = imgSum[0]\n", |
|
" else:\n", |
|
" #row sum\n", |
|
" title = \"rowSum\"\n", |
|
" imgSum = cv2.reduce(img, 1, cv2.REDUCE_AVG, dtype=cv2.CV_32S)\n", |
|
" imgSum = imgSum.reshape(len(imgSum))\n", |
|
" \n", |
|
" imgSumSmooth = smooth(imgSum, smoothFactor*2)\n", |
|
"\n", |
|
" #https://qingkaikong.blogspot.com/2018/07/find-peaks-in-data.html\n", |
|
" #peaks_positive, _ = scipy.signal.find_peaks(imgSumSmooth, height=200, threshold = None, distance=60)\n", |
|
" dips, props = scipy.signal.find_peaks(-imgSumSmooth, height=(None,None), distance=30, prominence=(None,None))\n", |
|
" \n", |
|
" #threshold=(None,None), \n", |
|
" #, plateau_size=(None,None)\n", |
|
" \n", |
|
" #print(props)\n", |
|
"\n", |
|
" prs = props[\"prominences\"]\n", |
|
" if len(prs) < 6:\n", |
|
" top_6_dips = dips\n", |
|
" #print (\"!!!! less than 6 dips\")\n", |
|
" #raise BoggleError(\"less than 6 dips\")\n", |
|
" else:\n", |
|
" prsIdx = sorted(range(len(prs)), key=lambda i: prs[i], reverse=True)\n", |
|
" #print(prsIdx)\n", |
|
" prsIdx = prsIdx[:6]\n", |
|
" #print(prsIdx)\n", |
|
" top_6_dips = [p for i,p in enumerate(dips) if i in prsIdx]\n", |
|
"\n", |
|
" #fig = plt.figure()\n", |
|
" #ax1 = fig.add_subplot(111)\n", |
|
" \n", |
|
" if ax is not None:\n", |
|
" q = [i for i in range(len(imgSumSmooth))]\n", |
|
" \n", |
|
" ax.plot(q, imgSumSmooth, 'b-', linewidth=2, label=\"smooth\")\n", |
|
" ax.plot(np.linspace(smoothFactor,len(imgSumSmooth)-smoothFactor, len(imgSum)), imgSum, 'r-', linewidth=1, label=title)\n", |
|
"\n", |
|
" #ax.plot(\n", |
|
" #[q[p] for p in peaks_positive],\n", |
|
" #[imgSumSmooth[p] for p in peaks_positive],\n", |
|
" #'ro', label = 'positive peaks')\n", |
|
" \n", |
|
" ax.plot(\n", |
|
" [q[p] for p in dips],\n", |
|
" [imgSumSmooth[p] for p in dips],\n", |
|
" 'go', label='dips')\n", |
|
" \n", |
|
" ax.plot(\n", |
|
" [q[p] for p in top_6_dips],\n", |
|
" [imgSumSmooth[p] for p in top_6_dips],\n", |
|
" 'c.', label='top 6 dips')\n", |
|
" \n", |
|
" ax.legend(loc='best')\n", |
|
" \n", |
|
" #return top_6_dips\n", |
|
" #print(\"before clip\", top_6_dips)\n", |
|
" top_6_dips_scaled = [np.clip(0, p-smoothFactor, len(imgSum)-1) for p in top_6_dips]\n", |
|
" return top_6_dips_scaled\n", |
|
"\n", |
|
"def plotToImg():\n", |
|
" #https://stackoverflow.com/questions/5314707/matplotlib-store-image-in-variable\n", |
|
" buf = io.BytesIO()\n", |
|
" plt.savefig(buf, format='png', bbox_inches='tight')\n", |
|
" plt.close()\n", |
|
" buf.seek(0)\n", |
|
" \n", |
|
" #https://stackoverflow.com/questions/11552926/how-to-read-raw-png-from-an-array-in-python-opencv\n", |
|
" file_bytes = np.asarray(bytearray(buf.read()), dtype=np.uint8)\n", |
|
" img_data_ndarray = cv2.imdecode(file_bytes, cv2.IMREAD_UNCHANGED)\n", |
|
" #img_data_cvmat = cv.fromarray(img_data_ndarray) # convert to old cvmat if needed\n", |
|
"\n", |
|
" img_rgb = cv2.cvtColor(img_data_ndarray, cv2.COLOR_RGBA2RGB)\n", |
|
" return img_rgb" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"def findBoggleBoard(image, normalPlots=True, harshErrors=False, generate=(\"debugimage\", \"debugmask\", \"contourPlotImg\", \"warpedimage\", \"imgSumPlotImg\", \"diceRaw\", \"dice\")):\n", |
|
" resultImages = {}\n", |
|
"\n", |
|
" # maskThresholdMin = (108, 28, 12)\n", |
|
" # maskThresholdMax = (125, 255, 241)\n", |
|
" # maskThresholdMin = (108, 28, 6)\n", |
|
" # maskThresholdMax = (130, 255, 241)\n", |
|
" maskThresholdMin = (108, 28, 6)\n", |
|
" maskThresholdMax = (144, 255, 241)\n", |
|
" size = max(image.shape)\n", |
|
" #print(\"size\", size)\n", |
|
" blurAmount = int(.02 * size)\n", |
|
" blurAmount = (blurAmount, blurAmount)\n", |
|
" # blurThreshold = 80\n", |
|
" blurThreshold = 40\n", |
|
" contourApprox = cv2.CHAIN_APPROX_NONE\n", |
|
" # contourApprox = cv2.CHAIN_APPROX_SIMPLE\n", |
|
" # contourApprox = cv2.CHAIN_APPROX_TC89_L1\n", |
|
" # contourApprox = cv2.CHAIN_APPROX_TC89_KCOS\n", |
|
"\n", |
|
" hsvimg = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)\n", |
|
" mask = cv2.inRange(hsvimg, maskThresholdMin, maskThresholdMax)\n", |
|
"\n", |
|
" maskblur = cv2.blur(mask, blurAmount)\n", |
|
" # maskblur = cv2.threshold(maskblur, 80, 255, cv2.THRESH_BINARY_INV)\n", |
|
" maskblur = cv2.inRange(maskblur, (blurThreshold,), (255,))\n", |
|
" \n", |
|
" #API CHANGE: findContours no longer returns a modified image\n", |
|
" #contourimg, contours, hierarchy = cv2.findContours(maskblur, cv2.RETR_LIST, contourApprox)\n", |
|
" contours, hierarchy = cv2.findContours(maskblur, cv2.RETR_LIST, contourApprox)\n", |
|
" # print(hierarchy) #TODO\n", |
|
" \n", |
|
" bestCtr = findBestCtr(contours)\n", |
|
" # bestCtr = cv2.convexHull(bestCtr)\n", |
|
"\n", |
|
" #draw the contours for debugging\n", |
|
" if \"debugimage\" in generate:\n", |
|
" debugimage = image.copy()\n", |
|
" cv2.drawContours(debugimage, contours, -1, RED, CONTOUR_THICKNESS)\n", |
|
" cv2.drawContours(debugimage, [bestCtr], -1, BLUE, CONTOUR_THICKNESS)\n", |
|
" if \"debugmask\" in generate:\n", |
|
" debugmask = cv2.cvtColor(maskblur, cv2.COLOR_GRAY2BGR)\n", |
|
" cv2.drawContours(debugmask, contours, -1, RED, CONTOUR_THICKNESS)\n", |
|
" cv2.drawContours(debugmask, [bestCtr], -1, BLUE, CONTOUR_THICKNESS)\n", |
|
" \n", |
|
"\n", |
|
" step = 10\n", |
|
" avgWindow = 0.1\n", |
|
" debounceFactor = .05\n", |
|
" \n", |
|
" angles = angleEveryFew(bestCtr, step)\n", |
|
"\n", |
|
" xs = range(len(angles))\n", |
|
"\n", |
|
" anglesAvg = runningAvg(angles, int(avgWindow * len(angles)))\n", |
|
" diffs = diffAbs(anglesAvg)\n", |
|
" \n", |
|
" avgDiff = np.mean(diffs)\n", |
|
" #print(avgDiff)\n", |
|
" \n", |
|
" binDiffs = [int(i > avgDiff) for i in diffs]\n", |
|
" binDiffs = debounce(binDiffs, int(len(binDiffs) * debounceFactor))\n", |
|
" gapsStart, gapsStartY, gapsEnd, gapsEndY, diffs2, xs2 = findGaps(binDiffs)\n", |
|
" gaps = [i for i in zip(gapsStart, gapsEnd, gapsEndY)]\n", |
|
" \n", |
|
" #scale for viewing on the plot\n", |
|
" gapsEndY2 = gapsEndY\n", |
|
" if len(gapsEndY) > 0:\n", |
|
" q = max(gapsEndY)\n", |
|
" if q > 6: gapsEndY2 = [i * 6 / q for i in gapsEndY]\n", |
|
"\n", |
|
" if \"contourPlotImg\" in generate:\n", |
|
" contourPlotImg = contourPlot(xs, xs2, angles, anglesAvg, diffs, diffs2, gapsStart, gapsStartY, gapsEnd, gapsEndY2, normalPlots)\n", |
|
" if contourPlotImg is not None:\n", |
|
" resultImages[\"contourPlotImg\"] = contourPlotImg\n", |
|
" \n", |
|
" if len(gaps) < 4:\n", |
|
" print(\"!!!! less than 4 gaps\")\n", |
|
" if harshErrors:\n", |
|
" raise BoggleError(\"less than 4 gaps\")\n", |
|
" if \"contourPlotImg\" in generate:\n", |
|
" contourPlotImg = contourPlot(xs, xs2, angles, anglesAvg, diffs, diffs2, gapsStart, gapsStartY, gapsEnd, None, normalPlots)\n", |
|
" if contourPlotImg is not None:\n", |
|
" resultImages[\"contourPlotImg\"] = contourPlotImg\n", |
|
" \n", |
|
" if \"debugmask\" in generate:\n", |
|
" resultImages[\"debugmask\"] = debugmask\n", |
|
" if \"debugimage\" in generate:\n", |
|
" resultImages[\"debugimage\"] = debugimage\n", |
|
" return resultImages, None\n", |
|
" \n", |
|
" endFraction = 0.01\n", |
|
" \n", |
|
" gaps = top4gaps(gaps)\n", |
|
" segments = invertGaps(gaps)\n", |
|
" sidePoints = findSidePoints(segments, bestCtr, step)\n", |
|
" sidePoints = [getEndVals(sp, endFraction) for sp in sidePoints]\n", |
|
" #print(\"sidepoints len\", len(sidePoints[0]))\n", |
|
" \n", |
|
" \n", |
|
" lines = fitSidePointsToLines(sidePoints)\n", |
|
" points = findCorners(lines)\n", |
|
" if \"debugimage\" in generate:\n", |
|
" cv2.drawContours(debugimage, sidePoints, -1, YELLOW, CONTOUR_THICKNESS)\n", |
|
" drawLinesAndPoints(debugimage, lines, points)\n", |
|
" resultImages[\"debugimage\"] = debugimage\n", |
|
" if \"debugmask\" in generate:\n", |
|
" cv2.drawContours(debugmask, sidePoints, -1, YELLOW, CONTOUR_THICKNESS)\n", |
|
" drawLinesAndPoints(debugmask, lines, points)\n", |
|
" resultImages[\"debugmask\"] = debugmask\n", |
|
"\n", |
|
" npPoints = np.array(points)\n", |
|
" size = 300\n", |
|
" warpedimage = four_point_transform(image, npPoints, size)\n", |
|
" warpgray = cv2.cvtColor(warpedimage, cv2.COLOR_BGR2GRAY)\n", |
|
" \n", |
|
" if \"warpedimage\" in generate:\n", |
|
" resultImages[\"warpedimage\"] = warpedimage\n", |
|
" \n", |
|
" if \"warpgray\" in generate:\n", |
|
" resultImages[\"warpgray\"] = warpgray\n", |
|
" \n", |
|
" smoothFactor = .05\n", |
|
" \n", |
|
" if \"imgSumPlotImg\" in generate:\n", |
|
" fig, (ax0, ax1) = plt.subplots(2, figsize=(8,10))\n", |
|
" else:\n", |
|
" ax0 = ax1 = None\n", |
|
" \n", |
|
" rowSumLines = findRowsOrCols(warpgray, False, smoothFactor, ax0)\n", |
|
" #print(\"rows\", rowSumLines)\n", |
|
" colSumLines = findRowsOrCols(warpgray, True, smoothFactor, ax1)\n", |
|
" #print(\"cols\", colSumLines)\n", |
|
" \n", |
|
" \n", |
|
" if \"imgSumPlotImg\" in generate:\n", |
|
" if normalPlots:\n", |
|
" plt.show(block=False)\n", |
|
" else:\n", |
|
" resultImages[\"imgSumPlotImg\"] = plotToImg()\n", |
|
"\n", |
|
"\n", |
|
" if len(rowSumLines) < 6 or len(colSumLines) < 6:\n", |
|
" print(\"!!!! not enough grid lines\")\n", |
|
" if harshErrors:\n", |
|
" raise BoggleError(\"not enough gridlines\")\n", |
|
" return resultImages, None\n", |
|
" \n", |
|
" #fix the outermost lines of the board\n", |
|
" h1 = rowSumLines[2] - rowSumLines[1]\n", |
|
" h2 = rowSumLines[3] - rowSumLines[2]\n", |
|
" h3 = rowSumLines[4] - rowSumLines[3]\n", |
|
" h = max(h1, h2, h3)\n", |
|
" \n", |
|
" newCSL0 = colSumLines[1] - h\n", |
|
" if newCSL0 > colSumLines[0]:\n", |
|
" colSumLines[0] = newCSL0\n", |
|
" newCSL5 = colSumLines[4] + h\n", |
|
" if newCSL5 < colSumLines[5]:\n", |
|
" colSumLines[5] = newCSL5\n", |
|
" \n", |
|
" w1 = colSumLines[2] - colSumLines[1]\n", |
|
" w2 = colSumLines[3] - colSumLines[2]\n", |
|
" w3 = colSumLines[4] - colSumLines[3]\n", |
|
" w = max(w1, w2, w3)\n", |
|
" \n", |
|
" newRSL0 = rowSumLines[1] - w\n", |
|
" if newRSL0 > rowSumLines[0]:\n", |
|
" rowSumLines[0] = newRSL0\n", |
|
" newRSL5 = rowSumLines[4] + w\n", |
|
" if newRSL5 < rowSumLines[5]:\n", |
|
" rowSumLines[5] = newRSL5\n", |
|
"\n", |
|
" #print(\"rows2\", rowSumLines)\n", |
|
" #print(\"cols2\", colSumLines)\n", |
|
"\n", |
|
" #just display\n", |
|
" if \"diceRaw\" in generate:\n", |
|
" plt.figure(figsize=(10,10))\n", |
|
" i = 1\n", |
|
" for y in range(5):\n", |
|
" for x in range(5):\n", |
|
" plt.subplot(5,5,i)\n", |
|
" i += 1\n", |
|
" plt.xticks([])\n", |
|
" plt.yticks([])\n", |
|
" plt.grid(False)\n", |
|
" minX = colSumLines[x]\n", |
|
" maxX = colSumLines[x+1]\n", |
|
" minY = rowSumLines[y]\n", |
|
" maxY = rowSumLines[y+1]\n", |
|
" crop_img = warpgray[minY:maxY, minX:maxX]\n", |
|
" plt.imshow(crop_img, cmap=plt.cm.gray)\n", |
|
" if normalPlots:\n", |
|
" plt.show(block=False)\n", |
|
" else:\n", |
|
" resultImages[\"diceRaw\"] = plotToImg()\n", |
|
"\n", |
|
" \n", |
|
" if \"dice\" in generate:\n", |
|
" plt.figure(figsize=(10,10))\n", |
|
" i = 1\n", |
|
"\n", |
|
" letterResize = 30\n", |
|
" #make square, resize, display, and save to an array\n", |
|
" letterImgs = []\n", |
|
" for y in range(5):\n", |
|
" letterImgRow = []\n", |
|
" for x in range(5):\n", |
|
" minX = colSumLines[x]\n", |
|
" maxX = colSumLines[x+1]\n", |
|
" minY = rowSumLines[y]\n", |
|
" maxY = rowSumLines[y+1]\n", |
|
" w = maxX - minX\n", |
|
" h = maxY - minY\n", |
|
" #print(\"w,h 1:\", w, h)\n", |
|
" d = abs(w - h)\n", |
|
" if d > 0:\n", |
|
" if int(d/2) == d/2:\n", |
|
" #even difference\n", |
|
" d1 = d2 = int(d/2)\n", |
|
" else:\n", |
|
" #odd difference\n", |
|
" d1 = int((d-1)/2)\n", |
|
" d2 = int((d+1)/2)\n", |
|
" if w > h:\n", |
|
" #wider than it is tall\n", |
|
" minX += d1\n", |
|
" maxX -= d2\n", |
|
" else:\n", |
|
" #taller than it is wide\n", |
|
" minY += d1\n", |
|
" maxY -= d2\n", |
|
" #print(\"w,h 2:\", maxX-minX, maxY-minY)\n", |
|
" crop_img = warpgray[minY:maxY, minX:maxX]\n", |
|
" letterImg = cv2.resize(crop_img, (letterResize,letterResize), interpolation=cv2.INTER_AREA)\n", |
|
" if \"dice\" in generate:\n", |
|
" plt.subplot(5,5,i)\n", |
|
" i += 1\n", |
|
" plt.xticks([])\n", |
|
" plt.yticks([])\n", |
|
" plt.grid(False)\n", |
|
" plt.imshow(letterImg, cmap=plt.cm.gray)\n", |
|
" letterImgRow.append(letterImg)\n", |
|
" letterImgs.append(letterImgRow)\n", |
|
"\n", |
|
" if \"dice\" in generate:\n", |
|
" if normalPlots:\n", |
|
" plt.show(block=False)\n", |
|
" else:\n", |
|
" resultImages[\"dice\"] = plotToImg()\n", |
|
" \n", |
|
" return resultImages, letterImgs\n", |
|
"\n", |
|
"def findAndShowBoggleBoard(imgDir, imgFilename):\n", |
|
" imgPath = imgDir + \"/\" + imgFilename\n", |
|
"\n", |
|
" #if True:\n", |
|
" try:\n", |
|
" print(imgPath)\n", |
|
" image = cv2.imread(imgPath)\n", |
|
" generate = (\"debugimage\", \"debugmask\", \"contourPlotImg\", \"warpedimage\", \"imgSumPlotImg\", \"diceRaw\", \"dice\")\n", |
|
" #generate = (\"dice\")\n", |
|
" #normalPlots = False\n", |
|
" normalPlots = True\n", |
|
" #harshErrors = False\n", |
|
" harshErrors = True\n", |
|
" imgs, letterImgs = findBoggleBoard(image, normalPlots, harshErrors, generate)\n", |
|
" for title, img in imgs.items():\n", |
|
" #print(\"title, img:\", title, img)\n", |
|
" #cv2.imshow(title, img)\n", |
|
" imshow_fit(title, img)\n", |
|
" if len(imgs) > 0:\n", |
|
" waitForKey()\n", |
|
" else:\n", |
|
" waitForConsoleEnter()\n", |
|
" return letterImgs\n", |
|
" except Exception as e:\n", |
|
" #print(str(e))\n", |
|
" print(traceback.format_exc())\n", |
|
" waitForConsoleEnter()\n", |
|
" return None" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"#process 1 image\n", |
|
"letters5x5grid = findAndShowBoggleBoard(IMAGE_DIR, IMAGE_FILE)\n", |
|
"print(len(letters5x5grid[0][0]))" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"#https://www.tensorflow.org/tutorials/keras/classification\n", |
|
"\n", |
|
"from __future__ import absolute_import, division, print_function, unicode_literals\n", |
|
"\n", |
|
"# TensorFlow and tf.keras\n", |
|
"import tensorflow as tf\n", |
|
"from tensorflow import keras\n", |
|
"from tensorflow.keras import datasets, layers, models\n", |
|
"\n", |
|
"# Helper libraries\n", |
|
"import numpy as np\n", |
|
"import matplotlib.pyplot as plt\n", |
|
"\n", |
|
"import json\n", |
|
"\n", |
|
"#DATA_FILE=\"/home/johanv/downloads/labelled.json\"\n", |
|
"DATA_FILE=\"/home/johanv/nextcloud/projects/boggle2.0/labelled.json\"\n", |
|
"#DATA_FILE=\"/home/johanv/nextcloud/projects/boggle2.0/labelled-20200124_155523.mp4.json\"\n", |
|
"\n", |
|
"#import os\n", |
|
"#DATA_FILE = \"/labelled.json\"\n", |
|
"#DATA_URL = \"https://drive.confuzer.cloud/index.php/s/2fQ5KkGi3Bi2SLq/download\"\n", |
|
"#os.system(\"curl \" + DATA_URL + \" > \" + DATA_FILE)\n", |
|
"\n", |
|
"MODEL_SAVE_FILE=\"/home/johanv/nextcloud/projects/boggle2.0/model.h5\"" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"print(tf.__version__)\n", |
|
"\n", |
|
"#fashion_mnist = keras.datasets.fashion_mnist\n", |
|
"#(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()\n", |
|
"#class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',\n", |
|
"# 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\n", |
|
"\n", |
|
"with open(DATA_FILE, 'r') as f:\n", |
|
" data = json.load(f)\n", |
|
"\n", |
|
"IMG_DIM = 30\n", |
|
"\n", |
|
"images_in = data[\"imgs\"]\n", |
|
"labels_in = data[\"labels\"]\n", |
|
"\n", |
|
"images = []\n", |
|
"labels = []\n", |
|
"for rot in range(4):\n", |
|
" for i in range(len(images_in)):\n", |
|
" #https://artemrudenko.wordpress.com/2014/08/28/python-rotate-2d-arraymatrix-90-degrees-one-liner/\n", |
|
" images_in[i] = list(zip(*images_in[i][::-1])) #rotate 90 degrees (still the same letter!)\n", |
|
" images.extend(images_in)\n", |
|
" labels.extend(labels_in)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"images = np.array(images, dtype=np.uint8).reshape((-1, IMG_DIM, IMG_DIM, 1))\n", |
|
"\n", |
|
"split = int(0.15 * len(images))\n", |
|
"\n", |
|
"train_images = np.array(images[split:],dtype=np.uint8)\n", |
|
"train_labels = np.array(labels[split:],dtype=np.uint8)\n", |
|
"\n", |
|
"test_images = np.array(images[:split],dtype=np.uint8)\n", |
|
"test_labels = np.array(labels[:split],dtype=np.uint8)\n", |
|
"\n", |
|
"class_names = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n", |
|
"\n", |
|
"L = len(class_names)\n", |
|
"\n", |
|
"print(train_images.shape)\n", |
|
"print(train_labels.shape)\n", |
|
"print(test_images.shape)\n", |
|
"print(test_labels.shape)\n", |
|
"\n", |
|
"# plt.figure()\n", |
|
"# plt.imshow(train_images[1])\n", |
|
"# plt.colorbar()\n", |
|
"# plt.grid(False)\n", |
|
"# plt.show()\n", |
|
"\n", |
|
"train_images = train_images / 255.0\n", |
|
"test_images = test_images / 255.0\n" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"plt.figure(figsize=(10,10))\n", |
|
"for i in range(25):\n", |
|
" print(\"{}/25\".format(i))\n", |
|
" plt.subplot(5,5,i+1)\n", |
|
" plt.xticks([])\n", |
|
" plt.yticks([])\n", |
|
" plt.grid(False)\n", |
|
" plt.imshow(train_images[i].reshape((IMG_DIM, IMG_DIM)), cmap=plt.cm.binary)\n", |
|
" plt.xlabel(class_names[train_labels[i]])\n", |
|
"plt.show()\n" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"model = tf.keras.models.load_model(MODEL_SAVE_FILE)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"predictions = model.predict(test_images)\n", |
|
"print(\"predictions[0]: \", predictions[0])\n", |
|
"print(\"np.argmax(predictions[0]): \", np.argmax(predictions[0]))\n", |
|
"print(\"test_labels[0]: \", test_labels[0])" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"def get_label(predictions_array):\n", |
|
" predicted_label = np.argmax(predictions_array)\n", |
|
"# if class_names[predicted_label] == 'E' and 100*np.max(predictions_array) < 99.4:\n", |
|
"# predicted_label = class_names.index('Q')\n", |
|
"# if class_names[predicted_label] == 'T' and 100*np.max(predictions_array) < 96:\n", |
|
"# predicted_label = class_names.index('L')\n", |
|
"# if class_names[predicted_label] == 'U' and 100*np.max(predictions_array) < 67:\n", |
|
"# predicted_label = class_names.index('L')\n", |
|
"# if class_names[predicted_label] == 'R' and 100*np.max(predictions_array) < 85:\n", |
|
"# predicted_label = class_names.index('U')\n", |
|
" return predicted_label\n", |
|
"\n", |
|
"def plot_image(i, predictions_array, true_label, img):\n", |
|
" predictions_array, true_label, img = predictions_array, true_label[i], img[i]\n", |
|
" plt.grid(False)\n", |
|
" plt.xticks([])\n", |
|
" plt.yticks([])\n", |
|
"\n", |
|
" plt.imshow(img.reshape((IMG_DIM, IMG_DIM)), cmap=plt.cm.binary)\n", |
|
"\n", |
|
" predicted_label = get_label(predictions_array)\n", |
|
" if predicted_label == true_label:\n", |
|
" color = 'blue'\n", |
|
" else:\n", |
|
" color = 'red'\n", |
|
"\n", |
|
" plt.xlabel(\"{} {:2.2f}% ({})\".format(class_names[predicted_label],\n", |
|
" 100*np.max(predictions_array),\n", |
|
" class_names[true_label]),\n", |
|
" color=color)\n", |
|
"\n", |
|
"def plot_value_array(i, predictions_array, true_label):\n", |
|
" predictions_array, true_label = predictions_array, true_label[i]\n", |
|
" plt.grid(False)\n", |
|
" plt.xticks(range(L))\n", |
|
" plt.yticks([])\n", |
|
" thisplot = plt.bar(range(L), predictions_array, color=\"#777777\")\n", |
|
" plt.ylim([0, 1])\n", |
|
" predicted_label = get_label(predictions_array)\n", |
|
"\n", |
|
" thisplot[predicted_label].set_color('red')\n", |
|
" thisplot[true_label].set_color('blue')" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"i = 0\n", |
|
"plt.figure(figsize=(6,3))\n", |
|
"plt.subplot(1,2,1)\n", |
|
"plot_image(i, predictions[i], test_labels, test_images)\n", |
|
"plt.subplot(1,2,2)\n", |
|
"plot_value_array(i, predictions[i], test_labels)\n", |
|
"plt.show()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"i = 12\n", |
|
"plt.figure(figsize=(6,3))\n", |
|
"plt.subplot(1,2,1)\n", |
|
"plot_image(i, predictions[i], test_labels, test_images)\n", |
|
"plt.subplot(1,2,2)\n", |
|
"plot_value_array(i, predictions[i], test_labels)\n", |
|
"plt.show()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"# Plot the first X test images, their predicted labels, and the true labels.\n", |
|
"# Color correct predictions in blue and incorrect predictions in red.\n", |
|
"for j in range(1):\n", |
|
" num_rows = 5\n", |
|
" num_cols = 3\n", |
|
" num_images = num_rows*num_cols\n", |
|
" plt.figure(figsize=(2*2*num_cols, 2*num_rows))\n", |
|
" for i in range(num_images):\n", |
|
" plt.subplot(num_rows, 2*num_cols, 2*i+1)\n", |
|
" plot_image(i+j*num_images, predictions[i+j*num_images], test_labels, test_images)\n", |
|
" plt.subplot(num_rows, 2*num_cols, 2*i+2)\n", |
|
" plot_value_array(i+j*num_images, predictions[i+j*num_images], test_labels)\n", |
|
" plt.tight_layout()\n", |
|
" plt.show()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"# Grab an image from the test dataset.\n", |
|
"img = test_images[1]\n", |
|
"\n", |
|
"print(img.shape)\n", |
|
"\n", |
|
"# Add the image to a batch where it's the only member.\n", |
|
"img = (np.expand_dims(img,0))\n", |
|
"\n", |
|
"print(img.shape)\n", |
|
"\n", |
|
"predictions_single = model.predict(img)\n", |
|
"\n", |
|
"print(predictions_single)\n", |
|
"\n", |
|
"plot_value_array(1, predictions_single[0], test_labels)\n", |
|
"_ = plt.xticks(range(L), class_names, rotation=45)\n", |
|
"\n", |
|
"print(\"np.argmax(predictions_single[0]): \", np.argmax(predictions_single[0]))" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"#show letters5x5grid\n", |
|
"letters5x5gridFlat = []\n", |
|
"for row in letters5x5grid:\n", |
|
" letters5x5gridFlat.extend(row)\n", |
|
"\n", |
|
" \n", |
|
"letters5x5gridLabelsStr = \"RONLICTSSDNMPPNUAEQIHINRM\"\n", |
|
"letters5x5gridLabels = [class_names.index(letter) for letter in letters5x5gridLabelsStr]\n", |
|
"\n", |
|
"num_rows = 5\n", |
|
"num_cols = 5\n", |
|
"num_images = num_rows*num_cols\n", |
|
"plt.figure(figsize=(2*num_cols, 2*2*num_rows))\n", |
|
"for row in range(num_rows):\n", |
|
" for col in range(num_cols):\n", |
|
" letterImg = letters5x5grid[row][col]\n", |
|
" letterImg = letterImg / 255\n", |
|
" # print(letterImg.shape)\n", |
|
" letterImg = (np.expand_dims(letterImg,0))\n", |
|
" letterImg = (np.expand_dims(letterImg,axis=3))\n", |
|
"\n", |
|
" # print(letterImg.shape)\n", |
|
" pred = model.predict(letterImg)[0]\n", |
|
" # print(np.argmax(pred))\n", |
|
"\n", |
|
" i = col+row*num_cols\n", |
|
" plt.subplot(2*num_rows, num_cols, col+2*row*num_cols+1)\n", |
|
" plot_image(i, pred, letters5x5gridLabels, letters5x5gridFlat)\n", |
|
" plt.subplot(2*num_rows, num_cols, col+2*row*num_cols+num_cols+1)\n", |
|
" plot_value_array(i, pred, letters5x5gridLabels)\n", |
|
"plt.tight_layout()\n", |
|
"plt.show()" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
} |
|
], |
|
"metadata": { |
|
"kernelspec": { |
|
"display_name": "Python 3", |
|
"language": "python", |
|
"name": "python3" |
|
}, |
|
"language_info": { |
|
"codemirror_mode": { |
|
"name": "ipython", |
|
"version": 3 |
|
}, |
|
"file_extension": ".py", |
|
"mimetype": "text/x-python", |
|
"name": "python", |
|
"nbconvert_exporter": "python", |
|
"pygments_lexer": "ipython3", |
|
"version": "3.8.5" |
|
} |
|
}, |
|
"nbformat": 4, |
|
"nbformat_minor": 2 |
|
}
|
|
|