Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions mathics/builtin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,3 @@ def contribute(definitions):
if not definitions.have_definition(ensure_context(operator)):
op = ensure_context(operator)
definitions.builtin[op] = Definition(name=op)

# Special case for Image[]: Image[] is an atom, and so Image[...]
# will not usually evaluate to anything, since there are no rules
# attached to it. we're adding one special rule here, that allows
# to construct Image atoms by using Image[] (using the helper
# builin ImageCreate).
from mathics.core.rules import Rule
from mathics.builtin.image import Image
from mathics.core.parser import parse_builtin_rule

definition = Definition(
name='System`Image', rules=[
Rule(parse_builtin_rule('Image[x_]'),
parse_builtin_rule('ImageCreate[x]'), system=True)])
definitions.builtin['System`Image'] = definition
10 changes: 10 additions & 0 deletions mathics/builtin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ def init(self, *args, **kwargs):
pass


class AtomBuiltin(Builtin):
# allows us to define apply functions, rules, messages, etc. for Atoms
# which are by default not in the definitions' contribution pipeline.
# see Image[] for an example of this.

def get_name(self):
name = super(AtomBuiltin, self).get_name()
return re.sub(r"Atom$", "", name)


class Operator(Builtin):
operator = None
precedence = None
Expand Down
85 changes: 65 additions & 20 deletions mathics/builtin/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from __future__ import division

from mathics.builtin.base import (
Builtin, Test, BoxConstruct, String)
Builtin, AtomBuiltin, Test, BoxConstruct, String)
from mathics.core.expression import (
Atom, Expression, Integer, Rational, Real, Symbol, from_python)
from mathics.core.evaluation import Evaluation
Expand Down Expand Up @@ -1114,9 +1114,15 @@ def apply(self, image, evaluation):


class BinaryImageQ(Test):
def apply(self, image, evaluation):
'BinaryImageQ[image_Image]'
return Symbol('True') if image.storage_type() == 'Bit' else Symbol('False')
'''
<dl>
<dt>'BinaryImageQ[$image]'
<dd>returns True if the pixels of $image are binary bit values, and False otherwise.
</dl>
'''

def test(self, expr):
return isinstance(expr, Image) and expr.storage_type() == 'Bit'


# Image core classes
Expand All @@ -1134,16 +1140,31 @@ def _image_pixels(matrix):
return None


class ImageCreate(Builtin):
def apply(self, array, evaluation):
'''ImageCreate[array_]'''
pixels = _image_pixels(array.to_python())
if pixels is not None:
shape = pixels.shape
is_rgb = (len(shape) == 3 and shape[2] == 3)
return Image(pixels.clip(0, 1), 'RGB' if is_rgb else 'Grayscale')
else:
return Symbol('$Aborted')
class ImageQ(Test):
'''
<dl>
<dt>'ImageQ[Image[$pixels]]'
<dd>returns True if $pixels has dimensions from which an Image can be constructed, and False otherwise.
</dl>

>> ImageQ[Image[{{0, 1}, {1, 0}}]]
= True

>> ImageQ[Image[{{{0, 0, 0}, {0, 1, 0}}, {{0, 1, 0}, {0, 1, 1}}}]]
= True

>> ImageQ[Image[{{{0, 0, 0}, {0, 1}}, {{0, 1, 0}, {0, 1, 1}}}]]
= False

>> ImageQ[Image[{1, 0, 1}]]
= False

>> ImageQ["abc"]
= False
'''

def test(self, expr):
return isinstance(expr, Image)


class ImageBox(BoxConstruct):
Expand All @@ -1152,11 +1173,19 @@ def boxes_to_text(self, leaves, **options):

def boxes_to_xml(self, leaves, **options):
# see https://tools.ietf.org/html/rfc2397
img = '<img src="data:image/png;base64,%s" />' % (leaves[0].get_string_value())
img = '<img src="data:image/png;base64,%s" width="%d" height="%d" />' % (
leaves[0].get_string_value(), leaves[1].get_int_value(), leaves[2].get_int_value())

# see https://github.com/mathjax/MathJax/issues/896
xml = '<mtext>%s</mtext>' % img
return xml
# if we have Mathics JavaScript frontend processing that rewrites MathML tags using
# <mspace>, we must not embed our tag in <mtext> here.
uses_mathics_frontend_processing = False

if not uses_mathics_frontend_processing:
# see https://github.com/mathjax/MathJax/issues/896
xml = '<mtext>%s</mtext>' % img
return xml
else:
return img

def boxes_to_tex(self, leaves, **options):
return '-Image-'
Expand Down Expand Up @@ -1198,12 +1227,16 @@ def make_boxes(self, form):

width = shape[1]
height = shape[0]
scaled_width = width
scaled_height = height

# if the image is very small, scale it up using nearest neighbour.
min_size = 128
if width < min_size and height < min_size:
scale = min_size / max(width, height)
pixels = skimage.transform.resize(pixels, (int(scale * height), int(scale * width)), order=0)
scaled_width = int(scale * width)
scaled_height = int(scale * height)
pixels = skimage.transform.resize(pixels, (scaled_height, scaled_width), order=0)

stream = BytesIO()
skimage.io.imsave(stream, pixels, 'pil', format_str='png')
Expand All @@ -1215,7 +1248,7 @@ def make_boxes(self, form):
if not six.PY2:
encoded = encoded.decode('utf8')

return Expression('ImageBox', String(encoded), Integer(width), Integer(height))
return Expression('ImageBox', String(encoded), Integer(scaled_width), Integer(scaled_height))
except:
return Symbol("$Failed")

Expand Down Expand Up @@ -1268,3 +1301,15 @@ def storage_type(self):
return 'Bit'
else:
return str(dtype)


class ImageAtom(AtomBuiltin):
def apply_create(self, array, evaluation):
'Image[array_]'
pixels = _image_pixels(array.to_python())
if pixels is not None:
shape = pixels.shape
is_rgb = (len(shape) == 3 and shape[2] == 3)
return Image(pixels.clip(0, 1), 'RGB' if is_rgb else 'Grayscale')
else:
return Expression('Image', array)
4 changes: 2 additions & 2 deletions mathics/web/media/js/mathics.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ function translateDOMElement(element, svg) {
drawGraphics3D(div, data);
dom = div;
}
if (nodeName == 'svg' || nodeName == 'graphics3d') {
if (nodeName == 'svg' || nodeName == 'graphics3d' || nodeName.toLowerCase() == 'img') {
// create <mspace> that will contain the graphics
object = createMathNode('mspace');
var width, height;
if (nodeName == 'svg') {
if (nodeName == 'svg' || nodeName.toLowerCase() == 'img') {
width = dom.getAttribute('width');
height = dom.getAttribute('height');
} else {
Expand Down