diff --git a/projects/Kalman Filter/KalmanDelrin/.gitignore b/projects/Kalman Filter/KalmanDelrin/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/projects/Kalman Filter/KalmanDelrin/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/projects/Kalman Filter/KalmanDelrin/.project b/projects/Kalman Filter/KalmanDelrin/.project new file mode 100644 index 0000000..433cbd6 --- /dev/null +++ b/projects/Kalman Filter/KalmanDelrin/.project @@ -0,0 +1,18 @@ + + + KalmanDelrin + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + edu.wpi.first.wpilib.plugins.core.nature.FRCProjectNature + + diff --git a/projects/Kalman Filter/KalmanDelrinConfig/.gitignore b/projects/Kalman Filter/KalmanDelrinConfig/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/projects/Kalman Filter/KalmanDelrinConfig/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/projects/Kalman Filter/KalmanDelrinConfig/.project b/projects/Kalman Filter/KalmanDelrinConfig/.project new file mode 100644 index 0000000..9a62506 --- /dev/null +++ b/projects/Kalman Filter/KalmanDelrinConfig/.project @@ -0,0 +1,17 @@ + + + KalmanDelrinConfig + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/projects/PythonCV/IPCamera.py b/projects/PythonCV/IPCamera.py new file mode 100644 index 0000000..90fbcf6 --- /dev/null +++ b/projects/PythonCV/IPCamera.py @@ -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. diff --git a/projects/PythonCV/PythonCV.py b/projects/PythonCV/PythonCV.py new file mode 100644 index 0000000..7a91bf7 --- /dev/null +++ b/projects/PythonCV/PythonCV.py @@ -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() diff --git a/projects/PythonCV/TrackYellowTote/.classpath b/projects/PythonCV/TrackYellowTote/.classpath new file mode 100644 index 0000000..daf6ebb --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/projects/PythonCV/TrackYellowTote/.project b/projects/PythonCV/TrackYellowTote/.project new file mode 100644 index 0000000..f4b23e3 --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/.project @@ -0,0 +1,18 @@ + + + TrackYellowTote + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + edu.wpi.first.wpilib.plugins.core.nature.FRCProjectNature + + diff --git a/projects/PythonCV/TrackYellowTote/build.properties b/projects/PythonCV/TrackYellowTote/build.properties new file mode 100644 index 0000000..61774c2 --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/build.properties @@ -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 \ No newline at end of file diff --git a/projects/PythonCV/TrackYellowTote/build.xml b/projects/PythonCV/TrackYellowTote/build.xml new file mode 100644 index 0000000..2182d37 --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/build.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/Robot.java b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/Robot.java new file mode 100644 index 0000000..c3452b0 --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/Robot.java @@ -0,0 +1,72 @@ + +package org.usfirst.frc.team3316.robot; + +import edu.wpi.first.wpilibj.IterativeRobot; +import edu.wpi.first.wpilibj.command.Command; +import edu.wpi.first.wpilibj.command.Scheduler; +import edu.wpi.first.wpilibj.livewindow.LiveWindow; +import org.usfirst.frc.team3316.robot.subsystems.Vision; + +/** + * The VM is configured to automatically run this class, and to call the + * functions corresponding to each mode, as described in the IterativeRobot + * documentation. If you change the name of this class or the package after + * creating this project, you must also update the manifest file in the resource + * directory. + */ +public class Robot extends IterativeRobot { + + public static final Vision v = new Vision(); + + /** + * This function is run when the robot is first started up and should be + * used for any initialization code. + */ + public void robotInit() { + // instantiate the command used for the autonomous period + } + + public void disabledPeriodic() { + Scheduler.getInstance().run(); + } + + public void autonomousInit() { + // schedule the autonomous command (example) + } + + /** + * This function is called periodically during autonomous + */ + public void autonomousPeriodic() { + Scheduler.getInstance().run(); + } + + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + } + + /** + * This function is called when the disabled button is hit. + * You can use it to reset subsystems before shutting down. + */ + public void disabledInit(){ + + } + + /** + * This function is called periodically during operator control + */ + public void teleopPeriodic() { + Scheduler.getInstance().run(); + } + + /** + * This function is called periodically during test mode + */ + public void testPeriodic() { + LiveWindow.run(); + } +} diff --git a/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/commands/SaveImage.java b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/commands/SaveImage.java new file mode 100644 index 0000000..65be80c --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/commands/SaveImage.java @@ -0,0 +1,66 @@ +package org.usfirst.frc.team3316.robot.commands; + +import org.usfirst.frc.team3316.robot.Robot; + +import com.ni.vision.NIVision; +import com.ni.vision.NIVision.Image; +import com.ni.vision.NIVision.RGBValue; + +import edu.wpi.first.wpilibj.command.Command; + +/** + * + */ + +public class SaveImage extends Command { + + Image frame; + int session; + boolean failedToOpenCamera; + + public SaveImage() { + setRunWhenDisabled(true); + // Use requires() here to declare subsystem dependencies + // eg. requires(chassis); + requires(Robot.v); + this.setRunWhenDisabled(true); + failedToOpenCamera = false; + } + + // Called just before this Command runs the first time + protected void initialize() { + try { + session = NIVision.IMAQdxOpenCamera("cam0", NIVision.IMAQdxCameraControlMode.CameraControlModeController); + NIVision.IMAQdxConfigureGrab(session); + NIVision.IMAQdxStartAcquisition(session); + } catch (Exception e) { + e.printStackTrace(); + failedToOpenCamera = true; + } + + } + + // Called repeatedly when this Command is scheduled to run + protected void execute() { + System.out.println("aaaaaa"); + frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0); + NIVision.IMAQdxGrab(session, frame, 1); + NIVision.imaqWriteVisionFile(frame, "/home/lvuser/Pics/image_from_camarea.jpeg", new RGBValue(0, 0, 0, 1)); + frame.free(); + } + + // Make this return true when this Command no longer needs to run execute() + protected boolean isFinished() { + return failedToOpenCamera; + } + + // Called once after isFinished returns true + protected void end() { + NIVision.IMAQdxCloseCamera(session); + } + + // Called when another command which requires one or more of the same + // subsystems is scheduled to run + protected void interrupted() { + } +} diff --git a/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/subsystems/Vision.java b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/subsystems/Vision.java new file mode 100644 index 0000000..e949399 --- /dev/null +++ b/projects/PythonCV/TrackYellowTote/src/org/usfirst/frc/team3316/robot/subsystems/Vision.java @@ -0,0 +1,20 @@ +package org.usfirst.frc.team3316.robot.subsystems; + +import org.usfirst.frc.team3316.robot.commands.SaveImage; + +import edu.wpi.first.wpilibj.command.Subsystem; + +/** + * + */ +public class Vision extends Subsystem { + + // Put methods for controlling this subsystem + // here. Call these from Commands. + + public void initDefaultCommand() { + // Set the default command for a subsystem here. + setDefaultCommand(new SaveImage()); + } +} + diff --git a/projects/PythonCV/TrackYellowTote/sysProps.xml b/projects/PythonCV/TrackYellowTote/sysProps.xml new file mode 100644 index 0000000..fbc7ec6 Binary files /dev/null and b/projects/PythonCV/TrackYellowTote/sysProps.xml differ