Reputation: 2895
I have the following image (please note the transparent background):
I also have a black/white mask of the same size:
I would like to "crop" the dress and get just the portion of the first image contained in the black circle. I tried many different methods but they didn't work or are too slow:
1) ImageMagick (command line) <== which command can I use to achieve this? I tried multiply and copyopacity but they didn't work
2) WideImage is working: $maskedImage = $source->applyMask($mask);
but it takes more than 12 seconds.
I am interested in a ImageMagick solution if possible.
EDIT
The provided solutions work fine if the mask is smaller than the original image and if the original image is simple. With these source image and mask the result is "smeared":
Source:
Mask:
Command:
convert source.png \( mask.png -negate \) -alpha off -compose copy_opacity -composite result.png
Result (I added a grey background instead of the transparent one in order to show the wrong white):
Upvotes: 2
Views: 2365
Reputation: 2895
At the end of the day I kept using WideImage which is quite slow but works well. This is the class I use to mask images:
<?php
namespace AppBundle\Service\Import;
use WideImage\WideImage;
class ImageMasker
{
/**
* @var string
*/
private $tempDirectory;
public function __construct(string $tempDirectory)
{
$this->tempDirectory = $tempDirectory;
}
/**
* @param string $sourcePath
* @param string $maskPath
*/
public function mask($sourcePath, $maskPath)
{
$source = WideImage::load($sourcePath);
$mask = WideImage::load($maskPath);
$tempFilename = uniqid().'.png';
$tempPath = rtrim($this->tempDirectory, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$tempFilename;
// applies the mask and saves the file
$maskedImage = $source->applyMask($mask);
$maskedImage->saveToFile($tempPath);
return $tempPath;
}
}
Upvotes: 0
Reputation: 53109
It should work fine in either ImageMagick 6 or ImageMagick 7 using copy_opacity not copy_alpha. This works fine for me:
Input:
Mask:
convert dress.png \( mask.png -negate \) -alpha off -compose copy_opacity -composite result.png
The above command using convert is for ImageMagick 6. If using ImageMagick 7, change convert to magick. Both work for me.
Upvotes: 0
Reputation: 5395
Your version of ImageMagick appears to be too old to include the "copyalpha" compose operator. Here's another way to get your result...
convert dress.png \( circle.png -negate \) \
\( -clone 0 -transparent red +transparent red \) -insert 0 -composite result.png
That reads in your main image, then reads in your mask image and negates it, then creates a transparent layer and moves it to the first position in the list with "-insert". ImageMagick's default handling of "-composite" with three input images is to use the third image, now the one with the black circle, as an alpha mask. You still have to "-negate" that mask, or make a new mask with the black and white inverted.
The method used there to create the transparent canvas is to read in one of the other images inside parentheses, change everything red to transparent, then change everything not red to transparent. That results in an entirely transparent canvas to use as the first image, the destination image, in the composite list.
Upvotes: 0
Reputation: 207540
I think you want this:
magick dress.png \( mask.png -alpha off -negate \) -compose copyalpha -composite result.png
Or, if you dislike parentheses, load the mask and sort out your alpha channel first, then load the dress, then +swap
the order before compositing:
magick mask.png -alpha off -negate dress.png +swap -compose copyalpha -composite result.png
Upvotes: 0