? ? ? ? AI抠图可以自动将图片中主体扣取出来,但是图片尺寸不变,就导致主体周边存在多余的透明图层:
现在需要将多余透明图层自动删除掉。
之前只知道imagecrop(),按理说顺序应该是检测确定图片主体的x,y坐标及裁剪宽度和高度。
首先确定起始坐标值和位置,可以通过一张四个角有不同颜色的图片来确定:
测试代码:
//获取图片指定位置rgba值
$im = ImageCreateFromPng("./img/02.png");
$imgIndex = ImageColorAt($im, 0, 0); //坐标从0开始
$colorInfo = imagecolorsforindex($im, $imgIndex);
$imgIndex2 = ImageColorAt($im, 699, 437);
$colorInfo2 = imagecolorsforindex($im, $imgIndex2);
var_dump($colorInfo, $colorInfo2);
通过简单调整上面代码的参数和打印出来的结果就可以确定,坐标值是从0开始的,且坐标方向是从左上角到右下角。完全透明时colorInfo数组中的‘alpha’值为127。
因为AI抠图返回的数据是base64格式(也可以返回路径,这里需求是要返回内容),所以要写的自动裁剪方法接收两个参数,一个是图片base64数据,一个是保存图片的路径,方法如下
//获取最小非透明区域图片
function getMinAreaImg($imgStr,$saveFile)
{
if(empty($imgStr)){
return false;
}
$data = base64_decode($imgStr);
$im = imagecreatefromstring($data);
if($im !== false){
$size = getimagesizefromstring($data);
$width = $size[0];
$height = $size[1];
$area = [
'left' => [0, 0],
'right' => ['width' => $width, 'height' => $height]
];
$lw = -1;//左裁剪点w坐标
$lh = -1;//左裁剪点h坐标
$firstPoint = [0, 0]; //第一个发现点,没用
//查找左裁剪点
for ($w=0; $w < $width; $w++) {
for ($h=0; $h < $height; $h++) {
$imgIndex = ImageColorAt($im, $w, $h);
$colorInfo = imagecolorsforindex($im, $imgIndex);
if($colorInfo['alpha'] != 127){
$firstPoint = [$w, $h];
$lw = $w;
$lh = $h;
if($lh == 0){
break 2;
}
//已确定左w,下面确定左h(右上角区域最小h)
for ($ow=$lw+1; $ow < $width; $ow++) {
for ($oh=$lh-1; $oh >= 0; $oh--) {
$ooindex = ImageColorAt($im, $ow, $oh);
$ooinfo = imagecolorsforindex($im, $ooindex);
if($ooinfo['alpha'] != 127){
$lh = $oh;
if($lh == 0){
break 4;
}
}
}
}
break 2;
}
}
}
if($lw == -1 && $lh == -1){//纯透明图层
return false;
}
$rw = $width-1;//右裁剪点w坐标
$rh = $height-1;//右裁剪点h坐标
//右下角到左裁剪点查找右裁剪点
for ($sw=$width-1; $sw >= $lw; $sw--) { //这里条件使用>=$lw或>=$firstPoint[0]都行(第一个发现点是采用宽度优先得来的)
for ($sh=$height-1; $sh >= $lh; $sh--) { //这里条件需要使用>=$lh,使用高度最小值(而不是第一个发现点,发现点是宽度优先得来的)才能在下面的查找中找到最大的宽度值
$sgIndex = ImageColorAt($im, $sw, $sh);
$sgColorInfo = imagecolorsforindex($im, $sgIndex);
if($sgColorInfo['alpha'] != 127){
$rw = $sw;
$rh = $sh;
if($rh == $height-1){
break 2;
}
for ($sow=$rw-1; $sow >= $lw; $sow--) {
for ($soh=$rh+1; $soh <= $height-1; $soh++) {
$sogIndex = ImageColorAt($im, $sow, $soh);
$sogColorInfo = imagecolorsforindex($im, $sogIndex);
if($sogColorInfo['alpha'] != 127){
$rh = $soh;
if($rh == $height-1){
break 4;
}
}
}
}
break 2;
}
}
}
//根据左右裁剪点,裁剪图片
$cropWidth = $rw - $lw + 1;
$cropHeight = $rh - $lh + 1;
// var_dump($lw,$lh,$cropWidth,$cropHeight);exit;
$im2 = imagecrop($im, ['x' => $lw, 'y' => $lh, 'width' => $cropWidth, 'height' => $cropHeight]);
if ($im2 !== FALSE) {
imagesavealpha($im2, true); //保存图像时是否保留完整的 alpha 通道信息
imagepng($im2, $saveFile);
imagedestroy($im2);
}
imagedestroy($im);
return true;
}
return false;
}
整体思路是从左上角开始,以w优先,遍历h,先确定最左边(最小)w,然后再遍历右上角区域确定最小h,示例图(椭圆是主体)如下:
这样就确定了左上角起始坐标了,然后确定右下角坐标,从图片右下角开始,相同的道理,如下紫色区域(结束位置即上面确定的起始坐标位置)是计算需要考虑的范围。
这样又得出了最小裁剪目标的右下角坐标。
剩下的就是计算裁剪宽高和保存图片了,具体看上述代码。
测试一下:
$img = "./img/01.png";
$saveFile = './img/01_crop.png';
$imgStr = base64_encode(file_get_contents($img));
$result = getMinAreaImg($imgStr,$saveFile);
var_dump($result);
结果符合预期。
以为到这里结束了?其实不然。
在我保存裁剪图片后,发现图片透明图片丢失了,于是各种调试并查找官方文档,解决方案就是上述代码中imagesavealpha方法。
但令我崩溃是的在翻阅的过程中发现了imagecropauto()方法。auto?这明摆着就是可以自动裁剪吗,
点进去一看,还真是。imagecropauto两个参数,第一个参数基本明确,主要是第二个参数存在几种类型【IMG_CROP_DEFAULT,IMG_CROP_TRANSPARENT,IMG_CROP_BLACK,IMG_CROP_WHITE,IMG_CROP_SIDES,IMG_CROP_THRESHOLD】
于是又写了一个小方法,
//自动裁剪
function cropAutoImg($imgStr,$saveFile)
{
if(empty($imgStr)){
return false;
}
$data = base64_decode($imgStr);
$im = imagecreatefromstring($data);
if($im !== false){
$cropped = imagecropauto($im, IMG_CROP_SIDES);
if ($cropped !== false) {
imagesavealpha($cropped, true); //保存图像时是否保留完整的 alpha 通道信息
imagepng($cropped, $saveFile);
imagedestroy($cropped);
}
imagedestroy($im);
return true;
}
return false;
}
大概测试了一下,乍一看似乎是IMG_CROP_TRANSPARENT,但事与愿违,根据我提供的图片的裁剪基本确定IMG_CROP_SIDES才是符合我预期的,IMG_CROP_WHITE适合白色背景的图片,IMG_CROP_BLACK应该是适合黑色背景的吧(没测,感兴趣的可以测一下),IMG_CROP_THRESHOLD似乎跟颜色阈值有关没有深究。至于IMG_CROP_TRANSPARENT可能是我理解错了,不想费时间去探索了,有知道怎么使用的大佬希望提供下示例。
有时候对文档的熟练掌握真的能够事半功倍,但这次尝试自己编写自动裁剪也是个不错的体验吧,也希望这篇文章对大家有用。