文档扫描与图像处理

如何基于Exif信息处理图像旋转

可交换图像文件格式(正式名称Exif )是一种标准,用于指定数码相机(包括智能手机)的图像和声音使用的格式和辅助标签。它包含相机参数、图像尺寸和图像方向等元数据。

大多数相机传感器的形状是宽度大于高度的长方形。即使竖着拍摄照片,原始图像数据仍按照传感器的形状进行存储。在查看照片时,我们需要知道如何正确显示它们。此时需要Exif中的方向数据。相机的方向传感器可用于提供这一信息。

以下是关于方向和用于显示所需的旋转角度的示意图(图源)。

方向标记

如果图像是镜像翻转的,则方向标记如下图中的右图所示:

方向标记-镜像

大多数图像查看器和网页浏览器会自动根据方向标记显示图像。一些图像编辑器,如GIMP,会提示用户是否旋转图像。

gimp方向提示

用于校正图像方向的代码

在开发移动文档扫描应用程序时,我们可能需要根据方向旋转拍摄的图像。

以下是执行此操作的代码。

Android(Java):

private Bitmap rotatedImageBasedOnExif(Bitmap bitmap, String path) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        ExifInterface exif = null;
        try {
            exif = new ExifInterface(path);
        } catch (IOException e) {
            return bitmap;
        }
        int rotate = 0;
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_270:
                rotate = 270;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                rotate = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                rotate = 90;
                break;
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(rotate);
        Bitmap rotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);
        try (FileOutputStream out = new FileOutputStream(path)) {
            rotated.compress(Bitmap.CompressFormat.JPEG, 100, out);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rotated;
    }
    return bitmap;
}

注意Bitmap不包含Exif。需要直接传递文件路径给ExifInterface

iOS(Swift):

static func normalizedImage(_ image:UIImage) -> UIImage {
    if image.imageOrientation == UIImage.Orientation.up {
        return image
    }
    UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
    image.draw(in: CGRect(x:0,y:0,width:image.size.width,height:image.size.height))
    let normalized = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext();
    return normalized
}

源代码

安卓和iOS文档扫描demo的源代码:

使用了Dynamsoft Document Normalizer SDK来执行文档检测和裁剪操作。