pythonpython-3.ximagemagickwand

Use imagemagick "convert" function with subprocess, input image from bytesio


I'm trying to make a distortion effect, but if we use the "convert" function using the command line, it requires us to specify the path to the file we are going to convert and at the end the name of the final image. I wanted to try to make the input image from bytesio or just bytesarray

here's my attempt but unfortunately it didn't work out(, it say like Wrong parameter: -liquid-rescale

# Distort function using imagemmagick
def distort(image, x=45, y=45):
    
    img = Image.open(BytesIO(image))
    w, h = img.size

    command = [
        "convert",
        "pipe:0",  # Input from stdin
        "-liquid-rescale", f"{x}x{y}%!",
        "-resize", f"{w}x{h}!",
        "pipe:1",  # Output to stdout
    ]

    try:
        process = subprocess.Popen(
            command,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        stdout, stderr = process.communicate(image)

        if process.returncode != 0:
            logger.info(f"ImageMagick error: {stderr.decode(encoding="latin-1")}")
            return None

        return stdout

    except Exception:
        import traceback
        traceback.print_exc()
        return None

Solution

  • Your question could do with more context showing what your images are like (size, format etc) and how you plan to use your code. If my answer is insufficient, update your question and I will try to update my answer to match.

    The problem appears to be that you are using ffmpeg-style descriptors for input and output (i.e. pipe:0 and pipe:1) rather than what ImageMagick expects, which is a dash/hypen, or an fd number. So, to read a TIFF from stdin and create a JPEG, use:

    cat image.tif | magick - result.jpg
    

    Or you can use:

    cat image.tif | magick fd:0 result.jpg
    

    Or you can use:

    cat image.tif | magick /dev/stdin result.jpg
    

    Note that the last option will probably not work on Windows, whereas the first 2 probably will.


    However, that is not the whole story. You may want to force ImageMagick to write a JPEG or a PNG on stdout. You can do that by prepending a format specifier to the output file like this - note that I am redirecting to a file to avoid making a mess in your Terminal:

    magick image.tif JPEG:-    > file.jpg
    

    Or:

    magick image.tif PNG:-     > file.png
    

    Note that you can force specific types of PNG, such as 8-bit, 16-bit, palette, RGBA by reading my answer here.


    In concrete terms, your inner code should look more like this:

    command = [
        "convert",
        "-",                             # Input from stdin
        "-liquid-rescale", f"{x}x{y}%!",
        "-resize", f"{w}x{h}!",
        "PNG:-",                         # Output PNG to stdout
    ]
    

    Note also that there are Python bindings directly to ImageMagick via wand so you can avoid all this jiggery-pokery with subprocesses using that.