0

I have a CompSci project I designed where I wanted to make a program that can tell me the pixel information under the cursor, match it to a list of RGB values in a CSV file, find the closest color, and display it.

here is my CSV file

Color,Red,Green,Blue
Black,0,0,0
White,255,255,255
Red,255,0,0
Lime,0,255,0
Blue,0,0,255
Yellow,255,255,0
Cyan,0,255,255
Magenta,255,0,255
Silver,192,192,192
Gray,128,128,128
Maroon,128,0,0
Olive,128,128,0
Green,0,128,0
Purple,128,0,128
Teal,0,128,128
Navy,0,0,128

My goal is to use the min function to find the closest red value in my RGB value under the cursor. Then, to check the corresponding green values of the reds that matched. Then, check for the closest blue value for the matching red and green. Then, I can pull the color value and i will know what color the selected pixel is. My issue is that I have no idea on whether I should be turning my CSV data into lists, creating dictionaries, or what. I've tried everything and keep getting stuck. Based on what I said I want to do, can someone help me out, or point me in the right direction?

from image import *
from PIL import *
from pynput.mouse import Listener
import PIL.ImageGrab

#Create an imagewindow based on image dimensions; display the image
def printImage(imageFile):
    myImage = FileImage(imageFile)
    _width = myImage.getWidth()
    _height = myImage.getHeight()
    myWindow = ImageWin(_height, _width, "window")
    myImage.draw(myWindow)

printImage("mickey.png")

#Color finding function
def getColor(_x, _y):
    return PIL.ImageGrab.grab().load()[_x, _y]

#Uses mouse position for x and y value of color finding function
def on_move(x, y):
    global _color
    _x = x
    _y = y
    _color = getColor(_x, _y)
    print(_color)

def on_click(x, y, button, pressed):
    print('done')
    if not pressed:
        return False
#allows on move to be updated every time it occurs
with Listener(on_move=on_move, on_click=on_click) as listener:
    listener.join()

colorRed = _color[0]
colorGreen = _color[1]
colorBlue = _color[2]


#Take pixel information and match it to a color in the text file
from csv import *
with open("Color Collection.csv") as myFile:
    myReader = reader(myFile)
    for line in myReader:
        print(line[1])
my account
  • 69
  • 6
  • create a function that will go thru your list of colors and will find the most near one by calculating the percentage of how near is this color: [how to calculate percentage](https://www.oracle.com/webfolder/technetwork/data-quality/edqhelp/Content/processor_library/matching/comparisons/percent_difference.htm) – Nicolae Apr 20 '20 at 20:22
  • check this answer on SO [https://stackoverflow.com/a/12070940/3210415](https://stackoverflow.com/a/12070940/3210415) – Nicolae Apr 20 '20 at 20:44

1 Answers1

1

Think about these colors as points in three-dimensional space. You want to find the nearest point to yours from these already existing points. Well fortunately, there's an established formula to do that. Let's start in your code sample at

from csv import *

First, we're gonna need some math functions, so let's import what we need:

from math import sqrt, pow

Let's add a variable to track the current closest color. This will be a 2 element array: the name of the color from the file AND the distance from the requested point.

closestColor = []

After your myReader = reader(myFile) declaration, let's advance past the headers (Idea taken from https://stackoverflow.com/a/3428633/4462517)

next(reader, None)

Now, inside the loop you've defined, let's calculate the distance between the point in the file and the point you've already collected:

lineRed = line[1]
lineGreen = line[2]
lineBlue = line[3]

distance = sqrt(pow(colorRed - lineRed, 2) + pow(colorGreen - lineGreen, 2) + pow (colorBlue - lineBlue, 2))

Now, let's determine if the current closest color is closer or further than the color we just calculated

if distance < closestColor[1]:
    closestColor = [line[0], distance]

BUT, wait! That if statement could (and will) throw an error the first time, because there is no closestColor[1]. Let's change the if block to say something like this:

if (len(closestColor) < 2) or (distance < closestColor[1]):
    closestColor = [line[0], distance]

Now, outside of the list you can do what you need to! So, the end of your snippet should look something like this (I assume your previous code capturing the color has been tested and is correct):

#Take pixel information and match it to a color in the text file
from csv import *
from math import sqrt, pow

closestColor = []
with open("Color Collection.csv") as myFile:
    myReader = reader(myFile)
    next(reader, None)
    for line in myReader:
        lineRed = line[1]
        lineGreen = line[2]
        lineBlue = line[3]

        distance = sqrt(pow(colorRed - lineRed, 2) + \
                        pow(colorGreen - lineGreen, 2) + \
                        pow(colorBlue - lineBlue, 2))

        if (len(closestColor) < 2) or (distance < closestColor[1]):
            closestColor = [line[0], distance]

# This style works on Python3.6+
print(f'The closest color to the point clicked is {closestColor[0]}')

# If you have to use <3.5, I'm sorry, but you can do this instead
# print('The closest color to the point clicked is %s', %(closestColor[0]))

Give this a whirl, and hope it helps!

Josh Ghiloni
  • 1,260
  • 8
  • 19
  • This was exactly what I needed. Thank you for the in depth and thought out explination -- I was able to understand and learned from it, so I wasn't just blindly copying code (: I made a couple of tweaks, turning the line variables into integers for the math, and using ```titles = next(myReader)``` instead of ```next(reader, None)```. Thank you! – my account Apr 21 '20 at 16:18