Thursday, February 26, 2015

Last Snow Of the Season?

One more snow "storm," one more chance to take snow related pics. This time, Shira even got in the on action taking the first two photos.

Wednesday, February 25, 2015

Adventures in Dithering: Making some gray in a sea of black and white

Yesterday I implemented support for printing images in my DropPrint Android app. One issue with the printer is the range of values it prints: mainly it has none. As they say: you can have any color you want, as long as it's black. So a typical photo, which is filled with all sorts of grays, gets turned in a photo filled only with black and white. Like this one:

In some cases this effect may be desirable, but I was curious if I could leverage the halftone effect to simulate shades of gray. With a halftone you trick the eye into seeing gray by by varying the mixture of white and black dots. One Google search told me that while halftoning may get the job done, there's another important option to consider:

If you are doing this because you like the effect, that's cool. But if you just want to dither down to a black and white image consider using a "Floyd-Steinberg" dither. It provides high quality results and distributes the error across the image. http://en.wikipedia.org/wiki/Floyd-Steinberg_dithering

The Floyd–Steinberg dithering looked exactly like what I wanted, and the Wikipedia page even gave me an algorithm I could translate to scheme code:

(for-each-coord src-w src-h
                (lambda (x y)
                  (let* ((old-pixel (rgb->gray (pixels (idx x y))))
                         (new-pixel (if (> old-pixel 128) 255 0))
                         (quant-error (- old-pixel new-pixel)))
                    (store! x y new-pixel)
                    (update! (inc x) y       (* quant-error (/ 7 16)))
                    (update! (dec x) (inc y) (* quant-error (/ 3 16)))
                    (update! x       (inc y) (* quant-error (/ 5 16)))
                    (update! (inc x) (inc y) (* quant-error (/ 1 16))))))

After fixing update! so it wouldn't try to update pixels outside of the image (I'm looking at you (-1, 1)), I was surprised at the quality of the image generated. Here's the same image as above, but with dithering in place:

Look at that, there's now some "gray" in the image!

I'm not sure what to make of the white vertical bars. They are almost certainly a defect in code as I've printed other images that don't have them.

The main issue I have with this algorithm is that it's terribly slow. Dithering a 384x447 pixel image takes almost 30 seconds, with the vast majority of that time spent looping over every pixel in the image. I'm sure I'm doing something inefficient, though it's possible that I'm running into a performance issue with Kawa Scheme. At some point, I'll probably debug it further and see why it's so slow.

Next up: I've got to see if I can get rid of those annoying white bars and then I need to make the Bluetooth connectivity far more bullet proof. When that's done,I should have a pretty dang functional app.

As usual, the DropPrint source code can be found here.

Tuesday, February 24, 2015

Gotcha of the Day: CORS Requests fail when switching to an HTTPs Environment

Tonight I was migrating a client's site from HTTP to HTTPs when I ran into this conundrum: when I updated www.foo.com to invoke api.foo.com over HTTPs I started getting CORS related errors in Firebug:

This is confusing because api.foo.com was and is setup to return back the appropriate CORS related headers. Surely CORS isn't supposed to just break when you switch to HTTPs, is it?

I checked and double checked the various CORS headers. Then I came across this notation in the specification:

The term user credentials for the purposes of this specification means cookies, HTTP authentication, and client-side SSL certificates that would be sent based on the user agent's previous interactions with the origin.

Perhaps by switching to HTTPs I now needed to follow the instructions related to user credentials including:

If the resource supports credentials add a single Access-Control-Allow-Origin header, with the value of the Origin header as value, and add a single Access-Control-Allow-Credentials header with the case-sensitive string "true" as value.

Otherwise, add a single Access-Control-Allow-Origin header, with either the value of the Origin header or the string "*" as value.

The string "*" cannot be used for a resource that supports credentials.

Alas, making sure that Access-Control-Allow-Origin was set to the exact value of Origin and sending Access-Control-Allow-Credentials: true made no difference.

Finally, through little more than dumb luck I visited https://api.foo.com in my browser. I'm working in a dev environment, so no big surprise, I got the usual Certificate warning:

Naturally, I accepted the certificate.

And to my complete shock and amazement, the problem was fixed!

This kind of sort of makes sense. I can see why Firefox wouldn't allow CORS requests from a certificate that wasn't (a) valid or (b) added to the exception list. But man, they could have saved me quite a headache if they had somehow indicated the issue in Firebug. At the end of the day, this wasn't really a CORS issue in that CORS was perfectly setup.

DropPrint 2.0: Image and QR Code Support

This morning I finished up the next iteration of DropPrint, a tiny Android app that drives a thermal bluetooth printer. The big improvements: DropPrint now supports images as well as bar codes.

Check it out:

The image printing protocol for the printer I'm using, a DL58, is pretty dang simple. It consists of little more than sending each row of an image with the following format:

 0x1F 0x10 NN 0x00 B1 B2 B3 ...

Where NN is the number of bytes being sent to represent the line of the image. B1, B2, etc. are bytes containing the relevant bits (0 black, 1 white) for each pixel. In other words, if my image was 1 pixel high and 8 pixels wide (I know, not a particularly interesting image):

  Black Black Black White White White Black White

I'd send this as:

 0x1F 0x10 0x01 0x0 [00011101]

It through me off at first, but that binary header (0X1F 0x10 ...) is sent at the start of each row, not just at the start of the image.

This whole mapping of image pixels to individual bits, followed by compressing the bits into bytes, was an interesting exercise to say the least. Not being much of a hardware interface guy, these are usually details I'm not worrying about. An interesting side effect of printer's binary protocol is that the height of the image is effectively irrelevant. All the printer knows about are the individual rows of the image. I'm using this to my advantage in DropPrint by rotating images that are taller than they are wider. The result is that especially narrow or wide images, like a panoramic shot, actually do well on the printer.

One obvious issue with the printer is that it only prints black and white, there's no ability to send any sort of gray pixels. The result is that your typical photograph, filled with not-quite-black-not-quite-white areas, looks awful when printed. Still, there's hope. The notion of the halftone was invented to solve exactly this problem. Invented back in the 1830's, it's hardly new tech. The idea is to simulate gray by interspersing black and white dots. Just like our brains can be fooled into seeing fluid movement using the principles of animation, we can also be fooled into seeing gray when only black and white is present. Anyway, this is an area I'll be coming back to.

With the basic image printing ability out of the way adding QRCode support was quite easy. I grabbed the zxing library and put it it to work. Now when DropPrint discovers a .qrc file, it encodes the text found within as a QR Code and prints that.

Next up: I'll work on improving the image printing quality as well as improving how the app handles being put in the background and losing connection to the printer. Still lots to do, but it's amazing when this guy prints out an image I've sent to it.

Check out the source code here.

Monday, February 23, 2015

Nifty emacs trick: impatient-mode for instant buffer sharing

It may only only be 10:20am, but I'm willing to bet this is going to be the coolest thing I see all day. Perhaps all week. Lifted from this post: Emacs: Peer-to-peer coaching is easier when you use impatient-mode to share your buffer I typed the following into emacs:

 M-x package-install impatient-mode
 M-x httpd-start
 C-x b foo
 M-x impatient-mode

I then opened up: http://localhost:8080/imp/ and clicked on foo. As I typed in emacs, my browser automagically updated:

(Yeah, that static screenshot doesn't do the functionality justice.)

I have no idea how I'm going to put this capability to work. But the fact that it just magically works leaves me in awe.

Emacs just never ceases to amaze.

Just a Little Impossible: Morris Counting

I'll often advise entrepreneurs I talk with that it's ideal if their Software Idea is just a little impossible. ProgrammingPraxis recently published a challenge / solution that fits this description well. It's quirky, but still instructive. Here's my own word-problem based description of the challenge:

Imagine you're given the task at counting entrants to the state fair (yum!). Your boss hands you one of those crowd counter devices and walks away. As you examine the counter you realize that it only counts up to 255. Once the 256th person walks in, your screwed. The counter won't work anymore.

What do you do? Pray that the state fair has 254 attendees? Flee for your life? If you're Robert Morris, you get creative and devise a new way of counting, one that solves this seemingly impossible problem.

Here's what you do: you borrow a coin from your fellow fair employees and you stand by the gate. The first time someone walks in, you click the counter. Now it reads one. The next time someone walks you, you look down at the counter and flip the coin however many times is shown on its face. If your coin comes up heads every time, then you click the button to increment the counter. And repeat.

So if the counter says 8, then you have to flip the coin 8 times in a row. And if you get heads 8 times (unlikely, but do it enough, and it'll happen), you increment the counter to 9.

When your boss comes by and asks how many people visited the fair you bust out your pocket calculator compute 2x-1, where x is the number shown on the counter.

Of course, this won't be the exact number of people who visited the fair, but it will be in the ballpark. It'll certainly tell you if there were 10's, 100's or 1000's of visitors that day. And that's far better data than having nothing.

Here's some random executions of the this algorithm:

In some cases, the number is pretty accurate (524 was estimated at 511, 242 was estimated at 255). In other cases, it's pretty out there (2956 vs. 4095). But still, considering that you're making use of nothing more than a very limited counter and a single coin, the results are quite impressive.

The bigger lesson though is the recipe at play here: find a problem which others think is impossible, solve it, and you're on your way to changing the world. That's not too much to ask, is it?

Here's the code that implements the above algorithm:

;;
;; http://programmingpraxis.com/2015/02/20/morris-counting/
;;

(define (show . words)
 (for-each display words)
 (newline))

(define (heads? n)
 (let ((flip (= 1 (random-integer 2))))
  (cond ((not flip) #f)
        ((and flip (= n 1)) #t)
        (else (heads? (- n 1))))))
        
(define (int-count n)
 (+ 1 n))
 
(define (morris-count c)
 (cond ((= c 0) 1)
       ((heads? c) (int-count c))
       (else c)))
       
(define (morris-value c)
 (- (expt 2 c) 1))
  
(define (trial upper)
 (let loop ((n (random-integer upper))
            (i 0)
            (c 0))
  (cond ((= n 0)
         (show "actual=" i ", morris=" (morris-value c)))
        (else
         (loop (- n 1)
               (int-count i)
               (morris-count c))))))
               
(define (test)
 (for-each trial '(10 50 100 200 500 800 1000
                   1500 2000 5000 7000 10000)))

Thursday, February 19, 2015

Screenshots for the Lazy Linux User

One of tools I used most frequently on Windows isthe Snipping Tool. This relatively tiny program allows you to grab a screenshot and crudely annotate it. There are fancier versions out there, but the Snipping Tool has been built into Windows long enough that you can just depend on it.

So what program should I use to take screenshots on Linux? One solution I found and immediately liked was the peculiarly named scrot. scrot is your typical command line app, so it doesn't do a whole lot (which is ideal!). You can type:

 scrot

and it just grabs a screenshot of the current screen. Or you can dress it up a little bit by adding a delay (which lets you get the right window position) or use -s flag to select a region to snapshot from your current screen.

The "problem" with scrot is that once I'm done with an image, I like to typically annotate it. And for that, I typically use Gimp. So while using scrot by itself is handy, it's not particularly efficient.

Changing gears a bit, it occurred to me that I could just grab a screenshot from within Gimp. That works well enough, but I was curious if I could streamline the process. As is, I found myself navigating to the File » Create menu, clicking on the 'Grab' button, and then switching the foreground color to red so that I could annotate the image in a clear way. Rather than perform these steps manually, I thought I combine them into a bit of script-fu:

(define (bs-snip-it)
  (let ((image (car (plug-in-screenshot RUN-INTERACTIVE 0 0 0 0 0 0)))) ; [XXX]
    (gimp-context-set-foreground '(255 0 0))
    image))

(script-fu-register
 "bs-snip-it"
 "Snip-It"
 "Grab a screenshot and be ready to edit it"
 "Ben Simon"
 "Copyright 2015, Ideas2Executables, LLC"
 "2015/02/18"
 "")

(script-fu-menu-register "bs-snip-it" "<Image>/File/Create")

I have to admit though, I still wasn't satisfied. To grab a screenshot I needed to switch to gimp, kick off this plugin, and interact with the screenshot dialog. I thought I could improve this by running plug-in-screenshot with RUN-NONINTERACTIVE. However, there appears to be a bug in the C code that keeps this from properly working. And regardless, having to switch to Gimp in the first place is a pain.

So on to version 3.0 I went. I ended up combing both scrot and Gimp, driving the whole thing with a shell script:

#!/bin/bash
##
## grab a screenshot and open it in gimp
##
sleep .2
img=$HOME/Pictures/screenshots/`date +%Y-%m-%d-%H%M`.png

(echo "Select area to screen shot" | festival --tts) &
scrot -s $img

startgimp $img
sendgimp "(gimp-context-set-foreground '(255 0 0))"

I wired this script into ratpoison with the following line in my .ratpoisonrc:

  definekey top s-g exec snipit

Now, when I hit Windows-g (Windows is the Super key, hence the s- prefix) the above script is executed. The sleep .2 is required by ratposion, without it the script doesn't execute properly.

The first order of business of the script is to read aloud some instructions to me. Then it kicks off scrot -s $img which allows me to select just the area of interest and saves it into an image.

From there, startgimp is executed, which as you'll see below is a thin shell script wrapper around Gimp. Gimp already has the nice feature that if you hand it an image and there's an instance running, it won't start a new instance, but will use the existing one. The next line sends a bit of code to gimp telling to set the foreground color to red.

This means that I hit one keystroke, select the area of interest, and Gimp pops up in front of me, ready to annotate away. So for now, I'm happy with my screenshot capability.

The scripts below, startgimp and sendgimp leverage the Script-Fu server built into Gimp. When this server is running it's possible to send Gimp arbitrary commands, essentially scripting it from the command line. startgimp kicks off Gimp, insuring the Script-Fu server is running, and sendgimp sends script-fu code to Gimp using the correct protocol. These scripts should be reusable outside of the screenshot context. Essentially, I now have command line scripting of Gimp, to go along with its built in plugin based architecture.

Here are those scripts:

# ########################################################################
# startgimp
# ########################################################################
#!/bin/bash

##
## Kick start gimp off just the right way
##
SERVER_LOG=$HOME/.gimp-2.8/server.log

exec gimp -b "(plug-in-script-fu-server 1 \"127.0.0.1\" 10008 \"$SERVER_LOG\")" "$@"


# ########################################################################
# sendgimp
# ########################################################################
#!/bin/bash

##
## Send gimp all the expressions on the command line. Or, if
## no command line, then read from stdin
##

SERVER_HOST=127.0.0.1
SERVER_PORT=10008

function gimp_send {
  expr="$1"
  len=`echo -n $expr | wc -c | cut -d ' ' -f1`
  (perl -e "print pack('an', 'G', $len)" ;
   echo $expr) | nc $SERVER_HOST $SERVER_PORT
}

if [ -z "$1" ] ; then
  expr=`cat`
  gimp_send "$expr"
else
  while [ -n "$1" ]; do
    gimp_send "$1" 
    shift
  done
fi

Wednesday, February 18, 2015

The Next Best Thing to a Fleet of Personal Reconnaissance Drones

Yesterday the DMV was hit by a "major snow storm" that shut down the area. As the new day began I was curious what conditions were like around the area. But how to know? Turn on the news and trust the Main Stream Media? Don't think so.

No, I wanted to get a first hand view of the situation, and I wanted to do it while sipping hot tea in my PJs. So it was off to the Internet!

TrafficLand has a DC area map that allows you to view the area traffic cameras. It's this kind of data I was after. But manually selecting each camera feed is just so tedious. I'm on Linux baby, surely I can do better than hunting and pecking on a Google Map.

The first thing to note is that if you right mouse click on any traffic cam, you can access an image URL that looks like this guy:

 http://ie.trafficland.com/660/full?system=trafficland-www&pubtoken=defa0f069743edb951716d02aa17111bf26&cache=56104

Assuming you have the feh image viewer (sudo yum install feh), you can grab and view this image like so:

 curl -s 'http://ie.trafficland.com/660/full?system=trafficland-www&pubtoken=defa0f069743edb951716d02aa17111bf26&cache=56104' |
    feh -. -

That's already an improvement over the Google Maps UI, but we can do even better. Next up, if you've got ImageMagick installed (sudo yum install ImageMagick), then you've got the montage command at your disposal. montage combines images into a sort of old school contact sheet. So if we can grab multiple camera images we can trivially combine them into one combined image.

Another item worth noticing is that the above URL contains a parameter named pubtoken. That value almost certainly expires after a few minutes, meaning that if you hard code the above URL in a script, it's going to stop working after some time.

So what's the right way to get that camera URL with a fresh pubtoken? Well, it's not particularly obvious. But by examining the web traffic under Firebug, it's also not that hard to figure out. At the end of the day, the page in your browser needs a valid URL, and your script is simulating the web browser, so it needs to simulate this step as well.

With those kinks worked out I ended up with the script printed below. Now when I type trafficcams I get an image like the following to pop-up:

That's a montage of 6 cameras that interest me. I came up with the camera ID's (see below) by examining the URL mentioned above.

I suppose one could ask if what I'm doing is appropriate from an ethical stand point. That is, is it OK to lift image data off TrafficLand without using their UI? I'm far from qualified to answer this question, but I will suggest this: we're used to thinking of web browsers as being Internet Explorer, Chrome or Firefox. But the reality is, a web browser is any tool that allows you to, well, browse the web. If you were blind, you'd expect your web browsing experience to be audio based. If you're Linux user, and lazy, then why not have your web browser experience cut right to chase? The agreement has always been that a web browser requests content from a site, that site returns it and it's the job of the browser to render this content. I've just decided that I'd like to render the TrafficLand site a little differently than most users.

It certainly wouldn't be appropriate to grab the image data from TrafficLand and sell it, or utilize it in a project without their permission.

I mention all this because I think it's easy to get stuck of thinking of the web browsing experience as sort of like watching TV. All TVs show the same rendering (though, quality obviously differs) of the same content. But, on the web, we're not limited to that narrow mindset. Make your tools work for you!

OK, enough chatter. Here's that script:

#!/bin/bash

cache=`echo $RANDOM`
base_dir=/tmp/tc.$$
cams="401807 200003 930 401770 200113 640"

mkdir -p $base_dir
for c in $cams ; do
  url=`curl -s "http://www.trafficland.com/video_feeds/url/$c?cache=$cache"`
  echo -n "Grabbing: $c ... " > /dev/stderr
  curl -s "$url" > $base_dir/$c.jpg
  echo "Done" > /dev/stderr
done

dest=$1 ; shift
if [ "$dest" = "-" ] ; then
  output="cat -"
else  
  output="feh -. -B black - "
fi

montage  $base_dir/*.jpg -geometry '400x400>' - | $output
rm -fr $base_dir

Our Snow Day Experience in One Photo

This photo pretty much perfectly captures our snow day experience yesterday. What you can't tell is that: (a) I promised Shira we would shovel together, (b) when she said it was time to shovel I told her I'd be joining her in just a few minutes (that is, as soon as some code I was working on was finished) (c) I lied about (b), (d) when I finally did show up, Shira was 97.9% done witih Shoveling, and (e) I promised I'd help her finish up.

And then you have this photo:

You can almost hear me saying: "whoa! check out those icicles. Let me grab some photos and *then* I'll shovel."

If it makes Shira feel any better, I was always good at getting out of shoveling growing up, too.

Monday, February 16, 2015

Gotcha of the Day: Google Docs API Suddenly Starts Returning 401 Unauthorized

Some time back I created a tool that uses the Zend Gdata Spreadsheets API to pull data in from various spreadsheets and store it in a system database. The code has been rock solid. That is, until today, when attempts to access one particular Google doc resulted in this error:

  HTTP 401 Unauthorized

Yeah, that wasn't a heck of a lot to go on. Not only that, but nothing obvious had changed: the document in question was still shared with the user trying to access the doc, and the user credentials hadn't been updated. So why the lack of authorization?

My first thought was that it had something to do with the automatic migration Google promised for older docs. Perhaps this one particular document had been migrated, and in doing so, the spreadsheet key had been altered. If that was the case, the existing code may very well be expected to fail (as it looks up the spreadsheet by this unique ID).

At first I thought I was on the right track: the Google Doc in question did have an updated URL. But alas, putting in the new spreadsheet key made no difference. The system still got an unauthorized message when it tried to access the document.

Next up, I tried using a different set of user credentials. Again, no change.

Then I started picking apart the Zend API calls. I'm essentially executing this code:

    $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
    $spreadsheetService = new Zend_Gdata_Spreadsheets($client);

    $query = new Zend_Gdata_Spreadsheets_DocumentQuery();
    $query->setSpreadsheetKey($spreadsheetKey);
    $feed = $spreadsheetService->getWorksheetFeed($query);  // Failing here

I updated the above code to print out just a list of possible spreadsheets. The code was something along these lines:

    $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
    $spreadsheetService = new Zend_Gdata_Spreadsheets($client);
    $feed = $spreadsheetService->getSpreadsheetFeed();
    var_dump($feed);

I finally caught a break here and was pleased to see that the spreadsheet in question was indeed found in the list =Google was returning to me.

So this proves that the user can see the spreadsheet, yet he still can't access it.

After spending more time digging around the Zend code I finally took an altogether different tact.

The Google Spreadsheet API, at least accessing the list of worksheets, is actually quite simple. You can cobble together client to accomplish this using little more than curl. So that's what I did. I followed this guide and manually executed the URL requests to authorize my user and access the spreadsheet in question. Again, to my surprise, it worked.

So what was I doing manually with curl that the Zend API wasn't doing? Hmmm...looking closely I realized that the Zend request to pick up the spreadsheet was using http. the curl example was, you guessed it, using https. I opened up Zend/Gdata/App.php and scrolled down to around line 640 and added this line of code:

    public function performHttpRequest($method, $url, $headers = null,
        $body = null, $contentType = null, $remainingRedirects = null)
    {
        ...

        // Make sure the HTTP client object is 'clean' before making a request
        // In addition to standard headers to reset via resetParameters(),
        // also reset the Slug header
        $this->_httpClient->resetParameters();
        $this->_httpClient->setHeaders('Slug', null);

        $url = str_replace('http', 'https', $url);  // [ADDED]

        // Set the params for the new request to be performed
        $this->_httpClient->setHeaders($headers);
        $this->_httpClient->setUri($url);
        $this->_httpClient->setConfig(array('maxredirects' => 0));

That line of code blindly re-writes the URL from 'http' to 'https' -- and what do you know, that solved the problem!

I can't really blame this on Zend. I'm probably using version 1.x of their API, and they have version 2.3.

My guess is that it was the spreadsheet upgrade that ended up biting me. On the surface the change Google made was pretty tiny: you must now access Google Docs over HTTPs (versues having HTTP and HTTPs both work). That change, however, was enough to throw a monkey wrench into my code.

In hindsight, requiring docs be accessed over HTTPs makes perfect sense. Though I would have appreciated a clearer error message when I violated this requirement. With that said, it sure was nice to re-familiarize myself with the Google Docs API and how it can be accessed from the command line. Who knows, maybe I'll find a way to integrate this in with my other Linux hacking projects.

LinkWithin

Related Posts with Thumbnails