In [1]:
import numpy as np
import cv2
from matplotlib import pyplot as plt
from math import sqrt, sin, cos, atan2, pi
from colorsys import rgb_to_hsv
from scipy.signal import argrelextrema as extrema
#from scipy.signal import find_peaks
In [2]:
def samplecolours(filename, x0, y0, x1, y1, draw=False):
    img = cv2.imread(filename, 1)  # 1=colour image
    if draw:
        detect = img.copy()

    lines = 200   # number of sample lines over the length of the resistor
    points = 50   # number of points per sample line

    # Coordinates of the endpoints of the resistor = detection line
    rxy = (x0, y0, x1, y1)

    # Draw thick red line over the length of the resistor
    if draw:
        cv2.line(detect, (x0, y0), (x1, y1), (0,0,255), 10)

    # Resistor dimensions
    rdx = x0 - x1                       # resistor dx
    rdy = y0 - y1                       # resistor dy
    rlen = sqrt(rdx * rdx + rdy * rdy)  # resistor length

    # Sample line dimensions
    slen = rlen / 8                     # sample line length (from centre) relative to resistor length
    sang = atan2(rdy, rdx) + pi / 2     # sample line angle perpendicular to the resistor
    sdx = slen * cos(sang)              # sample line dx (from centre)
    sdy = slen * sin(sang)              # sample line dy (from centre)

    # Coordinates along the resistor on the detection line
    rx = np.linspace(x0, x1, num=lines)
    ry = np.linspace(y0, y1, num=lines)

    # BGR and HSV arrays with average colour values along the sample lines
    bgr = np.zeros((lines, 3), dtype=np.float64)
    hsv = np.zeros((lines, 3), dtype=np.float64)

    # Sampling lines perpendicular along the resistor
    for i in range(lines):
        # Endpoint tuples of the sample line
        sxy1 = (round(rx[i] + sdx), round(ry[i] + sdy))
        sxy2 = (round(rx[i] - sdx), round(ry[i] - sdy))

        # Draw thin yellow line on the sample line
        if draw:
            cv2.line(detect, sxy1, sxy2, (0,255,255), 1)

        # Coordinates along the sample line
        sx = np.linspace(sxy1[0], sxy2[0], num=points)
        sy = np.linspace(sxy1[1], sxy2[1], num=points)

        # Average of BGR colour values along the sample line
        avg = np.zeros(3, dtype=np.float64)

        # Summing points along the sample line
        # and calculating the average
        for j in range(points):
            avg += img[round(sy[j]), round(sx[j])]
        avg /= points

        # Save BGR and HSV results of the sample line
        # all values must be or are in range 0.0 - 1.0
        bgr[i] = avg / 255
        hsv[i] = rgb_to_hsv(bgr[i, 2], bgr[i, 1], bgr[i, 0])

        # Hue correction modulo 1.0 for useable peaks
        if i > 0 and hsv[i - 1, 0] >= 0.8 and hsv[i, 0] <= 0.2:
            hsv[i, 0] += 1

    if draw:
        dpi = 96
        w = img.shape[1]
        h = img.shape[0]
        plt.figure(figsize=(2 * w/dpi,h/dpi),dpi=dpi)
        plt.subplot(121)
        plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        plt.title("Origineel")
        plt.subplot(122)
        plt.imshow(cv2.cvtColor(detect, cv2.COLOR_BGR2RGB))
        plt.title("Detectie & bemonstering")
        plt.show()
    
    return (bgr, hsv)

# Smoothing filter with moving average
# returns: new array with length reduced by window-1
def movingaverage(data, window=9):
    return np.convolve(data, np.ones(window, dtype=np.float64), 'valid') / window

# Get indices of peaks via smoothed data
#   extrema() = scipy.signal.argrelextrema()
# returns: one-dimensional Numpy array with original indices of peaks
def peaks(data, window=9):
    return extrema(movingaverage(data, window), np.greater)[0] + window // 2

# Get indices of troughs via smoothed data
#   extrema() = scipy.signal.argrelextrema()
# returns: one-dimensional Numpy array with original indices of troughs
def troughs(data, window=9):
    return extrema(movingaverage(data, window), np.less)[0] + window // 2

def colourids(bgr, hsv, window=9):
    ix = peaks(hsv[:, 0], window)                     # hue peaks index array
    ids = np.concatenate((bgr[ix], hsv[ix]), axis=1)  # array of [b,g,r,h,s,v] arrays
    return (ix, ids.astype(np.float32))

# def colourids_average(bgr, hsv, window=9, average=3):
#     side = average // 2                           # one-sided width of averaging set
#     ix = peaks(hsv[:, 0], window)                 # hue peaks index array
#     count = len(peaks)
#     ids = np.zeros((count, 6), dtype=np.float64)  # array of [b,g,r,h,s,v] arrays
#     for i in range(count):
#         j = ix[i] - side
#         k = ix[i] + side + 1
#         a = np.mean(bgr[j:k], axis=0)
#         b = np.mean(hsv[j:k], axis=0)
#         ids[i] = np.concatenate((a, b))
#     return (ix, ids)

def drawhisto(bgr, hsv):
    plt.figure(figsize=(20, 20))

    plt.subplot(231)
    _ = plt.hist(bgr[:, 2], bins=10, range=(0.0, 1.0))
    plt.title("Rood")

    plt.subplot(232)
    _ = plt.hist(bgr[:, 1], bins=10, range=(0.0, 1.0))
    plt.title("Groen")

    plt.subplot(233)
    _ = plt.hist(bgr[:, 0], bins=10, range=(0.0, 1.0))
    plt.title("Blauw")

    plt.subplot(234)
    _ = plt.hist(hsv[:,0], bins=12, range=(0.0, 1.2))
    plt.title("Tint")

    plt.subplot(235)
    _ = plt.hist(hsv[:,1], bins=12, range=(0.0, 1.2))
    plt.title("Verzadiging")

    plt.subplot(236)
    _ = plt.hist(hsv[:,2], bins=12, range=(0.0, 1.2))
    plt.title("Intensiteit")

    plt.tight_layout()
    plt.show()

def drawsamples(bgr, hsv, window=9):
    x = np.arange(len(bgr) - window + 1)

    plt.figure(figsize=(15, 20))

    plt.subplot(211)
    plt.title("Geëffende gemiddelde BGR-waardes haaks op de as van de weerstand")
    plt.xlabel("Monsterlijn")
    plt.plot(x, movingaverage(bgr[:, 0], window))
    plt.plot(x, movingaverage(bgr[:, 1], window))
    plt.plot(x, movingaverage(bgr[:, 2], window))
    plt.legend(("Blauw", "Rood", "Groen"))

    plt.subplot(212)
    plt.title("Geëffende gemiddelde HSV-waardes haaks op de as van de weerstand")
    plt.plot(x, movingaverage(hsv[:, 0], window))
    plt.plot(x, movingaverage(hsv[:, 1], window))
    plt.plot(x, movingaverage(hsv[:, 2], window))
    plt.legend(("Tint", "Verzadiging", "Intensiteit"))

    plt.tight_layout()
    plt.show()
In [3]:
bgr, hsv = samplecolours("weerstand1.jpg", 278, 395, 290, 210, draw=True)
In [4]:
drawhisto(bgr, hsv)
In [5]:
drawsamples(bgr, hsv, window=9)
In [6]:
ix, traindata = colourids(bgr, hsv, window=9)
responses = np.array([[1,3,0,0,1]], dtype=np.float32).T  # manually determined from photo

t = ix[0] + 10  # index of another sample (test)
newcomer = np.expand_dims(np.concatenate((bgr[t], hsv[t])), axis=0).astype(np.float32)

print(ix)
print(traindata)
print(responses)
print(newcomer)

knn = cv2.ml.KNearest_create()
knn.train(traindata, cv2.ml.ROW_SAMPLE, responses)
_,result,_,_ = knn.findNearest(newcomer, 1)

print(result)
[ 25  71 100 129 162]
[[0.1654902  0.13756862 0.2967843  0.97077173 0.53646934 0.2967843 ]
 [0.19560784 0.2684706  0.6067451  1.0295371  0.6776112  0.6067451 ]
 [0.16698039 0.10815686 0.12760784 0.7217778  0.35227805 0.16698039]
 [0.13647059 0.09615687 0.11466666 0.74319065 0.2954023  0.13647059]
 [0.1882353  0.20470588 0.3781961  1.0144509  0.5022812  0.3781961 ]]
[[1.]
 [3.]
 [0.]
 [0.]
 [1.]]
[[0.56196076 0.32164705 0.20666666 0.61272997 0.63224006 0.56196076]]
[[1.]]
In [ ]: