so deep a rabbit hole

Posted on
Sun Apr 29, 2018 8:08 pm
Different Computers offline
User avatar
Posts: 1999
Joined: Jan 02, 2016
Location: East Coast

so deep a rabbit hole

Ok, prefacing this here with the reminder that I'm not even to the level of script kiddie here. I'm hanging on to this with a thread.

I'm using curl to save an image to my Indigo public directory. That's working fine. But the native image is too big for the reflector or something. I get an Indigo error trying to display it.

So I've been trying various methods to resize the image automatically, and I'm now trying Python, which seems capable. Found this:
Code: Select all
import Image, ImageFile 
pic = "/Library/Application Support/Perceptive Automation/Indigo 7/IndigoWebServer/public/whatev.jpg"
im = Image.open(pic)
width, height = im.size
if width > 1200 or height > 1200:
    width = width*.7
    height = height*.7
im = im.resize((int(math.floor(width)), int(math.floor(height))), Image.ANTIALIAS)
try:
    im.save(pic,optimize=True,quality=70)
except IOError:
    ImageFile.MAXBLOCK = width * height
    im.save(pic,optimize=True,quality=70)


But when I try running this inside Indigo I get "No module named Image."

I DO have pillow installed, but it's installed via Python 2.7 apparently. Indigo uses 2.6, right?

I tried pip install pillow, but all I got was "requirement already satisfied."

Trying to follow instructions at https://stackoverflow.com/questions/553 ... on#5538353 got me more errors, and at that point I'm lost down this hole.

Alternately, if you think it's easier, I've also tried using ffmpeg inside a shell script to do this, but I keep running into trouble there too, as it appears that even though I try retrieving the image via curl then ffmpeg resizing, the shell script multi threads or something and ffmpeg is resizing the previous image before the curl retrieves the new image. And adding "sleep" commands didn't help.

SmartThings refugee, so happy to be on Indigo. 10.13.6 on an i5 MBP w/Harmony Hub, Hue, DomoPad, Dynamic URL, Device Extensions, HomeKit Bridge, MatplotLib, Plex, uniFAP, Wunderground, Nanoleaf, LED Simple Effects, Airfoil Pro, Grafana.

Posted on
Mon Apr 30, 2018 7:06 am
FlyingDiver offline
User avatar
Posts: 4670
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: so deep a rabbit hole

First, Indigo 7 uses Python 2.7, not 2.6.

Second, your import line should be "from PIL import Image".

Third, this code makes no sense:

Code: Select all
except IOError:
    ImageFile.MAXBLOCK = width * height
    im.save(pic,optimize=True,quality=70)


Where did you get that from?

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Mon Apr 30, 2018 7:17 am
Different Computers offline
User avatar
Posts: 1999
Joined: Jan 02, 2016
Location: East Coast

Re: so deep a rabbit hole

Thanks FD!

Got it from https://stackoverflow.com/questions/207 ... -in-python

I'll give it a try asap, depending on how my remote access is working. I planned to tune the image modification from the original code, but you're right I can't figure out what that is supposed to do. The ironic thing is all I apparently need to do is the slightest of compression and the file goes from 922 kb to ~200 kb. It's almost as if the camera is saving it with every possible quality setting at max.

Edit: I debugged all by my own self!!!

Here's what the script looks like now. I'm so proud. Still don't know what I'm doing, but I'm proud.
Code: Select all
from PIL import Image
import math
pic = "/Library/Application Support/Perceptive Automation/Indigo 7/IndigoWebServer/public/whatev.jpg"
im = Image.open(pic)
width, height = im.size
if width > 1200 or height > 1200:
    width = width*.99
    height = height*.99
im = im.resize((int(math.floor(width)), int(math.floor(height))), Image.ANTIALIAS)
im.save(pic,optimize=True,quality=90)


Improvement suggestions welcome.

SmartThings refugee, so happy to be on Indigo. 10.13.6 on an i5 MBP w/Harmony Hub, Hue, DomoPad, Dynamic URL, Device Extensions, HomeKit Bridge, MatplotLib, Plex, uniFAP, Wunderground, Nanoleaf, LED Simple Effects, Airfoil Pro, Grafana.

Posted on
Mon Apr 30, 2018 10:11 am
jay (support) offline
Site Admin
User avatar
Posts: 16460
Joined: Mar 19, 2008
Location: Austin, Texas

Re: so deep a rabbit hole

Cool! Congrats on your first Python debug!

Another thought: I think that there's an Automator action in macOS for resizing images - you could dump the image into a temporary directory and have a folder action attached to the folder that watches for a new image, does the resize, then moves it to the Indigo directory.

I like your solution (var) better, but if someone runs across this thread and wants a solution that doesn't require installing a Python module/script, that's an alternative (albeit with more moving parts).

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Apr 30, 2018 2:07 pm
Different Computers offline
User avatar
Posts: 1999
Joined: Jan 02, 2016
Location: East Coast

Re: so deep a rabbit hole

That's hilarious. It's exactly what I tried for my second go at fixing this--once I was unable to make ffmpeg do what I wanted.

The problem with this approach was that somehow all the parts of it would not happen automatically in the right order. A shell script sent a curl to the camera and saved the result to a watched folder, which the Automator action should then have reduced. This all worked GREAT when I ran each step manually, but when I tried to make each bit of it trigger the next bit it all fell apart, to the point that the curl would make Automator reduce the file size of the file that was about to be overwritten.

I could not fix this with sleep in the shell script, or pause in Automator. And in any case, that's a kludge because really, I want it all to happen as fast as possible--as long as it happens in the right order!

All of this is a sour reminder of how much acquired knowledge is built in to any how-to instructions. Three or four times I hit barricades because what I needed to do with ffmpeg or curl or import had examples, but they didn't exactly match up with what I needed to do, and I couldn't figure out how to piece it all together. (For example, how to make curl do authentication, or how to make ffmpeg change the quality of a static image.)

Mostly, I'm inordinately and unreasonably proud of realizing I needed to add "import math" up at the top of that script.

SmartThings refugee, so happy to be on Indigo. 10.13.6 on an i5 MBP w/Harmony Hub, Hue, DomoPad, Dynamic URL, Device Extensions, HomeKit Bridge, MatplotLib, Plex, uniFAP, Wunderground, Nanoleaf, LED Simple Effects, Airfoil Pro, Grafana.

Posted on
Mon Apr 30, 2018 2:28 pm
Different Computers offline
User avatar
Posts: 1999
Joined: Jan 02, 2016
Location: East Coast

Re: so deep a rabbit hole

Maybe this can be simplified even more. Right now it's a shell script for getting the image, and python to make it smaller.

is this python equivalent to a curl command?

Code: Select all
import requests
res = requests.get('user:pwd@http://192.168.0.999')

seems like it doesn't say where to put the file that is retrieved. I think I'm supposed to do something like
Code: Select all
with open(image_name, 'webcampic') as outfile: outfile.write(r.content)


Am I on the right track?

SmartThings refugee, so happy to be on Indigo. 10.13.6 on an i5 MBP w/Harmony Hub, Hue, DomoPad, Dynamic URL, Device Extensions, HomeKit Bridge, MatplotLib, Plex, uniFAP, Wunderground, Nanoleaf, LED Simple Effects, Airfoil Pro, Grafana.

Posted on
Mon Apr 30, 2018 3:12 pm
jay (support) offline
Site Admin
User avatar
Posts: 16460
Joined: Mar 19, 2008
Location: Austin, Texas

Re: so deep a rabbit hole

The URL in your post is incorrect - if it is supposed to be:

Code: Select all
http://user@pass:IP/path/to/stuff


Then you'd need to specify the user/pass a bit differently:

Code: Select all
import requests
from requests.auth import HTTPBasicAuth
import StringIO
pic_destination = "/Library/Application Support/Perceptive Automation/Indigo 7/IndigoWebServer/public/whatev.jpg"
reply = requests.get("http://IP/path/to/stuff", auth=HTTPBasicAuth(user, pass))
# now, reply.content is the image, so you can use that to create the Image object:
im = Image.open(StringIO.StringIO(reply.content))
# then this part should work:
width, height = im.size
if width > 1200 or height > 1200:
    width = width*.99
    height = height*.99
im = im.resize((int(math.floor(width)), int(math.floor(height))), Image.ANTIALIAS)
im.save(pic_destination,optimize=True,quality=90)


I think. I just pasted together the parts without actually trying it - that's your next debug lesson!!

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Apr 30, 2018 9:34 pm
RogueProeliator offline
User avatar
Posts: 2370
Joined: Nov 13, 2012
Location: Baton Rouge, LA

Re: so deep a rabbit hole

FWIW, just for posterity, you can also use the "sips" command on OS X to resize an image pretty easily:

Code: Select all
sips -z <imageheight> <imagewidth> <outputfile>
sips -Z <imagemaxsize> <outputfile>
The former resizes to an exact dimension, the latter resamples it so that neither dimension is greater than the max size.

Adam

Posted on
Tue May 01, 2018 7:10 am
Different Computers offline
User avatar
Posts: 1999
Joined: Jan 02, 2016
Location: East Coast

Re: so deep a rabbit hole

@jay I almost think you used double quotes on the URL to test me. :) The other trick was realizing that
Code: Select all
auth=HTTPBasicAuth(user, pass)
needs to be
Code: Select all
auth=HTTPBasicAuth('user', 'pass')


Got it working and it's much, much simpler than any of the other methods I was trying. I've commented out the size stuff, because I don't need it currently.
Code: Select all
from PIL import Image
import math
import requests
from requests.auth import HTTPBasicAuth
import StringIO
pic_destination = "/Library/Application Support/Perceptive Automation/Indigo 7/IndigoWebServer/public/whatev.jpg"
reply = requests.get('http://192.168.0.999/cgi-bin/currentpic.cgi', auth=HTTPBasicAuth('user', 'pass'))
# now, reply.content is the image, so you can use that to create the Image object:
im = Image.open(StringIO.StringIO(reply.content))
# then this part should work:
width, height = im.size
#if width > 1200 or height > 1200:
 #   width = width*.99
  #  height = height*.99
im = im.resize((int(math.floor(width)), int(math.floor(height))), Image.ANTIALIAS)
im.save(pic_destination,optimize=True,quality=80)


And things I think I've learned also:
1. In the above "pic_destination" and "reply" are variables, right? Containers?
2. code snippet examples from the internet rarely include all the "Include PythonThingYouNeed" at the top
3. Just 'cause it compiles doesn't mean it runs.

@RogueProeliator thanks for that tip, I bet it will be handy for other situations, but all I really needed to do with this image was reduce its quality rather than its dimensions.

SmartThings refugee, so happy to be on Indigo. 10.13.6 on an i5 MBP w/Harmony Hub, Hue, DomoPad, Dynamic URL, Device Extensions, HomeKit Bridge, MatplotLib, Plex, uniFAP, Wunderground, Nanoleaf, LED Simple Effects, Airfoil Pro, Grafana.

Posted on
Tue May 01, 2018 9:23 am
jay (support) offline
Site Admin
User avatar
Posts: 16460
Joined: Mar 19, 2008
Location: Austin, Texas

Re: so deep a rabbit hole

Different Computers wrote:
@jay I almost think you used double quotes on the URL to test me. :)


Only if user and auth aren't variables that contain the data (see below for more details)... ;)

Different Computers wrote:
In the above "pic_destination" and "reply" are variables, right?


Those are local variables, yes. You could put the actual username/password in variables rather than directly into the call (thus the quotes you point out) if the script were long enough that you needed to make multiple HTTP requests (then you'd only need to change the variables at the top if the username and/or password changed in the future).

Different Computers wrote:
code snippet examples from the internet rarely include all the "Include PythonThingYouNeed" at the top


It's definitely hit or miss - I always try to make sure any code snippits I post have any imports necessary at the top. Out on the net it's less consistent.

Different Computers wrote:
Just 'cause it compiles doesn't mean it runs.


In the case of an embedded Python script in Indigo, we only do a syntax check when you hit the Compile button (the exact same thing would happen when you hit the Run button but it would also actually execute the script as well). Python scripts are interpreted at runtime so there actually isn't really a "compile" phase as with other languages like C/C++/Objective-C.

Great progress! You'll be a Python programmer before you know it!

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 1 guest