diff --git a/gradio.egg-info/PKG-INFO b/gradio.egg-info/PKG-INFO index d9113ad17e..ba0d00ffb5 100644 --- a/gradio.egg-info/PKG-INFO +++ b/gradio.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: gradio -Version: 1.7.0 +Version: 1.7.1 Summary: Python library for easily interacting with trained machine learning models Home-page: https://github.com/gradio-app/gradio-UI Author: Abubakar Abid diff --git a/gradio.egg-info/requires.txt b/gradio.egg-info/requires.txt index 26a62435ba..1f28d63fc3 100644 --- a/gradio.egg-info/requires.txt +++ b/gradio.egg-info/requires.txt @@ -6,12 +6,8 @@ flask-cachebuster Flask-Login paramiko scipy -IPython -scikit-image analytics-python pandas ffmpy -librosa -colorama>=0.3.9 markdown2 pycryptodome diff --git a/gradio/inputs.py b/gradio/inputs.py index 7422c615da..d923aec38f 100644 --- a/gradio/inputs.py +++ b/gradio/inputs.py @@ -14,7 +14,6 @@ from gradio.component import Component import base64 import numpy as np import PIL -from skimage.segmentation import slic import scipy.io.wavfile from gradio import processing_utils, test_data import pandas as pd @@ -678,6 +677,11 @@ class Image(InputComponent): if self.shape is not None: x = processing_utils.resize_and_crop(x, self.shape) image = np.array(x) + try: + from skimage.segmentation import slic + except ImportError: + print("Running default interpretation for images requires scikit-image, please install it first.") + return segments_slic = slic(image, self.interpretation_segments, compactness=10, sigma=1) leave_one_out_tokens, masks = [], [] replace_color = np.mean(image, axis=(0, 1)) diff --git a/gradio/interface.py b/gradio/interface.py index 2ba4c9782e..51e3a79151 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -456,15 +456,18 @@ class Interface: if inline is None: inline = utils.ipython_check() if inline: - from IPython.display import IFrame, display - # Embed the remote interface page if on google colab; otherwise, embed the local page. - print(strings.en["INLINE_DISPLAY_BELOW"]) - if share: - while not networking.url_ok(share_url): - time.sleep(1) - display(IFrame(share_url, width=1000, height=500)) - else: - display(IFrame(path_to_local_server, width=1000, height=500)) + try: + from IPython.display import IFrame, display + # Embed the remote interface page if on google colab; otherwise, embed the local page. + print(strings.en["INLINE_DISPLAY_BELOW"]) + if share: + while not networking.url_ok(share_url): + time.sleep(1) + display(IFrame(share_url, width=1000, height=500)) + else: + display(IFrame(path_to_local_server, width=1000, height=500)) + except ImportError: + pass # IPython is not available so does not print inline. send_launch_analytics(analytics_enabled=self.analytics_enabled, inbrowser=inbrowser, is_colab=is_colab, share=share, share_url=share_url) diff --git a/gradio/processing_utils.py b/gradio/processing_utils.py index 99f1a2c7b3..2a30e60f16 100644 --- a/gradio/processing_utils.py +++ b/gradio/processing_utils.py @@ -5,7 +5,6 @@ import tempfile import scipy.io.wavfile from scipy.fftpack import dct import numpy as np -import skimage from gradio import encryptor ######################### @@ -37,7 +36,7 @@ def encode_plot_to_base64(plt): def encode_array_to_base64(image_array): with BytesIO() as output_bytes: - PIL_image = Image.fromarray(skimage.img_as_ubyte(image_array)) + PIL_image = Image.fromarray(_convert(image_array, np.uint8, force_copy=False)) PIL_image.save(output_bytes, 'PNG') bytes_data = output_bytes.getvalue() base64_str = str(base64.b64encode(bytes_data), 'utf-8') @@ -92,6 +91,290 @@ def decode_base64_to_file(encoding, encryption_key=None): file_obj.flush() return file_obj +def _convert(image, dtype, force_copy=False, uniform=False): + """ + Adapted from: https://github.com/scikit-image/scikit-image/blob/main/skimage/util/dtype.py#L510-L531 + + Convert an image to the requested data-type. + Warnings are issued in case of precision loss, or when negative values + are clipped during conversion to unsigned integer types (sign loss). + Floating point values are expected to be normalized and will be clipped + to the range [0.0, 1.0] or [-1.0, 1.0] when converting to unsigned or + signed integers respectively. + Numbers are not shifted to the negative side when converting from + unsigned to signed integer types. Negative values will be clipped when + converting to unsigned integers. + Parameters + ---------- + image : ndarray + Input image. + dtype : dtype + Target data-type. + force_copy : bool, optional + Force a copy of the data, irrespective of its current dtype. + uniform : bool, optional + Uniformly quantize the floating point range to the integer range. + By default (uniform=False) floating point values are scaled and + rounded to the nearest integers, which minimizes back and forth + conversion errors. + .. versionchanged :: 0.15 + ``_convert`` no longer warns about possible precision or sign + information loss. See discussions on these warnings at: + https://github.com/scikit-image/scikit-image/issues/2602 + https://github.com/scikit-image/scikit-image/issues/543#issuecomment-208202228 + https://github.com/scikit-image/scikit-image/pull/3575 + References + ---------- + .. [1] DirectX data conversion rules. + https://msdn.microsoft.com/en-us/library/windows/desktop/dd607323%28v=vs.85%29.aspx + .. [2] Data Conversions. In "OpenGL ES 2.0 Specification v2.0.25", + pp 7-8. Khronos Group, 2010. + .. [3] Proper treatment of pixels as integers. A.W. Paeth. + In "Graphics Gems I", pp 249-256. Morgan Kaufmann, 1990. + .. [4] Dirty Pixels. J. Blinn. In "Jim Blinn's corner: Dirty Pixels", + pp 47-57. Morgan Kaufmann, 1998. + """ + dtype_range = {bool: (False, True), + np.bool_: (False, True), + np.bool8: (False, True), + float: (-1, 1), + np.float_: (-1, 1), + np.float16: (-1, 1), + np.float32: (-1, 1), + np.float64: (-1, 1)} + + def _dtype_itemsize(itemsize, *dtypes): + """Return first of `dtypes` with itemsize greater than `itemsize` + Parameters + ---------- + itemsize: int + The data type object element size. + Other Parameters + ---------------- + *dtypes: + Any Object accepted by `np.dtype` to be converted to a data + type object + Returns + ------- + dtype: data type object + First of `dtypes` with itemsize greater than `itemsize`. + """ + return next(dt for dt in dtypes if np.dtype(dt).itemsize >= itemsize) + + def _dtype_bits(kind, bits, itemsize=1): + """Return dtype of `kind` that can store a `bits` wide unsigned int + Parameters: + kind: str + Data type kind. + bits: int + Desired number of bits. + itemsize: int + The data type object element size. + Returns + ------- + dtype: data type object + Data type of `kind` that can store a `bits` wide unsigned int + """ + + s = next(i for i in (itemsize, ) + (2, 4, 8) if + bits < (i * 8) or (bits == (i * 8) and kind == 'u')) + + return np.dtype(kind + str(s)) + + + def _scale(a, n, m, copy=True): + """Scale an array of unsigned/positive integers from `n` to `m` bits. + Numbers can be represented exactly only if `m` is a multiple of `n`. + Parameters + ---------- + a : ndarray + Input image array. + n : int + Number of bits currently used to encode the values in `a`. + m : int + Desired number of bits to encode the values in `out`. + copy : bool, optional + If True, allocates and returns new array. Otherwise, modifies + `a` in place. + Returns + ------- + out : array + Output image array. Has the same kind as `a`. + """ + kind = a.dtype.kind + if n > m and a.max() < 2 ** m: + mnew = int(np.ceil(m / 2) * 2) + if mnew > m: + dtype = "int{}".format(mnew) + else: + dtype = "uint{}".format(mnew) + n = int(np.ceil(n / 2) * 2) + return a.astype(_dtype_bits(kind, m)) + elif n == m: + return a.copy() if copy else a + elif n > m: + # downscale with precision loss + if copy: + b = np.empty(a.shape, _dtype_bits(kind, m)) + np.floor_divide(a, 2**(n - m), out=b, dtype=a.dtype, + casting='unsafe') + return b + else: + a //= 2**(n - m) + return a + elif m % n == 0: + # exact upscale to a multiple of `n` bits + if copy: + b = np.empty(a.shape, _dtype_bits(kind, m)) + np.multiply(a, (2**m - 1) // (2**n - 1), out=b, dtype=b.dtype) + return b + else: + a = a.astype(_dtype_bits(kind, m, a.dtype.itemsize), copy=False) + a *= (2**m - 1) // (2**n - 1) + return a + else: + # upscale to a multiple of `n` bits, + # then downscale with precision loss + o = (m // n + 1) * n + if copy: + b = np.empty(a.shape, _dtype_bits(kind, o)) + np.multiply(a, (2**o - 1) // (2**n - 1), out=b, dtype=b.dtype) + b //= 2**(o - m) + return b + else: + a = a.astype(_dtype_bits(kind, o, a.dtype.itemsize), copy=False) + a *= (2**o - 1) // (2**n - 1) + a //= 2**(o - m) + return a + + image = np.asarray(image) + dtypeobj_in = image.dtype + if dtype is np.floating: + dtypeobj_out = np.dtype('float64') + else: + dtypeobj_out = np.dtype(dtype) + dtype_in = dtypeobj_in.type + dtype_out = dtypeobj_out.type + kind_in = dtypeobj_in.kind + kind_out = dtypeobj_out.kind + itemsize_in = dtypeobj_in.itemsize + itemsize_out = dtypeobj_out.itemsize + + # Below, we do an `issubdtype` check. Its purpose is to find out + # whether we can get away without doing any image conversion. This happens + # when: + # + # - the output and input dtypes are the same or + # - when the output is specified as a type, and the input dtype + # is a subclass of that type (e.g. `np.floating` will allow + # `float32` and `float64` arrays through) + + if np.issubdtype(dtype_in, np.obj2sctype(dtype)): + if force_copy: + image = image.copy() + return image + + if kind_in in 'ui': + imin_in = np.iinfo(dtype_in).min + imax_in = np.iinfo(dtype_in).max + if kind_out in 'ui': + imin_out = np.iinfo(dtype_out).min + imax_out = np.iinfo(dtype_out).max + + # any -> binary + if kind_out == 'b': + return image > dtype_in(dtype_range[dtype_in][1] / 2) + + # binary -> any + if kind_in == 'b': + result = image.astype(dtype_out) + if kind_out != 'f': + result *= dtype_out(dtype_range[dtype_out][1]) + return result + + + # float -> any + if kind_in == 'f': + if kind_out == 'f': + # float -> float + return image.astype(dtype_out) + + if np.min(image) < -1.0 or np.max(image) > 1.0: + raise ValueError("Images of type float must be between -1 and 1.") + # floating point -> integer + # use float type that can represent output integer type + computation_type = _dtype_itemsize(itemsize_out, dtype_in, + np.float32, np.float64) + + if not uniform: + if kind_out == 'u': + image_out = np.multiply(image, imax_out, + dtype=computation_type) + else: + image_out = np.multiply(image, (imax_out - imin_out) / 2, + dtype=computation_type) + image_out -= 1.0 / 2. + np.rint(image_out, out=image_out) + np.clip(image_out, imin_out, imax_out, out=image_out) + elif kind_out == 'u': + image_out = np.multiply(image, imax_out + 1, + dtype=computation_type) + np.clip(image_out, 0, imax_out, out=image_out) + else: + image_out = np.multiply(image, (imax_out - imin_out + 1.0) / 2.0, + dtype=computation_type) + np.floor(image_out, out=image_out) + np.clip(image_out, imin_out, imax_out, out=image_out) + return image_out.astype(dtype_out) + + # signed/unsigned int -> float + if kind_out == 'f': + # use float type that can exactly represent input integers + computation_type = _dtype_itemsize(itemsize_in, dtype_out, + np.float32, np.float64) + + if kind_in == 'u': + # using np.divide or np.multiply doesn't copy the data + # until the computation time + image = np.multiply(image, 1. / imax_in, + dtype=computation_type) + # DirectX uses this conversion also for signed ints + # if imin_in: + # np.maximum(image, -1.0, out=image) + else: + image = np.add(image, 0.5, dtype=computation_type) + image *= 2 / (imax_in - imin_in) + + return np.asarray(image, dtype_out) + + # unsigned int -> signed/unsigned int + if kind_in == 'u': + if kind_out == 'i': + # unsigned int -> signed int + image = _scale(image, 8 * itemsize_in, 8 * itemsize_out - 1) + return image.view(dtype_out) + else: + # unsigned int -> unsigned int + return _scale(image, 8 * itemsize_in, 8 * itemsize_out) + + # signed int -> unsigned int + if kind_out == 'u': + image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out) + result = np.empty(image.shape, dtype_out) + np.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe') + return result + + # signed int -> signed int + if itemsize_in > itemsize_out: + return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1) + + image = image.astype(_dtype_bits('i', itemsize_out * 8)) + image -= imin_in + image = _scale(image, 8 * itemsize_in, 8 * itemsize_out, copy=False) + image += imin_out + return image.astype(dtype_out) + + ################## # AUDIO FILES ################## diff --git a/gradio/utils.py b/gradio/utils.py index 5fb72bad1d..db9385a9ac 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -1,7 +1,6 @@ import requests import pkg_resources from distutils.version import StrictVersion -from IPython import get_ipython analytics_url = 'https://api.gradio.app/' PKG_VERSION_URL = "https://api.gradio.app/pkg-version" @@ -40,10 +39,11 @@ def colab_check(): """ is_colab = False try: # Check if running interactively using ipython. + from IPython import get_ipython from_ipynb = get_ipython() if "google.colab" in str(from_ipynb): is_colab = True - except NameError: + except (ImportError, NameError): error_analytics("NameError") return is_colab @@ -54,9 +54,10 @@ def ipython_check(): :return is_ipython (bool): True or False """ try: # Check if running interactively using ipython. + from IPython import get_ipython get_ipython() is_ipython = True - except NameError: + except (ImportError, NameError): is_ipython = False return is_ipython diff --git a/setup.py b/setup.py index 0dedaa0634..0e7569b516 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ except ImportError: setup( name='gradio', - version='1.7.0', + version='1.7.1', include_package_data=True, description='Python library for easily interacting with trained machine learning models', author='Abubakar Abid', @@ -22,13 +22,9 @@ setup( 'Flask-Login', 'paramiko', 'scipy', - 'IPython', - 'scikit-image', 'analytics-python', 'pandas', 'ffmpy', - 'librosa', - 'colorama >= 0.3.9', 'markdown2', 'pycryptodome' ],