├── Appendix └── programs │ └── prog00.py ├── Chapter03 └── programs │ ├── 01_LED_Blink.py │ ├── 02_SOS.py │ ├── 03_Alternate_01.py │ ├── 04_Alternate_02.py │ ├── 05_Chaser.py │ ├── 3d_array_visual.py │ ├── Alternate_Blink.fzz │ ├── Blink_LEDs.fzz │ ├── LED_Chaser.fzz │ ├── Pull_Up.fzz │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ └── pull_down.fzz ├── Chapter04 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ ├── prog08.py │ ├── prog09.py │ ├── prog10.py │ ├── prog11.py │ ├── prog12.py │ ├── prog13.py │ ├── prog14.py │ ├── prog15.py │ ├── prog16.py │ ├── prog17.py │ ├── prog18.py │ └── timelapse.sh ├── Chapter05 └── programs │ ├── Pull_Up_circuit.fzz │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ ├── prog08.py │ └── prog09.py ├── Chapter06 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ ├── prog08.py │ ├── prog09.py │ ├── prog10.py │ ├── prog11.py │ ├── prog12.py │ ├── prog13.py │ ├── prog14.py │ ├── prog15.py │ └── prog16.py ├── Chapter07 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ └── prog07.py ├── Chapter08 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ └── prog07.py ├── Chapter09 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ └── prog06.py ├── Chapter10 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ └── prog08.py ├── Chapter11 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ └── prog06.py ├── Chapter12 └── programs │ ├── prog00.py │ ├── prog01.py │ ├── prog02.py │ ├── prog03.py │ ├── prog04.py │ ├── prog05.py │ ├── prog06.py │ ├── prog07.py │ └── test01.ipynb ├── LICENSE └── README.md /Appendix/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | cv2.setUseOptimized(True) 4 | print(cv2.useOptimized()) 5 | img = cv2.imread('/home/pi/book/dataset/4.1.01.tiff', 0) 6 | e1 = cv2.getTickCount() 7 | img1 = cv2.medianBlur(img, 23) 8 | e2 = cv2.getTickCount() 9 | t = (e2 - e1)/cv2.getTickFrequency() 10 | print(t) 11 | -------------------------------------------------------------------------------- /Chapter03/programs/01_LED_Blink.py: -------------------------------------------------------------------------------- 1 | # sudo apt-get install python3-rpi.gpio 2 | import RPi.GPIO as GPIO 3 | from time import sleep 4 | 5 | GPIO.setwarnings(False) # Ignore Warnings 6 | GPIO.setmode(GPIO.BOARD) # Use Physical Pin Numbering 7 | GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW) 8 | 9 | while True: 10 | GPIO.output(8, GPIO.HIGH) 11 | sleep(1) 12 | GPIO.output(8, GPIO.LOW) 13 | sleep(1) 14 | -------------------------------------------------------------------------------- /Chapter03/programs/02_SOS.py: -------------------------------------------------------------------------------- 1 | # sudo apt-get install python3-rpi.gpio 2 | import RPi.GPIO as GPIO 3 | from time import sleep 4 | 5 | GPIO.setwarnings(False) # Ignore Warnings 6 | GPIO.setmode(GPIO.BOARD) # Use Physical Pin Numbering 7 | GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW) 8 | 9 | def flash(led, duration): 10 | GPIO.output(led, GPIO.HIGH) 11 | sleep(duration) 12 | GPIO.output(led, GPIO.LOW) 13 | sleep(duration) 14 | 15 | while True: 16 | flash(8, 0.2) 17 | flash(8, 0.2) 18 | flash(8, 0.2) 19 | sleep(0.3) 20 | 21 | flash(8, 0.5) 22 | flash(8, 0.5) 23 | flash(8, 0.5) 24 | 25 | flash(8, 0.2) 26 | flash(8, 0.2) 27 | flash(8, 0.2) 28 | sleep(1) 29 | -------------------------------------------------------------------------------- /Chapter03/programs/03_Alternate_01.py: -------------------------------------------------------------------------------- 1 | # sudo apt-get install python3-rpi.gpio 2 | import RPi.GPIO as GPIO 3 | from time import sleep 4 | 5 | GPIO.setwarnings(False) # Ignore Warnings 6 | GPIO.setmode(GPIO.BOARD) # Use Physical Pin Numbering 7 | GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW) 8 | GPIO.setup(10, GPIO.OUT, initial=GPIO.LOW) 9 | 10 | while True: 11 | GPIO.output(8, GPIO.HIGH) 12 | GPIO.output(10, GPIO.LOW) 13 | sleep(1) 14 | 15 | GPIO.output(8, GPIO.LOW) 16 | GPIO.output(10, GPIO.HIGH) 17 | sleep(1) 18 | -------------------------------------------------------------------------------- /Chapter03/programs/04_Alternate_02.py: -------------------------------------------------------------------------------- 1 | # sudo apt-get install python3-rpi.gpio 2 | import RPi.GPIO as GPIO 3 | from time import sleep 4 | 5 | GPIO.setwarnings(False) # Ignore Warnings 6 | GPIO.setmode(GPIO.BOARD) # Use Physical Pin Numbering 7 | GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW) 8 | GPIO.setup(10, GPIO.OUT, initial=GPIO.LOW) 9 | counter = 0 10 | 11 | while True: 12 | if counter % 2 == 0: 13 | led1 = 8 14 | led2 = 10 15 | else: 16 | led1 = 10 17 | led2 = 8 18 | 19 | GPIO.output(led1, GPIO.HIGH) 20 | GPIO.output(led2, GPIO.LOW) 21 | sleep(1) 22 | counter = counter + 1 23 | -------------------------------------------------------------------------------- /Chapter03/programs/05_Chaser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from gpiozero import LED 4 | from time import sleep 5 | 6 | led1 = LED(2) 7 | led2 = LED(3) 8 | led3 = LED(4) 9 | led4 = LED(17) 10 | led5 = LED(27) 11 | led6 = LED(22) 12 | led7 = LED(10) 13 | led8 = LED(9) 14 | led9 = LED(11) 15 | 16 | sleeptime = 0.2 17 | 18 | while True: 19 | led1.on() 20 | sleep(sleeptime) 21 | led1.off() 22 | led2.on() 23 | sleep(sleeptime) 24 | led2.off() 25 | led3.on() 26 | sleep(sleeptime) 27 | led3.off() 28 | led4.on() 29 | sleep(sleeptime) 30 | led4.off() 31 | led5.on() 32 | sleep(sleeptime) 33 | led5.off() 34 | led6.on() 35 | sleep(sleeptime) 36 | led6.off() 37 | led7.on() 38 | sleep(sleeptime) 39 | led7.off() 40 | led8.on() 41 | sleep(sleeptime) 42 | led8.off() 43 | led9.on() 44 | sleep(sleeptime) 45 | led9.off() 46 | -------------------------------------------------------------------------------- /Chapter03/programs/3d_array_visual.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | x = np.random.rand(3, 3, 3) 5 | 6 | plt.imshow(x) 7 | 8 | plt.axis('off') 9 | plt.grid('off') 10 | 11 | plt.show() 12 | -------------------------------------------------------------------------------- /Chapter03/programs/Alternate_Blink.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter03/programs/Alternate_Blink.fzz -------------------------------------------------------------------------------- /Chapter03/programs/Blink_LEDs.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter03/programs/Blink_LEDs.fzz -------------------------------------------------------------------------------- /Chapter03/programs/LED_Chaser.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter03/programs/LED_Chaser.fzz -------------------------------------------------------------------------------- /Chapter03/programs/Pull_Up.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter03/programs/Pull_Up.fzz -------------------------------------------------------------------------------- /Chapter03/programs/prog00.py: -------------------------------------------------------------------------------- 1 | print('Hello World!') 2 | -------------------------------------------------------------------------------- /Chapter03/programs/prog01.py: -------------------------------------------------------------------------------- 1 | class Person: 2 | 3 | def __init__(self, name='', age=0): 4 | self.name = name 5 | self.age = age 6 | 7 | def show(self): 8 | print(self.name) 9 | print(self.age) 10 | 11 | p1 = Person('Ashwin', 25) 12 | p1.show() 13 | 14 | p2 = Person() 15 | p2.name = 'Jane' 16 | p2.age = 20 17 | print(p2.name) 18 | print(p2.age) 19 | -------------------------------------------------------------------------------- /Chapter03/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | x = np.array([1, 2, 3, 4], dtype=np.uint8) 5 | 6 | y = x**2 + 1 7 | 8 | plt.plot(x, y) 9 | 10 | plt.grid('on') 11 | 12 | plt.show() 13 | -------------------------------------------------------------------------------- /Chapter03/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | x = np.array([1, 2, 3, 4], dtype=np.uint8) 5 | 6 | y = x**2 + 1 7 | 8 | plt.plot(x, y) 9 | 10 | y = x + 1 11 | plt.plot(x, y) 12 | plt.title('Graph') 13 | plt.xlabel('X-Axis') 14 | plt.ylabel('Y-Axis') 15 | 16 | plt.grid('on') 17 | 18 | plt.savefig('test1.png', dpi=300, bbox_inches='tight') 19 | 20 | plt.show() 21 | 22 | -------------------------------------------------------------------------------- /Chapter03/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | x = np.array([[0, 1, 2, 3, 4], 5 | [5, 6, 7, 8, 9]], dtype = np.uint8 ) 6 | 7 | plt.imshow(x, cmap='Accent') 8 | 9 | plt.axis('off') 10 | plt.grid('off') 11 | 12 | plt.show() 13 | -------------------------------------------------------------------------------- /Chapter03/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import RPi.GPIO as GPIO 2 | from time import sleep 3 | 4 | GPIO.setwarnings(False) 5 | GPIO.setmode(GPIO.BOARD) 6 | GPIO.setup(8, GPIO.OUT, initial=GPIO.LOW) 7 | 8 | while True: 9 | GPIO.output(8, GPIO.HIGH) 10 | sleep(1) 11 | GPIO.output(8, GPIO.LOW) 12 | sleep(1) 13 | -------------------------------------------------------------------------------- /Chapter03/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import time 2 | import RPi.GPIO as GPIO 3 | GPIO.setmode(GPIO.BOARD) 4 | GPIO.setwarnings(False) 5 | 6 | button = 7 7 | 8 | GPIO.setup(button, GPIO.IN, GPIO.PUD_UP) 9 | 10 | while True: 11 | button_state = GPIO.input(button) 12 | if button_state == GPIO.HIGH: 13 | print ("HIGH") 14 | else: 15 | print ("LOW") 16 | time.sleep(0.5) 17 | -------------------------------------------------------------------------------- /Chapter03/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import time 2 | import RPi.GPIO as GPIO 3 | GPIO.setmode(GPIO.BOARD) 4 | GPIO.setwarnings(False) 5 | 6 | button = 7 7 | 8 | GPIO.setup(button, GPIO.IN, GPIO.PUD_DOWN) 9 | 10 | while True: 11 | button_state = GPIO.input(button) 12 | if button_state == GPIO.HIGH: 13 | print ("HIGH") 14 | else: 15 | print ("LOW") 16 | time.sleep(0.5) 17 | -------------------------------------------------------------------------------- /Chapter03/programs/pull_down.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter03/programs/pull_down.fzz -------------------------------------------------------------------------------- /Chapter04/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | cv2.imshow('Mandrill', img) 4 | cv2.waitKey(0) 5 | cv2.destroyWindow('Mandrill') 6 | -------------------------------------------------------------------------------- /Chapter04/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | cv2.imshow('Mandrill', img) 4 | keyPress = cv2.waitKey(0) 5 | if keyPress == ord('q'): 6 | cv2.destroyWindow('Mandrill') 7 | elif keyPress == ord('s'): 8 | cv2.imwrite('test.jpg', img) 9 | cv2.destroyWindow('Mandrill') 10 | -------------------------------------------------------------------------------- /Chapter04/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 0) 3 | 4 | import matplotlib.pyplot as plt 5 | plt.imshow(img, cmap='gray') 6 | plt.title('Mandrill') 7 | plt.axis('off') 8 | plt.show() -------------------------------------------------------------------------------- /Chapter04/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | image = np.zeros((200, 200, 3), np.uint8) 4 | cv2.line(image, (0, 199), (199, 0), (0, 0, 255), 2) 5 | cv2.rectangle(image, (20, 20), (60, 60), (255, 0, 0), 1) 6 | cv2.circle(image, (80, 80), 10, (0, 255, 0), -1) 7 | cv2.ellipse(image, (99, 99), (40, 20), 0, 0, 360, (128, 128, 128), -1) 8 | points = np.array([[100, 5], [125, 30], [175, 20], [185, 10]], np.int32) 9 | points = points.reshape((-1, 1, 2)) 10 | cv2.polylines(image, [points], True, (255, 255, 0)) 11 | cv2.putText(image, 'Test', (80, 180), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 255)) 12 | cv2.imshow('Shapes', image) 13 | cv2.waitKey(0) 14 | cv2.destroyAllWindows() 15 | -------------------------------------------------------------------------------- /Chapter04/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | def empty(z): 4 | pass 5 | image = np.zeros((300, 512, 3), np.uint8) 6 | cv2.namedWindow('Palette') 7 | cv2.createTrackbar('B', 'Palette', 0, 255, empty) 8 | cv2.createTrackbar('G', 'Palette', 0, 255, empty) 9 | cv2.createTrackbar('R', 'Palette', 0, 255, empty) 10 | while(True): 11 | cv2.imshow('Palette', image) 12 | if cv2.waitKey(1) == 27 : 13 | break 14 | blue = cv2.getTrackbarPos('B', 'Palette') 15 | green = cv2.getTrackbarPos('G', 'Palette') 16 | red = cv2.getTrackbarPos('R', 'Palette') 17 | image[:] = [blue, green, red] 18 | cv2.destroyWindow('Palette') 19 | -------------------------------------------------------------------------------- /Chapter04/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | windowName = 'Drawing' 5 | img = np.zeros((512, 512, 3), np.uint8) 6 | cv2.namedWindow(windowName) 7 | 8 | def draw_circle(event, x, y, flags, param): 9 | if event == cv2.EVENT_LBUTTONDBLCLK: 10 | cv2.circle(img, (x, y), 40, (0, 255, 0), -1) 11 | if event == cv2.EVENT_MBUTTONDOWN: 12 | cv2.circle(img, (x, y), 20, (0, 0, 255), -1) 13 | if event == cv2.EVENT_LBUTTONDOWN: 14 | cv2.circle(img, (x, y), 30, (255, 0, 0), -1) 15 | 16 | cv2.setMouseCallback(windowName, draw_circle) 17 | 18 | while(True): 19 | cv2.imshow(windowName, img) 20 | if cv2.waitKey(20) == 27: 21 | break 22 | 23 | cv2.destroyAllWindows() 24 | 25 | -------------------------------------------------------------------------------- /Chapter04/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | 4 | cap = cv2.VideoCapture(0) 5 | 6 | if cap.isOpened(): 7 | ret, frame = cap.read() 8 | else: 9 | ret = False 10 | 11 | print(ret) 12 | print(type(frame)) 13 | cv2.imshow('Frame from Webcam', frame) 14 | cv2.waitKey(0) 15 | cap.release() 16 | cv2.destroyAllWindows() 17 | -------------------------------------------------------------------------------- /Chapter04/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | windowName = "Live Video Feed" 4 | cv2.namedWindow(windowName) 5 | cap = cv2.VideoCapture(0) 6 | 7 | if cap.isOpened(): 8 | ret, frame = cap.read() 9 | else: 10 | ret = False 11 | 12 | while ret: 13 | ret, frame = cap.read() 14 | cv2.imshow(windowName, frame) 15 | if cv2.waitKey(1) == 27: 16 | break 17 | 18 | cv2.destroyAllWindows() 19 | cap.release() 20 | 21 | -------------------------------------------------------------------------------- /Chapter04/programs/prog08.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | windowName = "Live Video Feed" 4 | cv2.namedWindow(windowName) 5 | cap = cv2.VideoCapture(0) 6 | 7 | print('Width : ' + str(cap.get(3))) 8 | print('Height : ' + str(cap.get(4))) 9 | 10 | cap.set(3, 5000) 11 | cap.set(4, 5000) 12 | 13 | print('Width : ' + str(cap.get(3))) 14 | print('Height : ' + str(cap.get(4))) 15 | 16 | if cap.isOpened(): 17 | ret, frame = cap.read() 18 | else: 19 | ret = False 20 | 21 | while ret: 22 | ret, frame = cap.read() 23 | cv2.imshow(windowName, frame) 24 | 25 | if cv2.waitKey(1) == 27: 26 | break 27 | 28 | cv2.destroyAllWindows() 29 | cap.release() 30 | 31 | -------------------------------------------------------------------------------- /Chapter04/programs/prog09.py: -------------------------------------------------------------------------------- 1 | import time 2 | import cv2 3 | 4 | cap = cv2.VideoCapture(0) 5 | 6 | fps = cap.get(cv2.CAP_PROP_FPS) 7 | print("FPS with CAP_PROP_FPS : {0}".format(fps)) 8 | 9 | num_frames = 120 10 | print("Capturing {0} frames".format(num_frames)) 11 | 12 | start = time.time() 13 | for i in range(0, num_frames): 14 | ret, frame = cap.read() 15 | end = time.time() 16 | 17 | seconds = end - start 18 | print("Time taken : {0} seconds".format(seconds)) 19 | 20 | fps = num_frames / seconds 21 | print("Actual FPS calculated : {0}".format(fps)) 22 | 23 | cap.release() 24 | -------------------------------------------------------------------------------- /Chapter04/programs/prog10.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | windowName = "Live Video Feed" 4 | cv2.namedWindow(windowName) 5 | cap = cv2.VideoCapture(0) 6 | filename = 'output.avi' 7 | codec = cv2.VideoWriter_fourcc('W', 'M', 'V', '2') 8 | framerate = 30 9 | resolution = (640, 480) 10 | Output = cv2.VideoWriter(filename, codec, 11 | framerate, resolution) 12 | 13 | if cap.isOpened(): 14 | ret, frame = cap.read() 15 | else: 16 | ret = False 17 | 18 | while ret: 19 | ret, frame = cap.read() 20 | Output.write(frame) 21 | cv2.imshow(windowName, frame) 22 | if cv2.waitKey(1) == 27: 23 | break 24 | 25 | cv2.destroyAllWindows() 26 | cap.release() 27 | -------------------------------------------------------------------------------- /Chapter04/programs/prog11.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | windowName = "OpenCV Video Player" 4 | cv2.namedWindow(windowName) 5 | filename = 'output.avi' 6 | cap = cv2.VideoCapture(filename) 7 | 8 | while(cap.isOpened()): 9 | ret, frame = cap.read() 10 | if ret: 11 | cv2.imshow(windowName, frame) 12 | if cv2.waitKey(33) == 27: 13 | break 14 | else: 15 | break 16 | 17 | cv2.destroyAllWindows() 18 | cap.release() 19 | 20 | -------------------------------------------------------------------------------- /Chapter04/programs/prog12.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | from picamera import PiCamera 3 | 4 | camera = PiCamera() 5 | camera.resolution = (1024, 768) 6 | camera.start_preview() 7 | sleep(2) 8 | camera.capture('test.png') 9 | 10 | -------------------------------------------------------------------------------- /Chapter04/programs/prog13.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | from picamera import PiCamera 3 | 4 | camera = PiCamera() 5 | camera.start_preview() 6 | sleep(2) 7 | for filename in camera.capture_continuous('img{counter:03d}.png'): 8 | print('Captured %s' % filename) 9 | sleep(1) 10 | -------------------------------------------------------------------------------- /Chapter04/programs/prog14.py: -------------------------------------------------------------------------------- 1 | import picamera 2 | 3 | camera = picamera.PiCamera() 4 | for filename in camera.record_sequence('%d.h264' % i for i in range(1, 11)): 5 | camera.wait_recording(2) 6 | 7 | -------------------------------------------------------------------------------- /Chapter04/programs/prog15.py: -------------------------------------------------------------------------------- 1 | import picamera 2 | 3 | camera = picamera.PiCamera() 4 | camera.resolution = (320, 240) 5 | camera.start_recording('my_video.h264') 6 | camera.wait_recording(5) 7 | camera.stop_recording() 8 | -------------------------------------------------------------------------------- /Chapter04/programs/prog16.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | from picamera import PiCamera 3 | 4 | camera = PiCamera() 5 | camera.resolution = (1024, 768) 6 | camera.start_preview() 7 | camera.annotate_text = 'Hello World!' 8 | sleep(2) 9 | camera.capture('test.png') 10 | 11 | -------------------------------------------------------------------------------- /Chapter04/programs/prog17.py: -------------------------------------------------------------------------------- 1 | import time, picamera 2 | import numpy as np 3 | 4 | with picamera.PiCamera() as camera: 5 | camera.resolution = (320, 240) 6 | camera.framerate = 24 7 | time.sleep(2) 8 | output = np.empty((240, 320, 3), dtype=np.uint8) 9 | camera.capture(output, 'rgb') 10 | print(type(output)) 11 | 12 | -------------------------------------------------------------------------------- /Chapter04/programs/prog18.py: -------------------------------------------------------------------------------- 1 | import time, picamera 2 | import numpy as np 3 | 4 | with picamera.PiCamera() as camera: 5 | camera.resolution = (320, 240) 6 | camera.framerate = 24 7 | time.sleep(2) 8 | image = np.empty((240 * 320 * 3, ), dtype=np.uint8) 9 | camera.capture(image, 'bgr') 10 | image = image.reshape((240, 320, 3)) 11 | print(type(image)) 12 | 13 | -------------------------------------------------------------------------------- /Chapter04/programs/timelapse.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DATE=$(date +"%Y-%m-%d_%H%M") 3 | fswebcam -r 1280x960 --no-banner Image_$DATE.png 4 | -------------------------------------------------------------------------------- /Chapter05/programs/Pull_Up_circuit.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/raspberry-pi-computer-vision-programming/b04ce333243343a4047243e06a7c88ccdfee33fd/Chapter05/programs/Pull_Up_circuit.fzz -------------------------------------------------------------------------------- /Chapter05/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/4.1.01.tiff', 1) 3 | b1 = cv2.copyMakeBorder(img, 10, 10, 10, 10, cv2.BORDER_WRAP) 4 | b2 = cv2.copyMakeBorder(img, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[255, 0, 0]) 5 | cv2.imshow('Wrap', b1) 6 | cv2.imshow('Constant', b2) 7 | cv2.waitKey(0) 8 | cv2.destroyAllWindows() 9 | -------------------------------------------------------------------------------- /Chapter05/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 4 | cv2.imshow('NumPy Addition', img1 + img2 ) 5 | cv2.imshow('OpenCV Addition', cv2.add(img1, img2)) 6 | cv2.waitKey(0) 7 | cv2.destroyAllWindows() 8 | -------------------------------------------------------------------------------- /Chapter05/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 4 | cv2.imshow('NumPy Subtract', img1 - img2 ) 5 | cv2.imshow('OpenCV Subtract', cv2.subtract(img1, img2)) 6 | cv2.waitKey(0) 7 | cv2.destroyAllWindows() 8 | 9 | -------------------------------------------------------------------------------- /Chapter05/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 4 | cv2.imshow('Blended Image', 5 | cv2.addWeighted(img1, 0.5, img2, 0.5, 0)) 6 | cv2.waitKey(0) 7 | cv2.destroyAllWindows() 8 | -------------------------------------------------------------------------------- /Chapter05/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import time 3 | import numpy as np 4 | 5 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 6 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 7 | for i in np.linspace(0, 1, 100): 8 | alpha = i 9 | beta = 1-alpha 10 | print('ALPHA =' + str(alpha) + ' BETA =' + str(beta)) 11 | cv2.imshow('Image Transition', 12 | cv2.addWeighted(img1, alpha, img2, beta, 0)) 13 | time.sleep(0.05) 14 | if cv2.waitKey(1) == 27 : 15 | break 16 | cv2.destroyAllWindows() 17 | -------------------------------------------------------------------------------- /Chapter05/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import time 3 | import numpy as np 4 | 5 | def emptyFunction(): 6 | pass 7 | 8 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 9 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 10 | output = cv2.addWeighted(img1, 0.5, img2, 0.5, 0) 11 | windowName = "Transition Demo" 12 | cv2.namedWindow(windowName) 13 | cv2.createTrackbar('Alpha', windowName, 0, 14 | 1000, emptyFunction) 15 | while(True): 16 | cv2.imshow(windowName, output) 17 | if cv2.waitKey(1) == 27: 18 | break 19 | alpha = cv2.getTrackbarPos('Alpha', windowName) / 1000 20 | beta = 1 - alpha 21 | output = cv2.addWeighted(img1, alpha, img2, beta, 0) 22 | print(alpha, beta) 23 | cv2.destroyAllWindows() 24 | -------------------------------------------------------------------------------- /Chapter05/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import time 2 | import RPi.GPIO as GPIO 3 | import cv2 4 | 5 | alpha = 0 6 | 7 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 8 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 9 | 10 | GPIO.setmode(GPIO.BOARD) 11 | GPIO.setwarnings(False) 12 | 13 | button1 = 7 14 | button2 = 11 15 | 16 | GPIO.setup(button1, GPIO.IN, GPIO.PUD_UP) 17 | GPIO.setup(button2, GPIO.IN, GPIO.PUD_UP) 18 | 19 | while True: 20 | button1_state = GPIO.input(button1) 21 | 22 | if button1_state == GPIO.LOW and alpha < 1: 23 | alpha = alpha + 0.2 24 | 25 | button2_state = GPIO.input(button2) 26 | 27 | if button2_state == GPIO.LOW: 28 | if (alpha > 0): 29 | alpha = alpha - 0.2 30 | 31 | if (alpha < 0): 32 | alpha = 0 33 | 34 | beta = 1 - alpha 35 | 36 | output = cv2.addWeighted(img1, alpha, img2, beta, 0) 37 | cv2.imshow('Transition App', output) 38 | if cv2.waitKey(1) == 27: 39 | break 40 | 41 | time.sleep(0.5) 42 | print(alpha) 43 | cv2.destroyAllWindows() 44 | -------------------------------------------------------------------------------- /Chapter05/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img1 = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 3 | img2 = cv2.imread('/home/pi/book/dataset/4.2.05.tiff', 1) 4 | cv2.imshow('Image1', img1 * 2) 5 | cv2.waitKey(0) 6 | cv2.destroyAllWindows() 7 | -------------------------------------------------------------------------------- /Chapter05/programs/prog08.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 0) 3 | negative = abs(255 - img) 4 | cv2.imshow('Grayscale', img) 5 | cv2.imshow('Negative', negative) 6 | cv2.waitKey(0) 7 | cv2.destroyAllWindows() 8 | -------------------------------------------------------------------------------- /Chapter05/programs/prog09.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | a = [0, 255, 0] 5 | img1 = np.array([a, a, a], dtype=np.uint8) 6 | img2 = np.transpose(img1) 7 | not_out = cv2.bitwise_not(img1 ) 8 | and_out = cv2.bitwise_and(img1, img2) 9 | or_out = cv2.bitwise_or(img1, img2) 10 | xor_out = cv2.bitwise_xor(img1, img2) 11 | titles = ['Image 1', 'Image 2', 'Image 1 NOT', 'AND', 'OR', 'XOR'] 12 | images = [img1, img2, not_out, and_out, or_out, xor_out] 13 | for i in range(6): 14 | plt.subplot(2, 3, i+1) 15 | plt.imshow(images[i], cmap='gray') 16 | plt.title(titles[i]) 17 | plt.axis('off') 18 | plt.show() 19 | -------------------------------------------------------------------------------- /Chapter06/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 1) 4 | b,g,r = cv2.split (img) 5 | img = cv2.merge((r, g, b)) 6 | plt.imshow (img) 7 | plt.title ('COLOR IMAGE') 8 | plt.axis('off') 9 | plt.show() 10 | -------------------------------------------------------------------------------- /Chapter06/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 1) 4 | img = cv2.cvtColor (img, cv2.COLOR_BGR2RGB) 5 | plt.imshow (img) 6 | plt.title ('COLOR IMAGE') 7 | plt.axis('off') 8 | plt.show() 9 | -------------------------------------------------------------------------------- /Chapter06/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | j=0 3 | for filename in dir(cv2): 4 | if filename.startswith('COLOR_'): 5 | print(filename) 6 | j = j + 1 7 | print('There are ' + str(j) + 8 | ' Colorspace Conversion flags in OpenCV ' 9 | + cv2.__version__ + '.') 10 | -------------------------------------------------------------------------------- /Chapter06/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | c = cv2.cvtColor(np.array([[[255, 0, 0]]], 4 | dtype=np.uint8), 5 | cv2.COLOR_BGR2HSV) 6 | print(c) 7 | -------------------------------------------------------------------------------- /Chapter06/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | def emptyFunction(): 4 | pass 5 | 6 | img = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 1) 7 | 8 | windowName = "Saturation Demo" 9 | cv2.namedWindow(windowName) 10 | cv2.createTrackbar('Saturation Level', 11 | windowName, 0, 12 | 24, emptyFunction) 13 | while(True): 14 | hsv = cv2.cvtColor( img, cv2.COLOR_BGR2HSV) 15 | h, s, v = cv2.split(hsv) 16 | saturation = cv2.getTrackbarPos('Saturation Level', windowName) 17 | s = s + saturation 18 | v = v + saturation 19 | img1 = cv2.cvtColor(cv2.merge((h, s, v)), cv2.COLOR_HSV2BGR) 20 | cv2.imshow(windowName, img1) 21 | if cv2.waitKey(1) == 27: 22 | break 23 | cv2.destroyAllWindows() 24 | -------------------------------------------------------------------------------- /Chapter06/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | cap = cv2.VideoCapture(0) 4 | while ( True ): 5 | ret, frame = cap.read() 6 | hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 7 | image_mask = cv2.inRange(hsv, np.array([40, 50, 50]), 8 | np.array([80, 255, 255])) 9 | output = cv2.bitwise_and(frame, frame,mask=image_mask) 10 | cv2.imshow('Original', frame) 11 | cv2.imshow('Output', output) 12 | if cv2.waitKey(1) == 27: 13 | break 14 | cv2.destroyAllWindows() 15 | cap.release() 16 | -------------------------------------------------------------------------------- /Chapter06/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | def emptyFunction(): 4 | pass 5 | cap = cv2.VideoCapture(0) 6 | windowName = 'Object Tracker' 7 | trackbarName = 'Color Chooser' 8 | cv2.namedWindow(windowName) 9 | cv2.createTrackbar(trackbarName, 10 | windowName, 0, 1, 11 | emptyFunction) 12 | color = 0 13 | while ( True ): 14 | ret, frame = cap.read() 15 | hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 16 | color = cv2.getTrackbarPos(trackbarName, windowName) 17 | if color == 0: 18 | image_mask = cv2.inRange(hsv, np.array([40, 50, 50]), 19 | np.array([80, 255, 255])) 20 | else: 21 | image_mask = cv2.inRange(hsv, np.array([100, 50, 50]), 22 | np.array([140, 255, 255])) 23 | output = cv2.bitwise_and(frame, frame, mask=image_mask) 24 | cv2.imshow(windowName, output) 25 | if cv2.waitKey(1) == 27: 26 | break 27 | cv2.destroyAllWindows() 28 | cap.release() 29 | -------------------------------------------------------------------------------- /Chapter06/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 3 | upscale = cv2.resize(img, None, fx=1.5, fy=1.5, 4 | interpolation=cv2.INTER_CUBIC) 5 | downscale = cv2.resize(img, None, fx=0.5, fy=0.5, 6 | interpolation=cv2.INTER_AREA) 7 | cv2.imshow('upscale', upscale) 8 | cv2.imshow('downscale', downscale) 9 | cv2.waitKey(0) 10 | cv2.destroyAllWindows() 11 | -------------------------------------------------------------------------------- /Chapter06/programs/prog08.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import matplotlib.pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 5 | input=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 6 | rows, cols, channel = img.shape 7 | T = np.float32([[1, 0, -50], [0, 1, 50]]) 8 | output = cv2.warpAffine(input, T, (cols, rows)) 9 | plt.imshow(output) 10 | plt.title('Shifted Image') 11 | plt.show() 12 | -------------------------------------------------------------------------------- /Chapter06/programs/prog09.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 4 | input = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 5 | rows, cols, channel = img.shape 6 | R = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 0.5) 7 | output = cv2.warpAffine(input, R, (cols, rows)) 8 | plt.imshow(output) 9 | plt.title('Rotated and Downscaled Image') 10 | plt.show() 11 | -------------------------------------------------------------------------------- /Chapter06/programs/prog10.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from time import sleep 3 | image = cv2.imread('/home/pi/book/dataset/house.tiff',1) 4 | rows, cols, channels = image.shape 5 | angle = 0 6 | while(1): 7 | if angle == 360: 8 | angle = 0 9 | M = cv2.getRotationMatrix2D((cols/2, rows/2), angle, 1) 10 | rotated = cv2.warpAffine(image, M, (cols, rows)) 11 | cv2.imshow('Rotating Image', rotated) 12 | angle = angle +1 13 | sleep(0.2) 14 | if cv2.waitKey(1) == 27 : 15 | break 16 | cv2.destroyAllWindows() 17 | -------------------------------------------------------------------------------- /Chapter06/programs/prog11.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from time import sleep 3 | cap = cv2.VideoCapture(0) 4 | ret, frame = cap.read() 5 | rows, cols, channels = frame.shape 6 | angle = 0 7 | while(1): 8 | ret, frame = cap.read() 9 | if angle == 360: 10 | angle = 0 11 | M = cv2.getRotationMatrix2D((cols/2, rows/2), angle, 1) 12 | rotated = cv2.warpAffine(frame, M, (cols, rows)) 13 | cv2.imshow('Rotating Image', rotated) 14 | angle = angle +1 15 | sleep(0.2) 16 | if cv2.waitKey(1) == 27 : 17 | break 18 | cv2.destroyAllWindows() 19 | -------------------------------------------------------------------------------- /Chapter06/programs/prog12.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | image = cv2.imread('/home/pi/book/dataset/4.2.06.tiff', 1) 5 | input = cv2.cvtColor(image, cv2.COLOR_BGR2RGB ) 6 | rows, cols, channels = input.shape 7 | points1 = np.float32([[100, 100], [300, 100], [100, 300]]) 8 | points2 = np.float32([[200, 150], [400, 150], [100, 300]]) 9 | A = cv2.getAffineTransform(points1, points2) 10 | output = cv2.warpAffine(input, A, (cols, rows)) 11 | plt.subplot(121) 12 | plt.imshow(input) 13 | plt.title('Input') 14 | plt.subplot(122) 15 | plt.imshow(output) 16 | plt.title('Affine Output') 17 | plt.show() 18 | -------------------------------------------------------------------------------- /Chapter06/programs/prog13.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | image = cv2.imread('/home/pi/book/dataset/ruler.512.tiff', 1) 5 | input = cv2.cvtColor(image, cv2.COLOR_BGR2RGB ) 6 | rows, cols, channels = input.shape 7 | points1 = np.float32([[0, 0], [400, 0], [0, 400], [400, 400]]) 8 | points2 = np.float32([[0,0], [300, 0], [0, 300], [300, 300]]) 9 | P = cv2.getPerspectiveTransform(points1, points2) 10 | output = cv2.warpPerspective(input, P, (300, 300)) 11 | plt.subplot(121) 12 | plt.imshow(input) 13 | plt.title('Input Image') 14 | plt.subplot(122) 15 | plt.imshow(output) 16 | plt.title('Perspective Transform') 17 | plt.show() 18 | -------------------------------------------------------------------------------- /Chapter06/programs/prog14.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | img = cv2.imread('/home/pi/book/dataset/gray21.512.tiff', 1) 5 | th = 127 6 | max_val = 255 7 | ret, o1 = cv2.threshold(img, th, max_val, 8 | cv2.THRESH_BINARY) 9 | print(o1) 10 | ret, o2 = cv2.threshold(img, th, max_val, 11 | cv2.THRESH_BINARY_INV) 12 | ret, o3 = cv2.threshold(img, th, max_val, 13 | cv2.THRESH_TOZERO) 14 | ret, o4 = cv2.threshold(img, th, max_val, 15 | cv2.THRESH_TOZERO_INV) 16 | ret, o5 = cv2.threshold(img, th, max_val, 17 | cv2.THRESH_TRUNC) 18 | titles = ['Input Image', 'BINARY', 'BINARY_INV', 19 | 'TOZERO', 'TOZERO_INV', 'TRUNC'] 20 | output = [img, o1, o2, o3, o4, o5] 21 | for i in range(6): 22 | plt.subplot(2, 3, i+1) 23 | plt.imshow(output[i], cmap='gray') 24 | plt.title(titles[i]) 25 | plt.axis('off') 26 | plt.show() 27 | -------------------------------------------------------------------------------- /Chapter06/programs/prog15.py: -------------------------------------------------------------------------------- 1 | import time 2 | import RPi.GPIO as GPIO 3 | import cv2 4 | 5 | thresh = 127 6 | 7 | cap = cv2.VideoCapture(0) 8 | 9 | GPIO.setmode(GPIO.BOARD) 10 | GPIO.setwarnings(False) 11 | 12 | button1 = 7 13 | button2 = 11 14 | 15 | GPIO.setup(button1, GPIO.IN, GPIO.PUD_UP) 16 | GPIO.setup(button2, GPIO.IN, GPIO.PUD_UP) 17 | 18 | while True: 19 | 20 | ret, frame = cap.read() 21 | 22 | button1_state = GPIO.input(button1) 23 | 24 | if button1_state == GPIO.LOW and thresh < 256: 25 | thresh = thresh + 1 26 | 27 | button2_state = GPIO.input(button2) 28 | 29 | if button2_state == GPIO.LOW and thresh > -1: 30 | thresh = thresh - 1 31 | 32 | ret1, output = cv2.threshold(frame, thresh, 255, 33 | cv2.THRESH_BINARY) 34 | print(thresh) 35 | cv2.imshow('Thresholding App', output) 36 | if cv2.waitKey(1) == 27: 37 | break 38 | 39 | cv2.destroyAllWindows() 40 | -------------------------------------------------------------------------------- /Chapter06/programs/prog16.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0) 4 | block_size = 123 5 | constant = 6 6 | th1 = cv2.adaptiveThreshold(img, 255, 7 | cv2.ADAPTIVE_THRESH_MEAN_C, 8 | cv2.THRESH_BINARY, 9 | block_size, constant) 10 | th2 = cv2.adaptiveThreshold (img, 255, 11 | cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 12 | cv2.THRESH_BINARY, 13 | block_size, constant) 14 | output = [img, th1, th2] 15 | titles = ['Original', 'Mean Adaptive', 'Gaussian Adaptive'] 16 | for i in range(3): 17 | plt.subplot(1, 3, i+1) 18 | plt.imshow(output[i], cmap='gray') 19 | plt.title(titles[i]) 20 | plt.xticks([]) 21 | plt.yticks([]) 22 | plt.show() 23 | -------------------------------------------------------------------------------- /Chapter07/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import random 4 | import matplotlib.pyplot as plt 5 | img = cv2.imread('/home/pi/book/dataset/4.1.03.tiff', 0) 6 | output = np.zeros(img.shape, np.uint8) 7 | p = 0.05 8 | for i in range (img.shape[0]): 9 | for j in range(img.shape[1]): 10 | r = random.random() 11 | if r < p/2: 12 | output[i][j] = 0 13 | elif r < p: 14 | output[i][j] = 255 15 | else: 16 | output[i][j] = img[i][j] 17 | plt.imshow(output, cmap='gray') 18 | plt.title('Salt and Pepper Sprinkled') 19 | plt.axis('off') 20 | plt.show() 21 | -------------------------------------------------------------------------------- /Chapter07/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import RPi.GPIO as GPIO 2 | import cv2 3 | import numpy as np 4 | import random 5 | 6 | p = 0.00 7 | 8 | cap = cv2.VideoCapture(0) 9 | 10 | ret, frame = cap.read() 11 | output = np.zeros(frame.shape, np.uint8) 12 | 13 | GPIO.setmode(GPIO.BOARD) 14 | GPIO.setwarnings(False) 15 | 16 | button1 = 7 17 | button2 = 11 18 | 19 | GPIO.setup(button1, GPIO.IN, GPIO.PUD_UP) 20 | GPIO.setup(button2, GPIO.IN, GPIO.PUD_UP) 21 | 22 | while True: 23 | 24 | ret, frame = cap.read() 25 | 26 | button1_state = GPIO.input(button1) 27 | 28 | if button1_state == GPIO.LOW and p <= 0.1: 29 | p = p + 0.01 30 | 31 | if p > 0.1: 32 | p = 0.1 33 | 34 | button2_state = GPIO.input(button2) 35 | 36 | if button2_state == GPIO.LOW and p > 0: 37 | p = p - 0.01 38 | 39 | if p < 0: 40 | p = 0 41 | 42 | for i in range (frame.shape[0]): 43 | for j in range(frame.shape[1]): 44 | r = random.random() 45 | if r < p/2: 46 | output[i][j] = 0, 0, 0 47 | elif r < p: 48 | output[i][j] = 255, 255, 255 49 | else: 50 | output[i][j] = frame[i][j] 51 | 52 | print(p) 53 | cv2.imshow('Salt and pepper Noise App', output) 54 | if cv2.waitKey(1) == 27: 55 | break 56 | 57 | cap.release() 58 | cv2.destroyAllWindows() 59 | -------------------------------------------------------------------------------- /Chapter07/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import matplotlib.pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.1.03.tiff', 0) 5 | row, col = img.shape 6 | img = img.astype(np.float32) 7 | mean = 0 8 | var = 0.1 9 | sigma = var**0.5 10 | gauss = np.random.normal(mean, sigma, (row, col)) 11 | gauss = gauss.reshape(row, col) 12 | noisy = img + gauss 13 | print(abs(noisy-img)) 14 | plt.imshow(noisy, cmap='gray') 15 | plt.title('Gaussian (Normally distributed) Noise') 16 | plt.axis('off') 17 | plt.show() 18 | -------------------------------------------------------------------------------- /Chapter07/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import matplotlib.pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.1.03.tiff', 0) 5 | img = img.astype(np.float32) 6 | vals = len(np.unique(img)) 7 | vals = 2 ** np.ceil(np.log2(vals)) 8 | noisy = np.random.poisson(img * vals) / float(vals) 9 | print(abs(noisy-img)) 10 | plt.imshow(noisy, cmap='gray') 11 | plt.title('Poisson Noise') 12 | plt.axis('off') 13 | plt.show() 14 | -------------------------------------------------------------------------------- /Chapter07/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import matplotlib.pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.1.03.tiff', 0) 5 | img = img.astype(np.float32) 6 | row, col = img.shape 7 | rand_noise = np.random.randn(row, col) 8 | rand_noise = rand_noise.reshape(row, col) 9 | noisy = img + img * rand_noise 10 | print(abs(noisy-img)) 11 | plt.imshow(noisy, cmap='gray') 12 | plt.title('Random Normal Noise') 13 | plt.axis('off') 14 | plt.show() 15 | -------------------------------------------------------------------------------- /Chapter07/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import scipy.signal 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import cv2 5 | 6 | img = cv2.imread('/home/pi/book/dataset/4.1.03.tiff', 0) 7 | 8 | k1 = np.ones((7, 7), np.uint8)/49 9 | 10 | blurred = scipy.signal.convolve2d(img, k1) 11 | 12 | k2 = np.array([[0, -1, 0], 13 | [-1, 25, -1], 14 | [0, -1, 0]], dtype=np.int8) 15 | 16 | sharpened = scipy.signal.convolve2d(img, k2) 17 | 18 | plt.subplot(131) 19 | plt.imshow(img, cmap='gray') 20 | plt.title('Original Image') 21 | plt.axis('off') 22 | plt.subplot(132) 23 | plt.imshow(blurred, cmap='gray') 24 | plt.title('Blurred Image') 25 | plt.axis('off') 26 | plt.subplot(133) 27 | plt.imshow(sharpened, cmap='gray') 28 | plt.title('Sharpened Image') 29 | plt.axis('off') 30 | plt.show() -------------------------------------------------------------------------------- /Chapter07/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 5 | input = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 6 | output = cv2.filter2D(input, -1, np.ones((15, 15), np.uint8)/225) 7 | plt.subplot(121) 8 | plt.imshow(input) 9 | plt.title('Input') 10 | plt.axis('off') 11 | plt.subplot(122) 12 | plt.imshow(output) 13 | plt.title('Output') 14 | plt.axis('off') 15 | plt.show() 16 | -------------------------------------------------------------------------------- /Chapter07/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 5 | input = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 6 | output = cv2.sepFilter2D(img, ddepth=-1, kernelX=1, kernelY=1, delta=1) 7 | plt.subplot(121) 8 | plt.imshow(input) 9 | plt.title('Input') 10 | plt.axis('off') 11 | plt.subplot(122) 12 | plt.imshow(output) 13 | plt.title('Output') 14 | plt.axis('off') 15 | plt.show() 16 | -------------------------------------------------------------------------------- /Chapter08/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0) 4 | laplacian = cv2.Laplacian(img, ddepth=cv2.CV_32F, ksize=17, 5 | scale=1, delta=0, 6 | borderType=cv2.BORDER_DEFAULT) 7 | sobel = cv2.Sobel(img, ddepth=cv2.CV_32F, dx=1, dy=0, 8 | ksize=11, scale=1, delta=0, 9 | borderType=cv2.BORDER_DEFAULT) 10 | scharr = cv2.Scharr(img, ddepth=cv2.CV_32F, dx=1, dy=0, 11 | scale=1, delta=0, 12 | borderType=cv2.BORDER_DEFAULT) 13 | images=[img, laplacian, sobel, scharr] 14 | titles=['Original', 'Laplacian', 'Sobel', 'Scharr'] 15 | for i in range(4): 16 | plt.subplot(2, 2, i+1) 17 | plt.imshow(images[i], cmap = 'gray') 18 | plt.title(titles[i]) 19 | plt.axis('off') 20 | plt.show() 21 | -------------------------------------------------------------------------------- /Chapter08/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import RPi.GPIO as GPIO 2 | import cv2 3 | 4 | x = 0 5 | y = 1 6 | 7 | cap = cv2.VideoCapture(0) 8 | 9 | GPIO.setmode(GPIO.BOARD) 10 | GPIO.setwarnings(False) 11 | 12 | button1 = 7 13 | button2 = 11 14 | 15 | GPIO.setup(button1, GPIO.IN, GPIO.PUD_UP) 16 | GPIO.setup(button2, GPIO.IN, GPIO.PUD_UP) 17 | 18 | while True: 19 | print(x, y) 20 | ret, frame = cap.read() 21 | 22 | button1_state = GPIO.input(button1) 23 | 24 | if button1_state == GPIO.LOW: 25 | x = 0 26 | y = 1 27 | 28 | button2_state = GPIO.input(button2) 29 | 30 | if button2_state == GPIO.LOW: 31 | x = 1 32 | y = 0 33 | 34 | output = cv2.Scharr(frame, ddepth=cv2.CV_32F, 35 | dx=x, dy=y, 36 | scale=1, delta=0, 37 | borderType=cv2.BORDER_DEFAULT) 38 | 39 | 40 | cv2.imshow('Salt and pepper Noise App', output) 41 | if cv2.waitKey(1) == 27: 42 | break 43 | 44 | cap.release() 45 | cv2.destroyAllWindows() 46 | 47 | -------------------------------------------------------------------------------- /Chapter08/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | cap = cv2.VideoCapture(0) 3 | while True: 4 | ret, frame = cap.read() 5 | output1 = cv2.Scharr(frame, ddepth=cv2.CV_32F, 6 | dx=0, dy=1, 7 | scale=1, delta=0, 8 | borderType=cv2.BORDER_DEFAULT) 9 | 10 | output2 = cv2.Scharr(frame, ddepth=cv2.CV_32F, 11 | dx=1, dy=0, 12 | scale=1, delta=0, 13 | borderType=cv2.BORDER_DEFAULT) 14 | cv2.imshow('Addition of Vertical and Horizontal', 15 | cv2.add(output1, output2)) 16 | if cv2.waitKey(1) == 27: 17 | break 18 | cap.release() 19 | cv2.destroyAllWindows() 20 | 21 | -------------------------------------------------------------------------------- /Chapter08/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0) 4 | edges1 = cv2.Canny(img, 50, 300, L2gradient=False) 5 | edges2 = cv2.Canny(img, 100, 150, L2gradient=True) 6 | images = [img, edges1, edges2] 7 | titles = ['Original', 'L1 Gradient', 'L2 Gradient'] 8 | for i in range(3): 9 | plt.subplot(1, 3, i+1) 10 | plt.imshow(images[i], cmap = 'gray') 11 | plt.title(titles[i]) 12 | plt.axis('off') 13 | plt.show() 14 | -------------------------------------------------------------------------------- /Chapter08/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | cv2.namedWindow('Canny') 4 | img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0) 5 | 6 | def empty(z): 7 | pass 8 | 9 | cv2.createTrackbar('Threshold 1', 'Canny', 50, 100, empty) 10 | cv2.createTrackbar('Threshold 2', 'Canny', 150, 300, empty) 11 | 12 | while(True): 13 | 14 | l1 = cv2.getTrackbarPos('Threshold 1', 'Canny') 15 | l2 = cv2.getTrackbarPos('Threshold 2', 'Canny') 16 | 17 | output = cv2.Canny(img, l1, l2, L2gradient=False) 18 | 19 | cv2.imshow('Canny', output) 20 | if cv2.waitKey(1) == 27: 21 | break 22 | 23 | cv2.destroyAllWindows() 24 | -------------------------------------------------------------------------------- /Chapter08/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | cap = cv2.VideoCapture(0) 3 | while (True): 4 | ret , frame = cap.read() 5 | grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 6 | blur = cv2.blur(grey, (5, 5)) 7 | circles = cv2.HoughCircles(blur, 8 | method=cv2.HOUGH_GRADIENT, 9 | dp=1, minDist=200, 10 | param1=50, param2=13, 11 | minRadius=30, maxRadius=175) 12 | if circles is not None: 13 | for i in circles [0,:]: 14 | cv2.circle(frame, (i[0], i[1]), i[2], (0, 255, 0), 2) 15 | cv2.circle(frame, (i[0], i[1]), 2, (0, 0, 255), 3) 16 | cv2.imshow('Detected', frame) 17 | if cv2.waitKey(1) == 27: 18 | break 19 | cv2.destroyAllWindows() 20 | cap.release() 21 | -------------------------------------------------------------------------------- /Chapter08/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | cap = cv2.VideoCapture(0) 4 | while True: 5 | ret, img = cap.read() 6 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 7 | edges = cv2.Canny(gray, 50, 250, apertureSize=5, 8 | L2gradient=True) 9 | lines = cv2.HoughLines(edges, 1, np.pi/180, 200) 10 | if lines is not None: 11 | for rho,theta in lines[0]: 12 | a = np.cos(theta) 13 | b = np.sin(theta) 14 | x0 = a*rho 15 | y0 = b*rho 16 | pts1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a))) 17 | pts2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a))) 18 | cv2.line(img, pts1, pts2, (0, 0, 255), 2) 19 | cv2.imshow('Detected Lines', img) 20 | if cv2.waitKey(1) == 27: 21 | break 22 | cv2.destroyAllWindows() 23 | cap.release() 24 | -------------------------------------------------------------------------------- /Chapter08/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0) 5 | img = np.float32(img) 6 | dst = cv2.cornerHarris(img, 2, 3, 0.04) 7 | ret, dst = cv2.threshold(dst, 0.01*dst.max(), 255, 0) 8 | dst = np.uint8(dst) 9 | plt.imshow(dst, cmap='gray') 10 | plt.axis('off') 11 | plt.show() 12 | -------------------------------------------------------------------------------- /Chapter09/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | 4 | image = cv2.imread('/home/pi/book/dataset/Damaged.tiff') 5 | mask = cv2.imread('/home/pi/book/dataset/Mask.tiff', 0) 6 | 7 | input = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 8 | output_TELEA = cv2.inpaint(input, mask, 9 | 5, cv2.INPAINT_TELEA) 10 | output_NS = cv2.inpaint(input, mask, 11 | 5, cv2.INPAINT_NS) 12 | plt.subplot(221) 13 | plt.imshow(input) 14 | plt.title('Damaged Image') 15 | plt.axis('off') 16 | 17 | plt.subplot(222) 18 | plt.imshow(mask, cmap='gray'), 19 | plt.title('Mask') 20 | plt.axis('off') 21 | 22 | plt.subplot(223), 23 | plt.imshow(output_TELEA) 24 | plt.title('Telea Method') 25 | plt.axis('off') 26 | 27 | plt.subplot(224) 28 | plt.imshow(output_NS) 29 | plt.title('Navier Stokes Method') 30 | plt.axis('off') 31 | 32 | plt.show() 33 | -------------------------------------------------------------------------------- /Chapter09/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import pymeanshift as pms 3 | from matplotlib import pyplot as plt 4 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 5 | input = cv2.cvtColor(img, cv2.COLOR_BGR2RGB ) 6 | (segmented_image, labels_image, number_regions) = pms.segment( 7 | input, spatial_radius=2, range_radius=2, min_density=300) 8 | 9 | plt.subplot(131) 10 | plt.imshow(input) 11 | plt.title('Input') 12 | plt.axis('off') 13 | 14 | plt.subplot(132) 15 | plt.imshow(segmented_image) 16 | plt.title('Segmented Output') 17 | plt.axis('off') 18 | 19 | plt.subplot(133) 20 | plt.imshow(labels_image) 21 | plt.title('Labeled Output') 22 | plt.axis('off') 23 | 24 | plt.show() 25 | -------------------------------------------------------------------------------- /Chapter09/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import pymeanshift as pms 3 | 4 | cap = cv2.VideoCapture(0) 5 | 6 | while True: 7 | ret, frame = cap.read() 8 | 9 | (segmented_image, labels_image, number_regions) = pms.segment( 10 | frame, spatial_radius=2, range_radius=2, min_density=50) 11 | 12 | cv2.imshow('Segmented', segmented_image) 13 | if cv2.waitKey(1) == 27: 14 | break 15 | cv2.destroyAllWindows() 16 | cap.release() 17 | -------------------------------------------------------------------------------- /Chapter09/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | from matplotlib import pyplot as plt 4 | x = np.random.randint(25, 100, 25) 5 | y = np.random.randint(175, 255, 25) 6 | z = np.hstack((x, y)) 7 | z = z.reshape((50, 1)) 8 | z = np.float32(z) 9 | #plt.hist(z, 256, [0, 256]) 10 | #plt.show() 11 | criteria = (cv2.TERM_CRITERIA_EPS + 12 | cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) 13 | flags = cv2.KMEANS_RANDOM_CENTERS 14 | compactness, labels, centers = cv2.kmeans(z, 2, 15 | None, 16 | criteria, 17 | 10, flags) 18 | A = z[labels==0] 19 | B = z[labels==1] 20 | plt.hist(A, 256, [0, 256], color = 'g') 21 | plt.hist(B, 256, [0, 256], color = 'b') 22 | plt.hist(centers, 32, [0, 256], color = 'r') 23 | plt.show() 24 | -------------------------------------------------------------------------------- /Chapter09/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | from matplotlib import pyplot as plt 4 | 5 | X = np.random.randint(25, 50, (25, 2)) 6 | Y = np.random.randint(60, 85, (25, 2)) 7 | Z = np.vstack((X, Y)) 8 | 9 | Z = np.float32(Z) 10 | criteria = (cv2.TERM_CRITERIA_EPS + 11 | cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) 12 | ret,label,center=cv2.kmeans(Z, 2, None, criteria, 13 | 10, cv2.KMEANS_RANDOM_CENTERS) 14 | 15 | A = Z[label.ravel()==0] 16 | B = Z[label.ravel()==1] 17 | 18 | plt.scatter(A[:,0], A[:,1]) 19 | plt.scatter(B[:,0], B[:,1], c = 'g') 20 | plt.scatter(center[:,0], center[:,1], 21 | s = 80, c = 'r', marker = 's') 22 | plt.xlabel('X - Axis') 23 | plt.ylabel('Y - Axis') 24 | plt.show() 25 | -------------------------------------------------------------------------------- /Chapter09/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 6 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 7 | Z = img.reshape((-1, 3)) 8 | Z = np.float32(Z) 9 | criteria = (cv2.TERM_CRITERIA_EPS + 10 | cv2.TERM_CRITERIA_MAX_ITER, 11 | 10, 1.0) 12 | 13 | k = 2 14 | ret, label1, center1 = cv2.kmeans(Z, k, None, criteria, 10, 15 | cv2.KMEANS_RANDOM_CENTERS) 16 | center1=np.uint8(center1) 17 | res1 = center1[label1.flatten()] 18 | output1 = res1.reshape((img.shape)) 19 | 20 | k = 4 21 | ret, label1, center1 = cv2.kmeans(Z, k, None, criteria, 10, 22 | cv2.KMEANS_RANDOM_CENTERS) 23 | center1=np.uint8(center1) 24 | res1 = center1[label1.flatten()] 25 | output2 = res1.reshape((img.shape)) 26 | 27 | k = 15 28 | ret, label1, center1 = cv2.kmeans(Z, k, None, criteria, 10, 29 | cv2.KMEANS_RANDOM_CENTERS) 30 | center1=np.uint8(center1) 31 | res1 = center1[label1.flatten()] 32 | output3 = res1.reshape((img.shape)) 33 | 34 | output = [img, output1, output2, output3] 35 | titles = ['Original Image', 'K=2', 'K=4', 'K=12'] 36 | for i in range(4): 37 | plt.subplot(2, 2, i+1) 38 | plt.imshow(output[i]) 39 | plt.title(titles[i]) 40 | plt.xticks([]) 41 | plt.yticks([]) 42 | plt.show() 43 | -------------------------------------------------------------------------------- /Chapter09/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | Right= cv2.imread('/home/pi/book/dataset/imRsmall.jpg', 0) 4 | Left = cv2.imread('/home/pi/book/dataset/imLsmall.jpg', 0) 5 | stereo_BM_state=cv2.StereoBM_create() 6 | output_map=stereo_BM_state.compute(Left, Right) 7 | titles=['Left', 'Right', 'Depth Map'] 8 | output=[Left, Right, output_map] 9 | for i in range(3): 10 | plt.subplot(1, 3, i+1) 11 | plt.imshow(output[i], cmap='gray') 12 | plt.title(titles[i]) 13 | plt.axis('off') 14 | plt.show() 15 | -------------------------------------------------------------------------------- /Chapter10/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | a = np.array([1, 2, 1, 3, 4, 1, 2, 3, 4, 4, 2, 3, 4]) 5 | 6 | hist, bins = np.histogram(a) 7 | 8 | print(hist) 9 | print(bins) 10 | 11 | plt.hist(a) 12 | plt.show() 13 | -------------------------------------------------------------------------------- /Chapter10/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import cv2 4 | img = cv2.imread('/home/pi/book/dataset/boat.512.tiff', 0) 5 | plt.subplots_adjust(hspace=0.25, wspace=0.25) 6 | plt.subplot(1, 2, 1) 7 | plt.imshow(img, cmap='gray') 8 | plt.axis('off') 9 | plt.title('Original Image') 10 | 11 | plt.subplot(1, 2, 2) 12 | hist, bins = np.histogram(img.ravel(), 13 | bins=256, 14 | range=(0, 255)) 15 | plt.bar(bins[:-1], hist) 16 | plt.title('Histogram') 17 | plt.show() 18 | -------------------------------------------------------------------------------- /Chapter10/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import cv2 4 | img = cv2.imread('/home/pi/book/dataset/boat.512.tiff', 0) 5 | plt.subplots_adjust(hspace=0.25, wspace=0.25) 6 | plt.subplot(1, 2, 1) 7 | plt.imshow(img, cmap='gray') 8 | plt.axis('off') 9 | plt.title('Original Image') 10 | 11 | plt.subplot(1, 2, 2) 12 | plt.hist(img.ravel(), bins=256, range=(0, 255)) 13 | plt.title('Histogram') 14 | plt.show() 15 | -------------------------------------------------------------------------------- /Chapter10/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import cv2 4 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 5 | 6 | b = img[:, :, 0] 7 | g = img[:, :, 1] 8 | r = img[:, :, 2] 9 | 10 | plt.subplots_adjust(hspace=0.5, wspace=0.25) 11 | plt.subplot(2, 2, 1) 12 | plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB), 13 | cmap='gray') 14 | plt.axis('off') 15 | plt.title('Original Image') 16 | 17 | plt.subplot(2, 2, 2) 18 | plt.hist(r.ravel(), bins=256, range=(0, 255), color='r') 19 | plt.title('Red Histogram') 20 | 21 | plt.subplot(2, 2, 3) 22 | plt.hist(g.ravel(), bins=256, range=(0, 255), color='g') 23 | plt.title('Green Histogram') 24 | 25 | plt.subplot(2, 2, 4) 26 | plt.hist(b.ravel(), bins=256, range=(0, 255), color='b') 27 | plt.title('Blue Histogram') 28 | plt.show() 29 | -------------------------------------------------------------------------------- /Chapter10/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from matplotlib import pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/house.tiff', 1) 4 | input=cv2.cvtColor(img,cv2.COLOR_RGB2BGR) 5 | 6 | histr_RED = cv2.calcHist([input], [0], None, [256], [0, 255]) 7 | histr_GREEN = cv2.calcHist([input], [1], None, [256], [0, 255]) 8 | histr_BLUE = cv2.calcHist([input], [2], None, [256], [0, 255]) 9 | 10 | plt.subplot(221) 11 | plt.imshow(input) 12 | plt.title('Original Image') 13 | plt.axis('off') 14 | 15 | plt.subplot(222) 16 | plt.plot(histr_RED, color='r'), 17 | plt.title('Red') 18 | plt.xlim([0, 255]) 19 | plt.yticks([]) 20 | 21 | plt.subplot(223) 22 | plt.plot(histr_GREEN, color='g') 23 | plt.title('Green') 24 | plt.xlim([0, 255]) 25 | plt.yticks([]) 26 | 27 | plt.subplot(224) 28 | plt.plot(histr_BLUE, color='b') 29 | plt.title('Blue') 30 | plt.xlim([0, 255]) 31 | plt.yticks([]) 32 | plt.show() 33 | -------------------------------------------------------------------------------- /Chapter10/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | 4 | img = cv2.imread('/home/pi/book/dataset/4.2.03.tiff', 1) 5 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 6 | R, G, B = cv2.split(img) 7 | output1_R = cv2.equalizeHist(R) 8 | output1_G = cv2.equalizeHist(G) 9 | output1_B = cv2.equalizeHist(B) 10 | output1 = cv2.merge((output1_R, 11 | output1_G, 12 | output1_B)) 13 | clahe = cv2.createCLAHE(clipLimit=2.0, 14 | tileGridSize=(8, 8)) 15 | output2_R = clahe.apply(R) 16 | output2_G = clahe.apply(G) 17 | output2_B = clahe.apply(B) 18 | output2 = cv2.merge((output2_R, 19 | output2_G, 20 | output2_B)) 21 | output = [img, output1, output2] 22 | titles = ['Original Image', 23 | 'Adjusted Histogram', 'CLAHE'] 24 | for i in range(3): 25 | plt.subplot(1, 3, i+1) 26 | plt.imshow(output[i]) 27 | plt.title(titles[i]) 28 | plt.xticks([]) 29 | plt.yticks([]) 30 | plt.show() 31 | -------------------------------------------------------------------------------- /Chapter10/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | img = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 1) 4 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 5 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 6 | ret, thresh = cv2.threshold(gray, 75, 255, 0) 7 | contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, 8 | cv2.CHAIN_APPROX_SIMPLE) 9 | cv2.drawContours(img, contours, -1, (0, 0, 255), 2) 10 | original = cv2.imread('/home/pi/book/dataset/4.2.07.tiff', 1) 11 | original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB) 12 | output = [original, img] 13 | titles = ['Original', 'Contours'] 14 | for i in range(2): 15 | plt.subplot(1, 2, i+1) 16 | plt.imshow(output[i]) 17 | plt.title(titles[i]) 18 | plt.axis('off') 19 | plt.show() 20 | -------------------------------------------------------------------------------- /Chapter10/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | from matplotlib import pyplot as plt 4 | 5 | img = np.array([[0, 0, 0, 0, 0, 0, 0], 6 | [0, 0, 0, 0, 0, 0, 0], 7 | [0, 0, 255, 255, 255, 0, 0], 8 | [0, 0, 255, 255, 255, 0, 0], 9 | [0, 0, 255, 255, 255, 0, 0], 10 | [0, 0, 0, 0, 0, 0, 0], 11 | [0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8) 12 | 13 | kernel = np.ones((3, 3), np.uint8) 14 | 15 | erosion = cv2.erode(img, kernel, iterations = 1) 16 | dilation = cv2.dilate(img, kernel, iterations = 1) 17 | gradient = cv2.morphologyEx(img, 18 | cv2.MORPH_GRADIENT, 19 | kernel) 20 | titles=['Original', 'Erosion', 21 | 'Dilation', 'Gradient'] 22 | output=[img, erosion, dilation, gradient] 23 | for i in range(4): 24 | plt.subplot(2, 2, i+1) 25 | plt.imshow(output[i], cmap='gray') 26 | plt.title(titles[i]) 27 | plt.axis('off') 28 | plt.show() 29 | -------------------------------------------------------------------------------- /Chapter10/programs/prog08.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | from matplotlib import pyplot as plt 4 | 5 | img = np.array([[0, 0, 0, 0, 0, 0, 0], 6 | [0, 0, 0, 0, 0, 0, 0], 7 | [0, 0, 255, 255, 255, 0, 0], 8 | [0, 0, 255, 255, 255, 0, 0], 9 | [0, 0, 255, 255, 255, 0, 0], 10 | [0, 0, 0, 0, 0, 0, 0], 11 | [0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8) 12 | 13 | kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) 14 | 15 | open = cv2.morphologyEx(img, 16 | cv2.MORPH_OPEN, 17 | kernel) 18 | close = cv2.morphologyEx(img, 19 | cv2.MORPH_CLOSE, 20 | kernel) 21 | tophat = cv2.morphologyEx(img, 22 | cv2.MORPH_TOPHAT, 23 | kernel) 24 | blackhat = cv2.morphologyEx(img, 25 | cv2.MORPH_BLACKHAT, 26 | kernel) 27 | hitmiss = cv2.morphologyEx(img, 28 | cv2.MORPH_HITMISS, 29 | kernel) 30 | 31 | titles=['Original', 'Open', 32 | 'Close', 'Top hat', 33 | 'Black hat', 'Hit Miss'] 34 | output=[img, open, close, 35 | tophat, blackhat, 36 | hitmiss] 37 | for i in range(6): 38 | plt.subplot(2, 3, i+1) 39 | plt.imshow(output[i], cmap='gray') 40 | plt.title(titles[i]) 41 | plt.axis('off') 42 | plt.show() 43 | -------------------------------------------------------------------------------- /Chapter11/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | def maxRGB(img): 5 | b = img[:, :, 0] 6 | g = img[:, :, 1] 7 | r = img[:, :, 2] 8 | 9 | M = np.maximum(np.maximum(b, g), r) 10 | 11 | b[b < M] = 0 12 | g[g < M] = 0 13 | r[r < M] = 0 14 | 15 | return(cv2.merge((b, g, r))) 16 | 17 | cap = cv2.VideoCapture(0) 18 | 19 | while True: 20 | ret, frame = cap.read() 21 | cv2.imshow('Max RGB Filter', maxRGB(frame)) 22 | if cv2.waitKey(1) == 27: 23 | break 24 | 25 | cv2.destroyAllWindows() 26 | cap.release() -------------------------------------------------------------------------------- /Chapter11/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | cap = cv2.VideoCapture(0) 4 | fgbg = cv2.createBackgroundSubtractorKNN() 5 | while(True): 6 | ret, frame = cap.read() 7 | fgmask = fgbg.apply(frame) 8 | cv2.imshow('frame', fgmask) 9 | if cv2.waitKey(30) == 27: 10 | break 11 | cap.release() 12 | cv2.destroyAllWindows() 13 | -------------------------------------------------------------------------------- /Chapter11/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | cap = cv2.VideoCapture(0) 4 | fgbg = cv2.createBackgroundSubtractorMOG2() 5 | while(True): 6 | ret, frame = cap.read() 7 | fgmask = fgbg.apply(frame) 8 | cv2.imshow('frame', fgmask) 9 | if cv2.waitKey(30) == 27: 10 | break 11 | cap.release() 12 | cv2.destroyAllWindows() 13 | -------------------------------------------------------------------------------- /Chapter11/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | cap = cv2.VideoCapture(0) 4 | ret, frame1 = cap.read() 5 | prvs = cv2.cvtColor(frame1, 6 | cv2.COLOR_BGR2GRAY) 7 | hsv = np.zeros_like(frame1) 8 | hsv[..., 1] = 255 9 | while(cap): 10 | ret, frame2 = cap.read() 11 | next = cv2.cvtColor(frame2, 12 | cv2.COLOR_BGR2GRAY) 13 | flow = cv2.calcOpticalFlowFarneback(prvs, 14 | next, 15 | None, 0.5, 16 | 3, 15, 17 | 3, 5, 18 | 1.2, 0) 19 | mag, ang =cv2.cartToPolar(flow[..., 0], 20 | flow[..., 1]) 21 | hsv[..., 0] = ang * 180/np.pi/2 22 | hsv[..., 2] = cv2.normalize(mag, None, 0, 23 | 255, cv2.NORM_MINMAX) 24 | rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) 25 | cv2.imshow('Optical Flow', rgb) 26 | if cv2.waitKey(1) == 27: 27 | break 28 | prvs = next 29 | cap.release() 30 | cv2.destroyAllWindows() 31 | -------------------------------------------------------------------------------- /Chapter11/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | cap = cv2.VideoCapture(0) 4 | k = np.ones((3, 3), np.uint8) 5 | t0 = cap.read()[1] 6 | t1 = cap.read()[1] 7 | while(True): 8 | d=cv2.absdiff(t1, t0) 9 | grey = cv2.cvtColor(d, cv2.COLOR_BGR2GRAY) 10 | blur = cv2.GaussianBlur(grey, (3, 3), 0) 11 | ret, th = cv2.threshold( blur, 15, 255, 12 | cv2.THRESH_BINARY) 13 | dilated = cv2.dilate(th, k, iterations=2) 14 | contours, hierarchy = cv2.findContours(dilated, 15 | cv2.RETR_TREE, 16 | cv2.CHAIN_APPROX_SIMPLE) 17 | t2=t0 18 | cv2.drawContours(t2, contours, -1, (0, 255, 0), 2) 19 | cv2.imshow('Output', t2) 20 | t0=t1 21 | t1=cap.read()[1] 22 | if cv2.waitKey(5) == 27 : 23 | break 24 | cv2.destroyAllWindows() 25 | cap.release() 26 | -------------------------------------------------------------------------------- /Chapter11/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | image=cv2.imread('/home/pi/book/dataset/barcode.jpeg', 1) 4 | input = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 5 | hor_der = cv2.Sobel(input, ddepth=-1, dx=1, 6 | dy=0, ksize = 5) 7 | ver_der = cv2.Sobel(input, ddepth=-1, dx=0, 8 | dy=1, ksize=5) 9 | diff = cv2.subtract(hor_der, ver_der) 10 | diff = cv2.convertScaleAbs(diff) 11 | 12 | blur = cv2.GaussianBlur(diff, (3, 3), 0) 13 | ret, th = cv2.threshold(blur, 225, 255, 14 | cv2.THRESH_BINARY) 15 | dilated = cv2.dilate(th, None, iterations = 10) 16 | eroded = cv2.erode(dilated, None, iterations = 15) 17 | (contours, hierarchy) = cv2.findContours(eroded, 18 | cv2.RETR_TREE, 19 | cv2.CHAIN_APPROX_SIMPLE) 20 | areas = [cv2.contourArea(temp) for temp in contours] 21 | max_index = np.argmax(areas) 22 | largest_contour=contours[max_index] 23 | 24 | x,y,width,height = cv2.boundingRect(largest_contour) 25 | cv2.rectangle(image, (x, y), 26 | (x+width, y+height), 27 | (255, 0, 0), 2) 28 | cv2.imshow('Detected Barcode', image) 29 | cv2.waitKey(0) 30 | cv2.destroyAllWindows() 31 | -------------------------------------------------------------------------------- /Chapter11/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | cap = cv2.VideoCapture(1) 4 | cap.set(3, 640) 5 | cap.set(4, 480) 6 | bg = cv2.imread('/home/pi/book/dataset/bg.jpg', 1) 7 | print(bg.shape) 8 | while ( True ): 9 | ret, frame = cap.read() 10 | print(frame.shape) 11 | hsv=cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 12 | image_mask=cv2.inRange(hsv,np.array([40, 50, 50]), 13 | np.array([80, 255, 255])) 14 | bg_mask=cv2.bitwise_and(bg, bg, mask=image_mask) 15 | fg_mask=cv2.bitwise_and(frame, frame, 16 | mask=cv2.bitwise_not(image_mask)) 17 | # cv2.imshow('Output', fg_mask) 18 | cv2.imshow('Output', cv2.add(bg_mask, fg_mask)) 19 | if cv2.waitKey(1) == 27: 20 | break 21 | cap.release() 22 | cv2.destroyAllWindows() 23 | -------------------------------------------------------------------------------- /Chapter12/programs/prog00.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import mahotas 3 | photo = mahotas.demos.load('luispedro') 4 | plt.imshow(photo) 5 | plt.axis('off') 6 | plt.show() 7 | -------------------------------------------------------------------------------- /Chapter12/programs/prog01.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import mahotas 3 | #photo = mahotas.demos.load('luispedro') 4 | #plt.imshow(photo) 5 | photo = mahotas.demos.load('luispedro', as_grey=True) 6 | plt.imshow(photo, cmap='gray') 7 | plt.axis('off') 8 | plt.show() 9 | -------------------------------------------------------------------------------- /Chapter12/programs/prog02.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import mahotas 3 | photo= mahotas.imread('/home/pi/book/dataset/4.1.01.tiff') 4 | plt.imshow(photo, cmap='gray') 5 | plt.axis('off') 6 | plt.show() 7 | -------------------------------------------------------------------------------- /Chapter12/programs/prog03.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import mahotas 4 | photo = mahotas.demos.load('luispedro', as_grey=True) 5 | photo = photo.astype(np.uint8) 6 | T_otsu = mahotas.otsu(photo) 7 | plt.imshow(photo > T_otsu, cmap='gray') 8 | plt.axis('off') 9 | plt.show() 10 | -------------------------------------------------------------------------------- /Chapter12/programs/prog04.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import mahotas 4 | photo = mahotas.demos.load('luispedro', as_grey=True) 5 | photo = photo.astype(np.uint8) 6 | T_rc = mahotas.rc(photo) 7 | plt.imshow(photo > T_rc, cmap='gray') 8 | plt.axis('off') 9 | plt.show() 10 | -------------------------------------------------------------------------------- /Chapter12/programs/prog05.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import mahotas 4 | 5 | f = np.ones((256, 256), bool) 6 | f[64:191, 64:191] = False 7 | plt.subplot(121) 8 | plt.imshow(f, cmap='gray') 9 | plt.title('Original Image') 10 | 11 | dmap = mahotas.distance(f) 12 | plt.subplot(122) 13 | plt.imshow(dmap, cmap='gray') 14 | plt.title('Distance Transform') 15 | plt.show() 16 | 17 | -------------------------------------------------------------------------------- /Chapter12/programs/prog06.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import mahotas 3 | 4 | photo = mahotas.demos.load('luispedro') 5 | photo = mahotas.colors.rgb2sepia(photo) 6 | plt.imshow(photo) 7 | plt.axis('off') 8 | plt.show() 9 | -------------------------------------------------------------------------------- /Chapter12/programs/prog07.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import mahotas as mh 4 | 5 | cap = cv2.VideoCapture(0) 6 | 7 | while True: 8 | ret, frame = cap.read() 9 | frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 10 | T_otsu = mh.otsu(frame) 11 | output = frame > T_otsu 12 | output = output.astype(np.uint8) * 255 13 | cv2.imshow('Sepia', output) 14 | if cv2.waitKey(1) == 27: 15 | break 16 | 17 | cv2.destroyAllWindows() 18 | cap.release() 19 | -------------------------------------------------------------------------------- /Chapter12/programs/test01.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%matplotlib inline\n", 10 | "import cv2\n", 11 | "import matplotlib.pyplot as plt" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "data": { 21 | "image/png": "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\n", 22 | "text/plain": [ 23 | "
" 24 | ] 25 | }, 26 | "metadata": {}, 27 | "output_type": "display_data" 28 | } 29 | ], 30 | "source": [ 31 | "img = cv2.imread('/home/pi/book/dataset/7.1.02.tiff', 0)\n", 32 | "plt.imshow(img, cmap='gray')\n", 33 | "plt.show()" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [] 42 | } 43 | ], 44 | "metadata": { 45 | "kernelspec": { 46 | "display_name": "Python 3", 47 | "language": "python", 48 | "name": "python3" 49 | }, 50 | "language_info": { 51 | "codemirror_mode": { 52 | "name": "ipython", 53 | "version": 3 54 | }, 55 | "file_extension": ".py", 56 | "mimetype": "text/x-python", 57 | "name": "python", 58 | "nbconvert_exporter": "python", 59 | "pygments_lexer": "ipython3", 60 | "version": "3.7.3" 61 | } 62 | }, 63 | "nbformat": 4, 64 | "nbformat_minor": 4 65 | } 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Raspberry Pi Computer Vision Programming - Second Edition 5 | 6 | Raspberry Pi Computer Vision Programming - Second Edition 7 | 8 | This is the code repository for [Raspberry Pi Computer Vision Programming - Second Edition](https://www.packtpub.com/in/data/raspberry-pi-computer-vision-programming-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781800207219), published by Packt. 9 | 10 | **Design and implement computer vision applications with Raspberry Pi, OpenCV, and Python 3** 11 | 12 | ## What is this book about? 13 | Raspberry Pi is one of the popular single-board computers of our generation. All the major image processing and computer vision algorithms and operations can be implemented easily with OpenCV on Raspberry Pi. This updated second edition is packed with cutting-edge examples and new topics, and covers the latest versions of key technologies such as Python 3, Raspberry Pi, and OpenCV. This book will equip you with the skills required to successfully design and implement your own OpenCV, Raspberry Pi, and Python-based computer vision projects. 14 | 15 | This book covers the following exciting features: 16 | * Set up a Raspberry Pi for computer vision applications 17 | * Perform basic image processing with libraries such as NumPy, Matplotlib, and OpenCV 18 | * Demonstrate arithmetic, logical, and other operations on images 19 | * Work with a USB webcam and the Raspberry Pi Camera Module 20 | * Implement low-pass filters and high-pass filters and understand their applications in image processing 21 | 22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1800207212) today! 23 | 24 | https://www.packtpub.com/ 25 | 26 | ## Instructions and Navigations 27 | All of the code is organized into folders. 28 | 29 | The code will look like the following: 30 | ``` 31 | p2 = Person() 32 | p2.name = 'Jan 33 | p2.age = 20 34 | print(p2.name) 35 | int(p2.age) 36 | ``` 37 | 38 | **Following is what you need for this book:** 39 | This book is for Python 3 developers, computer vision professionals, and Raspberry Pi 40 | enthusiasts who are looking to implement computer vision applications on a low-cost 41 | platform. Basic knowledge of programming, mathematics, and electronics will be 42 | beneficial. However, even beginners in this area will be comfortable with covering 43 | the contents of the book as they are presented in a step-by-step manner 44 | 45 | With the following software and hardware list you can run all code files present in the book (Chapter 1-13). 46 | 47 | ### Software and Hardware List 48 | 49 | | Software required | OS required | 50 | | ------------------------------------| -----------------------------------| 51 | | Raspberry Pi with Raspbian OS (Raspberry Pi camera module and a USB webcam) | Windows, Mac OS X | 52 | | Python 3 interpreter | Windows, Mac OS X | 53 | 54 | 55 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781800207219_ColorImages.pdf). 56 | 57 | 58 | ## Code in Action 59 | 60 | Click on the following link to see the Code in Action: 61 | 62 | https://www.youtube.com/playlist?list=PLeLcvrwLe187nS94zlJC9u9QHBz_Hr3pO 63 | 64 | ### Related products 65 | * Mastering Computer Vision with TensorFlow 2.x [[Packt]](https://www.packtpub.com/in/data/advanced-computer-vision-with-tensorflow-2-x?utm_source=github&utm_medium=repository&utm_campaign=9781838827069) [[Amazon]](https://www.amazon.com/dp/1838827064) 66 | 67 | * PyTorch Computer Vision Cookbook [[Packt]](https://www.packtpub.com/in/data/pytorch-computer-vision-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781838644833) [[Amazon]](https://www.amazon.com/dp/1838644830) 68 | 69 | ## Get to Know the Author 70 | **Ashwin Pajankar** 71 | is a polymath. He is a science popularizer, a programmer, a maker, an 72 | author, and a YouTuber. He graduated from IIIT Hyderabad with an MTech in computer 73 | science and engineering. He has a keen interest in the promotion of science, technology, 74 | engineering, and mathematics (STEM) education. 75 | 76 | 77 | ## Other book by the author 78 | * [Raspberry Pi Computer Vision Programming](https://www.packtpub.com/in/hardware-and-creative/raspberry-pi-computer-vision-programming?utm_source=github&utm_medium=repository&utm_campaign=9781784398286) 79 | 80 | 81 | ### Suggestions and Feedback 82 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 83 | --------------------------------------------------------------------------------