最近做到一个项目要实现这样一个功能, 根据上传图片的主色调, 在图片上加水印或文字.
比如上传的图片颜色较深,文字的颜色用浅色标识,如果图片颜色较浅, 则文字颜色用深色标识.
解决方法是:
先确定要加文字的图片区域,遍历每一个像素点, 取得该区域像素点的平均亮度, 根据平均亮度决定文字的颜色. 如果该图片区域的颜色不复杂,只是一个纯色块,就不需要遍历了,只需要获取色块中某个像素点的亮度就可以了.
不建议遍历整张图, 而只是遍历需要加水印的图片文字的区域, 否则会有性能问题.
/**
* 获取图片某个像素的颜色及亮度
* @author church <church_qi@aliyun.com>
* @date 2017-4-5
*/
class ImgColor
{
/**
* RGB格式
*/
const RGB = 1;
/**
* 6位十六进制格式
*/
const HEX_6_BIT = 2;
/**
* 8位十六进制格式
*/
const HEX_8_BIT = 3;
/**
* 打开图片
* @param string $img_path 图片路径
* @return resource
*/
public static function openImg($img_path)
{
if(!is_file($img_path)) {
throw new Exception("不存在的图片");
}
$info = getimagesize($img_path);
if(false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))){
throw new Exception("非法的图片");
}
$img_info = array(
'width' => $info[0],
'height' => $info[1],
'type' => image_type_to_extension($info[2], false),
'mime' => $info['mime'],
);
$fun = "imagecreatefrom{$img_info['type']}";
$img = $fun($img_path);
return $img;
}
/**
* 获取某个像素的颜色
* @param string $img_path 图片路径
* @param integer $x 横坐标
* @param integer $y 纵坐标
* @param integer $format 格式
* @return mixed
*/
public static function getColor($img_path = '', $x, $y, $format = self::HEX_8_BIT)
{
static $img;
$getRgb = function($img) use ($x, $y) {
$rgb = imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
return [$r, $g, $b];
};
if (!(isset($img) && is_resource($img))) {
$img = self::openImg($img_path);
}
return self::rgbToFormat($getRgb($img), $format);
}
/**
* 获取某个像素的亮度 (范围为0~255, 越小越深)
* 算法参考: https://www.w3.org/TR/AERT#color-contrast
*
* @param string $img_path 图片路径
* @param integer $x 横坐标
* @param integer $y 纵坐标
* @return double
*/
public static function getBrightnessOfPixel($img_path = '', $x, $y)
{
list($r, $g, $b) = self::getColor($img_path, $x, $y, self::RGB);
$g = $r * 0.299 + $g * 0.587 + $b * 0.114;
return $g;
}
/**
* 把rgb格式转换成其它格式
* @param array $rgb
* @param integer $format 格式
* @return string
*/
public static function rgbToFormat($rgb, $format)
{
list($r, $g, $b) = $rgb;
$result = '#';
switch ($format) {
case self::RGB:
return $rgb;
break;
case self::HEX_6_BIT:
$result .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT);
$result .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT);
$result .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
return $result;
break;
case self::HEX_8_BIT:
$result .= 'ff';
$result .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT);
$result .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT);
$result .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
return $result;
break;
}
}
}
亮度的范围为0~255, 越小越深, 越大越浅; 判断时可以取中位, 大于125就算浅,小于就算深.
require './ImgColor.php';
$img_path = './test.png';
$brightness = 0;
for ($i = 0; $i < 100; $i++) {
for ($j = 0; $j < 100; $j++) {
$brightness += ImgColor::getBrightnessOfPixel($img_path, $i, $j);
}
}
$brightness /= 10000;
$font_color = '';
$brightness = $brightness > 125 ? '较浅' : '较深';
//若背景色较浅, 水印文字颜色设置为黑色, 反之则设置为白色
$font_color = $brightness == '较浅' ? '#00000000' : '#ffffffff';