From 9ea16a768cd4143d2a3508d7193f8d0d784e40be Mon Sep 17 00:00:00 2001 From: David Runge Date: Fri, 31 Jan 2014 11:48:32 +0100 Subject: First commit, still some TODOs --- .ropeproject/config.py | 85 ++++++++++++++++++++++++++ random241.py | 63 ++++++++++++++++++++ random241arg.py | 25 ++++++++ random241osc.py | 31 ++++++++++ random241sensor.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 361 insertions(+) create mode 100644 .ropeproject/config.py create mode 100755 random241.py create mode 100755 random241arg.py create mode 100755 random241osc.py create mode 100755 random241sensor.py diff --git a/.ropeproject/config.py b/.ropeproject/config.py new file mode 100644 index 0000000..ffebcd4 --- /dev/null +++ b/.ropeproject/config.py @@ -0,0 +1,85 @@ +# The default ``config.py`` + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + #prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + #prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + #prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/random241.py b/random241.py new file mode 100755 index 0000000..1f74485 --- /dev/null +++ b/random241.py @@ -0,0 +1,63 @@ +#!/usr/bin/python2 + +import cv +import time +import numpy as np +import logging +import random241arg as arg +import random241sensor as sensor +import random241osc as osc + +showStream = False +stop_key = 0 +capture = True +camNumber = 1 +time_delta = 60 * 60 / 2 +logging.basicConfig(filename='random241.log', + format='%(asctime)s %(message)s', + filemode='w', level=logging.INFO) + + +# Read parameters +params = arg.read_params() +# Define which cam to use, etc. +#cam = sensor.capture(params['-c'], showStream) + +# Get frame and size information from camera +cam = cv.CaptureFromCAM(camNumber) +frame = cv.QueryFrame(cam) +if frame is not None: + frame_size = cv.GetSize(frame) + logging.info('Grabbing random numbers from: %dx%dpx.', + frame_size[0], frame_size[1]) + + # Get matrix with values from frame + mat = cv.GetMat(frame) + frame_values = np.asarray(mat) + # Create grayscale image + gray_values = sensor.bgr2gray(frame_values) + + # Define the time to run the test + time_delta = time_delta + time.time() + + # Setup OSC + osc.connect_to_server("dvzrv", 57120) + + # Main loop for accessing the camera and calculating random numbers from it + #while True: + while time_delta > time.time(): + img = cv.QueryFrame(cam) + # Get a numpy array with rgb values + #frame = sensor.frame_to_mat(stream) + randomness = sensor.find_dot(sensor.bgr2gray(sensor.frame_to_mat(img))) + if randomness is not None: + osc.send_osc_msg_to_server(randomness) + if showStream: + while stop_key != ord("q"): + cv.ShowImage("Americium 241", img) + key = cv.WaitKey(2) + + #time.sleep(5) + #random241sensor.set_capture(False) +else: + logging.error('Connect camera %d first!', camNumber) diff --git a/random241arg.py b/random241arg.py new file mode 100755 index 0000000..4bd00d8 --- /dev/null +++ b/random241arg.py @@ -0,0 +1,25 @@ +#!/usr/bin/python2 + +from sys import argv + + +def read_params(): + # Checks the given parameters + info = """Use the program as following: + random241.py [option] ... + Options: + -c number of cam to use (starts with first found camera 0 (default)) + -r remote address to send the output to (standard 192.168.0.7) + """ + default = {'-c': "1", '-r': "192.168.0.7"} + parameters = default.copy() + if len(argv) != 3: + for i in xrange(1, len(argv) - 1, 2): + if argv[i] in parameters: + parameters[argv[i]] = argv[i + 1] + else: + print info + return 0 + return parameters + else: + return default diff --git a/random241osc.py b/random241osc.py new file mode 100755 index 0000000..3d95f69 --- /dev/null +++ b/random241osc.py @@ -0,0 +1,31 @@ +#import OSC as osc +import liblo as osc +import logging + +# Declare an empty target +target = None + + +# Connect to the server +def connect_to_server(hostname, port): + global target + if (hostname or port) is None: + target = osc.Adress('127.0.0.1', 57120, osc.TCP) + else: + try: + target = osc.Address(hostname, port, osc.TCP) + except osc.AddressError, err: + logging.error(err) + + +# Send a osc_message to the server +def send_osc_msg_to_server(randomness): + global target + # if the message is not empty and longer than 1 + if randomness is not None and len(randomness) > 1: + msg = osc.Message("/random") + msg.add(randomness[0], randomness[1]) + try: + osc.send(target, msg) + except: + logging.error('OSC: Sending of message failed.') diff --git a/random241sensor.py b/random241sensor.py new file mode 100755 index 0000000..624cc29 --- /dev/null +++ b/random241sensor.py @@ -0,0 +1,157 @@ +#!/usr/bin/python2 + +import logging +import cv +import numpy as np +#import time + +# Bool to define wether to capture the cam or not +capture = True +# Bool to define wether to show the capture stream or not +showStream = True +white_threshold = 30.0 +checked = np.zeros((1, 1), dtype=np.int) +mat = np.zeros((1, 1)) +clusters = [] + + +def capture(camNumber, showStream): + # Open stream for that camera + logging.info('Capture from camera #%d', camNumber) + cam = cv.CaptureFromCAM(int(camNumber)) + # Stream to output window as long as it is active + return cam + while capture: + stream = cv.QueryFrame(cam) + if showStream: + cv.ShowImage("Americium 241", stream) + + +def set_capture(onOrOff): + if onOrOff == bool: + global capture + capture = onOrOff + + +def frame_to_mat(img): + cv.Smooth(img, img, cv.CV_GAUSSIAN, 3, 0) + mat = cv.GetMat(img) + frame_values = np.asarray(mat) + return frame_values + + +# Convert a bgr matrix to grayscale +def bgr2gray(mat): + b, g, r = mat[:, :, 0], mat[:, :, 1], mat[:, :, 2] + gray = 0.1140 * b + 0.5870 * g + 0.2989 * r + return gray + + +# Find a white dot in the black input matrix +def find_dot(mat_input): + global mat + global checked + global clusters + mat = mat_input.copy() + if np.ndim(mat) >= 2: + # Create array to hold the already checked pixels + checked = np.zeros((len(mat), len(mat[0])), dtype=np.int) + # Traverse the grayscale values in search of a bright pixel + for i in range(0, len(mat) - 1): + for j in range(0, len(mat[0]) - 1): + # Check if it hasn't been checked yet + if (checked[i][j] != 1): + # Find clusters, if the pixel is above threshold + if (mat[i][j] >= white_threshold): + #print "Hit above white threshold" + # Add a new cluster to the list of clusters + cluster = [] + clusters.append(cluster) + # Find the rest of the cluster + find_cluster(i, j) + #print "Number at: %dx%dpx : %s" % (j, i, mat[i][j]) + checked[i][j] = 1 + # If there's one or more clusters, calculate its or their balance point + if len(clusters) > 0: + balance_point = cluster_to_balance_point() + logging.info('%s, %s', balance_point[1], balance_point[0]) + #print balance_point + # Empty the global clusters variable again + del clusters[:] + return balance_point + else: + logging.error('Input matrix has wrong dimension!') + + +# Find cluster around a non-black pixel +def find_cluster(x, y): + global checked + global mat + global clusters + # Append the current white dot to the last cluster + dot = np.array([x, y, mat[x][y]]) + clusters[len(clusters) - 1].append(dot) + # Search for surrounding white dots now + # Search one pixel further right + if (len(mat) - 1 >= (x + 1)) and (mat[x + 1][y] >= white_threshold) \ + and (checked[x + 1][y] != 1): + find_cluster(x + 1, y) + # Search one pixel further right and down + if (len(mat) - 1 >= (x + 1)) and (len(mat[0]) - 1 >= y + 1) and \ + (mat[x + 1][y + 1] >= white_threshold) \ + and (checked[x + 1][y] != 1): + find_cluster(x + 1, y + 1) + # Search one pixel further down + if (len(mat[0]) - 1 >= y + 1) and \ + (mat[x][y + 1] >= white_threshold) and (checked[x][y + 1] != 1): + find_cluster(x, y + 1) + # Search one pixel further down and further left + if (len(mat[0]) - 1 >= y + 1) and x - 1 >= 0 \ + and (mat[x - 1][y + 1] >= white_threshold) \ + and (checked[x - 1][y + 1] != 1): + find_cluster(x - 1, y + 1) + # Add this pixel to the list of checked pixels + checked[x][y] = 1 + + +# Create balance point from cluster +# TODO: Make possible to choose only most significant cluster +def cluster_to_balance_point(): + global clusters + cluster_balances = [] + x_balance = 0.0 + y_balance = 0.0 + for cluster in clusters: + mean_x = 0.0 + mean_y = 0.0 + sum_total = 0.0 + for dot in cluster: + # Calculate X balance (x * intensity) + mean_x = mean_x + dot[0] * dot[2] + # Calculate Y balance (y * intensity) + mean_y = mean_y + dot[1] * dot[2] + # Calculate Y total (all intensity summed up) + sum_total = sum_total + dot[2] + # Add up the balances and put them into a list + cluster_x_balance = mean_x / sum_total + cluster_y_balance = mean_y / sum_total + cluster_balances.append([cluster_x_balance, cluster_y_balance]) + # If it's more than one cluster, balance between them + if len(cluster_balances) > 1: + logging.info('Balancing between a couple of clusters.') + total_cluster_x_balance = 0.0 + total_cluster_y_balance = 0.0 + for balance in cluster_balances: + total_cluster_x_balance = total_cluster_x_balance + balance[0] + total_cluster_y_balance = total_cluster_y_balance + balance[1] + x_balance = total_cluster_x_balance / float(len(cluster_balances)) + y_balance = total_cluster_y_balance / float(len(cluster_balances)) + else: + logging.info('Balancing between one cluster.') + x_balance = cluster_x_balance + y_balance = cluster_y_balance + return [x_balance, y_balance] + + +# TODO: Function to add up balances +# TODO: Function to calculate forced balance -- cgit v1.2.3-54-g00ecf