Monday, December 7, 2015

OpenCV: Multi-Scale Template Matching, Raspberry Pi 2, Camera Module, Real-time detection

#7 DEC 2015
#This program uses multi-scale template matching to find an object in a video stream.
#The object is the "template" which is an image file (JPG, PNG, etc.)
#The video stream is from the raspberry pi camera module.
#This program works on the Raspberry PI 2, Jessie, OpenCV 3.0.0

import cv2
import picamera
import picamera.array
import numpy as np                              #for template matching
import imutils                                  #for template matching, "image processing convenience functions"  THIS IS A FUNCITON CALL!

with picamera.PiCamera() as camera:
    with picamera.array.PiRGBArray(camera) as stream:
        camera.resolution = (1944, 1944)                                                #I made it a square, you can make it whatever you want

        template = cv2.imread('glasses_mod.png')                                        #I used a photo of sunglasses, cropped down to just the sunglasses and nothing else
        (template_height, template_width) = template.shape[:2]
        template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)                           #gray it
        template = cv2.GaussianBlur     (template, (7,7), 0)                            #blur it
        template = cv2.Canny(template, 50, 150)                                         #edge it
        cv2.imshow("template", template)                                                #show it (not necessary, but I like to see what I'm working with

        while True:
            camera.capture(stream, 'bgr', use_video_port=True)
            image_color         = stream.array                                          #take an image from the video stream
            roi_image           = image_color[255:765, 510:1020]                        #select a region of interest
            image_gray          = cv2.cvtColor(roi_image, cv2.COLOR_BGR2GRAY)           #change the image to grayscale
            (h, w)              = image_gray.shape[:2]
            center              = (w/2,h/2)
            M                   = cv2.getRotationMatrix2D(center, 90, 1.0)
            image_gray_rotated  = cv2.warpAffine(image_gray, M, (w, h))                 #my camera is sideways so I have to rotate it (not required if your camera is upright)
            im_gblurred         = cv2.GaussianBlur(image_gray_rotated, (7,7), 0)        #blur the image

### MULTI-SCALE TEMPLATE MATCHING
            ms_image = im_gblurred                              #this line is not required, but I was playing around with different multiscale image transformations
            found = None                                        #flag to keep track of the matched region

            #scan each scale of the image
            #ending value (20%), starting value (100%), number of slices in between (20)
            for scale in np.linspace(0.2, 1.0, 20)[::-1]:
                #resize the image according to the scale and keep track of the ratio of the resizing
                resized = imutils.resize(ms_image, width = int(ms_image.shape[1] * scale))
                r       = ms_image.shape[1] / float(resized.shape[1])

                #if the resized image is smaller than the template then break from the loop
                if resized.shape[0] < template_height or resized.shape[1] < template_width:
                    break
                #detect the edges in the resized grayscale image and apply template matching to find the template in the image
                edged = cv2.Canny(resized, 50, 150)                     #must use the SAME parameters here as you did for the tempate ABOVE for best results

                #input image:   must be 8bit or 32bit-floating point
                #tempate image: must not be larger than the image to search, and same data type
                #method:        parameter specifying the comparison methods (SQDIFF, SQDIFFNORM, CCORR, CCORRNORM, CCOEFF, CCOEFFNORM)
                #mask:          mask of searched template.  must be same data type and size as template. It not set by default.
                #result:        map of comparison results, must be single channel 32bit float
                result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)              #this is where the magic happens!
                #The cv2.minMaxLoc function takes the correlation result and returns a 4-tuple
                #that includes the minimum correlation value, the maximum correlation value,
                #the (x, y)-coordinate of the minimum value, and the (x, y)-coordinate of the
                #maximum value, respectively. We are only interested in the maximum value and
                #(x, y)-coordinate so we keep the maximums and discard the minimums.
                (_, maxVal, _, maxLoc)  = cv2.minMaxLoc(result)

                #if we found a new maximum correlation value, then update the bookkeeping variable
                if found is None or maxVal > found[0]:
                    found = (maxVal, maxLoc, r)
                #unpack the bookkeeping variable and compute the (x, y) coordinates of the bounding box based on the resized ratio
            (_, maxLoc, r) = found
            (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
            (endX, endY) = (int((maxLoc[0] + template_width) * r), int((maxLoc[1] + template_height) * r))

                #draw a box around the detected result and display the image
            cv2.rectangle(ms_image, (startX, startY), (endX, endY), (0, 0, 255), 2)
            cv2.imshow("Image", ms_image)

            stream.truncate(0)                          #Must use this to eliminate the error: "Incorrect buffer length"
            if cv2.waitKey(1) & 0xFF == ord('q'):       #press CTRL and Q to stop the program from running
                break

# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()


### This code below should be in a separate file called imutils.py
# Import the necessary packages
import numpy as np
import cv2
def translate(image, x, y):
        # Define the translation matrix and perform the translation
        M = np.float32([[1, 0, x], [0, 1, y]])
        shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
        # Return the translated image
        return shifted
def rotate(image, angle, center = None, scale = 1.0):
        # Grab the dimensions of the image
        (h, w) = image.shape[:2]
        # If the center is None, initialize it as the center of the image
        if center is None:
                center = (w / 2, h / 2)
        # Perform the rotation
        M = cv2.getRotationMatrix2D(center, angle, scale)
        rotated = cv2.warpAffine(image, M, (w, h))
        # Return the rotated image
        return rotated
def resize(image, width = None, height = None, inter = cv2.INTER_AREA):
        # initialize the dimensions of the image to be resized and grab the image size
        dim = None
        (h, w) = image.shape[:2]
        # if both the width and height are None, then return the original image
        if width is None and height is None:
                return image
        # check to see if the width is None
        if width is None:
                # calculate the ratio of the height and construct the dimensions
                r = height / float(h)
                dim = (int(w * r), height)
        # otherwise, the height is None
        else:
                # calculate the ratio of the width and construct the dimensions
                r = width / float(w)
                dim = (width, int(h * r))
        # resize the image
        resized = cv2.resize(image, dim, interpolation = inter)
        # return the resized image
        return resized

Sunday, November 22, 2015

OpenCV: Using cv2.minMaxLoc to find the darkest and brightest region in an image

#Using cv2.minMaxLoc to find the darkest and brightest region in an image
#OpenCV 3.0.0
#Raspberry Pi 2, Jessie

#Must have an image in the same directory as this program.
#Enter the following on the command line:
#$python brightspot.py --image name.jpg --radius 21    #radius must be odd number

### Import the necessary packages
import numpy as np
import argparse
import cv2

### Construct the argument parse and parse the arguments
ap      = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help = "path to the image file")
ap.add_argument("-r", "--radius", type = int,
        help = "radius of Gaussian blur; must be odd")
args    = vars(ap.parse_args())

### Load the image
image   = cv2.imread(args["image"])                             #load the image
### Resize the image (may not be necessary if your images is already a reasonable size).
r       = 500.0 / image.shape[1]                                #calculate r, ratio of new width to old width
dim     = (500, int(image.shape[0] * r))                        #dimension = x pixels by r times the height to keep aspect ratio
image   = cv2.resize(image, dim, interpolation = cv2.INTER_AREA)

### Convert to grayscale
gray    = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)               #convert to gray
### Apply a Gaussian blur to the image
gray    = cv2.GaussianBlur(gray, (args["radius"], args["radius"]), 0)
### Find the darkest and brightest region
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
### Draw circles around the darkesst and brightest region
cv2.circle(image, maxLoc, args["radius"], (0, 0, 255), 2)       #draw a circle around the bright area
cv2.circle(image, minLoc, args["radius"], (0, 255, 0), 2)       #draw a circle around the dark area

### Display the results to the screen
cv2.imshow("Gray", gray)
cv2.imshow("Robust", image)
cv2.waitKey(0)

Monday, November 16, 2015

OpenCV: ORB - Oriented FAST and Rotated BRIEF - Python Example

#ORB detection
#OpenCV 3.0.0
#Raspberry Pi 2, Jessie
#An efficient alternative to SIFT or SURF

import numpy as np
import cv2
from matplotlib import pyplot as plt

img1 = cv2.imread('img1.jpg',0)        # this is the object you want to find
img2 = cv2.imread('img2.jpg',0)        # this is the "bigger picture"

# Initiate ORB detector
orb = cv2.ORB_create()

# Find the keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# Match descriptors.
matches = bf.match(des1,des2)

# Sort them in the order of their distance.
matches = sorted(matches, key = lambda x:x.distance)

# Draw first 10 matches.
img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10],None, flags=2)

plt.imshow(img3),plt.show()