text:
/*
 * linux/drivers/char/raw.c
 *
 * Front-end raw character devices.  These can be bound to any block
 * devices to provide genuine Unix raw character device semantics.
 *
 * We reserve minor number 0 for a control interface.  ioctl()s on this
 * device are used to bind the other minor numbers to block devices.
 */

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/raw.h>
#include <linux/capability.h>
#include <linux/uio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mutex.h>

#include <asm/uaccess.h>

struct raw_device_data {
	struct block_device *binding;
	int inuse;
};

static struct class *raw_class;
static struct raw_device_data raw_devices[MAX_RAW_MINORS];
static DEFINE_MUTEX(raw_mutex);
static const struct file_operations raw_ctl_fops; /* forward declaration */

/*
 * Open/close code for raw IO.
 *
 * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
 * point at the blockdev's address_space and set the file handle to use
 * O_DIRECT.
 *
 * Set the device's soft blocksize to the minimum possible.  This gives the
 * finest possible alignment and has no adverse impact on performance.
 */
static int raw_open(struct inode *inode, struct file *filp)
{
	const int minor = iminor(inode);
	struct block_device *bdev;
	int err;

	if (minor == 0) {	/* It is the control device */
		filp->f_op = &raw_ctl_fops;
		return 0;
	}

	mutex_lock(&raw_mutex);

	/*
	 * All we need to do on open is check that the device is bound.
	 */
	bdev = raw_devices[minor].binding;
	err = -ENODEV;
	if (!bdev)
		goto out;
	igrab(bdev->bd_inode);
	err = blkdev_get(bdev, filp->f_mode, 0);
	if (err)
		goto out;
	err = bd_claim(bdev, raw_open);
	if (err)
		goto out1;
	err = set_blocksize(bdev, bdev_hardsect_size(bdev));
	if (err)
		goto out2;
	filp->f_flags |= O_DIRECT;
	filp->f_mapping = bdev->bd_inode->i_mapping;
	if (++raw_devices[minor].inuse == 1)
		filp->f_path.dentry->d_inode->i_mapping =
			bdev->bd_inode->i_mapping;
	filp->private_data = bdev;
	mutex_unlock(&raw_mutex);
	return 0;

out2:
	bd_release(bdev);
out1:
	blkdev_put(bdev);
out:
	mutex_unlock(&raw_mutex);
	return err;
}

/*
 * When the final fd which refers to this character-special node is closed, we
 * make its ->mapping point back at its own i_data.
 */
static int raw_release(struct inode *inode, struct file *filp)
{
	const int minor= iminor(inode);
	struct block_device *bdev;

	mutex_lock(&raw_mutex);
	bdev = raw_devices[minor].binding;
	if (--raw_devices[minor].inuse == 0) {
		/* Here  inode->i_mapping == bdev->bd_inode->i_mapping  */
		inode->i_mapping = &inode->i_data;
		inode->i_mapping->backing_dev_info = &default_backing_dev_info;
	}
	mutex_unlock(&raw_mutex);

	bd_release(bdev);
	blkdev_put(bdev);
	return 0;
}

/*
 * Forward ioctls to the underlying block device.
 */
static int
raw_ioctl(struct inode *inode, struct file *filp,
		  unsigned int command, unsigned long arg)
{
	struct block_device *bdev = filp->private_data;

	return blkdev_ioctl(bdev->bd_inode, NULL, command, arg);
}

static void bind_device(struct raw_config_request *rq)
{
	device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor));
	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor),
		      "raw%d", rq->raw_minor);
}

/*
 * Deal with ioctls against the raw-device control interface, to bind
 * and unbind other raw devices.
 */
static int raw_ctl_ioctl(struct inode *inode, struct file *filp,
			unsigned int command, unsigned long arg)
{
	struct raw_config_request rq;
	struct raw_device_data *rawdev;
	int err = 0;

	switch (command) {
	case RAW_SETBIND:
	case RAW_GETBIND:

		/* First, find out which raw minor we want */

		if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) {
			err = -EFAULT;
			goto out;
		}

		if (rq.raw_minor <= 0 || rq.raw_minor >= MAX_RAW_MINORS) {
			err = -EINVAL;
			goto out;
		}
		rawdev = &raw_devices[rq.raw_minor];

		if (command == RAW_SETBIND) {
			dev_t dev;

			/*
			 * This is like making block devices, so demand the
			 * same capability
			 */
			if (!capable(CAP_SYS_ADMIN)) {
				err = -EPERM;
				goto out;
			}

			/*
			 * For now, we don't need to check that the underlying
			 * block device is present or not: we can do that when
			 * the raw device is opened.  Just check that the
			 * major/minor numbers make sense.
			 */

			dev = MKDEV(rq.block_major, rq.block_minor);
			if ((rq.block_major == 0 && rq.block_minor != 0) ||
					MAJOR(dev) != rq.block_major ||
					MINOR(dev) != rq.block_minor) {
				err = -EINVAL;
				goto out;
			}

			mutex_lock(&raw_mutex);
			if (rawdev->inuse) {
				mutex_unlock(&raw_mutex);
				err = -EBUSY;
				goto out;
			}
			if (rawdev->binding) {
				bdput(rawdev->binding);
				module_put(THIS_MODULE);
			}
			if (rq.block_major == 0 && rq.block_minor == 0) {
				/* unbind */
				rawdev->binding = NULL;
				device_destroy(raw_class,
						MKDEV(RAW_MAJOR, rq.raw_minor));
			} else {
				rawdev->binding = bdget(dev);
				if (rawdev->binding == NULL)
					err = -ENOMEM;
				else {
					__module_get(THIS_MODULE);
					bind_device(&rq);
				}
			}
			mutex_unlock(&raw_mutex);
		} else {
			struct block_device *bdev;

			mutex_lock(&raw_mutex);
			bdev = rawdev->binding;
			if (bdev) {
				rq.block_major = MAJOR(bdev->bd_dev);
				rq.block_minor = MINOR(bdev->bd_dev);
			} else {
				rq.block_major = rq.block_minor = 0;
			}
			mutex_unlock(&raw_mutex);
			if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) {
				err = -EFAULT;
				goto out;
			}
		}
		break;
	default:
		err = -EINVAL;
		break;
	}
out:
	return err;
}

static const struct file_operations raw_fops = {
	.read	=	do_sync_read,
	.aio_read = 	generic_file_aio_read,
	.write	=	do_sync_write,
	.aio_write = 	generic_file_aio_write_nolock,
	.open	=	raw_open,
	.release=	raw_release,
	.ioctl	=	raw_ioctl,
	.owner	=	THIS_MODULE,
};

static const struct file_operations raw_ctl_fops = {
	.ioctl	=	raw_ctl_ioctl,
	.open	=	raw_open,
	.owner	=	THIS_MODULE,
};

static struct cdev raw_cdev = {
	.kobj	=	{.name = "raw", },
	.owner	=	THIS_MODULE,
};

static int __init raw_init(void)
{
	dev_t dev = MKDEV(RAW_MAJOR, 0);
	int ret;

	ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw");
	if (ret)
		goto error;

	cdev_init(&raw_cdev, &raw_fops);
	ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS);
	if (ret) {
		kobject_put(&raw_cdev.kobj);
		goto error_region;
	}

	raw_class = class_create(THIS_MODULE, "raw");
	if (IS_ERR(raw_class)) {
		printk(KERN_ERR "Error creating raw class.\n");
		cdev_del(&raw_cdev);
		ret = PTR_ERR(raw_class);
		goto error_region;
	}
	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), "rawctl");

	return 0;

error_region:
	unregister_chrdev_region(dev, MAX_RAW_MINORS);
error:
	return ret;
}

static void __exit raw_exit(void)
{
	device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
	class_destroy(raw_class);
	cdev_del(&raw_cdev);
	unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS);
}

module_init(raw_init);
module_exit(raw_exit);
MODULE_LICENSE("GPL");

redpajama_set_name: RedPajamaGithub


This adorable hooded Bunny Onesie is a perfect gift and outfit. It's soft, comfortable and stylish, keeping your little one cozy and looking ridiciulously cute!
3D Bunny ears and detachable fluffy bunny tail. Very cute design. Your baby will love it.
Snap closure for easy diaper changing.
This cute bunny onesie is great for Easter, Thanksgiving, Party, Playwear, Casual Daily wear, Photo shoot etc.
Hand wash or machine wash cold or warm. Tumble dry low or hang to dry.


they are alsobuilt on general-purpose operating systems such as Unix, or MicrosoftWindows, linux, proxy server download online which can make them vulnerable to OS-related attacks.
once you have that set up, you follow the process in the article to use the SOCKS protocol to tell your browser to use that proxy location proxy server download online to download all information from.
251 77 Bew. 13. LastPass (64 Bit) 4.21 Deutsch Spezial-Version für 64 Bit-Rechner: Mit LastPass sind Ihre vielen Online-Passwörter sicher verstaut und jederzeit von. 247 192 Bew. 14. CHIP Download Adventskalender 2018 für Chrome Deutsch Vom 1. Dezember bis Weihnachten bietet Ihnen CHIP Online im.
India: Proxy server download online!
iPVanish boasts high standards of user support, you can contact support via email and live chat right on the site (on work days)). Support As a paid service, proxy server download online your work on the Web leaves no traces behind, there are very detailed FAQs and guidelines for all basic operating systems. While NAT technology provides an extra level of security. And rightly so.
assistenza clienti tramite Chat. O rete privata virtuale, accesso a tutto il catalogo Netflix 30 giorni di prova Estensione unblock netflix australia 2017 per Chrome Pi proxy server download online di 30 paesi disponibili 8.4 Vai al Sito su Safervpn.com Una VPN,.
The Pirate Bay website is the best source where you can download torrents. If the Pirate Bay is blocked by your Internet Service Provider or youre unable to access it for any reason, simply access one of the Pirate Bay Proxy sites.
The VPN works fine on my PC over the same router. I am in the UAE where they block many sites. I have tried to log on to the witopia server using both the ip address and the server name with the same results.
the governments plans to weaken the means to protect privacy and freedom of speech is posing great proxy server download online concerns to companies that are collecting consumer data. Because of massive surveillance practices in the UK, many software companies are now leaving the country.2. Una VPN la soluzione ideale. Privacy e anonimato Quando necessaria una completa privacy, a differenza di applicazioni o servizi proxy che setup vpn server on home pc nascondono solo lindirizzo IP del dispositivo utilizzato,let "Automatic Profile Switching" do the rest. One of its greatest features, introduced in version 3, little Snitch Little Snitch is a firewall that allows you to control connections from proxy server download online your computer to the internet.
a VPN connection provides high speed internet services, which proxy server download online was restricted before. Uninterrupted and very secure servers which are very much reliable. You will be able to complete all your work or get access to any videos you want to,click the article number below to view the article in the. Enable TCP/IP forwarding. Click to select the Enable IP routing check box (if it is not already selected)). Click OK. For additional information about how to enable IP forwarding,jurikSoft Proxy proxy server download online Provider can.
janus vpn Streaming VPN download, janus vpn the secure VPN (High-Speed VPN)) how to proxy server download online janus vpn for Related Articles.friendly name. .., enterprise CA OK."https www. W3.org/TR/xhtml1/DTD/xhtml1-strict.
my VPN setup scripts can be used on any dedicated server or KVM/Xen-based virtual private server (VPS)). Besides, which proxy server download online was forked from FreeS/WAN 2.04. Openswan, it had been forked from.


Can my non-EU wife be deported from France for a visa refusal?
I am a UK national. Can my non-EU wife be deported when we both live in France and I am a worker paying taxes, holding a French driving licence, a Carte Vital, self employed and also have a French contract job for the next 3 years?
Yes, but only under very limited circumstances that would not be applicable to most people.
Under directive 2004/38/EC, your wife can be deported only if she is a threat to public safety, health, or policy. This is particularly a consequence of Articles 3 (section 1) and 7 (sections 1 and 2). Provisions concerning expulsion are found in Chapter VI.
Another possibility that could lead to her expulsion is a finding by the French government that your marriage is not genuine, or was undertaken as a marriage of convenience (Article 35).
Until March 29th, EU freedom of movement allows you as a UK citizen to move to any EU country except the UK with your wife, and to many EU countries with your husband (assuming you are male). There are some formalities to meet, which is not difficult or expensive.
Nobody knows what the situation will be like after March 29th. It is reasonably likely that she will be Ok until the end of 2020; after that you'll have to see. Welcome to Brexit.
And the UK has deported women from the UK that were married to a UK citizen, and mother to a British child, all living in Britain.
Not the answer you're looking for? Browse other questions tagged european-union constitutional-law or ask your own question.
Micro businesses in EU selling electronic goods, how to comply with EU VAT regulations?
What are the limits of "implied consent" when obtaining a driver's license?
Is it illegal to broadcast/stream music for non-commercial purposes?


text:
HomeVideo GamesSTEAM (Page 17)
PAX EAST 2017 | "Kingsway"
Organizing a medieval adventure is never easy, so you would think that going on one from the comfort of your home would be a lot simpler. In [adult swim games]'s Kingsway, you quickly find out that any magical adventure is never a
PAX EAST 2017 | "ToeJam & Earl: Back in the Groove"
Let me be blunt: as a kid, I sucked at ToeJam & Earl. The Sega Genesis classic was a game that drove my patience, to the point where I had my first experience of rage-quitting a video game. Its groovy graphics
PAX EAST 2017 | "Rick and Morty VR: Virtual Rick-ality"
Rick and Morty are a godsend for this entertainment industry. Each episode of the [adult swim] cartoon is crammed with loads of humor, often climaxing with some incredibly dark and poignant realizations. With co-creator/star Justin Roiland being interested in virtual reality
Northgard lets you live like a Viking
If you are a fan of Norse mythology and anything Viking, then you need to check out Northgard, a new strategy game from acclaimed independent game developer Shiro Games.
PAX EAST 2017 | "Party Hard 2"
2015's Party Hard was a stealthy, original killer of a game that played true to its retro style and 80s attitude. That's why it comes as no surprise that a sequel is coming hard and fast later this year. At
PAX EAST 2017 | "Mr. Shifty"
If there's one game that I played at PAX East that just about everyone should keep their eyes on, it's Mr. Shifty. Of course, with his style of speed and combat, keeping an eye on him is easier said than
PAX EAST 2017 | "Streets of Rogue"
TinyBuild's Streets of Rogue bills itself as "the world's only RPG roguelike action stealth shooter brawler co-op megagame." Yes, that is quite the mouthful, but those who have gone & played it like I have will know that it contains every
PAX EAST 2017 | "Dead by Daylight"
Since last June, Behavior Interactive's Dead by Daylight has been offering frights, gore, and 80's-styled chills on Steam. This year, the 4 vs 1 multiplayer is sharpening its machete to take on the console realm. Those hankering to either be
PAX EAST 2017 | "RAID: World War II"
Imagine if the Allies in World War II had a group like The A-Team backing them behind-the-scenes. We're talking a group of smart-aleck soldiers who go to extreme lengths to get a plan successful accomplished, all the while punching a few
PAX EAST 2017 | "YIIK: A Post-Modern RPG"
The more I see of Ackk Studios' YIIK: A Post-Modern RPG, the more I crave to dive into the world that's been greatly influenced by Earthbound, Final Fantasy, and I'm pretty sure a couple Pixies records. With the game finally near-completion, I
Previous 1 … 16 17 18 … 36 Next

redpajama_set_name: RedPajamaCommonCrawl


text:
import sys
import os
import cv2
import numpy as np
import time
import StringIO
from threading import Lock

from os import system   # for say command on OSX
from thread import start_new_thread # multithreading for say command

from misc import WithTimer
from numpy_cache import FIFOLimitedArrayCache
from app_base import BaseApp
from core import CodependentThread
from image_misc import norm01, norm01c, norm0255, tile_images_normalize, ensure_float01, tile_images_make_tiles, ensure_uint255_and_resize_to_fit, caffe_load_image, get_tiles_height_width
from image_misc import FormattedString, cv2_typeset_text, to_255

def net_preproc_forward(net, img):
    assert img.shape == (227,227,3), 'img is wrong size'
    #resized = caffe.io.resize_image(img, net.image_dims)   # e.g. (227, 227, 3)
    data_blob = net.transformer.preprocess('data', img)                # e.g. (3, 227, 227), mean subtracted and scaled to [0,255]
    data_blob = data_blob[np.newaxis,:,:,:]                   # e.g. (1, 3, 227, 227)
    output = net.forward(data=data_blob)
    return output

# DEPRECATED
#
# class SmartPadder(object):
#     def __init__(self):
#         self.calls = 0
#
#     def pad_function(self, vector, iaxis_pad_width, iaxis, kwargs):
#         if self.calls > 100:
#             if iaxis_pad_width[0] > 0:
#                 vector[:iaxis_pad_width[0]] = np.random.uniform(0,1,iaxis_pad_width[0])
#             if iaxis_pad_width[1] > 0:
#                 vector[-iaxis_pad_width[1]:] = np.random.uniform(0,1,iaxis_pad_width[1])
#         self.calls += 1
#         return vector
#
#
#     #def get_pad_function(self):
#     #    return lambda args : self.pad_function(*args)
#
# def jy_pad_fn(vector, iaxis_pad_width, iaxis, kwargs):
#     '''
#     Called like this:
#     jy_pad_fn: (100,) (0, 4) 0
#     jy_pad_fn: (29,) (1, 1) 1
#     jy_pad_fn: (29,) (1, 1) 2
#     jy_pad_fn: (3,) (0, 0) 3
#     '''
#     #if np.random.uniform(0,1) < .01:
#     #    print 'jy_pad_fn:', vector.shape, iaxis_pad_width, iaxis
#     #vector[:] = np.random.uniform(0,1,(vector.shape))
#     #vector[:] = np.random.uniform(0,1,(vector.shape))
#
#     if iaxis_pad_width[0] > 0:
#         vector[:iaxis_pad_width[0]] = np.random.uniform(0,1,iaxis_pad_width[0])
#     if iaxis_pad_width[1] > 0:
#         vector[-iaxis_pad_width[1]:] = np.random.uniform(0,1,iaxis_pad_width[1])
#     return vector
#     #assert False

layer_renames = {
    'pool1': 'p1',
    'norm1': 'n1',
    'pool2': 'p2',
    'norm2': 'n2',
    'pool5': 'p5',
    }

def get_pp_layer_name(layer_name):
    return layer_renames.get(layer_name, layer_name)

def read_label_file(filename):
    ret = []
    with open(filename, 'r') as ff:
        for line in ff:
            label = line.strip()
            if len(label) > 0:
                ret.append(label)
    return ret

class CaffeProcThread(CodependentThread):
    '''Runs Caffe in separate thread.'''

    def __init__(self, net, state, loop_sleep, pause_after_keys, heartbeat_required):
        CodependentThread.__init__(self, heartbeat_required)
        self.daemon = True
        self.net = net
        self.input_dims = self.net.blobs['data'].data.shape[2:4]    # e.g. (227,227)
        self.state = state
        self.frames_processed_fwd = 0
        self.frames_processed_back = 0
        self.loop_sleep = loop_sleep
        self.pause_after_keys = pause_after_keys
        self.debug_level = 0

    def run(self):
        print 'CaffeProcThread.run called'
        frame = None

        while not self.is_timed_out():
            with self.state.lock:
                if self.state.quit:
                    #print 'CaffeProcThread.run: quit is True'
                    #print self.state.quit
                    break

                #print 'CaffeProcThread.run: caffe_net_state is:', self.state.caffe_net_state

                #print 'CaffeProcThread.run loop: next_frame: %s, caffe_net_state: %s, back_enabled: %s' % (
                #    'None' if self.state.next_frame is None else 'Avail',
                #    self.state.caffe_net_state,
                #    self.state.back_enabled)

                frame = None
                run_fwd = False
                run_back = False
                if self.state.caffe_net_state == 'free' and time.time() - self.state.last_key_at > self.pause_after_keys:
                    frame = self.state.next_frame
                    self.state.next_frame = None
                    back_enabled = self.state.back_enabled
                    back_mode = self.state.back_mode
                    back_stale = self.state.back_stale
                    #state_layer = self.state.layer
                    #selected_unit = self.state.selected_unit
                    backprop_layer = self.state.backprop_layer
                    backprop_unit = self.state.backprop_unit

                    # Forward should be run for every new frame
                    run_fwd = (frame is not None)
                    # Backward should be run if back_enabled and (there was a new frame OR back is stale (new backprop layer/unit selected))
                    run_back = (back_enabled and (run_fwd or back_stale))
                    self.state.caffe_net_state = 'proc' if (run_fwd or run_back) else 'free'

            #print 'run_fwd,run_back =', run_fwd, run_back

            if run_fwd:
                #print 'TIMING:, processing frame'
                self.frames_processed_fwd += 1
                im_small = cv2.resize(frame, self.input_dims)
                with WithTimer('CaffeProcThread:forward', quiet = self.debug_level < 1):
                    net_preproc_forward(self.net, im_small)

            if run_back:
                diffs = self.net.blobs[backprop_layer].diff * 0
                diffs[0][backprop_unit] = self.net.blobs[backprop_layer].data[0,backprop_unit]

                assert back_mode in ('grad', 'deconv')
                if back_mode == 'grad':
                    with WithTimer('CaffeProcThread:backward', quiet = self.debug_level < 1):
                        #print '**** Doing backprop with %s diffs in [%s,%s]' % (backprop_layer, diffs.min(), diffs.max())
                        self.net.backward_from_layer(backprop_layer, diffs, zero_higher = True)
                else:
                    with WithTimer('CaffeProcThread:deconv', quiet = self.debug_level < 1):
                        #print '**** Doing deconv with %s diffs in [%s,%s]' % (backprop_layer, diffs.min(), diffs.max())
                        self.net.deconv_from_layer(backprop_layer, diffs, zero_higher = True)

                with self.state.lock:
                    self.state.back_stale = False

            if run_fwd or run_back:
                with self.state.lock:
                    self.state.caffe_net_state = 'free'
                    self.state.drawing_stale = True
            else:
                time.sleep(self.loop_sleep)

        print 'CaffeProcThread.run: finished'
        print 'CaffeProcThread.run: processed %d frames fwd, %d frames back' % (self.frames_processed_fwd, self.frames_processed_back)

class JPGVisLoadingThread(CodependentThread):
    '''Loads JPGs necessary for caffevis_jpgvis pane in separate
    thread and inserts them into the cache.
    '''

    def __init__(self, settings, state, cache, loop_sleep, heartbeat_required):
        CodependentThread.__init__(self, heartbeat_required)
        self.daemon = True
        self.settings = settings
        self.state = state
        self.cache = cache
        self.loop_sleep = loop_sleep
        self.debug_level = 0

    def run(self):
        print 'JPGVisLoadingThread.run called'

        while not self.is_timed_out():
            with self.state.lock:
                if self.state.quit:
                    break

                #print 'JPGVisLoadingThread.run: caffe_net_state is:', self.state.caffe_net_state
                #print 'JPGVisLoadingThread.run loop: next_frame: %s, caffe_net_state: %s, back_enabled: %s' % (
                #    'None' if self.state.next_frame is None else 'Avail',
                #    self.state.caffe_net_state,
                #    self.state.back_enabled)

                jpgvis_to_load_key = self.state.jpgvis_to_load_key

            if jpgvis_to_load_key is None:
                time.sleep(self.loop_sleep)
                continue

            state_layer, state_selected_unit, data_shape = jpgvis_to_load_key

            # Load three images:
            images = [None] * 3

            # Reize each component images only using one direction as
            # a constraint. This is straightfowrad but could be very
            # wasteful (making an image much larger then much smaller)
            # if the proportions of the stacked image are very
            # different from the proportions of the data pane.
            #resize_shape = (None, data_shape[1]) if self.settings.caffevis_jpgvis_stack_vert else (data_shape[0], None)
            # As a heuristic, instead just assume the three images are of the same shape.
            if self.settings.caffevis_jpgvis_stack_vert:
                resize_shape = (data_shape[0]/3, data_shape[1])
            else:
                resize_shape = (data_shape[0], data_shape[1]/3)

            # 0. e.g. regularized_opt/conv1/conv1_0037_montage.jpg
            jpg_path = os.path.join(self.settings.caffevis_unit_jpg_dir,
                                    'regularized_opt',
                                    state_layer,
                                    '%s_%04d_montage.jpg' % (state_layer, state_selected_unit))
            try:
                img = caffe_load_image(jpg_path, color = True)
                img_corner = crop_to_corner(img, 2)
                images[0] = ensure_uint255_and_resize_to_fit(img_corner, resize_shape)
            except IOError:
                pass

            # 1. e.g. max_im/conv1/conv1_0037.jpg
            jpg_path = os.path.join(self.settings.caffevis_unit_jpg_dir,
                                    'max_im',
                                    state_layer,
                                    '%s_%04d.jpg' % (state_layer, state_selected_unit))
            try:
                img = caffe_load_image(jpg_path, color = True)
                images[1] = ensure_uint255_and_resize_to_fit(img, resize_shape)
            except IOError:
                pass

            # 2. e.g. max_deconv/conv1/conv1_0037.jpg
            try:
                jpg_path = os.path.join(self.settings.caffevis_unit_jpg_dir,
                                        'max_deconv',
                                        state_layer,
                                        '%s_%04d.jpg' % (state_layer, state_selected_unit))
                img = caffe_load_image(jpg_path, color = True)
                images[2] = ensure_uint255_and_resize_to_fit(img, resize_shape)
            except IOError:
                pass

            # Prune images that were not found:
            images = [im for im in images if im is not None]

            # Stack together
            if len(images) > 0:
                #print 'Stacking:', [im.shape for im in images]
                stack_axis = 0 if self.settings.caffevis_jpgvis_stack_vert else 1
                img_stacked = np.concatenate(images, axis = stack_axis)
                #print 'Stacked:', img_stacked.shape
                img_resize = ensure_uint255_and_resize_to_fit(img_stacked, data_shape)
                #print 'Resized:', img_resize.shape
            else:
                img_resize = np.zeros(shape=(0,))   # Sentinal value when image is not found.

            self.cache.set(jpgvis_to_load_key, img_resize)

            with self.state.lock:
                self.state.jpgvis_to_load_key = None
                self.state.drawing_stale = True

        print 'JPGVisLoadingThread.run: finished'

class CaffeVisAppState(object):
    '''State of CaffeVis app.'''

    def __init__(self, net, settings, bindings):
        self.lock = Lock()  # State is accessed in multiple threads
        self.settings = settings
        self.bindings = bindings
        self._layers = net.blobs.keys()
        self._layers = self._layers[1:]  # chop off data layer
        self.layer_boost_indiv_choices = self.settings.caffevis_boost_indiv_choices   # 0-1, 0 is noop
        self.layer_boost_gamma_choices = self.settings.caffevis_boost_gamma_choices   # 0-inf, 1 is noop
        self.caffe_net_state = 'free'     # 'free', 'proc', or 'draw'
        # Which layer and unit (or channel) to use for backprop
        self.tiles_height_width = (1,1)   # Before any update
        self.tiles_number = 1
        self.extra_msg = ''
        self.back_stale = True       # back becomes stale whenever the last back diffs were not computed using the current backprop unit and method (bprop or deconv)
        self.next_frame = None
        self.jpgvis_to_load_key = None
        self.last_key_at = 0
        self.quit = False

        self._reset_user_state()

    def _reset_user_state(self):
        self.layer_idx = 0
        self.layer = self._layers[0]
        self.layer_boost_indiv_idx = self.settings.caffevis_boost_indiv_default_idx
        self.layer_boost_indiv = self.layer_boost_indiv_choices[self.layer_boost_indiv_idx]
        self.layer_boost_gamma_idx = self.settings.caffevis_boost_gamma_default_idx
        self.layer_boost_gamma = self.layer_boost_gamma_choices[self.layer_boost_gamma_idx]
        self.cursor_area = 'top'   # 'top' or 'bottom'
        self.selected_unit = 0
        self.backprop_layer = self.layer
        self.backprop_unit = self.selected_unit
        self.backprop_selection_frozen = False    # If false, backprop unit tracks selected unit
        self.back_enabled = False
        self.back_mode = 'grad'      # 'grad' or 'deconv'
        self.back_filt_mode = 'raw'  # 'raw', 'gray', 'norm', 'normblur'
        self.pattern_mode = False    # Whether or not to show desired patterns instead of activations in layers pane
        self.layers_pane_zoom_mode = 0       # 0: off, 1: zoom selected (and show pref in small pane), 2: zoom backprop
        self.layers_show_back = False   # False: show forward activations. True: show backward diffs
        self.show_label_predictions = self.settings.caffevis_init_show_label_predictions
        self.show_unit_jpgs = self.settings.caffevis_init_show_unit_jpgs
        self.drawing_stale = True
        kh,_ = self.bindings.get_key_help('help_mode')
        self.extra_msg = '%s for help' % kh[0]

        # ANH: Start the program with the 1st filter being selected
        self.move_selection('down')

    def handle_key(self, key):
        #print 'Ignoring key:', key
        if key == -1:
            return key

        with self.lock:
            key_handled = True
            self.last_key_at = time.time()
            tag = self.bindings.get_tag(key)
            if tag == 'reset_state':
                self._reset_user_state()
            elif tag == 'sel_layer_left':
                #hh,ww = self.tiles_height_width
                #self.selected_unit = self.selected_unit % ww   # equivalent to scrolling all the way to the top row
                #self.cursor_area = 'top' # Then to the control pane
                self.layer_idx = max(0, self.layer_idx - 1)
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
            elif tag == 'sel_layer_right':
                #hh,ww = self.tiles_height_width
                #self.selected_unit = self.selected_unit % ww   # equivalent to scrolling all the way to the top row
                #self.cursor_area = 'top' # Then to the control pane
                self.layer_idx = min(len(self._layers) - 1, self.layer_idx + 1)
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()

            elif tag == 'sel_left':
                self.move_selection('left')
            elif tag == 'sel_right':
                self.move_selection('right')
            elif tag == 'sel_down':
                self.move_selection('down')
            elif tag == 'sel_up':
                self.move_selection('up')

            elif tag == 'sel_left_fast':
                self.move_selection('left', self.settings.caffevis_fast_move_dist)
            elif tag == 'sel_right_fast':
                self.move_selection('right', self.settings.caffevis_fast_move_dist)
            elif tag == 'sel_down_fast':
                self.move_selection('down', self.settings.caffevis_fast_move_dist)
            elif tag == 'sel_up_fast':
                self.move_selection('up', self.settings.caffevis_fast_move_dist)

            elif tag == 'boost_individual':
                self.layer_boost_indiv_idx = (self.layer_boost_indiv_idx + 1) % len(self.layer_boost_indiv_choices)
                self.layer_boost_indiv = self.layer_boost_indiv_choices[self.layer_boost_indiv_idx]
            elif tag == 'boost_gamma':
                self.layer_boost_gamma_idx = (self.layer_boost_gamma_idx + 1) % len(self.layer_boost_gamma_choices)
                self.layer_boost_gamma = self.layer_boost_gamma_choices[self.layer_boost_gamma_idx]
            elif tag == 'pattern_mode':
                self.pattern_mode = not self.pattern_mode
            elif tag == 'show_back':
                # If in pattern mode: switch to fwd/back. Else toggle fwd/back mode
                if self.pattern_mode:
                    self.pattern_mode = False
                else:
                    self.layers_show_back = not self.layers_show_back
                if self.layers_show_back:
                    if not self.back_enabled:
                        self.back_enabled = True
                        self.back_stale = True
            elif tag == 'back_mode':
                if not self.back_enabled:
                    self.back_enabled = True
                    self.back_mode = 'grad'
                    self.back_stale = True
                else:
                    if self.back_mode == 'grad':
                        self.back_mode = 'deconv'
                        self.back_stale = True
                    else:
                        self.back_enabled = False
            elif tag == 'back_filt_mode':
                    if self.back_filt_mode == 'raw':
                        self.back_filt_mode = 'gray'
                    elif self.back_filt_mode == 'gray':
                        self.back_filt_mode = 'norm'
                    elif self.back_filt_mode == 'norm':
                        self.back_filt_mode = 'normblur'
                    else:
                        self.back_filt_mode = 'raw'
            elif tag == 'ez_back_mode_loop':
                # Cycle:
                # off -> grad (raw) -> grad(gray) -> grad(norm) -> grad(normblur) -> deconv
                if not self.back_enabled:
                    self.back_enabled = True
                    self.back_mode = 'grad'
                    self.back_filt_mode = 'raw'
                    self.back_stale = True
                elif self.back_mode == 'grad' and self.back_filt_mode == 'raw':
                    self.back_filt_mode = 'norm'
                elif self.back_mode == 'grad' and self.back_filt_mode == 'norm':
                    self.back_mode = 'deconv'
                    self.back_filt_mode = 'raw'
                    self.back_stale = True
                else:
                    self.back_enabled = False
            elif tag == 'freeze_back_unit':
                # Freeze selected layer/unit as backprop unit
                self.backprop_selection_frozen = not self.backprop_selection_frozen
                if self.backprop_selection_frozen:
                    # Grap layer/selected_unit upon transition from non-frozen -> frozen
                    self.backprop_layer = self.layer
                    self.backprop_unit = self.selected_unit
            elif tag == 'zoom_mode':

                self.layers_pane_zoom_mode = (self.layers_pane_zoom_mode + 1) % 3
                if self.layers_pane_zoom_mode == 2 and not self.back_enabled:
                    # Skip zoom into backprop pane when backprop is off
                    self.layers_pane_zoom_mode = 0

            elif tag == 'toggle_label_predictions':
                self.show_label_predictions = not self.show_label_predictions

            elif tag == 'toggle_unit_jpgs':
                self.show_unit_jpgs = not self.show_unit_jpgs

            else:
                key_handled = False

            if not self.backprop_selection_frozen:
                # If backprop_selection is not frozen, backprop layer/unit follows selected unit
                if not (self.backprop_layer == self.layer and self.backprop_unit == self.selected_unit):
                    self.backprop_layer = self.layer
                    self.backprop_unit = self.selected_unit
                    self.back_stale = True    # If there is any change, back diffs are now stale

            self.drawing_stale = key_handled   # Request redraw any time we handled the key

        return (None if key_handled else key)

    def handle_mouse(self, key, sel_idx=None):
        #print 'Ignoring key:', key
        if key == -1:
            return key

        layers = [ 'conv1', 'p1', 'n1', 'conv2', 'p2', 'n2', 'conv3', 'conv4', 'conv5', 'p5', 'fc6', 'fc7', 'fc8', 'prob' ]

        with self.lock:
            key_handled = True
            # tag = self.bindings.get_tag(key)

            if key in layers:

                self.layer_idx = layers.index(key)
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = -1

                msg = 'say Layer "%s"' % key
                msg = msg.replace("conv", "convolutional")
                start_new_thread(system, (msg,))

            elif key == 'Face detector':
                self.layer_idx = 8
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = 151

                msg = 'say "%s. Show me your face"' % key
                start_new_thread(system, (msg,))

            elif key == 'Circle detector':
                self.layer_idx = 8
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = 81

                msg = 'say "%s. Show me some circle"' % key
                start_new_thread(system, (msg,))

            elif key == 'Edge detector':
                self.layer_idx = 0
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = 43

                msg = 'say "%s. Show me some object."' % key
                start_new_thread(system, (msg,))

            elif key == 'Text detector':
                self.layer_idx = 8
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = 4

                msg = 'say "%s. Please show me some text"' % key
                start_new_thread(system, (msg,))

            elif key == 'Wrinkle detector':
                self.layer_idx = 8
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = 129

                msg = 'say "%s. show me some wrinkles on your shirt."' % key
                start_new_thread(system, (msg,))

            elif key == 'jump_to_cell':
                self.layer = self._layers[self.layer_idx]
                self._ensure_valid_selected()
                self.selected_unit = sel_idx

                msg = 'say neuron %s on layer %s' % (sel_idx, self.layer)
                msg = msg.replace("conv", "convolutional")

                start_new_thread(system, (msg,))

            # Right panel
            elif key == 'Synthetic images':
                msg = 'say Synthetic images that maximally activate the highlighted neuron'
                start_new_thread(system, (msg,))

            elif key == 'Real images':
                msg = 'say Real images that maximally activate the highlighted neuron'
                start_new_thread(system, (msg,))

            elif key == 'Deconv images':
                msg = 'say Visual patterns in the real images that activate the highlighted neuron'
                start_new_thread(system, (msg,))

            else:
                key_handled = False

            if not self.backprop_selection_frozen:
                # If backprop_selection is not frozen, backprop layer/unit follows selected unit
                if not (self.backprop_layer == self.layer and self.backprop_unit == self.selected_unit):
                    self.backprop_layer = self.layer
                    self.backprop_unit = self.selected_unit
                    self.back_stale = True    # If there is any change, back diffs are now stale

            self.drawing_stale = key_handled   # Request redraw any time we handled the key

        return (None if key_handled else key)

    def redraw_needed(self):
        with self.lock:
            return self.drawing_stale

    def move_selection(self, direction, dist = 1):
        hh,ww = self.tiles_height_width
        if direction == 'left':
            if self.cursor_area == 'top':
                self.layer_idx = max(0, self.layer_idx - dist)
                self.layer = self._layers[self.layer_idx]
            else:
                self.selected_unit -= dist
        elif direction == 'right':
            if self.cursor_area == 'top':
                self.layer_idx = min(len(self._layers) - 1, self.layer_idx + dist)
                self.layer = self._layers[self.layer_idx]
            else:
                self.selected_unit += dist
        elif direction == 'down':
            if self.cursor_area == 'top':
                self.cursor_area = 'bottom'
            else:
                self.selected_unit += ww * dist
        elif direction == 'up':
            if self.cursor_area == 'top':
                pass
            else:
                self.selected_unit -= ww * dist
                if self.selected_unit < 0:
                    self.selected_unit += ww
                    self.cursor_area = 'top'
        self._ensure_valid_selected()

    def update_tiles_height_width(self, height_width, n_valid):
        '''Update the height x width of the tiles currently
        displayed. Ensures (a) that a valid tile is selected and (b)
        that up/down/left/right motion works as expected. n_valid may
        be less than prod(height_width).
        '''

        assert len(height_width) == 2, 'give as (hh,ww) tuple'
        self.tiles_height_width = height_width
        self.tiles_number = n_valid
        # If the number of tiles has shrunk, the selection may now be invalid
        self._ensure_valid_selected()

    def _ensure_valid_selected(self):
        self.selected_unit = max(0, self.selected_unit)
        self.selected_unit = min(self.tiles_number-1, self.selected_unit)

class CaffeVisApp(BaseApp):
    '''App to visualize using caffe.'''

    def __init__(self, settings, key_bindings):
        super(CaffeVisApp, self).__init__(settings, key_bindings)
        print 'Got settings', settings
        self.settings = settings
        self.bindings = key_bindings

        sys.path.insert(0, os.path.join(settings.caffevis_caffe_root, 'python'))
        import caffe

        try:
            self._data_mean = np.load(settings.caffevis_data_mean)
        except IOError:
            print '\n\nCound not load mean file:', settings.caffevis_data_mean
            print 'Ensure that the values in settings.py point to a valid model weights file, network'
            print 'definition prototxt, and mean. To fetch a default model and mean file, use:\n'
            print '$ cd models/caffenet-yos/'
            print '$ ./fetch.sh\n\n'
            raise

        # Crop center region (e.g. 227x227) if mean is larger (e.g. 256x256)
        excess_h = self._data_mean.shape[1] - self.settings.caffevis_data_hw[0]
        excess_w = self._data_mean.shape[2] - self.settings.caffevis_data_hw[1]
        assert excess_h >= 0 and excess_w >= 0, 'mean should be at least as large as %s' % repr(self.settings.caffevis_data_hw)
        self._data_mean = self._data_mean[:, excess_h:(excess_h+self.settings.caffevis_data_hw[0]),
                                          excess_w:(excess_w+self.settings.caffevis_data_hw[1])]
        self._net_channel_swap = (2,1,0)
        self._net_channel_swap_inv = tuple([self._net_channel_swap.index(ii) for ii in range(len(self._net_channel_swap))])
        self._range_scale = 1.0      # not needed; image comes in [0,255]
        #self.net.set_phase_test()
        #if settings.caffevis_mode_gpu:
        #    self.net.set_mode_gpu()
        #    print 'CaffeVisApp mode: GPU'
        #else:
        #    self.net.set_mode_cpu()
        #    print 'CaffeVisApp mode: CPU'
        # caffe.set_phase_test()       # TEST is default now
        if settings.caffevis_mode_gpu:
            caffe.set_mode_gpu()
            print 'CaffeVisApp mode: GPU'
        else:
            caffe.set_mode_cpu()
            print 'CaffeVisApp mode: CPU'
        self.net = caffe.Classifier(
            settings.caffevis_deploy_prototxt,
            settings.caffevis_network_weights,
            mean = self._data_mean,
            channel_swap = self._net_channel_swap,
            raw_scale = self._range_scale,
            #image_dims = (227,227),
        )

        self.labels = None
        if self.settings.caffevis_labels:
            self.labels = read_label_file(self.settings.caffevis_labels)
        self.proc_thread = None
        self.jpgvis_thread = None
        self.handled_frames = 0
        if settings.caffevis_jpg_cache_size < 10*1024**2:
            raise Exception('caffevis_jpg_cache_size must be at least 10MB for normal operation.')
        self.img_cache = FIFOLimitedArrayCache(settings.caffevis_jpg_cache_size)

    def start(self):
        self.state = CaffeVisAppState(self.net, self.settings, self.bindings)
        self.state.drawing_stale = True
        self.layer_print_names = [get_pp_layer_name(nn) for nn in self.state._layers]

        if self.proc_thread is None or not self.proc_thread.is_alive():
            # Start thread if it's not already running
            self.proc_thread = CaffeProcThread(self.net, self.state,
                                               self.settings.caffevis_frame_wait_sleep,
                                               self.settings.caffevis_pause_after_keys,
                                               self.settings.caffevis_heartbeat_required)
            self.proc_thread.start()

        if self.jpgvis_thread is None or not self.jpgvis_thread.is_alive():
            # Start thread if it's not already running
            self.jpgvis_thread = JPGVisLoadingThread(self.settings, self.state, self.img_cache,
                                                     self.settings.caffevis_jpg_load_sleep,
                                                     self.settings.caffevis_heartbeat_required)
            self.jpgvis_thread.start()

    def get_heartbeats(self):
        return [self.proc_thread.heartbeat, self.jpgvis_thread.heartbeat]

    def quit(self):
        print 'CaffeVisApp: trying to quit'

        with self.state.lock:
            self.state.quit = True

        if self.proc_thread != None:
            for ii in range(3):
                self.proc_thread.join(1)
                if not self.proc_thread.is_alive():
                    break
            if self.proc_thread.is_alive():
                raise Exception('CaffeVisApp: Could not join proc_thread; giving up.')
            self.proc_thread = None

        print 'CaffeVisApp: quitting.'

    def _can_skip_all(self, panes):
        return ('caffevis_layers' not in panes.keys())

    def handle_input(self, input_image, panes):
        if self.debug_level > 1:
            print 'handle_input: frame number', self.handled_frames, 'is', 'None' if input_image is None else 'Available'
        self.handled_frames += 1
        if self._can_skip_all(panes):
            return

        with self.state.lock:
            if self.debug_level > 1:
                print 'CaffeVisApp.handle_input: pushed frame'
            self.state.next_frame = input_image
            if self.debug_level > 1:
                print 'CaffeVisApp.handle_input: caffe_net_state is:', self.state.caffe_net_state

    def redraw_needed(self):
        return self.state.redraw_needed()

    def draw(self, panes):
        if self._can_skip_all(panes):
            if self.debug_level > 1:
                print 'CaffeVisApp.draw: skipping'
            return False

        with self.state.lock:
            # Hold lock throughout drawing
            do_draw = self.state.drawing_stale and self.state.caffe_net_state == 'free'
            #print 'CaffeProcThread.draw: caffe_net_state is:', self.state.caffe_net_state
            if do_draw:
                self.state.caffe_net_state = 'draw'

        if do_draw:
            if self.debug_level > 1:
                print 'CaffeVisApp.draw: drawing'

            #if 'input' in panes:
            #    self._draw_input_pane(panes['input'])
            if 'caffevis_control' in panes:
                self._draw_control_pane(panes['caffevis_control'])
            if 'caffevis_status' in panes:
                self._draw_status_pane(panes['caffevis_status'])
            layer_data_3D_highres = None
            if 'caffevis_layers' in panes:
                layer_data_3D_highres = self._draw_layer_pane(panes['caffevis_layers'])
            if 'caffevis_aux' in panes:
                self._draw_aux_pane(panes['caffevis_aux'], layer_data_3D_highres)
            if 'caffevis_prob' in panes:
                self._draw_prob_pane(panes['caffevis_prob'], layer_data_3D_highres)

            # ANH: add a pane for feature detectors
            if 'caffevis_feature_detectors' in panes:
                self._draw_feature_detector_pane(panes['caffevis_feature_detectors'])

            if 'caffevis_back' in panes:
                # Draw back pane as normal
                # self._draw_back_pane(panes['caffevis_back'])
                if self.state.layers_pane_zoom_mode == 2:
                    # ALSO draw back pane into layers pane
                    self._draw_back_pane(panes['caffevis_layers'])
            if 'caffevis_jpgvis' in panes:
                self._draw_jpgvis_pane(panes['caffevis_jpgvis'])

            with self.state.lock:
                self.state.drawing_stale = False
                self.state.caffe_net_state = 'free'
        return do_draw

    def _OLDDEP_draw_control_pane(self, pane):
        pane.data[:] = to_255(self.settings.window_background)

        with self.state.lock:
            layer_idx = self.state.layer_idx

        face = getattr(cv2, self.settings.caffevis_control_face)
        loc = self.settings.caffevis_control_loc[::-1]   # Reverse to OpenCV c,r order
        clr = to_255(self.settings.caffevis_control_clr)
        clr_sel = to_255(self.settings.caffevis_control_clr_selected)
        clr_high = to_255(self.settings.caffevis_control_clr_cursor)
        fsize = self.settings.caffevis_control_fsize
        thick = self.settings.caffevis_control_thick
        thick_sel = self.settings.caffevis_control_thick_selected
        thick_high = self.settings.caffevis_control_thick_cursor

        st1 = ' '.join(self.layer_print_names[:layer_idx])
        st3 = ' '.join(self.layer_print_names[layer_idx+1:])
        st2 = ((' ' if len(st1) > 0 else '')
               + self.layer_print_names[layer_idx]
               + (' ' if len(st3) > 0 else ''))
        st1 = ' ' + st1
        cv2.putText(pane.data, st1, loc, face, fsize, clr, thick)
        boxsize1, _ = cv2.getTextSize(st1, face, fsize, thick)
        loc = (loc[0] + boxsize1[0], loc[1])

        if self.state.cursor_area == 'top':
            clr_this, thick_this = clr_high, thick_high
        else:
            clr_this, thick_this = clr_sel, thick_sel
        cv2.putText(pane.data, st2, loc, face, fsize, clr_this, thick_this)
        boxsize2, _ = cv2.getTextSize(st2, face, fsize, thick_this)
        loc = (loc[0] + boxsize2[0], loc[1])

        cv2.putText(pane.data, st3, loc, face, fsize, clr, thick)

        #print 'st1', st1
        #print 'st2', st2
        #print 'st3', st3

    def _draw_prob_labels_pane(self, pane):
        '''Adds text label annotation atop the given pane.'''

        if not self.labels or not self.state.show_label_predictions:
            return

        #pane.data[:] = to_255(self.settings.window_background)
        defaults = {'face':  getattr(cv2, self.settings.caffevis_class_face),
                    'fsize': self.settings.caffevis_class_fsize,
                    'clr':   to_255(self.settings.caffevis_class_clr_0),
                    'thick': self.settings.caffevis_class_thick}
        loc = self.settings.caffevis_class_loc[::-1]   # Reverse to OpenCV c,r order
        clr_0 = to_255(self.settings.caffevis_class_clr_0)
        clr_1 = to_255(self.settings.caffevis_class_clr_1)

        probs_flat = self.net.blobs['prob'].data.flatten()
        top_5 = probs_flat.argsort()[-1:-6:-1]

        strings = []

        # Background color for headings
        cv2.rectangle(pane.data,
                    (0, 0), (300, 32),
                    (30,30,30), cv2.cv.CV_FILLED)

        # Headings
        text = '%s %s' % ("Score", "What I think I see")
        fs = FormattedString(text, defaults)
        fs.fsize *= 0.9
        strings.append([fs])   # Line contains just fs

        # Spacing between headings and predictions
        space = FormattedString(" ", defaults)
        space.fsize *= 0.00
        strings.append([space])   # Line contains just fs

        # Top-5 predictions
        pmax = probs_flat[top_5[0]]
        for idx in top_5:
            prob = probs_flat[idx]
            text = '%.2f  %s' % (prob, self.labels[idx])
            fs = FormattedString(text, defaults)
            #fs.clr = tuple([clr_1[ii]*prob/pmax + clr_0[ii]*(1-prob/pmax) for ii in range(3)])
            fs.clr = tuple([clr_1[ii]*prob + clr_0[ii]*(1-prob) for ii in range(3)])
            strings.append([fs])   # Line contains just fs

        cv2_typeset_text(pane.data, strings, loc,
                         line_spacing = self.settings.caffevis_class_line_spacing)

    def _draw_control_pane(self, pane):
        pane.data[:] = to_255(self.settings.window_background)

        with self.state.lock:
            layer_idx = self.state.layer_idx

        loc = self.settings.caffevis_control_loc[::-1]   # Reverse to OpenCV c,r order

        list_buttons = [ 'conv1', 'p1', 'n1', 'conv2', 'p2', 'n2', 'conv3', 'conv4', 'conv5', 'p5', 'fc6', 'fc7', 'fc8', 'prob' ]

        # Draw buttons
        bgcolor = (0, 50, 150)
        bordercolor = (0, 0, 50)

        offset_x = 243
        offset_y = 5

        # Button
        for name in self.settings.buttons:

            if name in list_buttons:

                b = self.settings.buttons[name]

                cv2.rectangle(pane.data,
                    (b[0] - offset_x, b[2] - offset_y), (b[1] - offset_x, b[3] - offset_y),
                    bgcolor, cv2.cv.CV_FILLED)

                cv2.rectangle(pane.data,
                    (b[0] - offset_x, b[2] - offset_y), (b[1] - offset_x, b[3] - offset_y),
                    bordercolor)

        strings = []
        defaults = {'face':  getattr(cv2, self.settings.caffevis_control_face),
                    'fsize': self.settings.caffevis_control_fsize,
                    'clr':   to_255(self.settings.caffevis_control_clr),
                    'thick': self.settings.caffevis_control_thick}

        for ii in range(len(self.layer_print_names)):
            name = self.layer_print_names[ii]

            if ii == 0:
                name = " " + name

            fs = FormattedString(name, defaults)
            this_layer = self.state._layers[ii]
            if self.state.backprop_selection_frozen and this_layer == self.state.backprop_layer:
                fs.clr   = to_255(self.settings.caffevis_control_clr_bp)
                fs.thick = self.settings.caffevis_control_thick_bp
            if this_layer == self.state.layer:
                if self.state.cursor_area == 'top':
                    fs.clr = to_255(self.settings.caffevis_control_clr_cursor)
                    fs.thick = self.settings.caffevis_control_thick_cursor
                else:
                    if not (self.state.backprop_selection_frozen and this_layer == self.state.backprop_layer):
                        fs.clr = to_255(self.settings.caffevis_control_clr_selected)
                        fs.thick = self.settings.caffevis_control_thick_selected
            strings.append(fs)

        cv2_typeset_text(pane.data, strings, loc)

    def _draw_feature_detector_pane(self, pane):
        pane.data[:] = to_255(self.settings.window_background)

        defaults = {'face':  getattr(cv2, self.settings.caffevis_status_face),
                    'fsize': self.settings.caffevis_status_fsize,
                    'clr':   to_255(self.settings.caffevis_status_clr),
                    'thick': self.settings.caffevis_status_thick}
        loc = self.settings.caffevis_status_loc[::-1]   # Reverse to OpenCV c,r order

        list_buttons = [ "Face detector", "Text detector", "Circle detector", "Wrinkle detector", "Edge detector" ]
        status = StringIO.StringIO()
        with self.state.lock:
            # print >>status, 'opt' if self.state.pattern_mode else ('back' if self.state.layers_show_back else 'fwd'),
            # print >>status, '%s_%d |' % (self.state.layer, self.state.selected_unit),

            print >>status, "   ".join(list_buttons)

        # Draw buttons
        bgcolor = (0, 50, 150)
        bordercolor = (0, 0, 50)

        offset_x = 243
        offset_y = 720

        # Button
        for name in self.settings.buttons:

            if name in list_buttons:

                b = self.settings.buttons[name]

                cv2.rectangle(pane.data,
                    (b[0] - offset_x, b[2] - offset_y), (b[1] - offset_x, b[3] - offset_y),
                    bgcolor, cv2.cv.CV_FILLED)

                cv2.rectangle(pane.data,
                    (b[0] - offset_x, b[2] - offset_y), (b[1] - offset_x, b[3] - offset_y),
                    bordercolor)

        # Draw text
        strings = [FormattedString(line, defaults) for line in status.getvalue().split('\n')]
        cv2_typeset_text(pane.data, strings, loc,
                         line_spacing = self.settings.caffevis_status_line_spacing)

    def _draw_status_pane(self, pane):
        pane.data[:] = to_255(self.settings.window_background)

        defaults = {'face':  getattr(cv2, self.settings.caffevis_status_face),
                    'fsize': self.settings.caffevis_status_fsize,
                    'clr':   to_255(self.settings.caffevis_status_clr),
                    'thick': self.settings.caffevis_status_thick}
        loc = self.settings.caffevis_status_loc[::-1]   # Reverse to OpenCV c,r order

        status = StringIO.StringIO()
        with self.state.lock:
            print >>status, 'opt' if self.state.pattern_mode else ('back' if self.state.layers_show_back else 'fwd'),
            print >>status, '%s_%d' % (self.state.layer, self.state.selected_unit),

            # ANH: disable these as they're not needed
            '''
            print >>status, '%s_%d |' % (self.state.layer, self.state.selected_unit),
            if not self.state.back_enabled:
                print >>status, 'Back: off',
            else:
                print >>status, 'Back: %s' % ('deconv' if self.state.back_mode == 'deconv' else 'bprop'),
                print >>status, '(from %s_%d, disp %s)' % (self.state.backprop_layer,
                                                           self.state.backprop_unit,
                                                           self.state.back_filt_mode),
            print >>status, '|',
            print >>status, 'Boost: %g/%g' % (self.state.layer_boost_indiv, self.state.layer_boost_gamma)
            '''

            if self.state.extra_msg:
                print >>status, '|', self.state.extra_msg
                self.state.extra_msg = ''

        strings = [FormattedString(line, defaults) for line in status.getvalue().split('\n')]

        cv2_typeset_text(pane.data, strings, loc,
                         line_spacing = self.settings.caffevis_status_line_spacing)

    def _draw_layer_pane(self, pane):
        '''Returns the data shown in highres format, b01c order.'''

        if self.state.layers_show_back:
            layer_dat_3D = self.net.blobs[self.state.layer].diff[0]
        else:
            layer_dat_3D = self.net.blobs[self.state.layer].data[0]
        # Promote FC layers with shape (n) to have shape (n,1,1)
        if len(layer_dat_3D.shape) == 1:
            layer_dat_3D = layer_dat_3D[:,np.newaxis,np.newaxis]

        n_tiles = layer_dat_3D.shape[0]
        tile_rows,tile_cols = get_tiles_height_width(n_tiles)

        display_3D_highres = None
        if self.state.pattern_mode:
            # Show desired patterns loaded from disk

            #available = ['conv1', 'conv2', 'conv3', 'conv4', 'conv5', 'fc6', 'fc7', 'fc8', 'prob']
            jpg_path = os.path.join(self.settings.caffevis_unit_jpg_dir,
                                    'regularized_opt', self.state.layer, 'whole_layer.jpg')

            # Get highres version
            cache_before = str(self.img_cache)
            display_3D_highres = self.img_cache.get((jpg_path, 'whole'), None)
            if display_3D_highres is None:
                try:
                    with WithTimer('CaffeVisApp:load_sprite_image', quiet = self.debug_level < 1):
                        display_3D_highres = load_sprite_image(jpg_path, (tile_rows, tile_cols), n_sprites = n_tiles)
                except IOError:
                    # File does not exist, so just display disabled.
                    pass
                else:
                    self.img_cache.set((jpg_path, 'whole'), display_3D_highres)
            cache_after = str(self.img_cache)
            #print 'Cache was / is:\n  %s\n  %s' % (cache_before, cache_after)

            if display_3D_highres is not None:
                # Get lowres version, maybe. Assume we want at least one pixel for selection border.
                row_downsamp_factor = int(np.ceil(float(display_3D_highres.shape[1]) / (pane.data.shape[0] / tile_rows - 2)))
                col_downsamp_factor = int(np.ceil(float(display_3D_highres.shape[2]) / (pane.data.shape[1] / tile_cols - 2)))
                ds = max(row_downsamp_factor, col_downsamp_factor)
                if ds > 1:
                    #print 'Downsampling by', ds
                    display_3D = display_3D_highres[:,::ds,::ds,:]
                else:
                    display_3D = display_3D_highres
            else:
                display_3D = layer_dat_3D * 0  # nothing to show

        else:

            # Show data from network (activations or diffs)
            if self.state.layers_show_back:
                back_what_to_disp = self.get_back_what_to_disp()
                if back_what_to_disp == 'disabled':
                    layer_dat_3D_normalized = np.tile(self.settings.window_background, layer_dat_3D.shape + (1,))
                elif back_what_to_disp == 'stale':
                    layer_dat_3D_normalized = np.tile(self.settings.stale_background, layer_dat_3D.shape + (1,))
                else:
                    layer_dat_3D_normalized = tile_images_normalize(layer_dat_3D,
                                                                    boost_indiv = self.state.layer_boost_indiv,
                                                                    boost_gamma = self.state.layer_boost_gamma,
                                                                    neg_pos_colors = ((1,0,0), (0,1,0)))
            else:
                layer_dat_3D_normalized = tile_images_normalize(layer_dat_3D,
                                                                boost_indiv = self.state.layer_boost_indiv,
                                                                boost_gamma = self.state.layer_boost_gamma)
            #print ' ===layer_dat_3D_normalized.shape', layer_dat_3D_normalized.shape, 'layer_dat_3D_normalized dtype', layer_dat_3D_normalized.dtype, 'range', layer_dat_3D_normalized.min(), layer_dat_3D_normalized.max()

            display_3D         = layer_dat_3D_normalized

        # Convert to float if necessary:
        display_3D = ensure_float01(display_3D)
        # Upsample gray -> color if necessary
        #   (1000,32,32) -> (1000,32,32,3)
        if len(display_3D.shape) == 3:
            display_3D = display_3D[:,:,:,np.newaxis]
        if display_3D.shape[3] == 1:
            display_3D = np.tile(display_3D, (1, 1, 1, 3))
        # Upsample unit length tiles to give a more sane tile / highlight ratio
        #   (1000,1,1,3) -> (1000,3,3,3)
        if display_3D.shape[1] == 1:
            display_3D = np.tile(display_3D, (1, 3, 3, 1))
        if self.state.layers_show_back and not self.state.pattern_mode:
            padval = self.settings.caffevis_layer_clr_back_background
        else:
            padval = self.settings.window_background
        # Tell the state about the updated (height,width) tile display (ensures valid selection)
        self.state.update_tiles_height_width((tile_rows,tile_cols), display_3D.shape[0])

        #if self.state.layers_show_back:
        #    highlights = [(.5, .5, 1)] * n_tiles
        #else:
        highlights = [None] * n_tiles
        with self.state.lock:
            if self.state.cursor_area == 'bottom':
                highlights[self.state.selected_unit] = self.settings.caffevis_layer_clr_cursor  # in [0,1] range
            if self.state.backprop_selection_frozen and self.state.layer == self.state.backprop_layer:
                highlights[self.state.backprop_unit] = self.settings.caffevis_layer_clr_back_sel  # in [0,1] range

        _, display_2D = tile_images_make_tiles(display_3D, padval = padval, highlights = highlights)

        # print "----------------"
        # print tile_rows, tile_cols
        #print ' ===tile_conv dtype', tile_conv.dtype, 'range', tile_conv.min(), tile_conv.max()

        if display_3D_highres is None:
            display_3D_highres = display_3D

        # Display pane based on layers_pane_zoom_mode
        state_layers_pane_zoom_mode = self.state.layers_pane_zoom_mode
        assert state_layers_pane_zoom_mode in (0,1,2)
        if state_layers_pane_zoom_mode == 0:
            # Mode 0: base case
            display_2D_resize = ensure_uint255_and_resize_to_fit(display_2D, pane.data.shape)
        elif state_layers_pane_zoom_mode == 1:
            # Mode 1: zoomed selection
            unit_data = display_3D_highres[self.state.selected_unit]
            display_2D_resize = ensure_uint255_and_resize_to_fit(unit_data, pane.data.shape)
        else:
            # Mode 2: ??? backprop ???
            display_2D_resize = ensure_uint255_and_resize_to_fit(display_2D, pane.data.shape) * 0

        pane.data[0:display_2D_resize.shape[0], 0:display_2D_resize.shape[1], :] = display_2D_resize

        return display_3D_highres

    def _draw_aux_pane(self, pane, layer_data_normalized):
        pane.data[:] = to_255(self.settings.window_background)

        mode = None
        with self.state.lock:
            if self.state.cursor_area == 'bottom':
                mode = 'selected'
            else:
                mode = 'prob_labels'

        if mode == 'selected':
            unit_data = layer_data_normalized[self.state.selected_unit]
            unit_data_resize = ensure_uint255_and_resize_to_fit(unit_data, pane.data.shape)
            pane.data[0:unit_data_resize.shape[0], 0:unit_data_resize.shape[1], :] = unit_data_resize

        # ANH: Always display probability panel
        # elif mode == 'prob_labels':
        # self._draw_prob_labels_pane(pane)

    def _draw_prob_pane(self, pane, layer_data_normalized):
        pane.data[:] = to_255(self.settings.window_background)

        # mode = None
        # with self.state.lock:
        #     if self.state.cursor_area == 'bottom':
        #         mode = 'selected'
        #     else:
        #         mode = 'prob_labels'

        # if mode == 'selected':
        #     unit_data = layer_data_normalized[self.state.selected_unit]
        #     unit_data_resize = ensure_uint255_and_resize_to_fit(unit_data, pane.data.shape)
        #     pane.data[0:unit_data_resize.shape[0], 0:unit_data_resize.shape[1], :] = unit_data_resize

        # ANH: Always display probability panel
        # elif mode == 'prob_labels':
        self._draw_prob_labels_pane(pane)

    def _draw_back_pane(self, pane):
        mode = None
        with self.state.lock:
            back_enabled = self.state.back_enabled
            back_mode = self.state.back_mode
            back_filt_mode = self.state.back_filt_mode
            state_layer = self.state.layer
            selected_unit = self.state.selected_unit
            back_what_to_disp = self.get_back_what_to_disp()

        if back_what_to_disp == 'disabled':
            pane.data[:] = to_255(self.settings.window_background)

        elif back_what_to_disp == 'stale':
            pane.data[:] = to_255(self.settings.stale_background)

        else:
            # One of the backprop modes is enabled and the back computation (gradient or deconv) is up to date

            grad_blob = self.net.blobs['data'].diff

            #print '****grad_blob min,max =', grad_blob.min(), grad_blob.max()
            #c1diff = self.net.blobs['conv1'].diff
            #print '****conv1diff min,max =', c1diff.min(), c1diff.max()

            # Manually deprocess (skip mean subtraction and rescaling)
            #grad_img = self.net.deprocess('data', diff_blob)
            grad_blob = grad_blob[0]                    # bc01 -> c01
            grad_blob = grad_blob.transpose((1,2,0))    # c01 -> 01c
            grad_img = grad_blob[:, :, self._net_channel_swap_inv]  # e.g. BGR -> RGB

            # Mode-specific processing
            assert back_mode in ('grad', 'deconv')
            assert back_filt_mode in ('raw', 'gray', 'norm', 'normblur')
            if back_filt_mode == 'raw':
                grad_img = norm01c(grad_img, 0)
            elif back_filt_mode == 'gray':
                grad_img = grad_img.mean(axis=2)
                grad_img = norm01c(grad_img, 0)
            elif back_filt_mode == 'norm':
                grad_img = np.linalg.norm(grad_img, axis=2)
                grad_img = norm01(grad_img)
            else:
                grad_img = np.linalg.norm(grad_img, axis=2)
                cv2.GaussianBlur(grad_img, (0,0), self.settings.caffevis_grad_norm_blur_radius, grad_img)
                grad_img = norm01(grad_img)

            # If necessary, re-promote from grayscale to color
            if len(grad_img.shape) == 2:
                grad_img = np.tile(grad_img[:,:,np.newaxis], 3)

            grad_img_resize = ensure_uint255_and_resize_to_fit(grad_img, pane.data.shape)

            pane.data[0:grad_img_resize.shape[0], 0:grad_img_resize.shape[1], :] = grad_img_resize

    def _draw_jpgvis_pane(self, pane):
        pane.data[:] = to_255(self.settings.window_background)

        with self.state.lock:
            state_layer, state_selected_unit, cursor_area, show_unit_jpgs = self.state.layer, self.state.selected_unit, self.state.cursor_area, self.state.show_unit_jpgs

        available = ['conv1', 'conv2', 'conv3', 'conv4', 'conv5', 'fc6', 'fc7', 'fc8', 'prob']
        if state_layer in available and cursor_area == 'bottom' and show_unit_jpgs:
            img_key = (state_layer, state_selected_unit, pane.data.shape)
            img_resize = self.img_cache.get(img_key, None)
            if img_resize is None:
                # If img_resize is None, loading has not yet been attempted, so show stale image and request load by JPGVisLoadingThread
                with self.state.lock:
                    self.state.jpgvis_to_load_key = img_key
                pane.data[:] = to_255(self.settings.stale_background)
            elif img_resize.nbytes == 0:
                # This is the sentinal value when the image is not
                # found, i.e. loading was already attempted but no jpg
                # assets were found. Just display disabled.
                pane.data[:] = to_255(self.settings.window_background)
            else:
                # Show image
                pane.data[:img_resize.shape[0], :img_resize.shape[1], :] = img_resize
        else:
            # Will never be available
            pane.data[:] = to_255(self.settings.window_background)

    def handle_key(self, key, panes):
        return self.state.handle_key(key)

    def handle_mouse(self, key, idx=None):
        return self.state.handle_mouse(key, idx)

    def get_back_what_to_disp(self):
        '''Whether to show back diff information or stale or disabled indicator'''
        if (self.state.cursor_area == 'top' and not self.state.backprop_selection_frozen) or not self.state.back_enabled:
            return 'disabled'
        elif self.state.back_stale:
            return 'stale'
        else:
            return 'normal'

    def set_debug(self, level):
        self.debug_level = level
        self.proc_thread.debug_level = level
        self.jpgvis_thread.debug_level = level

    def draw_help(self, help_pane, locy):
        defaults = {'face':  getattr(cv2, self.settings.caffevis_help_face),
                    'fsize': self.settings.caffevis_help_fsize,
                    'clr':   to_255(self.settings.caffevis_help_clr),
                    'thick': self.settings.caffevis_help_thick}
        loc_base = self.settings.caffevis_help_loc[::-1]   # Reverse to OpenCV c,r order
        locx = loc_base[0]

        lines = []
        lines.append([FormattedString('', defaults)])
        lines.append([FormattedString('Caffevis keys', defaults)])

        kl,_ = self.bindings.get_key_help('sel_left')
        kr,_ = self.bindings.get_key_help('sel_right')
        ku,_ = self.bindings.get_key_help('sel_up')
        kd,_ = self.bindings.get_key_help('sel_down')
        klf,_ = self.bindings.get_key_help('sel_left_fast')
        krf,_ = self.bindings.get_key_help('sel_right_fast')
        kuf,_ = self.bindings.get_key_help('sel_up_fast')
        kdf,_ = self.bindings.get_key_help('sel_down_fast')

        keys_nav_0 = ','.join([kk[0] for kk in (kl, kr, ku, kd)])
        keys_nav_1 = ''
        if len(kl)>1 and len(kr)>1 and len(ku)>1 and len(kd)>1:
            keys_nav_1 += ' or '
            keys_nav_1 += ','.join([kk[1] for kk in (kl, kr, ku, kd)])
        keys_nav_f = ','.join([kk[0] for kk in (klf, krf, kuf, kdf)])
        nav_string = 'Navigate with %s%s. Use %s to move faster.' % (keys_nav_0, keys_nav_1, keys_nav_f)
        lines.append([FormattedString('', defaults, width=120, align='right'),
                      FormattedString(nav_string, defaults)])

        #label = '%10s:' % (
        #help_string = 'Move cursor left, right, up, or down'
        #lines.append([FormattedString(label, defaults, width=120, align='right'),
        #              FormattedString(help_string, defaults)])
        #if len(kl)>1 and len(kr)>1 and len(ku)>1 and len(kd)>1:
        #    label = '%10s:' % (','.join([kk[1] for kk in (kl, kr, ku, kd)]))
        #    help_string = 'Move cursor left, right, up, or down'
        #    lines.append([FormattedString(label, defaults, width=120, align='right'),
        #                  FormattedString(help_string, defaults)])
        #label = '%10s:' % (','.join([kk[0] for kk in (klf, krf, kuf, kdf)]))
        #help_string = 'Move cursor left, right, up, or down (faster)'
        #lines.append([FormattedString(label, defaults, width=120, align='right'),
        #              FormattedString(help_string, defaults)])

        for tag in ('sel_layer_left', 'sel_layer_right', 'zoom_mode', 'pattern_mode',
                    'ez_back_mode_loop', 'freeze_back_unit', 'show_back', 'back_mode', 'back_filt_mode',
                    'boost_gamma', 'boost_individual', 'reset_state'):
            key_strings, help_string = self.bindings.get_key_help(tag)
            label = '%10s:' % (','.join(key_strings))
            lines.append([FormattedString(label, defaults, width=120, align='right'),
                          FormattedString(help_string, defaults)])

        locy = cv2_typeset_text(help_pane.data, lines, (locx, locy),
                                line_spacing = self.settings.caffevis_help_line_spacing)

        return locy

def crop_to_corner(img, corner, small_padding = 1, large_padding = 2):
    '''Given an large image consisting of 3x3 squares with small_padding padding concatenated into a 2x2 grid with large_padding padding, return one of the four corners (0, 1, 2, 3)'''
    assert corner in (0,1,2,3), 'specify corner 0, 1, 2, or 3'
    assert img.shape[0] == img.shape[1], 'img is not square'
    assert img.shape[0] % 2 == 0, 'even number of pixels assumption violated'
    half_size = img.shape[0]/2
    big_ii = 0 if corner in (0,1) else 1
    big_jj = 0 if corner in (0,2) else 1
    tp = small_padding + large_padding
    #tp = 0
    return img[big_ii*half_size+tp:(big_ii+1)*half_size-tp,
               big_jj*half_size+tp:(big_jj+1)*half_size-tp]

    #image_pixels = img.shape[0] - small_padding * 12 - large_padding * 4
    #assert image_pixels % 6 == 0, 'math error'
    #small_image_size = image_pixels / 6

def load_sprite_image(img_path, rows_cols, n_sprites = None):
    '''Load a 2D sprite image where (rows,cols) = rows_cols. Sprite
    shape is computed automatically. If n_sprites is not given, it is
    assumed to be rows*cols. Return as 3D tensor with shape
    (n_sprites, sprite_height, sprite_width, sprite_channels).
    '''

    rows,cols = rows_cols
    if n_sprites is None:
        n_sprites = rows * cols
    img = caffe_load_image(img_path, color = True, as_uint = True)
    assert img.shape[0] % rows == 0, 'sprite image has shape %s which is not divisible by rows_cols %' % (img.shape, rows_cols)
    assert img.shape[1] % cols == 0, 'sprite image has shape %s which is not divisible by rows_cols %' % (img.shape, rows_cols)
    sprite_height = img.shape[0] / rows
    sprite_width  = img.shape[1] / cols
    sprite_channels = img.shape[2]

    ret = np.zeros((n_sprites, sprite_height, sprite_width, sprite_channels), dtype = img.dtype)
    for idx in xrange(n_sprites):
        # Row-major order
        ii = idx / cols
        jj = idx % cols
        ret[idx] = img[ii*sprite_height:(ii+1)*sprite_height,
                       jj*sprite_width:(jj+1)*sprite_width, :]
    return ret


text:
Blogs | Oct. 25, 2016
Jack Chick Passes on to His Reward: 9 Things to Know and Share
Jack Chick, creator of the famous "Chick tracts", has died at the age of 92.
Yesterday reports emerged that Jack T. Chick passed on Sunday at the age of 92. This has been confirmed by a post on his company's Facebook page.
For those who may not be aware, Jack Chick was a famous creator of evangelistic comic books and comic tracts (known as "Chick tracts").
Here are 9 things to know and share . . .
1) Chick's Comics
According to his company's web site, more than 750,000,000 Chick publications have been distributed.
That's three quarters of a billion-with-a-"b" comic books.
And that would make him the single most published comic book creator in history—with far more copies of his works placed in circulation than even comic book legends like Jack Kirby, Stan Lee, Jerry Siegel, and Joe Schuster.
Originally, Chick both wrote and drew all his comics, but in later years some art was done by a man named Fred Carter, who had a much more realistic style than Chick.
2) Chick's Faith
Chick was an independent Baptist and a Fundamentalist who argued that only the King James Version of the Bible should be used.
He believed that we are living very close to the end of the world, that there are conspiracies everywhere, and that the devil is lurking behind numerous rocks.
The visual nature of Chick's chosen medium and the nature of his Fundamentalist faith meant that he sometimes walked a delicate line.
In his comics, Chick needed to regularly depict judgment scenes in which Christ alternately commends or condemns a person based on how they lived their life.
Yet Protestant Fundamentalism has an aversion to depicting images of Christ.
Chick's solution was to depict Jesus seated on a throne, but with a radiant, blank face—an image that some have described as a "lightbulb-headed Jesus":
3) Chick on Catholicism
As one might expect of a Fundamental Baptist, Chick was strongly opposed to the Catholic Faith.
Among the anti-Catholic tracts he produced are Last Rites (in which a Catholic goes to hell for following the teachings of his Church), Why Is Mary Crying? (against Catholic teaching on the Virgin Mary), and The Death Cookie (against Catholic teaching on the Eucharist).
His web site summarizes the tract Mama's Girls by saying:
The Catholic Church is mother to four great false religions. She will do anything to keep you from Jesus.
In this tract, Chick claims that Islam, Freemasonry, Communism, and Nazism were plots created by the Catholic Church to deceive people—despite the historical opposition of each of these toward the Catholic Faith.
Such, apparently, are the wiles of the Catholic Church.
He also supported the discredited anti-Catholic conspiracy claims of Alexander Hislop, Charles Chiniquy, and Alberto Rivera.
4) Chick's Popularity
The deadpan earnestness with which Chick's publications discussed these and other conspiracies, as well as their ardent simplicity, make them weirdly compelling.
Many people who violently disagree with Chick—including atheists—have found his publications have a campy, kitschy appeal and began collecting them.
There's even a tongue-in-cheek Chick Tract Club, and his work inspired parodies from many, many perspectives.
5) Chick's Film
In 2003, Chick branched out from the comic book world to produce a film called The Light of the World. His company has posted it to YouTube, here.
Chick wrote it, and the visuals are provided by pans and zooms of paintings by Fred Carter.
The film presents Chick's view of salvation history, including the final judgment (note Pope Pius XII marching off to hell).
Chick was not much of a theologian, and in the movie it shows. The first sentence of the film misquotes Genesis ("In the beginning, God created the heaven [singular] and the earth"), and the second sentence describes the Father, the Son, and the Holy Spirits as three "parts" of God (note well: God has no parts).
Still, Fred Carter's paintings are pretty to look at.
6) Meeting Chick
Chick himself was notoriously reclusive. I was fortunate, however, to meet him at the premier of The Light of the World.
So far as I know, I am the only Catholic apologist to have met him.
You can read an account of my meeting with Jack Chick here.
7) Impressions of Chick
Although Chick was visibly uncomfortable when he learned I was a Catholic evangelist, he remained civil, and we had a pleasant conversation.
Some have wondered, given the extreme implausibility of the claims made in his comics, whether he was simply a crook who was in it for the money.
If so, he gave no sign of it in our conversation. He seemed sincerely convinced of the viewpoint his comics would lead you to believe that he held.
8) The Face Behind the Comics
Because of his reclusiveness, almost no photos of Chick exist.
Consequently, after our meeting, I drew a picture of him:
The fact that I wrote about meeting him and produced one of the few pictures of Chick available has led, over the years, to me periodically getting requests for this to be used in various forms by those producing works about him (requests I have always granted, as long as they cite me as the source).
For what it's worth, I had hopes of being a comic book writer-artist in my youth. In fact, I practiced drawing comic book art for years, and I am pleased to say that my drawing of Chick was later confirmed when a more recent photograph of him was published to the Internet here:
9) Praying for Chick
It's great that Chick lived to the ripe old age of 92!
Despite his opposition to the Catholic Faith, Jack Chick was a man whom Christ loved and for whom he died.
My experience of Chick's sincerity suggests he was innocently unaware of the consequences of his actions, meaning that he would fall under Our Lord's prayer, "Father, forgive them, for they know not what they do" (Luke 23:34).
Jack Chick opposed praying for the dead, and it would have meant nothing to him that he died in the Year of Mercy proclaimed by Pope Francis.
But if his understanding of the divine mystery leads him to experience the particular judgment as standing before a giant, radiant Jesus (like the one depicted in his comics), I hope you will join me in praying:
Eternal rest grant unto him, O Lord, and may perpetual light shine upon him.
Jimmy was born in Texas, grew up nominally Protestant, but at age 20 experienced a profound conversion to Christ. Planning on becoming a Protestant pastor or seminary professor, he started an intensive study of the Bible. But the more he immersed himself in Scripture the more he found to support the Catholic faith. Eventually, he entered the Catholic Church. His conversion story, "A Triumph and a Tragedy," is published in Surprised by Truth. Besides being an author, Jimmy is the Senior Apologist at Catholic Answers, a contributing editor to Catholic Answers Magazine, and a weekly guest on "Catholic Answers Live."


Situated on Barcelona's famous La Rambla, this restored 19th-century building features a rooftop pool with a sun terrace. The elegant rooms include free Wi-Fi and a marble bathroom.
Credit cardholder must match guest name or provide authorization. If you are not the owner of the credit card used to make the reservation, please contact the property in advance Guests are required to show a photo identification and credit card upon check-in. Please note that all Special Requests are subject to availability and additional charges may apply.


Christine Belcher is responsible for supporting Schechter senior executives and management to ensure all administrative details are handled efficiently and effectively. She manages all digital solutions for the company including, IT support and systems, phone systems, Smartboard presentations, iPad presentations, teleconferencing and web-ex meetings. She plays a vital role in preparing sales reports and client presentation materials, combining and formatting the information used to illustrate strategies and concepts. Christine also assists our firm's marketing team with sponsorship tracking and ad development, website updates and more.
Chris lives in Berkley with her two children and runs like Forrest Gump every day.


Two of the local gardens that will be on view at Sunday's "Garden Tour," presented by the Women's City Club of Delaware.
A month after its "Art For Your Ears" event, the Women's City Club of Delaware will present its annual "Garden Tour" from 1 to 4 p.m. on Sunday, June 28.
Normally private residential gardens will be open, with the Ohio State University Extension Master Gardeners of Delaware County on hand to answer questions.
Tickets cost $10 at wccdelaware.org or $12 day of show; free to children under 12. "Ticket proceeds help provide sustainable, safe housing for low-income women in Delaware County," the event's flyer states.
The tour begins at the Women's City Club, 135 N. Franklin St.
The City Club also held its inaugural "Art For Your Ears" fundraising event on May 16, which included a swap meet and concerts by local bands and a special guest, Charlie Huhn, the current lead singer for the rock band Foghat.


I don't know about you, but making detergent isn't on my list of things I typically do during the day. So as educators, what does having a brand mean for us? And why is it important for us to be intentional with it?
What 10 adjectives would you use to describe me?
What do you feel I'm doing well?
What do you feel I could be doing better?
The power of telling our stories is enormous (one of my favorite self-proclaimed titles is Storyteller-in-Chief) and I realize that my principal brand plays a huge part. So I found myself a bit scared but nonetheless inspired to take on this challenge.
After a few days and reflecting on my list of words I would hope our team would use to describe me, I got the courage up to look at the results.
The good news? These two lists have lots in common. I feel affirmed that the principal brand I'm trying to live out, at least on some level, I am. This, along with what they feel I'm doing well, give me motivation to continue leading in a way that aligns with my hopes and core beliefs. Examples? Leading with intention, creating a vision and keeping them focused on the goal, reigniting them when things get blah. Creating a place where people want to work because they feel supported, have a purpose and a voice.
The still good but harder to hear feedback? While I'm the first to admit that I'm far from perfect and have so many things to improve, it's never fun hearing about those things from other people, especially the people I seek to serve daily. The good news, though, is that they feel comfortable enough to tell me. I've got some work to do on a few items. Example? Even though I feel like I'm a leader who is open to others' ideas and works to come up with win-win solutions, someone on my team feels that I need to improve on this. I also need to make sure that I tell my peeps, each of them, how individually awesome they are. At the end of the day, it doesn't matter how I perceive myself. It matters how they feel.
Are you ready to clarify and amplify your brand? Anyone can do it! Write your list and then go make it happen. As Amber suggests, taking one small action each day to live out your brand will help you stay motivated while being the best leader you can be.
What's on your list? What steps are you taking this week to live out your brand? I'd love to hear and connect! Tweet and tag me (@me1odystacy) to share. Or feel free to start a conversation by commenting below.


An orphan named Luneth has fallen deep into a cavern, which he explores, trying to find his way out. Near the end, he finds a large crystal, whose spirit tells him he is a Warrior of Light, destined to bring peace to the world. After he leaves, he begins his long trip across the world, finding the three other chosen Warriors of Light to save the world. Final Fantasy III was originally released on the Famicom in Japan and would be one of the few titles in the franchise never to reach North American shores until it was remade for the Nintendo DS in 2006. FF3 proves to be a solid title, although certain drawbacks that plagued the NES version haunt the DS version as well, preventing it from being a masterpiece.
As with the first three NES Final Fantasies, FF3 features completely-turn-based combat instead of the Active Time system of most of its successors. The player can input a command for up to four characters and lets them and the enemy beat up one another in a round. As with most turn-based battle systems, character and enemy turn order is unpredictable, and irritatingly varies at times. Still, normal random battles are pretty fast-paced and rarely drag on forever, perhaps because the game pits no more than three enemies against your party in all random encounters. Even so, many bosses and a few normal enemies can certainly be challenging at times.
The heart of FF3's battle engine is the class system, with new classes gained from each of the four crystals throughout the game, including warrior, monk, white mage, black mage, and so forth. Each class, of course, has its own strengths and weaknesses, and depending upon how often your characters fight while of a certain class, their Job Level with the class will gradually increase, bolstering that character's effectiveness with the class. Players can change a character's class any time outside of battle, although doing so will temporarily give that character crystal sickness, where their stats are decreased for a certain number of battles. It's still a solid battle system, though, and certainly a main draw to the remake.
The interface isn't too problematic, although there are nonetheless a few irritating features. For one, there are no save points in dungeons, which can sometimes deprive the player of progress when a boss at the end of a long dungeon slaughters their party. Still, the game does utilize a quick-delete-save feature, and a teleportation spell can spare players the inconvenience of retracing their steps to leave a dungeon. There's also an annoyance where, if you utilize the game's Wi-Fi connectivity to send mail to friends, you must switch to a separate screen to input punctuation and spaces in your mail, which, even with the stylus, can be irksome. Overall, interaction isn't terrible, although there are certain issues that could've easily been resolved.
Originality is always hard to discern in a remake (RPGamer, in this reviewer's opinion, should just abolish the category because of issues like this and the fact that no one's played every RPG in existence), although the visual overhaul especially separates the remake from the original, alongside features such as the aforementioned Wi-Fi mail system. The Famicom version, moreover, was the first of the series to have a class system, even if it wasn't the very first class system in the genre. Then there are features such as the music, the world, and by extent the story, that of course return from the original. All in all, FF3 is plentifully original for a remake.
The story received some improvement from the original, although it nonetheless retains the skeletal disposition of the Famicom version, much akin to most 8-bit-era RPG storylines. It is nice that the four generic Onion Kids of the original have been replaced with actual characters with names and a bit of description when you get them in your party, although the backstory largely reflects the generic "light vs. dark" camp of many other RPG stories, and both the pacing and character development are on the shallow side, with the playable characters mostly being interchangeable. In the end, the plot is, at best, generic and paper-thin.
The music, though, is one solid feature retained from the Famicom version. It is perhaps composer Nobuo Uematsu's best soundtrack among the Famicom Final Fantasies, with solid tracks such as the overworld theme, perhaps one of the best in the genre. Headphones, as is the apparent case with most portable titles, somewhat increases the musical quality. Granted, there are some imperfections, such as many tracks not lasting more than a minute before looping, although they largely compensate for this in terms of quality. Sound effects are plentifully diverse, as well, with most weapons in battle having different sounds, for instance. Overall, the aurals are definitely one of the main draws to the remake.
The visuals are, as well, with FF3 making a full leap to 3-D, which largely maximizes the DS's visual capabilities and even features an introductory FMV. Characters in battle, along with the enemies, are reasonably animated, although attacks contain the odd telekinetic appearance of the original six Final Fantasies. There is, moreover, a sizeable degree of graininess with the graphics, as well as pixilated texturing during close-ups of environs and your characters. Even so, the Nintendo DS's graphical capabilities aren't nearly on par with those of say, the Playstation Portable, so these shortcomings are certainly forgivable, and the visuals, in summation, are another decent draw to the remake.
FF3, finally, will keep players occupied for a good twenty-five to fifty hours, with a few sidequests, some of which require Wi-Fi communication to unlock, potentially boosting playtime. In the end, Final Fantasy III decently completes the saga of the legendary franchise in North America over a decade after the original version's release in Japan. Its solid gameplay and presentation are sure to draw gamers simply looking for a fun RPG to keep them occupied, although its story, admittedly, certainly won't keep players on edge. Still, with the remake's release, American gamers can, at long last, legally enjoy all installments of the Final Fantasy franchise.