Skip to content

Commit

Permalink
RunProgram Major Edits
Browse files Browse the repository at this point in the history
  • Loading branch information
CodingEZ committed Mar 16, 2018
1 parent 6f6f3f3 commit c48b8f5
Show file tree
Hide file tree
Showing 14 changed files with 916 additions and 352 deletions.
77 changes: 77 additions & 0 deletions ArduinoControl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import time

class ArduinoControl():

def __init__(self):
'''Calibration and set-up'''

self.unit_x = 180 # 1 x-stepper motor turn = unit_x pixels
self.unit_y = 180 # same for y
self.screen_x = ? # stepper motor turn per 1 screen width
self.screen_y = ? # vice versa

self.arduino = serial.Serial('COM3', 9600, timeout=.1) # set up the serial port
time.sleep(3) # allow Arduino to reset

def wait(self):
try:
byteNum = int((self.arduino.read()).decode())
except:
byteNum = -1

while byteNum != 0:
try:
byteNum = int((self.arduino.read()).decode())
except:
byteNum = -1

def arduino_move(self, right, down):
self.arduino.write(b'0') # haven't established yet, we need a list of commands and give each a number
try:
byteNum = int((self.arduino.read()).decode())
except:
byteNum = -1

while byteNum != 1:
try:
byteNum = int((self.arduino.read()).decode())
except:
byteNum = -1

right = str(right)
down = str(down)
for i in range(3-len(right)):
right = '0' + right
for j in range(3-len(down)):
down = '0' + down

for char in right:
self.arduino.write(char.encode('utf-8')) # need the bytes of the char
for char2 in down:
self.arduino.write(char2.encode('utf-8')) # need the bytes of the char

self.wait()

def arduino_water(self, timeLength=10):
'''Note: time length is in seconds.'''
waterStart = time.time()
while time.time() < waterStart + timeLength:
self.arduino.write(b'2')
self.wait()

def water_cycle(self):
'''Cycle through plant locations and water plants.'''
for location in self.plantLocations:
self.arduino_move(location)
self.arduino_water()
self.lastWater = time.time()

'''
while True:
if (control.lastWater == None) or (time.time() - control.lastWater > 300):
control.water_cycle()
control.find_weeds()
control.kill_weeds()
if time.time() - control.lastWater < 300:
'''
89 changes: 89 additions & 0 deletions ImageDetection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import numpy as np
import cv2
import ImageEdit6 as ImageEdit # version 4 contains polygon detection
import ImageGrab2 as ImageGrab
import GripEdit3 as GripEdit # version 3 only
import ImageHelp

class Detector():

def __init__(self):
'''Calibration and set-up'''
self.plantLocations = [(), (), (), ()]
self.numPlants = len(self.plantLocations)
self.lastWater = None
self.img = self.image_grab()
self.imgEdit = GripEdit.filter(self.img)
self.editor = ImageEdit.Editor(self.imgEdit)
self.thresholdBrightness = .4 # .6 is the default value
self.weeds = []
self.weedFactor = 1/100 # size of weeds in comparison to plant

# Initialized later
self.regions = None
self.plant = None
self.startSize = None
self.finalImg = None
self.largeRegions = None

def image_grab(self):
img = ImageGrab.grab(0) # built-in camera number = 0
# attached camera number = 1
#img = cv2.imread('Capture\\1521224317.jpg', 1)
return img

def find_plant(self):
'''Finds the largest object and designates as the plant.
Currently the most inefficient code.'''
maxPixel = self.editor.find_max()
while True:
locations = self.editor.max_locations(maxPixel, self.thresholdBrightness)
self.regions = self.editor.get_all_regions(locations)

# Find the largest region. In the case of a tie, lower the brightness threshold.
(largestRegion, self.startSize) = self.editor.obtain_largest_region(self.regions)
if len(largestRegion) == 1:
break
else:
self.thresholdBrightness -= .01
self.plant = largestRegion[0] # set the largest region as the plant

def find_weeds(self):
'''Determine if an object is a plant or a weed. Currently, the plant is removed, and everything
else is designated as a weed.'''
plantFound = False
plantIndex = None
self.largeRegions = self.editor.keep_large_regions(self.regions, self.startSize * self.weedFactor)
for index in range(len(self.largeRegions)):
region = self.largeRegions[index]
newLocation = self.editor.find_centroid(region)
if not plantFound and ImageHelp.equalArray(region, self.plant):
print("Location of plant:", newLocation)
plantFound = True
plantIndex = index
else:
self.weeds.append(newLocation)
self.largeRegions.pop(plantIndex)
print("Locations of weeds:", self.weeds)

def draw_plant(self):
'''Outlines the contour of the plant.'''
if ImageHelp.equalArray(self.finalImg, None):
self.finalImg = self.editor.outline(self.img, [self.plant],
needToCopy=True)
else:
self.editor.outline(self.finalImg, [self.plant])

def draw_weeds(self):
'''Outlines the contour of each weed.'''
if ImageHelp.equalArray(self.finalImg, None):
self.finalImg = self.editor.outline(self.img, self.largeRegions,
needToCopy=True)
else:
self.editor.outline(self.finalImg, self.largeRegions)

def display_drawings(self):
'''Displays a list of images.'''
imgNames = ['self.img', 'self.imgEdit', 'self.finalImg']
imgs = [self.img, self.imgEdit, self.finalImg]
ImageHelp.display(imgNames, imgs)
164 changes: 164 additions & 0 deletions ImageEdit6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import cv2
import numpy as np
import math
import copy

class Editor():

def __init__(self, img):
self.img = img
(self.height, self.width) = img.shape
self.black = 0
self.white = 1
self.blackImg = np.array( [[self.black] * self.width for _ in range(self.height)], dtype='uint8' )
print('Height: ', self.height, ', Width: ', self.width)

def find_max(self):
'''Finds the pixel of maximum brightness. This value is usually 255.'''
maxPixel = 0
for y in range(self.height):
for x in range(self.width):
if self.img[y, x] > maxPixel:
maxPixel = self.img[y, x]
return maxPixel

def max_locations(self, maxPixel, thresholdBrightness=.6):
'''Finds all locations above a threshold brightness compared to the maximum
brightness. The default is .6 (out of 1.0). No need to calculate the exact
value of brightness.'''
lowestIntensity = thresholdBrightness * maxPixel
ret, thresh = cv2.threshold(self.img, lowestIntensity, 255, cv2.THRESH_BINARY)
locations = copy.deepcopy(self.blackImg)
for y in range(self.height):
for x in range(self.width):
if thresh[y, x] == 255:
locations[y, x] = self.white
return locations

def find_next_location(self, locations):
'''Goes through each ROW from LEFT TO RIGHT until it encounters the first
true location. Skips every other column and row.'''
for y in range(0, self.height, 2):
for x in range(0, self.width, 2):
if locations[y, x]:
return (y, x)
return None

def find_centroid(self, region):
contour = cv2.findContours(region, 1, 2)[1][0]
M = cv2.moments(contour)
if M != None:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
return (cY, cX)
else:
return None

def get_connected_region(self, location, locations):
'''Obtains a single. The format of the region is a numpy array that has the
height and width of the original image. Be careful as this is a call by
reference that directly edits the array of locations.'''
checkLocations = set([location])
newCheckLocations = set()
checkedLocations = set()
region = copy.deepcopy(self.blackImg)
locations[location[0], location[1]] = self.black # first spot is already checked
while len(checkLocations) > 0:
for check in checkLocations:
if check not in checkedLocations:
checkedLocations.add(check)
if check[0] != 0 and locations[check[0]-1, check[1]] == 1:
newCheckLocations.add((check[0]-1, check[1]))
locations[check[0]-1, check[1]] = self.black
if check[0]+1 != self.height and locations[check[0]+1, check[1]] == 1:
newCheckLocations.add((check[0]+1, check[1]))
locations[check[0]+1, check[1]] = self.black
if check[1] != 0 and locations[check[0], check[1]-1] == 1:
newCheckLocations.add((check[0], check[1]-1))
locations[check[0], check[1]-1] = self.black
if check[1]+1 != self.width and locations[check[0], check[1]+1] == 1:
newCheckLocations.add((check[0], check[1]+1))
locations[check[0], check[1]+1] = self.black
for check2 in checkLocations:
region[check2[0], check2[1]] = self.white
checkLocations = newCheckLocations
newCheckLocations = set()
return region

def get_all_regions(self, locations):
'''Obtains each region. Returns a list of regions.'''
regions = []
start = self.find_next_location(locations)
while start != None:
region = self.get_connected_region(start, locations)
regions.append(region)
start = self.find_next_location(locations)
return regions

def keep_large_regions(self, regions, thresholdSize=100):
'''Take the regions and filters those that are below threshold size. Default
threshold size is 100 pixels. This function uses copies.'''
largeRegions = copy.deepcopy(regions)
for i in range(len(regions)-1, -1, -1):
contour = cv2.findContours(regions[i], 1, 2)[1][0]
if cv2.contourArea(contour) < thresholdSize:
largeRegions.pop(i)
return largeRegions

def obtain_largest_region(self, regions):
'''Take the largest region with greatest circularity (best leaf shape).
May result in a tie.'''
largestRegion = []
startSize = self.img.size
while len(largestRegion) == 0 and startSize > 0:
startSize //= 2
largestRegion = self.keep_large_regions(regions, startSize)
while len(largestRegion) != 0:
startSize += 100
largestRegion = self.keep_large_regions(regions, startSize)
while len(largestRegion) == 0:
startSize -= 1
largestRegion = self.keep_large_regions(regions, startSize)
return (largestRegion, startSize)

def mk_points_list(self, regions):
'''This creates an unedited list of points on a polygon.'''
pointsList = [None] * len(regions)
counter = 0
for region in regions:
pointsList[counter] = self.find_centroid(region)
counter += 1
return pointsList

def combine_regions(self, regions):
newImage = copy.deepcopy(self.blackImg)
for y in range(self.height):
for x in range(self.width):
boolArray = [None] * len(regions)
for index in range(len(regions)):
boolArray[index] = regions[index][y, x]
if self.white in boolArray:
newImage[y, x] = self.white
return newImage

def blacken_regions(self, regions):
'''This is just a check that the regions are correct. Be careful as this
is a call by reference that directly edits the image.'''
for region in regions:
for y in range(self.height):
for x in range(self.width):
if region[y, x]:
self.img[y, x] = self.black

def outline(self, img, largeRegions, needToCopy=False):
if needToCopy:
img = copy.deepcopy(img)

radius = 10
for index in range(len(largeRegions)):
contour = cv2.findContours(largeRegions[index], 1, 2)[1][0]
cv2.drawContours(img, [contour], -1, (255, 0, 255), 2)

if needToCopy:
return img

23 changes: 0 additions & 23 deletions ImageGrab2.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
import cv2
import numpy as np
import time

def equalArray(array1, array2):
'''Use if guaranteed non-empty deep arrays.'''
if len(array1) != len(array2):
return False
elif isinstance(array1[0], int) or isinstance(array1[0], np.uint8):
for index in range(len(array1)):
if array1[index] != array2[index]:
return False
return True
else:
for index in range(len(array1)):
if not equalArray(array1[index], array2[index]):
return False
return True

def grab(cameraNumber):
cap = cv2.VideoCapture(cameraNumber)

# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
# name, fourcc code, frame rate, frame size, isColor tag
output = cv2.VideoWriter('output.avi', fourcc, 10.0, (640,480), isColor=False)

frame = cap.read()[1]
cv2.imwrite('Capture\\' + str(int(time.time())) + '.jpg', frame)

# When everything done, release the capture
cap.release()
output.release()
cv2.destroyAllWindows()
return frame
Loading

0 comments on commit c48b8f5

Please sign in to comment.