Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
Add imresize and copyMakeBorder to mx.image (#13357)
Browse files Browse the repository at this point in the history
* Add imresize API to docs

* address comments

* copyMakeBorder
  • Loading branch information
anirudhacharya authored and sandeep-krishnamurthy committed Dec 11, 2018
1 parent ba02bf2 commit 75d1d4f
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 7 deletions.
4 changes: 4 additions & 0 deletions docs/api/python/image/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ images provided in
image.imread
image.imdecode
image.imresize
image.scale_down
image.copyMakeBorder
image.resize_short
image.fixed_crop
image.random_crop
Expand Down Expand Up @@ -166,7 +168,9 @@ and a list of augmenters specific for `Object detection` is provided
.. automethod:: mxnet.image.imread
.. automethod:: mxnet.image.imdecode
.. automethod:: mxnet.image.imresize
.. automethod:: mxnet.image.scale_down
.. automethod:: mxnet.image.copyMakeBorder
.. automethod:: mxnet.image.resize_short
.. automethod:: mxnet.image.fixed_crop
.. automethod:: mxnet.image.random_crop
Expand Down
118 changes: 113 additions & 5 deletions python/mxnet/image/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@
from ..base import numeric_types
from .. import ndarray as nd
from ..ndarray import _internal
from ..ndarray._internal import _cvimresize as imresize
from ..ndarray._internal import _cvcopyMakeBorder as copyMakeBorder
from .. import io
from .. import recordio


def imread(filename, *args, **kwargs):
"""Read and decode an image to an NDArray.
Note: `imread` uses OpenCV (not the CV2 Python library).
.. note:: `imread` uses OpenCV (not the CV2 Python library).
MXNet must have been built with USE_OPENCV=1 for `imdecode` to work.
Parameters
Expand Down Expand Up @@ -85,10 +83,67 @@ def imread(filename, *args, **kwargs):
return _internal._cvimread(filename, *args, **kwargs)


def imresize(src, w, h, *args, **kwargs):
r"""Resize image with OpenCV.
.. note:: `imresize` uses OpenCV (not the CV2 Python library). MXNet must have been built
with USE_OPENCV=1 for `imresize` to work.
Parameters
----------
src : NDArray
source image
w : int, required
Width of resized image.
h : int, required
Height of resized image.
interp : int, optional, default=1
Interpolation method (default=cv2.INTER_LINEAR).
Possible values:
0: Nearest Neighbors Interpolation.
1: Bilinear interpolation.
2: Area-based (resampling using pixel area relation). It may be a
preferred method for image decimation, as it gives moire-free
results. But when the image is zoomed, it is similar to the Nearest
Neighbors method. (used by default).
3: Bicubic interpolation over 4x4 pixel neighborhood.
4: Lanczos interpolation over 8x8 pixel neighborhood.
9: Cubic for enlarge, area for shrink, bilinear for others
10: Random select from interpolation method metioned above.
Note:
When shrinking an image, it will generally look best with AREA-based
interpolation, whereas, when enlarging an image, it will generally look best
with Bicubic (slow) or Bilinear (faster but still looks OK).
More details can be found in the documentation of OpenCV, please refer to
http://docs.opencv.org/master/da/d54/group__imgproc__transform.html.
out : NDArray, optional
The output NDArray to hold the result.
Returns
-------
out : NDArray or list of NDArrays
The output of this function.
Example
-------
>>> with open("flower.jpeg", 'rb') as fp:
... str_image = fp.read()
...
>>> image = mx.img.imdecode(str_image)
>>> image
<NDArray 2321x3482x3 @cpu(0)>
>>> new_image = mx.img.resize(image, 240, 360)
>>> new_image
<NDArray 240x360x3 @cpu(0)>
"""
return _internal._cvimresize(src, w, h, *args, **kwargs)


def imdecode(buf, *args, **kwargs):
"""Decode an image to an NDArray.
Note: `imdecode` uses OpenCV (not the CV2 Python library).
.. note:: `imdecode` uses OpenCV (not the CV2 Python library).
MXNet must have been built with USE_OPENCV=1 for `imdecode` to work.
Parameters
Expand Down Expand Up @@ -178,6 +233,59 @@ def scale_down(src_size, size):
return int(w), int(h)


def copyMakeBorder(src, top, bot, left, right, *args, **kwargs):
"""Pad image border with OpenCV.
Parameters
----------
src : NDArray
source image
top : int, required
Top margin.
bot : int, required
Bottom margin.
left : int, required
Left margin.
right : int, required
Right margin.
type : int, optional, default='0'
Filling type (default=cv2.BORDER_CONSTANT).
0 - cv2.BORDER_CONSTANT - Adds a constant colored border.
1 - cv2.BORDER_REFLECT - Border will be mirror reflection of the
border elements, like this : fedcba|abcdefgh|hgfedcb
2 - cv2.BORDER_REFLECT_101 or cv.BORDER_DEFAULT - Same as above,
but with a slight change, like this : gfedcb|abcdefgh|gfedcba
3 - cv2.BORDER_REPLICATE - Last element is replicated throughout,
like this: aaaaaa|abcdefgh|hhhhhhh
4 - cv2.BORDER_WRAP - it will look like this : cdefgh|abcdefgh|abcdefg
value : double, optional, default=0
(Deprecated! Use ``values`` instead.) Fill with single value.
values : tuple of <double>, optional, default=[]
Fill with value(RGB[A] or gray), up to 4 channels.
out : NDArray, optional
The output NDArray to hold the result.
Returns
-------
out : NDArray or list of NDArrays
The output of this function.
Example
--------
>>> with open("flower.jpeg", 'rb') as fp:
... str_image = fp.read()
...
>>> image = mx.img.imdecode(str_image)
>>> image
<NDArray 2321x3482x3 @cpu(0)>
>>> new_image = mx_border = mx.image.copyMakeBorder(mx_img, 1, 2, 3, 4, type=0)
>>> new_image
<NDArray 2324x3489x3 @cpu(0)>
"""
return _internal._cvcopyMakeBorder(src, top, bot, left, right, *args, **kwargs)


def _get_interp_method(interp, sizes=()):
"""Get the interpolation method for resize functions.
The major purpose of this function is to wrap a random interp method selection
Expand Down Expand Up @@ -236,7 +344,7 @@ def _get_interp_method(interp, sizes=()):
def resize_short(src, size, interp=2):
"""Resizes shorter edge to size.
Note: `resize_short` uses OpenCV (not the CV2 Python library).
.. note:: `resize_short` uses OpenCV (not the CV2 Python library).
MXNet must have been built with OpenCV for `resize_short` to work.
Resizes the original image by setting the shorter edge to size
Expand Down
49 changes: 47 additions & 2 deletions tests/python/unittest/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_imdecode(self):
try:
import cv2
except ImportError:
return
raise unittest.SkipTest("Unable to import cv2.")
for img in TestImage.IMAGES:
with open(img, 'rb') as fp:
str_image = fp.read()
Expand Down Expand Up @@ -175,11 +175,12 @@ def test_scale_down(self):
assert mx.image.scale_down((360, 1000), (480, 500)) == (360, 375)
assert mx.image.scale_down((300, 400), (0, 0)) == (0, 0)

@with_seed()
def test_resize_short(self):
try:
import cv2
except ImportError:
return
raise unittest.SkipTest("Unable to import cv2")
for img in TestImage.IMAGES:
cv_img = cv2.imread(img)
mx_img = mx.nd.array(cv_img[:, :, (2, 1, 0)])
Expand All @@ -196,6 +197,25 @@ def test_resize_short(self):
mx_resized = mx.image.resize_short(mx_img, new_size, interp)
assert_almost_equal(mx_resized.asnumpy()[:, :, (2, 1, 0)], cv_resized, atol=3)

@with_seed()
def test_imresize(self):
try:
import cv2
except ImportError:
raise unittest.SkipTest("Unable to import cv2")
for img in TestImage.IMAGES:
cv_img = cv2.imread(img)
mx_img = mx.nd.array(cv_img[:, :, (2, 1, 0)])
new_h = np.random.randint(1, 1000)
new_w = np.random.randint(1, 1000)
for interp_val in range(0, 2):
cv_resized = cv2.resize(cv_img, (new_w, new_h), interpolation=interp_val)
mx_resized = mx.image.imresize(mx_img, new_w, new_h, interp=interp_val)
assert_almost_equal(mx_resized.asnumpy()[:, :, (2, 1, 0)], cv_resized, atol=3)
out_img = mx.nd.zeros((new_h, new_w, 3), dtype=mx_img.dtype)
mx.image.imresize(mx_img, new_w, new_h, interp=interp_val, out=out_img)
assert_almost_equal(out_img.asnumpy()[:, :, (2, 1, 0)], cv_resized, atol=3)

def test_color_normalize(self):
for _ in range(10):
mean = np.random.rand(3) * 255
Expand Down Expand Up @@ -235,6 +255,31 @@ def test_imageiter(self):
]
_test_imageiter_last_batch(imageiter_list, (2, 3, 224, 224))

@with_seed()
def test_copyMakeBorder(self):
try:
import cv2
except ImportError:
raise unittest.SkipTest("Unable to import cv2")
for img in TestImage.IMAGES:
cv_img = cv2.imread(img)
mx_img = mx.nd.array(cv_img)
top = np.random.randint(1, 10)
bot = np.random.randint(1, 10)
left = np.random.randint(1, 10)
right = np.random.randint(1, 10)
new_h, new_w, _ = mx_img.shape
new_h += top + bot
new_w += left + right
val = [np.random.randint(1, 255)] * 3
for type_val in range(0, 5):
cv_border = cv2.copyMakeBorder(cv_img, top, bot, left, right, borderType=type_val, value=val)
mx_border = mx.image.copyMakeBorder(mx_img, top, bot, left, right, type=type_val, values=val)
assert_almost_equal(mx_border.asnumpy(), cv_border)
out_img = mx.nd.zeros((new_h , new_w, 3), dtype=mx_img.dtype)
mx.image.copyMakeBorder(mx_img, top, bot, left, right, type=type_val, values=val, out=out_img)
assert_almost_equal(out_img.asnumpy(), cv_border)

@with_seed()
def test_augmenters(self):
# ColorNormalizeAug
Expand Down

0 comments on commit 75d1d4f

Please sign in to comment.