Thursday, December 4, 2014

Juggle Music

I've made some progress with my Juggle Music project, there is a public repo over on github where I'm managing the code.

After more research I decided to skip SimpleCV and just dive directly into learning OpenCV.

I made this call due to the lessons I learned while coming to grips with developing the GUI for my python powered iRacing Stats project. Initially I used GUI2Py which wraps around the WXPython library, because WX looked very scary to begin with. As I progressed and my GUI became more complex I started hitting issues with GUI2Py and rather than hack on it to get it to interface how I wanted with WX I realized I was better off just doing it all in WX directly.

I've got the basics of the object tracking completed. It is color based and works nice and simply. You fire up the app and it prompts you to select the 3 colored objects. I do this rather than hard coding the color ranges because this method handles different lighting conditions and allows me to change props.

For the music playback I'm using the Mingus library along with Fluidsynth. I've got a lot of learning to do on this side of things, my knowledge of all things midi is very basic at this stage. Currently I've got it rigged up to simply playback notes based on how high the objects are thrown. At this stage it really is just a proof of concept.

I'll be working on configuring the "juggle space"; working out how many segments I can cut the webcam image up into so I'm able to accurately trigger samples. I can also see that drawing these segments on the output window would be helpful.

Monday, December 1, 2014

SimpleCV Object Tracking

I've had an "on-again off-again" project kicking around for a long time which I want to dive back into and dedicate some more time and effort into.

The idea is simple enough; I want to be able to trigger the playback of music and samples by juggling.

The best method I've come up with is using a webcam and some computer vision code. Since my skills in python have been progressing quite well I feel that I can finally produce something solid based on the SimpleCV library.

Here's a few projects which I can learn from:

  • Object Tracking with SimpleCV (White Ball)
  • Track cards using python and SimpleCV
  • Thursday, November 27, 2014

    Python Knowledge

    I learned a few things from an article linked to on HackerNews: Top 10 Python idioms I wish I'd learned earlier

    There was also a lot of great information in the comments thread for the post. This included a link to the youtube video below:

    Tuesday, November 25, 2014

    Google API's

    I've been exporting info from my iRacing Stats application to html to do weekly and end of season updates on my iRacing blog. This process had been fairly manual; I'd manually upload the graph images to blogger, create a new blog post and paste in the exported html and edit the img tags to point to the uploaded images. Its not that it was too painful, but since it was a repetitive task I wanted to see if I could automate it.

    So I dived into the Google API doco and pretty quickly worked out how to get my python application to post a new blog update. Getting the images uploaded was more painful, since blogger actually uses picasa to store images and the picasa API is terrible. I ended up using Google Drive to store the images, which means I needed to handle a few more steps than just uploading; namely changing the permissions to public viewing, uploading to a folder and retrieving the public url.

    I've got it all working now, including prompting the user for only their blog's URL from which it app pulls the BlogID (rather than having to have the user go off and find it). All in all I'm quite proud of getting it all working. I just hope Google don't go changing their APIs in the near future.

    I'm sure the knowledge I've gained here will be useful in many other projects.

    Here's a bunch of links which I found helpful:

  • Authorized Google API access from Python
  • google-api-python-client
  • oauth2client

    Here's a bit of code:

    from oauth2client import file, client, tools
    from apiclient.discovery import build
    from apiclient.http import MediaFileUpload
    from httplib2 import Http
    
    def blogger_post(outfile):
     try:
      html_file = open(outfile)
      html_lines = html_file.read()
      chop_start = html_lines.find('')
      chop_end = html_lines.find('')
      html_lines = html_lines[chop_start+6:chop_end]
      
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = 'https://www.googleapis.com/auth/blogger'
      store = file.Storage('storage_blogger.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)
         
      service = build('blogger', 'v3', creds.authorize(Http()))
      body = {
       "kind": "blogger#post",
       "id": cfg.config['Blogger']['blogid'],
       "title": os.path.basename(os.path.splitext(outfile)[0]),
       "content":html_lines
       }
    
      request = service.posts().insert(blogId=cfg.config['Blogger']['blogid'], isDraft=True, body=body)
      response = request.execute()
      return response['url']
     except:
      return "Failed"
    
    def blogger_img_upload(filename):
     try:
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = ('https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file')
      store = file.Storage('storage_drive.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)    
    
      service = build('drive', 'v2', creds.authorize(Http()))
      
      q = "title = 'iRacing Stats Graphs' and mimeType = 'application/vnd.google-apps.folder'"
    
      request = service.files().list(q=q)
      response = request.execute()
      
      if len(response['items']) == 0:
       body = {
        "title": "iRacing Stats Graphs",
        "mimeType": "application/vnd.google-apps.folder"
        }
    
       request = service.files().insert(body=body)
       response = request.execute()
       folderId = response['id']
      else:
       folderId = response['items'][0]['id']
     
      pub.sendMessage('Uploading', graph=os.path.basename(os.path.splitext(filename)[0]))
      body = {
       "title": os.path.basename(os.path.splitext(filename)[0]),
       }
      body['parents'] = [{'id': folderId}]
      media_body = MediaFileUpload(filename)
    
      request = service.files().insert(body=body, media_body=media_body)
      response = request.execute()
      fileId = response['id']
     
      body = {
       "type": "anyone",
       "role": "reader"
       }
      response = service.permissions().insert(fileId=fileId, body=body).execute()
      response = service.files().get(fileId=fileId).execute()
      return response['webContentLink'].split('&')[0]
     except:
      print("Upload of %s to blogger failed" % os.path.basename(os.path.splitext(filename)[0]))
      return "Failed"
    
    def blogger_config(url):
     try:
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = 'https://www.googleapis.com/auth/blogger'
      store = file.Storage('storage_blogger.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)
         
      service = build('blogger', 'v3', creds.authorize(Http()))
      response = service.blogs().getByUrl(url=url).execute()
      cfg.write_blogid(response['id'])
      return True
     except:
      pub.sendMessage('Alert', msg="Unable to find BlogID of: %s" % url, title="Blogger Config Failed")
      return False
    
  • Monday, November 24, 2014

    PhotoFrame PC Update

    The Digital PhotoFrame PC located in my kitchen sees near daily use; in the morning while I make coffee I trigger playback of the Australian Broadcasting Commision's 90 second news headlines and NPR's 5 minute news update. While I'm preparing dinner I'll often use it to stream jazz from KJZZ.org. Occasionally I'll use it to display a recipe to follow along with.

    All in all there isn't much reason that I'd want to change the setup, it currently functions exactly as I want. However, it is running Windows XP which is no longer support by Microsoft. I don't like the idea of having an unsecurable machine running on the network so I've started planning on switching it to run Lubuntu Linux.

    Obviously this would require that I recreate all the AutoIT scripts which currently trigger all of the above things. I'm very confident that I could easily do this via Python based scripts. I've also started looking for one to one replacements for the handful of other features I make use of:

  • Variety to handle background image display and rotation.
  • Conky or LXDE screenlet for weather and clock.