# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 2
#     language: python
#     name: python2
# ---

# +
"""
File: DistanceToRatioCats (Python, in Jupyter Notebook)
Purpose: This script processes the files in the cat pictures database and creates the
SQLite database queries to create the whole database of cat photos, which are stored
online. For every picture, we find the distance between the eyes, the length of the 
ears from top to bottom, and the distance from the mouth to the midpoint between the
eyes. On top of these distances, we also find the color of the cat through nearest
neighbor of the main RGB color groups of cats: black, brown, light grey, dark grey,
white, and orange. Instead of processing the entire image to determine this color, we 
use the pixels on the line that intersects the points that denote the eyes on the photo,
and the lines that lay on the each eye to their corresponding ear. This saves time on
runtime.
Cat images and annotations dataset used: https://www.kaggle.com/crawford/cat-dataset
"""


"""
Purpose of script: 
    find distance between ears, between eyes, between mouth and nose
    convert distances into ratios
    generate INSERT statement into an output txt file
        contains cat photo file name
            (eg https://res.cloudinary.com/dczoi0wxt/image/upload/00000001_000.jpg)
        and ratio
"""
import os
import math
import cv2
import numpy as np
import math


# From https://stackoverflow.com/questions/24702868/python3-pillow-get-all-pixels-on-a-line
def xiaoline(x0, y0, x1, y1):

        x=[]
        y=[]
        dx = x1-x0
        dy = y1-y0
        steep = abs(dx) < abs(dy)

        if steep:
            x0,y0 = y0,x0
            x1,y1 = y1,x1
            dy,dx = dx,dy

        if x0 > x1:
            x0,x1 = x1,x0
            y0,y1 = y1,y0

        gradient = float(dy) / float(dx)  # slope

        """ handle first endpoint """
        xend = round(x0)
        yend = y0 + gradient * (xend - x0)
        xpxl0 = int(xend)
        ypxl0 = int(yend)
        x.append(xpxl0)
        y.append(ypxl0) 
        x.append(xpxl0)
        y.append(ypxl0+1)
        intery = yend + gradient

        """ handles the second point """
        xend = round (x1);
        yend = y1 + gradient * (xend - x1);
        xpxl1 = int(xend)
        ypxl1 = int (yend)
        x.append(xpxl1)
        y.append(ypxl1) 
        x.append(xpxl1)
        y.append(ypxl1 + 1)

        """ main loop """
        for px in range(xpxl0 + 1 , xpxl1):
            x.append(px)
            y.append(int(intery))
            x.append(px)
            y.append(int(intery) + 1)
            intery = intery + gradient;

        if steep:
            y,x = x,y

        coords=zip(x,y)

        return coords

directory = "cats" #cats/CAT_00
outputFile = open("insertStatements2.txt", "w")
maxEyeDist = 0
maxEarDist = 0
maxEyeMouthDist = 0

# go through all the cat data and access just the .cat annotation files
for subdir, dirs, files in os.walk(directory):
    for file in files:
        # print os.path.join(subdir, file)
        filepath = subdir + os.sep + file
        
        if filepath.endswith(".cat"):
            # print (filepath)
            annotation = ""
            with open(filepath, "r") as file_object:
                annotation = file_object.read().replace('\n', '')
            
            annotatPts = annotation.split(" ")
            # if there are exactly nine points on image, as expected
            if annotatPts[0] == "9":
                # insert some calculations small, medium, large
                eyeLXY = (int(annotatPts[1]), int(annotatPts[2]))
                eyeRXY = (int(annotatPts[3]), int(annotatPts[4]))
                mouthXY = (int(annotatPts[5]), int(annotatPts[6]))
                earLXY2 = (int(annotatPts[9]), int(annotatPts[10]))
                earRXY2 = (int(annotatPts[15]), int(annotatPts[16]))
                midPteyesXY = (abs(eyeLXY[0]-eyeRXY[0])/2, abs(eyeLXY[1]-eyeRXY[1])/2)
                eyeDist = math.hypot(eyeLXY[0]-eyeRXY[0], eyeLXY[1]-eyeRXY[1])
                earDist = math.hypot(earLXY2[0]-earRXY2[0], earLXY2[1]-earRXY2[1])
                eyeMouthDist = math.hypot(mouthXY[0]-midPteyesXY[0], mouthXY[1]-midPteyesXY[1])
                earRXYhalfPt = ((int(annotatPts[13]) + int(annotatPts[15]))/2, (int(annotatPts[14]) + int(annotatPts[16]))/2)
                earRHeight = math.hypot((earRXYhalfPt[0]-int(annotatPts[17])), (earRXYhalfPt[1] - int(annotatPts[18])))
                                 
                eyeCat = str(eyeDist)
                earCat = str(earDist) # str(earDist/eyeDist)
                eyeMouthCat = str(eyeMouthDist) # str(eyeMouthDist/eyeDist)
                
                # grab middle colors, take the average color
                img = cv2.imread(filepath[:-4])
                
                widthMid = int(eyeDist/2)
                heightMid = int(eyeDist/2)
                startX = int(eyeLXY[0] + eyeDist/4)
                startY = int(eyeLXY[1] + eyeDist/4)
                
                pixels = xiaoline(eyeLXY[0], eyeLXY[1], eyeRXY[0], eyeRXY[1])
                pixels.append(xiaoline(eyeLXY[0], eyeLXY[1], earLXY2[0], earLXY2[1]))
                pixels.append(xiaoline(eyeRXY[0], eyeRXY[1], earRXY2[0], earRXY2[1]))
                
                ##### color
                
                blackCt = 0
                lightGreyCt = 0
                darkGreyCt = 0
                greyCt = 0
                whiteCt = 0
                orangeCt = 0
                brownCt = 0
                errorCt = 0

                for m in range(5, len(pixels) - 5):
                    p = pixels[m]
                    if p[0] < img.shape[0] and p[1] < img.shape[1]:
                        pt = img[p[0]][p[1]]
        
                        r = pt[0]
                        g = pt[1] 
                        b = pt[2] 
        
                        distBlack = math.sqrt((r - 32) ** 2 + (g - 32) ** 2 + (b - 32) ** 2)
        
                        #if r - 3 < g and g < r + 3 and r - 3 < b and b < r + 3:
                        if True:
                            # check dark grey
                            # distDarkGrey = math.sqrt((r - 128) ** 2 + (g - 128) ** 2 + (b - 128) ** 2)
                            # check light grey
                            # distLightGrey = math.sqrt((r - 192) ** 2 + (g - 192) ** 2 + (b - 192) ** 2)
                            distGrey = math.sqrt((r-109)**2+(g-109)**2+(b-109)**2)
                            # check white
                            distWhite = math.sqrt((r - 250) ** 2 + (g - 250) ** 2 + (b - 250) ** 2)
                        else:
                            # distDarkGrey = 255
                            # distLightGrey = 255
                            distGrey = 255
                            distWhite = 255
                        distLightGrey = 255
                        distDarkGrey = 255
                        # check orange
                        distLightOrange = math.sqrt((r - 254) ** 2 + (g - 195) ** 2 + (b - 139) ** 2)
                        distOrange = math.sqrt((r - 153) ** 2 + (g - 96) ** 2 + (b - 66) ** 2)
                        distDarkOrange = math.sqrt((r - 133) ** 2 + (g - 109) ** 2 + (b - 71) ** 2)
                        distOOrange = math.sqrt((r - 141) ** 2 + (g - 115) ** 2 + (b - 88) ** 2)
                        # check brown
                        distLightBrown = math.sqrt((r - 145) ** 2 + (g - 123) ** 2 + (b - 82) ** 2)
                        distBrown = math.sqrt((r - 140) ** 2 + (g - 82) ** 2 + (b - 63) ** 2)
                        distOBrown = math.sqrt((r - 109) ** 2 + (g - 83) ** 2 + (b - 46) ** 2)
        
                        # check all other colors
                        distLightGreen = math.sqrt((r - 153) ** 2 + (g - 255) ** 2 + (b - 153) ** 2)
                        distGreen = math.sqrt((r) ** 2 + (g - 192) ** 2 + (b) ** 2)
                        distDarkGreen = math.sqrt((r) ** 2 + (g - 102) ** 2 + (b) ** 2)
                        distLightBlue = math.sqrt((r - 204) ** 2 + (g - 229) ** 2 + (b - 255) ** 2)
                        distCyan = math.sqrt((r - 204) ** 2 + (g - 255) ** 2 + (b - 255) ** 2)
                        distBlue = math.sqrt((r) ** 2 + (g - 128) ** 2 + (b - 255) ** 2)
                        distLightPurple = math.sqrt((r - 204) ** 2 + (g - 153) ** 2 + (b - 255) ** 2)
                        distPurple = math.sqrt((r - 127) ** 2 + (g) ** 2 + (b - 255) ** 2)
                        distPink = math.sqrt((r - 255) ** 2 + (g - 102) ** 2 + (b - 255) ** 2)
                        distRed = math.sqrt((r - 255) ** 2 + (g) ** 2 + (b) ** 2)
                        distYellow = math.sqrt((r - 255) ** 2 + (g - 255) ** 2 + (b - 51) ** 2)
        
                        distCol = min([distGrey, distDarkOrange, distOOrange, distOBrown, distPink, distYellow, distLightGreen, distDarkGreen, distLightBlue, distCyan, distLightPurple, distBlack, distDarkGrey, distLightGrey, distWhite, distOrange, distBrown, distLightOrange, distGreen, distBlue, distPurple, distRed])
        
                        if distCol == distBlack:
                            blackCt = blackCt + 1
                        # elif distCol == distDarkGrey:
                            # darkGreyCt = darkGreyCt + 1
                        # elif distCol == distLightGrey:
                            # lightGreyCt = lightGreyCt + 1
                        elif distCol == distGrey:
                            greyCt = greyCt + 1
                        elif distCol == distWhite:
                            whiteCt = whiteCt + 1
                        elif distCol == distOrange or distCol == distLightOrange or distCol == distDarkOrange or distCol == distOOrange:
                            orangeCt = orangeCt + 1
                        elif distCol == distBrown or distCol == distLightBrown or distCol == distOBrown:
                            brownCt = brownCt + 1
                        else:
                            errorCt = errorCt + 1
 
                ct = max([blackCt, lightGreyCt, darkGreyCt, whiteCt, orangeCt, brownCt])
                if ct == blackCt:
                    color = "black"
                # elif ct == darkGreyCt:
                    # color = "dark grey"
                # elif ct == lightGreyCt:
                    # color = "light grey"
                elif ct == greyCt:
                    color = "grey"
                elif ct == whiteCt:
                    color = "white"
                elif ct == orangeCt:
                    color = "orange"
                elif ct == brownCt:
                    color = "brown"
                else:
                    color = "error"
    
                # print (orangeCt + brownCt)/float(ct)
                if ct != 0 and (orangeCt + brownCt)/float(ct) > 0.055:
                    color = "orange"
                # print (blackCt, darkGreyCt, lightGreyCt, whiteCt, orangeCt, brownCt, errorCt)
                # print color
                ##### end color
                
                # max distances calculations
                if eyeDist > maxEyeDist:
                    maxEyeDist = eyeDist
                if earDist > maxEarDist:
                    maxEarDist = earDist
                if eyeMouthDist > maxEyeMouthDist:
                    maxEyeMouthDist = eyeMouthDist
                
                outputFile.write("db.run(\"")
                outputFile.write("INSERT INTO all_cats (eyes, ears, nose_mouth, earLength, color, picture) ")
                outputFile.write("VALUES ('" + eyeCat + "', '" + earCat + "', '")
                outputFile.write(eyeMouthCat + "', '" + str(earRHeight) + "', '" + color + "', ")
                outputFile.write("'https://res.cloudinary.com/dczoi0wxt/image/upload/")
                outputFile.write(file[:-4] + "')\");\n")
            file_object.close()

outputFile.write("\n\n\n")
outputFile.write("maxEyeDist: " + str(maxEyeDist) + "\n")
outputFile.write("maxEarDist: " + str(maxEarDist) + "\n")
outputFile.write("maxEyeMouthDist: " + str(maxEyeMouthDist) + "\n")
    
outputFile.close()

# +
"""
Unused portion, this is meant for exploring possible alternatives to the above solution.
"""

import cv2
import numpy as np
import math

# https://stackoverflow.com/questions/24702868/python3-pillow-get-all-pixels-on-a-line
def interpolate_pixels_along_line(x0, y0, x1, y1):
    """Uses Xiaolin Wu's line algorithm to interpolate all of the pixels along a
    straight line, given two points (x0, y0) and (x1, y1)

    Wikipedia article containing pseudo code that function was based off of:
        http://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm
    """
    pixels = []
    steep = abs(y1 - y0) > abs(x1 - x0)

    # Ensure that the path to be interpolated is shallow and from left to right
    if steep:
        t = x0
        x0 = y0
        y0 = t

        t = x1
        x1 = y1
        y1 = t

    if x0 > x1:
        t = x0
        x0 = x1
        x1 = t

        t = y0
        y0 = y1
        y1 = t

    dx = x1 - x0
    dy = y1 - y0
    gradient = dy / dx  # slope

    # Get the first given coordinate and add it to the return list
    x_end = round(x0)
    y_end = y0 + (gradient * (x_end - x0))
    xpxl0 = x_end
    ypxl0 = round(y_end)
    if steep:
        pixels.extend([(ypxl0, xpxl0), (ypxl0 + 1, xpxl0)])
    else:
        pixels.extend([(xpxl0, ypxl0), (xpxl0, ypxl0 + 1)])

    interpolated_y = y_end + gradient

    # Get the second given coordinate to give the main loop a range
    x_end = round(x1)
    y_end = y1 + (gradient * (x_end - x1))
    xpxl1 = x_end
    ypxl1 = round(y_end)

    # Loop between the first x coordinate and the second x coordinate, interpolating the y coordinates
    for x in range(xpxl0 + 1, xpxl1):
        if steep:
            pixels.extend([(math.floor(interpolated_y), x), (math.floor(interpolated_y) + 1, x)])

        else:
            pixels.extend([(x, math.floor(interpolated_y)), (x, math.floor(interpolated_y) + 1)])

        interpolated_y += gradient

    # Add the second given coordinate to the given list
    if steep:
        pixels.extend([(ypxl1, xpxl1), (ypxl1 + 1, xpxl1)])
    else:
        pixels.extend([(xpxl1, ypxl1), (xpxl1, ypxl1 + 1)])

    return pixels


img = cv2.imread('cats/CAT_00/00000006_008.jpg')
print img.shape
print img.size
print img[0][0]

widthMid = img.shape[0]/2
heightMid = img.shape[1]/2
startX = widthMid/2
startY = heightMid/2
print widthMid
print heightMid

blackCt = 0
lightGreyCt = 0
darkGreyCt = 0
whiteCt = 0
orangeCt = 0
brownCt = 0
errorCt = 0

for x in range(startX, startX + widthMid):
    for y in range (startY, startY + heightMid):
        pt = img[x][y]
        
        r = pt[0] #- chroma[0]
        g = pt[1] #- chroma[1]
        b = pt[2] #- chroma[2]
        
        
        # check black
        distBlack = math.sqrt((r - 32) ** 2 + (g - 32) ** 2 + (b - 32) ** 2)
        
        if r - 3 < g and g < r + 3 and r - 3 < b and b < r + 3:
            # check dark grey
            distDarkGrey = math.sqrt((r - 128) ** 2 + (g - 128) ** 2 + (b - 128) ** 2)
            # check light grey
            distLightGrey = math.sqrt((r - 192) ** 2 + (g - 192) ** 2 + (b - 192) ** 2)
            # check white
            distWhite = math.sqrt((r - 250) ** 2 + (g - 250) ** 2 + (b - 250) ** 2)
        else:
            distDarkGrey = 255
            distLightGrey = 255
            distWhite = 255
        # check orange
        # distLightOrange = math.sqrt((r - 254) ** 2 + (g - 195) ** 2 + (b - 139) ** 2)
        distOrange = math.sqrt((r - 153) ** 2 + (g - 96) ** 2 + (b - 66) ** 2)
        # check brown
        distLightBrown = math.sqrt((r - 145) ** 2 + (g - 123) ** 2 + (b - 82) ** 2)
        distBrown = math.sqrt((r - 140) ** 2 + (g - 82) ** 2 + (b - 63) ** 2)
        
        # check all other colors
        distLightGreen = math.sqrt((r - 153) ** 2 + (g - 255) ** 2 + (b - 153) ** 2)
        distGreen = math.sqrt((r) ** 2 + (g - 192) ** 2 + (b) ** 2)
        distDarkGreen = math.sqrt((r) ** 2 + (g - 102) ** 2 + (b) ** 2)
        distLightBlue = math.sqrt((r - 204) ** 2 + (g - 229) ** 2 + (b - 255) ** 2)
        distCyan = math.sqrt((r - 204) ** 2 + (g - 255) ** 2 + (b - 255) ** 2)
        distBlue = math.sqrt((r) ** 2 + (g - 128) ** 2 + (b - 255) ** 2)
        distLightPurple = math.sqrt((r - 204) ** 2 + (g - 153) ** 2 + (b - 255) ** 2)
        distPurple = math.sqrt((r - 127) ** 2 + (g) ** 2 + (b - 255) ** 2)
        distPink = math.sqrt((r - 255) ** 2 + (g - 102) ** 2 + (b - 255) ** 2)
        distRed = math.sqrt((r - 255) ** 2 + (g) ** 2 + (b) ** 2)
        distYellow = math.sqrt((r - 255) ** 2 + (g - 255) ** 2 + (b - 51) ** 2)
        
        distCol = min([distPink, distYellow, distLightGreen, distDarkGreen, distLightBlue, distCyan, distLightPurple, distBlack, distDarkGrey, distLightGrey, distWhite, distOrange, distBrown, distLightOrange, distGreen, distBlue, distPurple, distRed])
        
        if distCol == distBlack:
            blackCt = blackCt + 1
            color = "black"
        elif distCol == distDarkGrey:
            darkGreyCt = darkGreyCt + 1
            color = "dark grey"
        elif distCol == distLightGrey:
            lightGreyCt = lightGreyCt + 1
            color = "light grey"
        elif distCol == distWhite:
            whiteCt = whiteCt + 1
            color = "white"
        elif distCol == distOrange or distCol == distLightOrange:
            orangeCt = orangeCt + 1
            color = "orange"
        elif distCol == distBrown or distCol == distLightBrown:
            brownCt = brownCt + 1
            color = "brown"
        else:
            errorCt = errorCt + 1
            color = "error"

ct = max([blackCt, lightGreyCt, darkGreyCt, whiteCt, orangeCt, brownCt])
if ct == blackCt:
    color = "black"
elif ct == darkGreyCt:
    color = "dark grey"
elif ct == lightGreyCt:
    color = "light grey"
elif ct == whiteCt:
    color = "white"
elif ct == orangeCt:
    color = "orange"
elif ct == brownCt:
    color = "brown"
else:
    color = "error"
# ct = blackCt + lightGreyCt + darkGreyCt + whiteCt + orangeCt + brownCt    

# print (orangeCt + brownCt)/float(ct)
# if (orangeCt + brownCt)/float(ct) > 0.055:
#    color = "orange"
    
print (blackCt, darkGreyCt, lightGreyCt, whiteCt, orangeCt, brownCt, errorCt)
print color

# +
"""
The update statements that actually calculates the height of the ears rather than how
far apart the ears are from each other. Database originally contained the distance between
the ears instead of how long the ears were, so these update statements corrected that.
"""
import os
import math
import cv2
import numpy as np
import math


directory = "cats" #cats/CAT_00
outputFile = open("insertStatements2.txt", "w")

maxEarLength = 0

# go through all the cat data and access just the .cat annotation files
for subdir, dirs, files in os.walk(directory):
    for file in files:
        # print os.path.join(subdir, file)
        filepath = subdir + os.sep + file
        
        if filepath.endswith(".cat"):
            # print (filepath)
            annotation = ""
            with open(filepath, "r") as file_object:
                annotation = file_object.read().replace('\n', '')
            
            annotatPts = annotation.split(" ")
            # if there are exactly nine points on image, as expected
            if annotatPts[0] == "9":
                # insert some calculations small, medium, large
                earRXY2 = (int(annotatPts[15]), int(annotatPts[16]))
                earRXYhalfPt = ((int(annotatPts[13]) + int(annotatPts[15]))/2, (int(annotatPts[14]) + int(annotatPts[16]))/2)
                earRHeight = math.hypot((earRXYhalfPt[0]-int(annotatPts[17])), (earRXYhalfPt[1] - int(annotatPts[18])))
                
                if maxEarLength < earRHeight:
                    maxEarLength = earRHeight
                
                outputFile.write("db.run(\"")
                outputFile.write("UPDATE all_cats SET ears = '")
                outputFile.write(str(earRHeight) + "' WHERE picture = ")
                outputFile.write("'https://res.cloudinary.com/dczoi0wxt/image/upload/")
                outputFile.write(file[:-4] + "')\");\n")
            file_object.close()
    

outputFile.write("\n\n\n")
outputFile.write("maxEarDist: " + str(maxEarLength) + "\n")
    
outputFile.close()

# -



StarcoderdataJupyterScriptsDedupFiltered