mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-08 00:50:59 +08:00
Added implementation and doc string
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
from skimage import io, data
|
||||
from skimage import transform
|
||||
from skimage import color, filters
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
def custom_sobel(img):
|
||||
if img.ndim == 3:
|
||||
img = color.rgb2gray(img)
|
||||
|
||||
return filters.sobel(img)
|
||||
|
||||
img = data.coins()
|
||||
out = transform.seam_carve(img, 'vertical', 80, energy_func = custom_sobel)
|
||||
out = transform.seam_carve(out, 'horizontal', 70, energy_func = custom_sobel)
|
||||
|
||||
io.imshow(out)
|
||||
plt.figure()
|
||||
io.imshow(img)
|
||||
io.show()
|
||||
@@ -12,6 +12,7 @@ from ._geometric import (warp, warp_coords, estimate_transform,
|
||||
from ._warps import swirl, resize, rotate, rescale, downscale_local_mean
|
||||
from .pyramids import (pyramid_reduce, pyramid_expand,
|
||||
pyramid_gaussian, pyramid_laplacian)
|
||||
from seam_carving import seam_carve
|
||||
|
||||
|
||||
__all__ = ['hough_circle',
|
||||
@@ -43,4 +44,5 @@ __all__ = ['hough_circle',
|
||||
'pyramid_reduce',
|
||||
'pyramid_expand',
|
||||
'pyramid_gaussian',
|
||||
'pyramid_laplacian']
|
||||
'pyramid_laplacian',
|
||||
'seam_carve']
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
# cython: cdivision=True
|
||||
# cython: boundscheck=False
|
||||
# cython: nonecheck=False
|
||||
# cython: wraparound=False
|
||||
import numpy as np
|
||||
cimport numpy as cnp
|
||||
|
||||
|
||||
cdef cnp.double_t ABSOLUTE_MAX = np.finfo(np.double).max
|
||||
|
||||
|
||||
cdef find_seam_v(cnp.double_t[:, ::1] energy_img, cnp.int8_t[:, ::1] track_img,
|
||||
cnp.double_t[::1] current_cost, cnp.double_t[::1] prev_cost,
|
||||
Py_ssize_t cols):
|
||||
"""Find a single vertical seam in an image that will be removed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_img : (M, N) ndarray
|
||||
The energy image where a higher value signifies a pixel of more
|
||||
importance.
|
||||
track_img : (M, N) ndarray
|
||||
The image used to store the optimal decision made at each point while
|
||||
finding a minimum cost path.
|
||||
current_cost : (N, ) ndarray
|
||||
An array to store the current cost of the optimal path for each column
|
||||
in row currently being processed.
|
||||
prev_cost : (N, ) ndarray
|
||||
An array to store the current cost of the optimal path for each column
|
||||
in row prior to the one being processed.
|
||||
cols : int
|
||||
The number of cols to process for seam carving. Columns with indices
|
||||
more than `cols` are ignored.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
seam : (M, ) ndarray
|
||||
An array containing the index of the row of the pixel to be removed
|
||||
for each column in the image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`track_img`, `current_cost` and `prev_cost` are passed as arguments to
|
||||
avoid memory allocation at each iteration of `_seam_carve_v`.
|
||||
"""
|
||||
|
||||
cdef Py_ssize_t rows, row, col
|
||||
rows = energy_img.shape[0]
|
||||
cdef cnp.double_t tmp, min_cost
|
||||
cdef Py_ssize_t offset, idx, offset_clip
|
||||
|
||||
cdef Py_ssize_t[::1] seam = np.zeros(rows, dtype=np.int)
|
||||
|
||||
for idx in range(cols):
|
||||
prev_cost[idx] = energy_img[0, idx]
|
||||
|
||||
for row in range(1, rows):
|
||||
for col in range(0, cols):
|
||||
|
||||
min_cost = ABSOLUTE_MAX
|
||||
for offset in range(-1, 2):
|
||||
idx = col + offset
|
||||
|
||||
if idx > cols - 1 or idx < 0:
|
||||
continue
|
||||
|
||||
if prev_cost[idx] < min_cost:
|
||||
min_cost = prev_cost[idx]
|
||||
track_img[row, col] = offset
|
||||
|
||||
current_cost[col] = min_cost + energy_img[row, col]
|
||||
|
||||
prev_cost[:] = current_cost
|
||||
|
||||
seam[rows-1] = np.argmin(current_cost)
|
||||
|
||||
for row in range(rows-2, -1, -1):
|
||||
col = seam[row + 1]
|
||||
offset = track_img[row, col]
|
||||
#print offset
|
||||
seam[row] = seam[row + 1] + offset
|
||||
|
||||
return seam
|
||||
|
||||
|
||||
cdef remove_seam_h_2d(cnp.double_t[:, ::1] img, Py_ssize_t[::1] seam,
|
||||
Py_ssize_t cols):
|
||||
cdef Py_ssize_t rows, row, col, idx
|
||||
rows = img.shape[0]
|
||||
""" Removes one horizontal seam from the image.
|
||||
|
||||
The method modifies `img` so that all pixels to the right of the vertical
|
||||
seam are pushed one place left.
|
||||
|
||||
image : (M, N) ndarray
|
||||
Input image whose vertical seam is to be removed.
|
||||
seam : (M, ) ndarray
|
||||
An array use to store the index of the column in the seam for each row.
|
||||
cols : int
|
||||
Number of columns in the input image to process. Column indices more
|
||||
than `cols` are ingored.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`seam` is passed as an argument so that we don't have to reallocate it for
|
||||
each iteration in `_seam_carve_v`.
|
||||
""""
|
||||
|
||||
for row in range(rows):
|
||||
for idx in range(seam[row], cols - 1):
|
||||
img[row, idx] = img[row, idx + 1]
|
||||
|
||||
|
||||
cdef remove_seam_h_3d(cnp.double_t[:, :, ::1] img, Py_ssize_t[::1] seam,
|
||||
Py_ssize_t cols):
|
||||
""" Removes one horizontal seam from the image.
|
||||
|
||||
The method modifies `img` so that all pixels to the right of the vertical
|
||||
seam are pushed one place left.
|
||||
|
||||
image : (M, N, 3) ndarray
|
||||
Input image whose vertical seam is to be removed.
|
||||
seam : (M, ) ndarray
|
||||
An array use to store the index of the column in the seam for each row.
|
||||
cols : int
|
||||
Number of columns in the input image to process. Column indices more
|
||||
than `cols` are ingored.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`seam` is passed as an argument so that we don't have to reallocate it for
|
||||
each iteration in `_seam_carve_v`.
|
||||
""""
|
||||
cdef Py_ssize_t rows, row, col, idx
|
||||
rows = img.shape[0]
|
||||
|
||||
for row in range(rows):
|
||||
for idx in range(seam[row], cols - 1):
|
||||
img[row, idx, :] = img[row, idx + 1, :]
|
||||
|
||||
|
||||
def _seam_carve_v(img, iters, energy_func, extra_args , extra_kwargs, border):
|
||||
""" Carve vertical seams off an image.
|
||||
|
||||
Carves out vertical seams off an image while using the given energy
|
||||
function to decide the importance of each pixel.[1]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : (M, N) or (M, N, 3) ndarray
|
||||
Input image whose vertical seams are to be removed.
|
||||
iters : int
|
||||
Number of vertical seams are to be removed.
|
||||
energy_func : callable
|
||||
The function used to decide the importance of each pixel. The higher
|
||||
the value corresponding to a pixel, the more the algorithm will try
|
||||
to keep it in the image. For every iteration `energy_func` is called
|
||||
as `energy_func(image, *extra_args, **extra_kwargs)`, where `image`
|
||||
is the cropped image during each iteration and is expected to return a
|
||||
(M, N) ndarray depicting each pixel's importance.
|
||||
extra_args : iterable
|
||||
The extra arguments supplied to `energy_func`.
|
||||
extra_kwargs : dict
|
||||
The extra keyword arguments supplied to `energy_func`.
|
||||
border : int
|
||||
The number of pixels in the right and left end of the image to be
|
||||
excluded from being considered for a seam. This is important as certain
|
||||
filters just ignore image boundaries and set them to `0`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
image : (M, N - iters) or (M, N - iters, 3) ndarray
|
||||
The cropped image with the vertical seams removed.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Shai Avidan and Ariel Shamir
|
||||
"Seam Carving for Content-Aware Image Resizing"
|
||||
http://www.cs.jhu.edu/~misha/ReadingSeminar/Papers/Avidan07.pdf
|
||||
"""
|
||||
cdef Py_ssize_t[::1] seam
|
||||
cdef Py_ssize_t ndim = img.ndim
|
||||
cdef Py_ssize_t cols = img.shape[1]
|
||||
|
||||
track_img = np.zeros(img.shape[0:2], dtype=np.int8)
|
||||
|
||||
current_cost = np.zeros_like(track_img[0], dtype = img.dtype)
|
||||
prev_cost = np.zeros_like(track_img[0], dtype = img.dtype)
|
||||
|
||||
for i in range(iters):
|
||||
|
||||
sliced_img = img[:, 0:cols]
|
||||
energy_img = energy_func(sliced_img, *extra_args, **extra_kwargs)
|
||||
|
||||
# So that borders are ignored.
|
||||
energy_img[:, 0:border] = ABSOLUTE_MAX
|
||||
energy_img[:, cols-border:cols] = ABSOLUTE_MAX
|
||||
|
||||
seam = find_seam_v(energy_img, track_img, current_cost, prev_cost,
|
||||
cols)
|
||||
|
||||
if ndim == 2:
|
||||
remove_seam_h_2d(img, seam, cols)
|
||||
elif ndim == 3:
|
||||
remove_seam_h_3d(img, seam, cols)
|
||||
|
||||
cols -= 1
|
||||
|
||||
return img[:, 0:cols]
|
||||
@@ -0,0 +1,73 @@
|
||||
from _seam_carving import _seam_carve_h
|
||||
from ..import filters
|
||||
from .. import util
|
||||
from .._shared import utils
|
||||
import numpy as np
|
||||
|
||||
|
||||
def seam_carve(img, mode, num, energy_func, extra_args = [],
|
||||
extra_kwargs = {}, border=1, force_copy = True):
|
||||
""" Carve vertical or horizontal seams off an image.
|
||||
|
||||
Carves out vertical/horizontal seams off an image while using the given
|
||||
energy function to decide the importance of each pixel.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : (M, N) or (M, N, 3) ndarray
|
||||
Input image whose vertical seams are to be removed.
|
||||
mode : str {'horizontal', 'vertical'}
|
||||
Indicates whether seams are to be removed vertically or horizontally.
|
||||
Removing seams horizontally will decrease the height whereas removing
|
||||
vertically will decrease the width.
|
||||
num : int
|
||||
Number of seams are to be removed.
|
||||
energy_func : callable
|
||||
The function used to decide the importance of each pixel. The higher
|
||||
the value corresponding to a pixel, the more the algorithm will try
|
||||
to keep it in the image. For every iteration `energy_func` is called
|
||||
as `energy_func(image, *extra_args, **extra_kwargs)`, where `image`
|
||||
is the cropped image during each iteration and is expected to return a
|
||||
(M, N) ndarray depicting each pixel's importance.
|
||||
extra_args : iterable, optional
|
||||
The extra arguments supplied to `energy_func`.
|
||||
extra_kwargs : dict, optional
|
||||
The extra keyword arguments supplied to `energy_func`.
|
||||
border : int, optional
|
||||
The number of pixels in the right and left end of the image to be
|
||||
excluded from being considered for a seam. This is important as certain
|
||||
filters just ignore image boundaries and set them to `0`. By default
|
||||
border is set to `1`.
|
||||
force_copy : bool, optional
|
||||
If set, the image is copied before being used by the method which
|
||||
modifies it in place. Set this to `False` if the original image is no
|
||||
loner needed after this opetration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The cropped image with the seams removed.
|
||||
"""
|
||||
|
||||
utils.assert_nD(img, (2,3))
|
||||
img = util.img_as_float(img)
|
||||
|
||||
|
||||
if mode == 'horizontal':
|
||||
img = np.ascontiguousarray(img)
|
||||
return _seam_carve_h(img, num, energy_func, extra_args ,extra_kwargs,
|
||||
border)
|
||||
elif mode == 'vertical' :
|
||||
if img.ndim == 3:
|
||||
img = np.transpose(img, (1, 0, 2))
|
||||
else:
|
||||
img = img.T
|
||||
|
||||
img = np.ascontiguousarray(img)
|
||||
out = _seam_carve_h(img, num, energy_func, extra_args , extra_kwargs,
|
||||
border)
|
||||
|
||||
if img.ndim == 3:
|
||||
return np.transpose(out, (1, 0, 2))
|
||||
else:
|
||||
return out.T
|
||||
@@ -16,6 +16,7 @@ def configuration(parent_package='', top_path=None):
|
||||
cython(['_hough_transform.pyx'], working_path=base_path)
|
||||
cython(['_warps_cy.pyx'], working_path=base_path)
|
||||
cython(['_radon_transform.pyx'], working_path=base_path)
|
||||
cython(['_seam_carving.pyx'], working_path=base_path)
|
||||
|
||||
config.add_extension('_hough_transform', sources=['_hough_transform.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
@@ -27,6 +28,8 @@ def configuration(parent_package='', top_path=None):
|
||||
sources=['_radon_transform.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
|
||||
config.add_extension('_seam_carving', sources=['_seam_carving.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
return config
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user