【Python】Labelme/PIL读取图片朝向错误解决
创始人
2024-03-08 15:41:44

文章目录

  • 一、问题背景
  • 二、产生原因
  • 三、解决方案

一、问题背景

发现使用labelme直接读取含imageData(将图片bytes数据使用base64编码后的str数据)的json文件时,读上来的图片会发生自动旋转的问题。比如原先是横放的图,读进来后就成竖放的了,图片朝向(orientation)错误

我们可以用一个形象的例子来说明这种错误:
在这里插入图片描述

为什么图片会多此一举地弄一个朝向信息,而不是一步到位呢?

试想这样一个例子:你想给女朋友拍照,她很高,所以你把相机旋转了90度,最后成功拍了个全身照。

可能这个过程你已经重复过无数次,觉得非常自然。但仔细想想,相机的镜头是不会旋转的,也就是说如果人为地把相机旋转90度再拍照,拍下来的女朋友就会是横着站立,而不是正常的竖着站立。就像当你歪着头时,看到的世界也是歪着的。

为了防止这种诡异的现象出现,相机很机智地给图片加上了orientation标签,并“提醒”自己:这张图拍的时候我被旋转了90度,给主人看的时候要记得正过来。
在这里插入图片描述

所以这种记录方式的产生其实是两个因素导致的:①相机总是忠实地记录当时的场景,正的就是正的,反的就是反的;②相机希望记录当时的朝向,以方便做任何朝向上的调整。


二、产生原因

那么具体来说,在labelme的图片读取过程中哪里出问题会导致平时自动调整朝向的图片没有调整呢?

接着我们顺着labelme的流程走一遍

  1. 获取图片信息:labelme在读取json时会有一个if语句,如果有imageData,则直接获取imageData;如果没有imageData,则顺着imagePath找图片文件,然后读取图片文件:

    if data["imageData"] is not None:imageData = base64.b64decode(data["imageData"])if PY2 and QT4:imageData = utils.img_data_to_png_data(imageData)
    else:# relative path from label file to relative path from cwdimagePath = osp.join(osp.dirname(filename), data["imagePath"])imageData = self.load_image_file(imagePath)
    

    那么直接读imageData和读文件有什么区别呢?

  2. 缺少imagaData,使用imagePath:此时直接调用PIL库读取图片文件。注意PIL读取图片上来后,是不会应用exif属性的,也就是说不会应用图片创建之后的朝向改变(包括旋转和对称)操作。

    但是!!!

    labelme很机智地调用了PIL自带的旋转功能,将图片转成了exif里的朝向。

    matplotlib底层也是PIL,也是像这样读上来再调整朝向。

    @staticmethod
    def load_image_file(filename):try:image_pil = PIL.Image.open(filename)except IOError:logger.error("Failed opening image file: {}".format(filename))return# apply orientation to image according to exifimage_pil = utils.apply_exif_orientation(image_pil)with io.BytesIO() as f:ext = osp.splitext(filename)[1].lower()if PY2 and QT4:format = "PNG"elif ext in [".jpg", ".jpeg"]:format = "JPEG"else:format = "PNG"image_pil.save(f, format=format)f.seek(0)return f.read()
    

    综上所述,从imagePath读取图片,是不会出问题的。

  3. 直接使用imageData:并没有对图片数据做什么处理,关键就是因为没做什么处理,导致该根据exif信息调整朝向的图没有调整。

  4. 加载图片:最后使用pyqt加载图片:

    image = QtGui.QImage.fromData(self.imageData)
    

    看不到pyqt的源码,但它肯定不会去旋转图片就是了,否则之前的代码根本没必要对PIL读上来的图旋转一次。

    综上所述,从imageData读取图片,不会应用图片创建之后的调整朝向操作。


三、解决方案

思路很简单:对图片批量处理,用PIL读上来后都应用一次旋转,再保存成新的图片就行了。

附上朝向表:

参数行起始(0行位置)列起始(0列位置)调整操作
1
2水平翻转
3180°
4垂直翻转
5顺时针90°+水平翻转
6顺时针90°
7顺时针90°+垂直翻转
8逆时针90°

在这里插入图片描述

细节来了:

从上图的朝向表中我们可以看出,朝向只能是1-8的整数。而实际情况是,有的时候我们获取不到exif属性,更不用说朝向了,或者能获取到朝向,但居然是0。

我们先继续沿用之前举的宝藏的例子:

在这里插入图片描述

也就是说如果图片原本就不带有朝向信息,当然不用担心朝向变化的问题了。

一般来说不是相机拍摄的图片自然是不会有exif属性了,比如表情包、画图软件里画的图、随手截的图等。而有exif属性的图经过聊天软件等渠道的处理,也会丢失exif属性。

至于朝向是0的图片,已知部分安卓手机拍摄的照片会出现这种情况。

开干:

import os
import json
import base64
import io
from PIL import Image, ImageOpsdef transpose_img(str_img):"""根据exif信息旋转图片的函数。:param str_img: 已经用base64编码之后的图片。:return: 图片格式、旋转后的Image对象。"""# 解码成二进制图片,如果你的图片本来就是二进制的,可以跳过这步bytes_img = base64.b64decode(str_img)# 实例化Image对象new_img = Image.open(io.BytesIO(bytes_img), mode='r')# 调用exif_transpose()函数,自动根据exif属性旋转# 这里是情况特殊,如果你不需要图片格式,可以不返回.formatreturn new_img.format, ImageOps.exif_transpose(new_img)
def to_bytes_img(pil_img, fmt):"""将图片重新二进制化的函数。:param pil_img: Image对象。:fmt: 图片的format属性。:return: 二进制形式的图片。"""secondIO = io.BytesIO()# 注意这里的quality默认是75,95为最佳;dpi得指定成原图的dpi;这种无图片路径的情况format必须指定;exif也得指定成原图的exif,否则会丢失pil_img.save(secondIO, format=fmt, quality=95, dpi=pil_img.info.get('dpi', (96.0, 96.0)), exif=pil_img.info.get('exif', b""))return secondIO.getvalue()

补充说明一些细节:

  • Image.info可以查看很多图片属性,包括exif属性,但这样得到的exif属性是二进制形式的。也可以使用Image.getexif()获取,但这样获取的属性是不全的。如果想看完整的exif属性,可以使用exifread库或者piexif库;
  • 原来的exif在调用exif_transpose()函数时会自动删除第274号orientation朝向属性,因为若原图exif里是90 cw(顺时针旋转90度),转完了肯定不能还显示90 cw。所以省得我们手动改exif了;
  • 获取字典中的值都用get,有的图片没有那么多info属性;
  • 第二个函数必须传format参数进来,因为图片调整朝向后可能会丢失format属性

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...