Saturday, July 26, 2008

The problem with tasers

While I don't mind much if people disagree with me, it bothers me a lot when a debate doesn't even include important aspects of a topic.

Tasers are a great example of this. Whenever I read an article about someone being apparently tasered inappropriately, the reporters only ever cover two topics: whether the use was excessive for the situation, and whether tasers are a serious health risk.

The question that seriously needs to be considered is whether tasers are cruel, and what their effects are on the shooter and the target psychologically, not just physiologically.

How does it affect someone when they have power and permission to deny someone the ability to control their body? How does it affect someone when, during a tense situation, they are suddenly and simultaneously shot with needles, suddenly in terrible pain, convulsing, and unable to control their emotions?

Lest you think this is merely a bleeding-heart liberal love-fest, consider it from the libertarian perspective: what is a police officer's role? Is it primarily to halt crimes in progress and bring the presumed-innocent suspects before a court for a speedy and fair trial? Or is it to be Obeyed by inflicting unlimited pain?

In other words, when I deal with a policeman, what is the proposition? Is it that if he feels he has probable cause, he can insist that I sit cuffed in the back of his car and be taken before a judge, using force if necessary? Or is it that I had better obey him, or he will hurt and subjugate me to himself?

The difference between coercion and cruelty becomes *more* important, not less, when Taser International, Inc. claims their product doesn't cause lasting harm!

Think about it: when that jerk cuts you off on the freeway, you sometimes get really ticked! And you probably want two things: you want him to stop doing that, and you want him to suffer (pay a fine, wreck his car, get yelled at).

You probably don't imagine yourself breaking his back with a club and leaving him crippled, or throwing acid in his face. That would be cruel... and unusual, as the constitution puts it. In fact, we don't even do those things to the worst criminals. Why? (That's a serious question -- what's your explanation as to why our system is set up that way?)

But what if you could hurt him from a distance... just a little. Press a button and give him a little jolt, let him know you didn't like what he just did. Nothing permanent. Tempting, isn't it?

If an officer (or if you) beat a man until he's crippled for life, it's quite obvious that serious harm has been done. Perhaps it was necessary, and we're damn well going to find that out. It's *obviously* a very serious matter, and there's no question whether it happened.

Bruises and broken bones from a club are likewise excellent indicators that force has been used. It's *obviously* a big deal to hit someone with a stick; you have to exert yourself to do it, and it's very personal. We intuitively know that hitting somebody is only okay if they're actively trying to hurt you.

But how much does 1.33 watts hurt someone when you push a button on the Taser X26? I love that the specification reads "Delivered into load: 1.33 watts". Don't think of the target as a person like you, think of them as a load for the main capacitors.

You should really read about the Stanford Prison Experiment if you haven't already. Science tells us in cold hard numbers that having immediate power over people changes us, especially when the people are considered criminals.

I don't think we understand how to control those changes yet; we sweep it under the rug and don't think about it. We make awkward jokes about getting raped in prison, and can only think to ask whether tasers kill people.

Are there situations as a cop in which a taser might prevent permanent injury to someone? Probably. Are there situations in which it's the least cruel option? Probably.

But I don't think that our society is spiritually mature enough to handle the temptation that goes along with total power over someone and infliction of pain at the push of a button. For the same reason that it's better to let a guilty man go free than to find an innocent man guilty, I think it's better to leave a permanent injury on a violent man than to have cruelty inflicted on a peaceful one.

Now, just to leave on a truly depressing note, look at what Taser International's latest offering to the military: Remote Area Denial. If controlling someone with the push of a button is psychologically dangerous, how do you think it changes when that someone is only a figure on a screen and the button is on a keyboard? Just like playing a video game.

Husky sucks

Another addition to the list of crappy tool brands. The Husky 26-gallon air compressor was broken out of the box: it can't get to 150psi, so it never turns itself off. I think the switch contacts are burned out now, because today it wouldn't turn on at all. After searching around, it's pretty obvious I'm not the only one who has had this problem.

Monday, July 21, 2008

Linux: libgphoto2 image capture from Canon EOS 1Ds Mark III

Here's the missing "hello, world" program for libgphoto2. I called it canon-capture, since I was working with Canon EOS SLR cameras, but it might actually work with anything supported by libgphoto2. Having to set-config capture=1 might be canon-specific, but the rest of the guts are pretty generic.


// compile with gcc -Wall -o canon-capture -lgphoto2 canon-capture.c
// This code released into the public domain 21 July 2008
//
// This program does the equivalent of:
// gphoto2 --shell
// > set-config capture=1
// > capture-image-and-download
// compile with gcc -Wall -o canon-capture -lgphoto2 canon-capture.c

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <gphoto2/gphoto2.h>

void errordumper(GPLogLevel level, const char *domain, const char *format,
va_list args, void *data) {


vfprintf(stdout, format, args);
fprintf(stdout, "\n");
}

void enable_capture(Camera *canon, GPContext *canoncontext) {
int retval;

printf("Get root config.\n");
CameraWidget *rootconfig; // okay, not really
CameraWidget *actualrootconfig;

retval = gp_camera_get_config(canon, &rootconfig, canoncontext);
actualrootconfig = rootconfig;
printf(" Retval: %d\n", retval);

printf("Get main config.\n");
CameraWidget *child;
retval = gp_widget_get_child_by_name(rootconfig, "main", &child);
printf(" Retval: %d\n", retval);

printf("Get settings config.\n");
rootconfig = child;
retval = gp_widget_get_child_by_name(rootconfig, "settings", &child);
printf(" Retval: %d\n", retval);

printf("Get capture config.\n");
rootconfig = child;
retval = gp_widget_get_child_by_name(rootconfig, "capture", &child);
printf(" Retval: %d\n", retval);


CameraWidget *capture = child;

const char *widgetinfo;
gp_widget_get_name(capture, &widgetinfo);
printf("config name: %s\n", widgetinfo );

const char *widgetlabel;
gp_widget_get_label(capture, &widgetlabel);
printf("config label: %s\n", widgetlabel);

int widgetid;
gp_widget_get_id(capture, &widgetid);
printf("config id: %d\n", widgetid);

CameraWidgetType widgettype;
gp_widget_get_type(capture, &widgettype);
printf("config type: %d == %d \n", widgettype, GP_WIDGET_TOGGLE);


printf("Set value.\n");

int one=1;
retval = gp_widget_set_value(capture, &one);
printf(" Retval: %d\n", retval);

printf("Enabling capture.\n");
retval = gp_camera_set_config(canon, actualrootconfig, canoncontext);
printf(" Retval: %d\n", retval);
}

/* This seems to have no effect on where images go
void set_capturetarget(Camera *canon, GPContext *canoncontext) {
int retval;
printf("Get root config.\n");
CameraWidget *rootconfig; // okay, not really
CameraWidget *actualrootconfig;

retval = gp_camera_get_config(canon, &rootconfig, canoncontext);
actualrootconfig = rootconfig;
printf(" Retval: %d\n", retval);

printf("Get main config.\n");
CameraWidget *child;
retval = gp_widget_get_child_by_name(rootconfig, "main", &child);
printf(" Retval: %d\n", retval);

printf("Get settings config.\n");
rootconfig = child;
retval = gp_widget_get_child_by_name(rootconfig, "settings", &child);
printf(" Retval: %d\n", retval);

printf("Get capturetarget.\n");
rootconfig = child;
retval = gp_widget_get_child_by_name(rootconfig, "capturetarget", &child);
printf(" Retval: %d\n", retval);


CameraWidget *capture = child;

const char *widgetinfo;
gp_widget_get_name(capture, &widgetinfo);
printf("config name: %s\n", widgetinfo );

const char *widgetlabel;
gp_widget_get_label(capture, &widgetlabel);
printf("config label: %s\n", widgetlabel);

int widgetid;
gp_widget_get_id(capture, &widgetid);
printf("config id: %d\n", widgetid);

CameraWidgetType widgettype;
gp_widget_get_type(capture, &widgettype);
printf("config type: %d == %d \n", widgettype, GP_WIDGET_RADIO);


printf("Set value.\n");

// capture to ram should be 0, although I think the filename also plays into
// it
int one=1;
retval = gp_widget_set_value(capture, &one);
printf(" Retval: %d\n", retval);

printf("Enabling capture to CF.\n");
retval = gp_camera_set_config(canon, actualrootconfig, canoncontext);
printf(" Retval: %d\n", retval);
}
*/

void capture_to_file(Camera *canon, GPContext *canoncontext, char *fn) {
int retval;

printf("Capturing.\n");
CameraFilePath camera_file_path;

// NOP: This gets overridden in the library to /capt0000.jpg
strcpy(camera_file_path.folder, "/");
strcpy(camera_file_path.name, "foo.jpg");

retval = gp_camera_capture(canon, GP_CAPTURE_IMAGE, &camera_file_path, canoncontext);
printf(" Retval: %d\n", retval);

printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);

CameraFile *canonfile;

retval = gp_file_new(&canonfile);
printf(" Retval: %d\n", retval);
retval = gp_camera_file_get(canon, camera_file_path.folder, camera_file_path.name,
GP_FILE_TYPE_NORMAL, canonfile, canoncontext);
printf(" Retval: %d\n", retval);

const char *filedata;
unsigned long int filesize;

retval = gp_file_get_data_and_size(canonfile, &filedata, &filesize);
printf(" Retval: %d\n", retval);

int fd = open(fn, O_CREAT | O_WRONLY, 0644);
write(fd, filedata, filesize);
close(fd);

printf("Deleting.\n");
retval = gp_camera_file_delete(canon, camera_file_path.folder, camera_file_path.name,
canoncontext);
printf(" Retval: %d\n", retval);

gp_file_free(canonfile);
}



main() {
Camera *canon;

gp_log_add_func(GP_LOG_ERROR, errordumper, NULL);
GPContext *canoncontext = gp_context_new();
gp_camera_new(&canon);

// When I set GP_LOG_DEBUG instead of GP_LOG_ERROR above, I noticed that the
// init function seems to traverse the entire filesystem on the camera. This
// is partly why it takes so long.
printf("Camera init. Takes about 10 seconds.\n");
int retval = gp_camera_init(canon, canoncontext);
printf(" Retval: %d\n", retval);

enable_capture(canon, canoncontext);
//set_capturetarget(canon, canoncontext);

int i;
capture_to_file(canon, canoncontext, "foo.jpg");

gp_camera_exit(canon, canoncontext);
}

Friday, July 18, 2008

Linux: Using the Canon EOS IDs Mark III with gphoto2

I've got the Canon EOS 1Ds Mark III capturing and downloading images via its built-in USB port (not via the external WFT-E2A ethernet/usb dongle) at just under 1fps (105 seconds for 100frames). The 21MP JPG images are 5.2MB each.

If I turn the JPG quality all the way to its lowest setting, the images are 1.5MB each and I get 100f/75s = 1.3fps.



$ gphoto2/gphoto2 --shell
gphoto2: {gphoto2: {.../gphoto2-2.4.2} /> } /> list-config
/main/settings/capturetarget
/main/settings/capture
/main/capturesettings/focuslock
gphoto2: {gphoto2: {.../gphoto2-2.4.2} /> } /> set-config capture=1
gphoto2: {gphoto2: {.../gphoto2-2.4.2} /> } /> capture-image-and-download
New file is in location /capt0000.jpg on the camera
Saving file as capt0000.jpg
Deleting file /capt0000.jpg on the camera
Deleting 'capt0000.jpg' from folder '/'...
gphoto2: {gphoto2: {.../gphoto2-2.4.2} /> } />

I can also extract metadata for images on the CF card:

gphoto2: {gphoto2: {.../gphoto2-2.4.2} /store_00010001/DCIM/880CANON> }
/store_00010001/DCIM/880CANON> show-info 0N8H8866.JPG
Information on file '0N8H8866.JPG' (folder '/store_00010001/DCIM/880CANON'):
File:
Name: '0N8H8866.JPG'
Mime type: 'image/jpeg'
Size: 3076382 byte(s)
Width: 5616 pixel(s)
Height: 3744 pixel(s)
Time: Thu Jul 17 09:54:58 2008
Thumbnail:
Mime type: 'image/jpeg'
Size: 3132 byte(s)
Width: 160 pixel(s)
Height: 120 pixel(s)
Audio data:
None available.

And once I set-config capture=1, I can see more config options:

gphoto2: {gphoto2: {.../gphoto2-2.4.2} /store_00010001/DCIM/880CANON> }
/store_00010001/DCIM/880CANON> list-config
/main/settings/eos-time
/main/settings/eos-synctime
/main/settings/capturetarget
/main/settings/capture
/main/imgsettings/eos-iso
/main/imgsettings/eos-whitebalance
/main/capturesettings/eos-exposurecompensation
/main/capturesettings/picturestyle
/main/capturesettings/eos-meteringmode
/main/capturesettings/focuslock
gphoto2: {gphoto2: {.../gphoto2-2.4.2} /store_00010001/DCIM/880CANON> }
/store_00010001/DCIM/880CANON>

The gphoto2 maintainer accepted my patch to libgphoto2, so the Mark III will eventually work by default.


I can also download 160x120 image thumbnails from CF:

gphoto2: {gphoto2: {...le/jholt/gphoto2-2.4.2} /store_00010001/DCIM/880CANON> } /store_00010001/DCIM/880CANON> get-thumbnail 0N8H8846.JPG
Downloading '0N8H8846.JPG' from folder '/store_00010001/DCIM/880CANON'...
Saving file as thumb_0N8H8846.jpg