def posterize(image):
    """
    Posterize an image, that is, more or less to clamp each color to the nearest multiple of 1/4

    :param image: the image to posterize

    :return: the converted image
    """
    image = numpyArray(image)
    return np.where(
        image <= 0.25, 0.20,
        np.where(image <= 0.5, 0.40, np.where(image <= 0.75, 0.60, 0.80)))
def waveImage(size=(256,256),repeats=2,angle=0,wave='sine',radial=False):
    """
    create an image based on a sine, saw, or triangle wave function
    """
    ret=np.zeros(size)
    if radial:
        raise NotImplementedError() # TODO: Implement radial wave images
    else:
        twopi=2*pi
        thetas=np.arange(size[0])*float(repeats)/size[0]*twopi
        if wave=='sine':
            ret[:,:]=0.5+0.5*np.sin(thetas)
        elif wave=='saw':
            n=np.round(thetas/twopi)
            thetas-=n*twopi
            ret[:,:]=np.where(thetas<0,thetas+twopi,thetas)/twopi
        elif wave=='triangle':
            ret[:,:]=1.0-2.0*np.abs(np.floor((thetas*(1.0/twopi))+0.5)-(thetas*(1.0/twopi)))
    return ret
示例#3
0
def selectByPoint(img,
                  location,
                  tolerance=0,
                  soften=10,
                  smartSoften=True,
                  colorspace='RGB',
                  pickMode='average'):
    """
    Works like the "magic wand" selection tool.
    It is different than selectByColor(img,pickColor(img,location)) in that only a contiguious
        region is selected

    :param img: can be a pil image, numpy array, etc
    :param location: can be a single point or an [x,y,w,h]
    :param tolerance: how close the selection must be
    :param soften: apply a soften radius to the edges of the selection
    :param smartSoften: multiply the soften radius by how near the pixel is to the selection color
    :param colorspace: change the given img (assumed to be RGB) into another corlorspace before matching
        TODO: implement this!

    :returns: black and white image in a numpy array (usable as a selection or a mask)
    """
    img = numpyArray(img)
    img = changeColorspace(img)
    # selectByColor, but the
    selection = selectByColor(img,
                              pickColor(img, location, pickMode),
                              tolerance,
                              soften,
                              smartSoften,
                              colorspace=imageMode(img))
    # now identify islands from selection
    labels, _ = scipy.ndimage.label(selection)
    # grab only the named islands within the original location
    if len(location) < 4:
        labelsInSel = [labels[location[0], location[1]]]
    else:
        labelsInSel = np.unique(
            labels[location[0]:location[0] + location[2] + 1,
                   location[0]:location[0] + location[2] + 1])
    # only keep portions of selection within our islands
    selection = np.where(np.isin(labels, labelsInSel), selection, 0.0)
    return selection
示例#4
0
def selectionToPath(img, midpoint=0.5):
    """
    convert a black and white selection (or mask) into a path

    :param img: a pil image, numpy array, etc
    :param midpoint: for grayscale images, this is the cuttoff point for yes/no

    :return: a closed polygon [(x,y)]

    TODO: I resize the image to give laplace a border to lock onto, but
        do I need to scale the points back down again??
    """
    img = imageBorder(img, 1, 0)
    img = numpyArray(img)
    img = np.where(img >= midpoint, 1.0, 0.0)
    img = scipy.ndimage.laplace(img)
    points = np.nonzero(img)  # returns [[y points],[x points]]
    points = np.transpose(points)
    # convert to a nice point-pair representation
    points = [(point[1], point[0]) for point in points]
    return points
def colormap(img, colors=None):
    """
    apply the colors to a grayscale image
    if a color image is provided, convert it (thus, acts like a "colorize" function)

    :param img:  a grayscale image
    :param colors:  [(decimalPercent,color),(...)]
        if no colors are given, then [(0.0,black),(1.0,white)]
        if a single color and no percent is given, assume [(0.0,black),(0.5,theColor),(1.0,white)]

    :return: the resulting image
    """
    img = grayscale(img)
    if not isFloat(img):
        img = img / 255.0
    if colors is None:
        colors = [(0.0, (0.0, 0.0, 0.0)), (1.0, (1.0, 1.0, 1.0))]
    elif not isinstance(colors[0], (tuple, list, np.ndarray)):
        white = []
        black = []
        if isinstance(colors, str):
            colors = strToColor(colors)
        if isFloat(colors):
            imax = 1.0
            imin = 0.0
        else:
            imax = 255
            imin = 0
        for _ in range(len(colors)):
            white.append(imax)
            black.append(imin)
        if len(colors) in [2, 4]:  # keep same alpha value
            black[-1] = colors[-1]
            white[-1] = white[-1]
        colors = [(0.0, black), (0.5, colors), (1.0, white)]
    else:
        colors.sort()  # make sure we go from low to high
    # make sure colors are in the shape we need
    colors = [[
        matchColorToImage(color[0], img),
        np.array(strToColor(color[1]))
    ] for color in colors]
    shape = (img.shape[0], img.shape[1], len(color[1]))
    img2 = np.ndarray(shape)
    img = img[..., None]
    if True:
        lastColor = None
        for color in colors:
            if lastColor is None:
                img2 += color[1]
            else:
                percent = (img - lastColor[0]) * lastColor[0] / color[0]
                img2 = np.where(
                    np.logical_and(img > lastColor[0], img <= color[0]),
                    (color[1] * percent) + (lastColor[1] * (1 - percent)),
                    img2)
            lastColor = color
        img2 = np.where(img > lastColor[0], lastColor[1], img2)
    else:

        def gradMap(c):
            lastColor = None
            for color in colors:
                if c < color[0]:
                    if lastColor is None:
                        return color[1]
                    percent = (c - lastColor[0]) / color[0]
                    return (lastColor[1] * percent + color[1]) / (2 * percent)
                lastColor = color
            return lastColor[1]

        img2 = perPixel(gradMap, img, clamp=False)
    return img2
示例#6
0
def shadows(img, threshold=0.1):
    """
    return a mask of all shadows
    """
    return np.where(img > threshold, 0, 1 - img)
示例#7
0
def highlights(img, threshold=0.9):
    """
    return a mask of all highlights
    """
    return np.where(img < threshold, 0, img)
示例#8
0
 def _hardMix(bottom, top):
     # NOTE: I think this is right...?
     return np.where(bottom > 0.5, 1.0, 0.0)
示例#9
0
 def _pinLight(bottom, top):
     # NOTE: I think this is right...?
     return np.where(bottom > 0.5, _darken(bottom, top),
                     _lighten(bottom, top))
示例#10
0
 def _linearLight(bottom, top):
     return np.where(top > 0.5, _linearDodge(bottom, top),
                     _linearBurn(bottom, top))
示例#11
0
 def _vividLight(bottom, top):
     return np.where(top > 0.5, _colorDodge(bottom, top),
                     _colorBurn(bottom, top))
示例#12
0
 def _hardLight(bottom, top):
     return np.where(bottom <= 0.5, _multiply(top, 2.0 * bottom),
                     _screen(top, 2.0 * bottom - 1.0))
示例#13
0
 def _hardOverlay(bottom, top):
     # this is a krita thing
     return np.where(bottom > 0.5, _multiply(top, 2.0 * bottom),
                     _divide(top, 2.0 * bottom - 1.0))
示例#14
0
 def _overlay(bottom, top):
     # TODO: the colors saturation comes out a little higher than gimp, but close
     return np.where(top <= 0.5, 2.0 * bottom * top,
                     1.0 - 2.0 * (1.0 - bottom) * (1.0 - top))
示例#15
0
 def _dissolve(bottom, top):
     # TODO: there is a bug.  instead of randomly merging pixels, it randomly merges color values
     rand = np.random.random(bottom.shape)
     return np.where(rand > 0.5, bottom, top)
示例#16
0
def selectByColor(img,
                  color,
                  tolerance=0,
                  soften=10,
                  smartSoften=True,
                  colorspace='RGB'):
    """
    Select all pixels of a given color

    :param img: can be a pil image, numpy array, etc
    :param color: (in colorspace units) can be anything that pickColor returns:
        a color int array
        a color string to decode
        a color range (colorLow,colorHigh)
        an array of any of these
    :param tolerance: how close the selection must be
    :param soften: apply a soften radius to the edges of the selection
    :param smartSoften: multiply the soften radius by how near the pixel is to the selection color
    :param colorspace: change the given img (assumed to be RGB) into another corlorspace before matching

    :returns: black and white image in a numpy array (usable as a selection or a mask)
    """
    from . import colorSpaces
    img = numpyArray(img)
    img = colorSpaces.changeColorspace(img, colorspace)
    if (isinstance(color, list)
            and isinstance(color[0], list)) or (isinstance(color, np.ndarray)
                                                and len(color.shape) > 1):
        # there are multiple colors, so select them all one at a time
        # TODO: this could possibly be made faster with array operations??
        ret = None
        for c in color:
            if ret is None:
                ret = selectByColor(img, c, tolerance, soften, smartSoften)
            else:
                ret = ret + selectByColor(img, c, tolerance, soften,
                                          smartSoften)
        ret = clampImage(ret)
    elif isinstance(color, tuple):
        # color range - select all colors "between" these two in the given color space
        color = (matchColorToImage(color[0],
                                   img), matchColorToImage(color[1], img))
        matches = np.logical_and(img >= color[0], img <= color[1])
        ret = np.where(matches.all(axis=2), 1.0, 0.0)
    else:
        # a single color (or a series of colors that have been averaged down to one)
        color = matchColorToImage(color, img)
        if isFloat(color):
            imax = 1.0
            imin = 0.0
        else:
            imax = 255
            imin = 0
        numColors = img.shape[-1]
        avgDelta = np.sum(np.abs(img[:, :] - color), axis=-1) / numColors
        ret = np.where(avgDelta <= tolerance, imax, imin)
        if soften > 0:
            ret = gaussianBlur(ret, soften)
            if smartSoften:
                ret = np.minimum(imax, ret / avgDelta)
    return ret