Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create PythonCV #1

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projects/Kalman Filter/KalmanDelrin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bin/
18 changes: 18 additions & 0 deletions projects/Kalman Filter/KalmanDelrin/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>KalmanDelrin</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>edu.wpi.first.wpilib.plugins.core.nature.FRCProjectNature</nature>
</natures>
</projectDescription>
1 change: 1 addition & 0 deletions projects/Kalman Filter/KalmanDelrinConfig/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bin/
17 changes: 17 additions & 0 deletions projects/Kalman Filter/KalmanDelrinConfig/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>KalmanDelrinConfig</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
189 changes: 189 additions & 0 deletions projects/PythonCV/IPCamera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
__author__ = 'Leon Agmon Nacht'

import cv2
import numpy as np
import urllib


class IPCamera():
'''
IPCamera- This class handles all the needed variables and methods for the camera (python side) of the project.
'''

def __init__(self, path, flag_file_path, results_file_path, minimum_bounding_size, min_color, max_color):
'''
This method initialising the variables for the IPCamera instance.
:param path: The FTP path for the current jpeg image (the jpeg image should be updated using the roborio code).
:param minimum_bounding_size: The minimum contour area to be considered as an object.
:param max_color: The max color to be passed in the mask. (in the format: np.array([R, G, B])
:param min_color: The min color to be passed in the mask. (in the format: np.array([R, G, B])
:param results_file_path: The path for the results to be used by the java process.
:param flag_file_path: The path for the flags:
- First line: Should read next image(1), if first line is 0 the java process should write image.
After finish reading image should change flag to 0.
:return: None
'''
self.path = path
self.current_image = None
self.current_image_object_width = None
self.known_distance = None
self.known_width = None
self.focal_length = None
self.distance_image = None
self.minimum_bounding_rect_size = minimum_bounding_size
self.max_color = max_color
self.min_color = min_color
self.image_width = None
self.image_height = None
self.flag_file_path = flag_file_path
self.results_file_path = results_file_path
self.masked_image = None
# The scales of the object in self.current_image, should be updated using self.update_object_scales.
self.object_width = None
self.object_height = None
self.object_x = None
self.object_y = None

# MARK: Running Once Methods.

def set_distance_vars(self, distance, width, image):
'''
This method sets the needed variables for calculating the distance of an object from the camera.

:param distance: The distance of the object from the camera in image.
:param width: The real world width of the object.
:param image: The pre-taken image of the object, in image the object is in distance distance(var) from the camera.
:return: None
'''
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 35, 125)

# find the contours in the edged image and keep the largest one
# we'll assume that this is our wanted object in the image
(cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
marker = max(cnts, key = cv2.contourArea)

self.distance_image = image
self.known_distance = distance
self.known_width = width

self.focal_length = (marker[1][0] * distance) / width


def set_image_scales(self):
'''
This method sets the scales of the image(self.current_image).
This method is auto called, and should be called only if the image size changes.
:return:
'''
height, width = self.current_image.shape[:2]
self.image_height = height
self.image_width
# MARK: Update Methods.

def update_image(self):

'''
This method is called when needed to update the self.current_image.
self.current_image is being updated to the image in self.path.
:return: None.
'''

if self.current_image is not None and self.image_width is None and self.image_height is None:
self.set_image_scales()

with open(self.flag_file_path, "r") as f:
flag = f.readline().strip()
#if flag == "1": # Should read image.
if True:
req = urllib.urlopen(self.path)
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
img = cv2.imdecode(arr, -1)
self.current_image = img # The current image in the instance path. (already in cv2 format).
self.update_flag_file()

def update_object_scales(self):
'''
This method updates the vars holding the scales of the object.
:return: None
'''
(x, y, w, h) = self.calculate_bounding_rect()
self.object_height = h
self.object_width = w
self.object__x = x
self.object_y = y

def update_results_file(self, results):
'''
This method updates the result in self.results_file.
:param results: The result to be written.
:return: None.
'''
result_file = open(self.results_file_path, "w")
result_file.write(str(results))
result_file.close()

def update_flag_file(self):
'''
This method sets the flag file to 0.
:return: None.
'''
with open(self.flag_file_path, "w") as f:
f.write("1")

# MARK: Calculating Methods.

def calculate_bounding_rect(self):
'''
This method calculates the bounding rect of the biggest right color object in self.current_image.
:return: None if no object else the bounding rect in the format of (x, y, w, h).
'''
hsv = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2HSV)

# Threshold the HSV image to get only blue colors.
mask = cv2.inRange(hsv, self.min_color, self.max_color)

# Bitwise-AND mask and original image.
res = cv2.bitwise_and(self.current_image, self.current_image, mask= mask)
self.masked_image = res
thresh = cv2.threshold(mask, 25, 255, cv2.THRESH_BINARY)[1]

# dilate the thresholded image to fill in holes, then find contours on thresholded image.
thresh = cv2.dilate(thresh, None, iterations=2)
(cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if cnts != []:
c = max(cnts, key = cv2.contourArea)
if cv2.contourArea(c) > self.minimum_bounding_rect_size:
return cv2.boundingRect(c)
return None

def calculate_distance(self):
'''
This method calculates the distance of the object from the camera.
Using- distance from camera = (self.known_width * self.focal_length) / self.image_object_width.
Should call self.update_object_scales before.
:return: The distance of the object from the camera.
'''
return float(self.known_width * self.focal_length) / float(self.object_width)

# MARK: Getters.

def get_object_center(self):
'''
This method returns the center of the object on the screen (x,y).
:return: The center of the object, in the format of (x,y)
'''
x = self.object_x + self.object_width/2
y = self.object_y + self.object_height/2
return (x,y)

def get_distance_from_frame_center(self):
'''
This method calculates the distance (from center) of the object from the center of the frame.
:return: The distance of the object from the center of the frame.
'''
frame_center = self.image_width/2
return frame_center - (self.object_x + self.object_width/2)
# If the object is left from the center of the frame,
# the result will be positive.
37 changes: 37 additions & 0 deletions projects/PythonCV/PythonCV.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
__author__ = 'Leon Agmon Nacht'

import cv2
import numpy as np
import IPCamera

path = 'ftp://roborio-3316.local/home/lvuser/Pics/image_from_camarea.jpeg'
#path = 'http://192.168.8.161/snap.jpg?'
flag_path = 'temp.txt'
result_file = 'result.txt'
min_color = np.array([110,50,50]) # Blue.
max_color = np.array([130,255,255]) # Blue.
minimum_bounding_size = 500
cam = IPCamera.IPCamera(path, flag_path, result_file, minimum_bounding_size , min_color, max_color)

with open(cam.flag_file_path, "w") as f:
f.write("1")

#distance, width, image = 10, 10, cv2.imread(path) # Read docs to implement, won't work without it!
#cam.set_distance_vars(distance, width, image)
cam.known_distance = 100
cam.known_width = 11
cam.focal_length = 100**2 / 11

while True:
cam.update_image()
cam.update_object_scales()
distance_from_camera = cam.calculate_distance()
if cam.masked_image is not None:
cv2.imshow('MASKED IMG', cam.masked_image)
cv2.imshow('ORIGINAL IMG', cam.current_image)
print(distance_from_camera)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break

cv2.destroyAllWindows()
7 changes: 7 additions & 0 deletions projects/PythonCV/TrackYellowTote/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="var" path="wpilib" sourcepath="wpilib.sources"/>
<classpathentry kind="var" path="networktables" sourcepath="networktables.sources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="output" path="bin"/>
</classpath>
18 changes: 18 additions & 0 deletions projects/PythonCV/TrackYellowTote/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>TrackYellowTote</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>edu.wpi.first.wpilib.plugins.core.nature.FRCProjectNature</nature>
</natures>
</projectDescription>
4 changes: 4 additions & 0 deletions projects/PythonCV/TrackYellowTote/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Project specific information
package=org.usfirst.frc.team3316.robot
robot.class=${package}.Robot
simulation.world.file=/usr/share/frcsim/worlds/GearsBotDemo.world
30 changes: 30 additions & 0 deletions projects/PythonCV/TrackYellowTote/build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>

<project name="FRC Deployment" default="deploy">

<!--
The following properties can be defined to override system level
settings. These should not be touched unless you know what you're
doing. The primary use is to override the wpilib version when
working with older robots that can't compile with the latest
libraries.
-->

<!-- By default the system version of WPI is used -->
<!-- <property name="version" value=""/> -->

<!-- By default the system team number is used -->
<!-- <property name="team-number" value=""/> -->

<!-- By default the target is set to 10.TE.AM.2 -->
<!-- <property name="target" value=""/> -->

<!-- Any other property in build.properties can also be overridden. -->

<property file="${user.home}/wpilib/wpilib.properties"/>
<property file="build.properties"/>
<property file="${user.home}/wpilib/java/${version}/ant/build.properties"/>

<import file="${wpilib.ant.dir}/build.xml"/>

</project>
Loading