diff --git a/mathics/builtin/__init__.py b/mathics/builtin/__init__.py
index 594b3d2eba..3d605df166 100755
--- a/mathics/builtin/__init__.py
+++ b/mathics/builtin/__init__.py
@@ -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
diff --git a/mathics/builtin/base.py b/mathics/builtin/base.py
index f56c7afca7..1f9693957b 100644
--- a/mathics/builtin/base.py
+++ b/mathics/builtin/base.py
@@ -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
diff --git a/mathics/builtin/image.py b/mathics/builtin/image.py
index 0ce4f500eb..b0ab9612bc 100644
--- a/mathics/builtin/image.py
+++ b/mathics/builtin/image.py
@@ -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
@@ -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')
+ '''
+
+ - 'BinaryImageQ[$image]'
+
- returns True if the pixels of $image are binary bit values, and False otherwise.
+
+ '''
+
+ def test(self, expr):
+ return isinstance(expr, Image) and expr.storage_type() == 'Bit'
# Image core classes
@@ -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):
+ '''
+
+ - 'ImageQ[Image[$pixels]]'
+
- returns True if $pixels has dimensions from which an Image can be constructed, and False otherwise.
+
+
+ >> 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):
@@ -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 = '
' % (leaves[0].get_string_value())
+ img = '
' % (
+ leaves[0].get_string_value(), leaves[1].get_int_value(), leaves[2].get_int_value())
- # see https://github.com/mathjax/MathJax/issues/896
- xml = '%s' % img
- return xml
+ # if we have Mathics JavaScript frontend processing that rewrites MathML tags using
+ # , we must not embed our tag in here.
+ uses_mathics_frontend_processing = False
+
+ if not uses_mathics_frontend_processing:
+ # see https://github.com/mathjax/MathJax/issues/896
+ xml = '%s' % img
+ return xml
+ else:
+ return img
def boxes_to_tex(self, leaves, **options):
return '-Image-'
@@ -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')
@@ -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")
@@ -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)
diff --git a/mathics/web/media/js/mathics.js b/mathics/web/media/js/mathics.js
index 2ea9d0d8e8..3feed392b2 100644
--- a/mathics/web/media/js/mathics.js
+++ b/mathics/web/media/js/mathics.js
@@ -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 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 {