forked from digital-concrete/light-sync
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathframe_color_lib.py
executable file
·196 lines (159 loc) · 7.25 KB
/
frame_color_lib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# coding: utf-8
"""
Frame color related lib
Helper methods and
Most relevand color algorithm
"""
from __future__ import print_function
import numpy
import cv2
import math
from convertor_lib import Converter
# Frame Color Definition
class FrameColor(object):
"""__init__() functions as the class constructor"""
def __init__(self, color, index, color_count,
min_threshold, max_threshold, color_converter):
self.color = color
self.index = index
self.color_count = color_count
self.is_dark = False
self.is_bright = False
self.min_threshold = min_threshold
self.max_threshold = max_threshold
self.calculate_light_dark_channels()
self.color_converter = color_converter
self.brightness = None
self.go_dark = None
self.diff_from_prev = None
def calculate_light_dark_channels(self):
"""Calculates whether color is bright or dark"""
bright_channels_count = 0
dark_channels_count = 0
for channel in range(0, 3):
if self.color[channel] > self.max_threshold:
bright_channels_count += 1
if self.color[channel] < self.min_threshold:
dark_channels_count += 1
if bright_channels_count == 3:
self.is_bright = True
if dark_channels_count == 3:
self.is_dark = True
def get_hue_color(self):
"""Return the color in Philips HUE XY format"""
return self.color_converter.rgb_to_xy(
self.color[2], self.color[1], self.color[0])
class FrameColorLib:
def __init__(self):
"init"
self.color_converter = Converter()
def shrink_image(self, input_img, input_image_reduced_size):
"Reduce image size to increase computation speed"
height, width = input_img.shape[:2]
max_height = input_image_reduced_size
max_witdh = input_image_reduced_size
if max_height < height or max_witdh < width:
# Get scaling factor
scaling_factor = max_height / float(height)
if max_witdh/float(width) < scaling_factor:
scaling_factor = max_witdh / float(width)
# Resize image. You can use INTER_AREA if you have a performant computer
input_img = cv2.resize(input_img, None, fx=scaling_factor,
fy=scaling_factor, interpolation=cv2.INTER_LINEAR)
return input_img
def apply_frame_mask(self, current_frame, channels_min_threshold):
"Apply dark color threshold and compute mask"
gray_frame = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(
gray_frame, channels_min_threshold, 255, cv2.THRESH_BINARY)
# Apply mask to frame
masked_frame = cv2.bitwise_and(
current_frame, current_frame, mask=mask)
return masked_frame
def calculate_frame_brightness(self, frame, dim_brightness, starting_brightness,
min_non_zero_count, max_non_zero_count):
"Calculates frame brightness"
# Actual non zero thresholds in pixels
min_non_zero_count_pixels = frame.size * min_non_zero_count / 100
max_non_zero_count_pixels = frame.size * max_non_zero_count / 100
# Check the non zero pixels. If their count is too low turn the lights off
gray_image_output = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
nz_count = cv2.countNonZero(gray_image_output)
if nz_count < min_non_zero_count_pixels:
current_brightness = dim_brightness - 1
else:
# If False go through all routines
if nz_count > max_non_zero_count_pixels:
current_brightness = starting_brightness
else:
current_brightness = (nz_count - min_non_zero_count_pixels) * (
starting_brightness - dim_brightness) / (
max_non_zero_count_pixels - min_non_zero_count_pixels) + (
dim_brightness)
current_brightness = int(current_brightness)
return current_brightness
def frame_colors_are_similar(self, first_color, second_color,
color_skip_sensitivity,
brightness_skip_sensitivity):
"checks if 2 frame colors are similar"
result = False
if first_color is not None and\
second_color is not None:
if(first_color.go_dark == True and second_color.go_dark == True):
return True
if abs(first_color.brightness - second_color.brightness) < brightness_skip_sensitivity:
for j in range(0, 3):
ch_diff = math.fabs(
int(first_color.color[j]) - int(second_color.color[j]))
if ch_diff < color_skip_sensitivity:
result = True
break
return result
def calculate_hue_color(self, input_img, k_means,
color_spread_threshold,
channels_min_threshold,
channels_max_threshold):
"Calculates the color to be sent to HUE"
# COMPUTE K MEANS
k_means_input_img = input_img.reshape((-1, 4))
# Convert to np.float32
k_means_input_img = numpy.float32(k_means_input_img)
# Define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
k = k_means
ret, label, center = cv2.kmeans(
k_means_input_img, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = numpy.uint8(center)
# COMPUTE MOST PREVALENT CLUSTER
# Calculate the prevalence for each one of the resulted colors
label_counts = []
for j in range(k_means):
label_counts.append(0)
for j in range(0, label.size):
label_counts[label[j][0]] += 1
# Init and populate Array of FrameColor objects for further calculations/decisions
frame_colors = []
for j in range(k_means):
frame_color = FrameColor(center[j], j, label_counts[j],
channels_min_threshold, channels_max_threshold, self.color_converter)
frame_colors.append(frame_color)
# Sort by prevalence
frame_colors.sort(key=lambda x: x.color_count, reverse=True)
# Calculate color to be sent to Hue
result_color = frame_colors[0]
if frame_colors[0].is_bright:
for j in range(1, k_means):
if (not frame_colors[j].is_bright
and not frame_colors[j].is_dark
and (frame_colors[j].color_count / label.size) * 100
> color_spread_threshold):
result_color = frame_colors[j]
break
if frame_colors[0].is_dark:
for j in range(1, k_means):
if not frame_colors[j].is_dark:
result_color = frame_colors[j]
break
return result_color