Skip to content
Permalink
Browse files

colorspace param added to ImageInnotation; Open CV2 no longer (someti…

…mes) required
  • Loading branch information...
danlester committed Jun 12, 2019
1 parent 36f8386 commit 82b58892a28b2706a14192a03ff89d9200aed8e2
@@ -0,0 +1,278 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from jupyter_innotater import *\n",
"import numpy as np, os"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Image Filenames and Bounding Boxes"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d80008144bff46a7943c0303406d05eb",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Innotater(children=(HBox(children=(VBox(children=(ImagePad(value=b'\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x01\\x0…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"foodfns = sorted(os.listdir('./foods/'))\n",
"targets = np.zeros((len(foodfns), 4), dtype='int') # (x,y,w,h) for each data row\n",
"\n",
"Innotater( ImageInnotation(foodfns, path='./foods'), BoundingBoxInnotation(targets) )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Press 'n' or 'p' to move to next or previous image in the Innotater above."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"targets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Numpy Image Data and Multi-classification"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(300, 400, 3)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import cv2\n",
"classes = ['vegetable', 'biscuit', 'fruit']\n",
"foods = [cv2.imread('./foods/'+f) for f in foodfns]\n",
"targets = [0] * len(foodfns)\n",
"foods[0].shape"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "75f10293e36d49bc8fa4e4f203b23233",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Innotater(children=(HBox(children=(VBox(children=(ImagePad(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"w2 = Innotater(\n",
" ImageInnotation(foods, name='Food'), \n",
" MultiClassInnotation(targets, name='FoodType', classes=classes, desc='Food Type')\n",
")\n",
"display(w2)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(300, 400, 3)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"foodsmpl = [plt.imread('./foods/'+f) for f in foodfns]\n",
"foodsmpl[0].shape"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e3bbc452a73a44a2b3eb6492986e79eb",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Innotater(children=(HBox(children=(VBox(children=(ImagePad(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"w3 = Innotater(\n",
" ImageInnotation(foodsmpl, name='Food', colorspace='RGB'), \n",
" MultiClassInnotation(targets, name='FoodType', classes=classes, desc='Food Type')\n",
")\n",
"display(w3)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b98bdf456d4741e7a86c28b4d9ef72c9",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Innotater(children=(HBox(children=(VBox(children=(ImagePad(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"foodsbw = [f[:,:,0] for f in foods]\n",
"w4 = Innotater(\n",
" ImageInnotation(foodsbw, name='Food', colorspace='RGB'), \n",
" MultiClassInnotation(targets, name='FoodType', classes=classes, desc='Food Type')\n",
")\n",
"display(w4)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e05e07b678c54dbc96a13fb51f68b196",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Innotater(children=(HBox(children=(VBox(children=(ImagePad(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"foodsbw2 = [np.expand_dims(f, axis=-1) for f in foodsbw]\n",
"\n",
"w5 = Innotater(\n",
" ImageInnotation(foodsbw2, name='Food'), \n",
" MultiClassInnotation(targets, name='FoodType', classes=classes, desc='Food Type')\n",
")\n",
"display(w5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -4,3 +4,8 @@ Requirements
Innotater has been developed under Jupyter 5.7 with a Python 3.7 kernel.
I will be pleased to look into any problems running under older versions
of each.

The minimum supported Python version is considered to be 3.3.

Open CV2 is used if available and will improve speed and reliability of
image handling, but it is not currently required.
@@ -63,11 +63,12 @@ ImageInnotation
^^^^^^^^^^^^^^^

data is expected to be an array of filenames, blobs, or numpy arrays
containing image data directly (RGB format).
(or similar, e.g. PyTorch Tensor) containing image data directly.

Extra optional parameters:

``path`` - a path to be prefixed to all filenames provided in data.
``path`` - a path to be prefixed to all filenames provided in ``data``
(this parameter is ignored if ``data`` does not contain filenames).

``width`` and/or ``height`` to specify the maximum size of image to
display as an integer number of pixels. For example, if you specify only
@@ -84,6 +85,18 @@ before it is processed to be displayed on the screen. For example, you
might set ``transform`` to a denormalization function because all images
in data have been normalized for training purposes.

``colorspace`` is a string containing either 'RGB' or 'BGR' (default is
'BGR'). This only has an effect if you pass numpy arrays or similar as
the ``data`` attribute. It specifies the meaning of the color channels
of the input data. For example, if you load images using Open CV2
(cv2.imread) then the default of 'BGR' will normally be correct; if you
load images via matplotlib imread, you will likely need colorspace to be
'RGB'.

Note that if a numpy array is provided channel-first, the Innotater
should detect this and automatically switch the channel to be the last
axis internally.

BoundingBoxInnotation
^^^^^^^^^^^^^^^^^^^^^

@@ -6,6 +6,13 @@
from ipywidgets import Checkbox, Select, Textarea, Dropdown, Text
import re
from pathlib import Path
import numpy as np # Required to manipulate numpy or pytorch image matrix
try:
import cv2 # Prefer Open CV2 but don't put in requirements.txt because it can be difficult to install
usecv2 = True
except ImportError:
import png, io # PyPNG is a pure-Python PNG manipulator
usecv2 = False


class Innotation:
@@ -113,6 +120,12 @@ def __init__(self, *args, **kwargs):

self.transform = kwargs.get('transform', None)

self.colorspace = 'BGR'
if 'colorspace' in kwargs:
self.colorspace = kwargs['colorspace']
if self.colorspace not in ('BGR', 'RGB'):
raise Exception("Parameter colorspace must be either 'RGB' or 'BGR'")

self.max_repeats = 0

self.watchlist = WatchList()
@@ -133,15 +146,35 @@ def update_ui(self, uindex):
p = Path(self.path) / p
self.get_widget().set_value_from_file(p)
elif 'numpy' in str(type(it)) or 'Tensor' in str(type(it)):
import cv2, numpy as np # Required to manipulate numpy or pytorch image matrix
npim = it.numpy() if hasattr(it, 'numpy') else it
if len(npim.shape) == 3 and npim.shape[2] not in (1,3,4):
# Channels dim needs to be moved to back
npim = npim.transpose((1,2,0))
if not np.issubdtype(npim.dtype, np.integer):
# Float so scale
npim = (npim * 255).astype('int')
self.get_widget().value = cv2.imencode('.png', npim)[1].tostring()

if usecv2 and self.colorspace == 'RGB':
npim = cv2.cvtColor(npim, cv2.COLOR_RGB2BGR)
elif not usecv2 and self.colorspace == 'BGR' and len(npim.shape) == 3:
npim = np.flip(npim, axis=2)

if usecv2:
self.get_widget().value = cv2.imencode('.png', npim)[1].tostring()
else:
pngbytes = io.BytesIO()
pngmode = 'L' # Greyscale
if len(npim.shape) == 3:
if npim.shape[2] > 4:
raise Exception("Image numpy array appears to have more than 4 channels")
pngmode = ('L','LA','RGB','RGBA')[npim.shape[2]-1]
else:
npim = np.expand_dims(npim, axis=-1) # Need a third axis for channel
pngim = png.from_array(npim, mode=pngmode) # Don't have BGR available so flipped to RGB above
pngim.write(pngbytes) if hasattr(pngim, 'write') else pngim.save(pngbytes) # PyPNG API due to change after v0.0.19
self.get_widget().value = pngbytes.getvalue()
pngbytes.close()

else:
# Actual raw image data
self.get_widget().value = it
@@ -1,4 +1,5 @@
ipywidgets>=7.1.0
widgetsnbextension>=3.1.0
notebook>=5.3.0
numpy
numpy>=1.4.0
pypng>=0.0.19

0 comments on commit 82b5889

Please sign in to comment.
You can’t perform that action at this time.