如何停止PHP iMagick根据EXIF“方向”数据自动旋转图像

30

目前使用PHP和iMagick开发海报打印Web应用程序。

这是我用来测试应用程序上传/图像编辑功能的示例图片:

alt text

该图像包含以下EXIF数据:

[FileName] => 1290599108_IMG_6783.JPG
    [FileDateTime] => 1290599109
    [FileSize] => 4275563
    [FileType] => 2
    [MimeType] => image/jpeg
    [SectionsFound] => ANY_TAG, IFD0, THUMBNAIL, EXIF, INTEROP, MAKERNOTE
    [COMPUTED] => Array
        (
            [html] => width="3504" height="2336"
            [Height] => 2336
            [Width] => 3504
            [IsColor] => 1
            [ByteOrderMotorola] => 0
            [CCDWidth] => 22mm
            [ApertureFNumber] => f/5.6
            [UserComment] => 
            [UserCommentEncoding] => UNDEFINED
            [Thumbnail.FileType] => 2
            [Thumbnail.MimeType] => image/jpeg
        )

    [Make] => Canon
    [Model] => Canon EOS 30D
    [Orientation] => 6
    [XResolution] => 72/1
    [YResolution] => 72/1
    [ResolutionUnit] => 2
    [DateTime] => 2009:08:31 08:23:49
    [YCbCrPositioning] => 2
    [Exif_IFD_Pointer] => 196

然而 - 当使用这个图片构造iMagick时,根据[Orientation] => 6 (我想是这样!)它会自动将其逆时针旋转90度。结果如下...

alt text

我想知道的是...

如何保持页面顶部所看到的图片的原始方向?是否可以通过禁用iMagick执行的自动旋转来实现呢?

非常感谢

更新:这是我想出来的解决方法... 它将基于EXIF数据修复图像的方向

   public function fixOrientation() {

       $exif = exif_read_data($this->imgSrc);
       $orientation = $exif['Orientation'];
       switch($orientation) {

           case 6: // rotate 90 degrees CW
               $this->image->rotateimage("#FFF", 90);
           break;

           case 8: // rotate 90 degrees CCW
              $this->image->rotateimage("#FFF", -90);
           break;

       }

 }

你应该考虑这里的示例:http://php.net/manual/en/imagick.getimageorientation.php,因为如果图像颠倒,你会错过一个180度旋转。 - Fmstrat
5个回答

63

"然而 - 当使用这个图像构造iMagick时,它会根据[方向] => 6(我想是这样!)自动将其逆时针旋转90度。"

实际上,问题恰恰相反。Imagick并没有自动旋转图像。您在其他软件/网络浏览器中正确地查看它,只是因为这些程序根据EXIF信息自动旋转了它。Imagick中的某些操作会导致您丢失正确的EXIF信息(例如:复制图像,thumbnailImage(),stripImage()和其他操纵)。因此,在这种情况下,您需要实际旋转图像。

ajmicek的答案很好,但可以通过使用Imagick自己内置的函数而不是PHP EXIF函数来进行改进。另外,该代码段似乎是一个类的一部分,因此不能直接按原样使用。在旋转图像后,设置正确的EXIF方向时,使用setImageOrientation()是一个好主意。

// Note: $image is an Imagick object, not a filename! See example use below.
function autoRotateImage($image) {
    $orientation = $image->getImageOrientation();

    switch($orientation) {
        case imagick::ORIENTATION_BOTTOMRIGHT: 
            $image->rotateimage("#000", 180); // rotate 180 degrees
            break;

        case imagick::ORIENTATION_RIGHTTOP:
            $image->rotateimage("#000", 90); // rotate 90 degrees CW
            break;

        case imagick::ORIENTATION_LEFTBOTTOM: 
            $image->rotateimage("#000", -90); // rotate 90 degrees CCW
            break;
    }

    // Now that it's auto-rotated, make sure the EXIF data is correct in case the EXIF gets saved with the image!
    $image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
}

示例用法:

$image = new Imagick('my-image-file.jpg');
autoRotateImage($image);
// - Do other stuff to the image here -
$image->writeImage('result-image.jpg');

@orrd 你能把它转换成CodeIgniter吗? - Kushal Suthar
1
我花了几个小时来尝试为所有移动设备适配旋转,但是这个解决方案一次性解决了问题,非常感谢! - Adrian
2小时的调试,然后发现了这个...一个传奇! :) - para
差不多10年后,感谢这些技巧:D - Mouke

4

谢谢izym。你有关于这些常量如何使用的更多信息吗? - kaese
2
请注意,Imagick :: setImageOrientation()实际上并不旋转图像,它只是更改将保存在图像中的EXIF旋转信息。 在某些情况下,这可能是您想要做的,但通常最好实际上物理旋转图像(请参见我发布的代码示例作为答案)。 依赖EXIF旋转信息的问题是,并非所有Web浏览器和图像查看软件都会自动正确定位它。 - orrd

2
好的开端 - 一些补充可以使函数更加健壮。首先,情况3发生在图像上下颠倒时。这里有一张很棒的插图,展示了不同的方向代码by Calvin Hass。可能方向信息会出现在exif_read_data数组的不同部分(取决于相机型号),因此我在我的示例代码中尝试考虑了这一点。
类似于这样:
public function fixOrientation() {

    $exif = exif_read_data($this->imgSrc);

    if( isset($exif['Orientation']) )
        $orientation = $exif['Orientation'];
    elseif( isset($exif['IFD0']['Orientation']) )
        $orientation = $exif['IFD0']['Orientation'];
    else
        return false;

    switch($orientation) {
        case 3: // rotate 180 degrees
            $this->image->rotateimage("#FFF", 180);
        break;

        case 6: // rotate 90 degrees CW
            $this->image->rotateimage("#FFF", 90);
        break;

        case 8: // rotate 90 degrees CCW
            $this->image->rotateimage("#FFF", -90);
        break;
    }
}

转换和保存操作会使您失去先前的 EXIF 信息,包括 Orientation。转换后图像中缺少 Orientation 将防止进一步处理尝试通过旋转来“纠正”图像。我希望 Imagick 支持 ImageMagick 的 -auto-orient,但是没关系。

另外:旋转是一种有损操作(除非使用 jpegtran),因此您应该尽量只在调整大小或其他转换的同时进行旋转。


第一个参数只是画布的颜色。 - ajmicek
1
仅仅9年后... "旋转是一种有损操作" - 实际上,以90度的倍数旋转是特殊情况,并且应该是无损的。 https://github.com/ImageMagick/ImageMagick/blob/341249be9340fc3382c86045779b756dae43fe87/MagickCore/distort.c#L2983 - Danack

0

在orrd的优秀答案中,此代码需要iMagick版本6.3+:

$image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);

完美地工作并处理了操作系统/设备方向差异。无法在6.2中使用。

我已编写代码以获取设备信息。这里提供给需要的人参考。

$ua = $_SERVER['HTTP_USER_AGENT'];
$strcut = stristr($ua, '(')."<br>";
$textlen = strpos($strcut,";");
$deviceos = substr($strcut,1,($textlen-1));
echo "Device O/S: * $deviceos"."<br>";

0
function thumbnailImage($imagePath,$color,$quality)
{   

    $fileType = pathinfo($imagePath, PATHINFO_EXTENSION);

    if (!empty($fileType))
    {
        switch($fileType)
        {
            case "gif":
                $im = imagecreatefromgif($imagePath);
                break;

            case "jpg":
                $im = imagecreatefromjpeg($imagePath);
                break;

            case "jpeg":
                $im = imagecreatefromjpeg($imagePath);
                break;

            case "png":
                $im = imagecreatefrompng($imagePath);
                break;
        }
    }

    $imagick = new Imagick();
    $imagick->readImage($imagePath);
    $compression_type = Imagick::COMPRESSION_JPEG; 
    $imagick->setImageCompression($compression_type);
    $imagick->setImageCompressionQuality($quality);

    if ($fileType == "jpeg" || $fileType == "jpeg" )
    {
        $exif = exif_read_data($imagePath);
        if (!empty($exif['Orientation'])) {
            switch ($exif['Orientation']) {
                case 3:
                    $imagick = imagerotate($imagick, 180, 0);
                    break;

                case 6:
                    $imagick = imagerotate($imagick, -90, 0);
                    break;

                case 8:
                    $imagick = imagerotate($imagick, 90, 0);
                    break;
            }
        }
    }
    $imagick->setImageBackgroundColor($color);
    $imagick->thumbnailImage(150, 150, true, true);
    header("Content-type: image/jpg");
    echo $imagick->getImageBlob();
}

超级简单的缩略图生成器。调用方式:

thumbnailImage('file.ext','#ffffff',50); // full path, color, quality 1-100

支持 jpggifpng 格式,但显然只对 jpeg 格式进行 EXIF 处理。

缩略图将加载在页面上,如果您指定了 $_GETs,则可以通过 URL 调用它。

享受吧!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接