# Display image from live webcam as taken, with four different color filters and in B/W

I have a live webcam windows which means there are six subdivided windows in a single window live. And show pictures in colored and black and white. Is there anyway I can make the code minimalistically minimized? I believe the code is considerably long.

import cv2
import cv
import numpy as np
import matplotlib.image as mpimg
from matplotlib import pyplot as plt

def threshold_slow(T, image):
# grab the image dimensions
h = image.shape[0]
w = image.shape[1]
d = image.shape[2]
# loop over the image, pixel by pixel
for y in range(0, h):
for x in range(0, w):
for z in range(0, d):
# threshold the pixel

if image[y, x,z] >= T:
image[y, x,z] = 255
else:
image[y, x,z] = 0

# return the thresholded image
return image
def grab_frame(cam):

#cv2.namedWindow("test")

#img_counter = 0

while True:
#r = 100.0 / color1.shape[1]
r = 640.0 / color1.shape[1]
#r = 0.25
dim = (100, int(color1.shape[0] * r))
dim = (640,480)
# perform the actual resizing of the image and show it
color = cv2.resize(color1, dim, interpolation = cv2.INTER_AREA)

#color = color1.copy()
b = color.copy()
# set green and red channels to 0
b[:, :, 1] = 0
b[:, :, 2] = 0

g = color.copy()
# set blue and red channels to 0
g[:, :, 0] = 0
g[:, :, 2] = 0

r = color.copy()
# set blue and green channels to 0
r[:, :, 0] = 0
r[:, :, 1] = 0

#y= color.copy()
#gray = cv2.cvtColor(l,cv2.COLOR_RGB2GRAY)
#_,y = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY)
#y = cv2.cvtColor(y, cv2.COLOR_GRAY2RGB)

d = color.copy()
gray1 = cv2.cvtColor(d,cv2.COLOR_RGB2GRAY)
_,p = cv2.threshold(gray1, 60, 255, cv2.THRESH_BINARY)
p = cv2.cvtColor(p, cv2.COLOR_GRAY2RGB)
#threshold_slow(220,p)
return [color,b,g,r,y,p]

cam = cv2.VideoCapture(0)

#cv2.waitKey(0)
while(1):
[color,b,g,r,y,p] = grab_frame(cam)
horiz = np.hstack((color,b,g))
#verti = np.vstack((color,r))
horiz1 = np.hstack((r,y,p))
verti = np.vstack((horiz,horiz1))
cv2.imshow('HORIZONTAL', verti)

if not ret:
break
k = cv2.waitKey(1)

if k%256 == 27:
# ESC pressed
print("Escape hit, closing...")
break

cam.release()

cv2.destroyAllWindows()


• First, get rid of all the unneeded whitespace. Use consistent amount between functions (Python's official style-guide, PEP8, recommends two).

• PEP8 also recommends using spaces in lists, after the commas, and lower_case for all variables and functions (your T in threshold_slow violates this).

• Don't use magic numbers in your code. Give them readable names and if necessary make them global constants:

WIDTH, HEIGHT = 640, 480

• Next, since your images are already numpy arrays, use that fact. Your (unused) threshold_slow function can be replaced by a single line using numpy.where:

def threshold_fast(T, image):
return np.where(image >= T, 255, 0)


Note that this does not modify the image inplace. It is a bad practice to do that and return a modified/new object. You should decide, either return a new object or modify in place and return None.

• The import cv is not used (and I could not even find a way to install it anymore).

• Tuple assignment works also without a list on the left side, just do color, b, g, r, y, p = grab_frame(cam). The same is true when returning a tuple (return color, b, g, r, y, p).

• Arguably, I would split up your grab_frame code into subfunctions like red(image), green(image), blue(image), yellow(image), black_and_white(image).

def red(image):
"""Copy only the red channel from image"""
out = np.zeros_like(image)
# for some reason red is in the last channel
out[:, :, 2] = image[:, :, 2]
return out
...


While this move will not make your code shorter, it will make it more readable.

• Note that the canonical order is red, green, blue (RGB). If at all possible I would stick to that. I'm not sure why openCV would deviate from that.

• You should at least add a docstring to each of your functions as a rudimentary documentation. See above for a short example.

• You can use while True instead of while(1). No parenthesis needed and True is unambiguous (even for those people who both know C-like languages, where 0 is False and shell scripting languages like bash, where non-zero is False).

• I would also add a tile(images, rows) function that puts your images into rows and columns. You could just use the itertools recipe grouper for this:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)

• Since you seem to want to use different amounts of tiles, and different effects, it might make sense to keep a list of functions to apply to the base image, so that in the end you only need one call:

def identity(x):
return x

def tile(images, cols, fillvalue=None):
return np.vstack(np.hstack(group)
for group in grouper(images, cols, fillvalue))

funcs = identity, red, black_and_white, canny
images = (func(image) for func in funcs)
# arrange them in a 2x2 grid
cv2.imshow('HORIZONTAL', tile(images, cols=2, fillvalue=np.zeros_like(image)))


If the number of images is not evenly divisible by the number of columns, the row is filled up with blank images.

• You should put your main calling code under a if __name__ == "__main__" guard to allow importing from this script.