当前位置: 首页 > 科技观察

为什么我的CV模型不起作用?没想到原因这么简单...

时间:2023-03-18 14:06:32 科技观察

计算机视觉模型表现不佳的原因有很多,比如架构设计缺陷、数据集表示不足、超参数选择错误等。但有一个很简单的原因经常被忽视的一点:图像的方向。机器学习专家AdamGeitgey最近发表了一篇文章,探讨了这个简单但令人头疼的问题,并分享了他为解决这个问题而编写的自动图像旋转程序。我写了很多关于计算机视觉和机器学习的项目,比如物体识别系统和人脸识别项目。我有一个开源的Python面部识别软件库,它是GitHub上最受欢迎的10个机器学习库之一。这也意味着我经常收到Python和计算机视觉新手的提问。根据我的经验,有一个技术问题比其他任何问题都更令人沮丧——而不是复杂的理论或昂贵的GPU。人们基本上没有意识到几乎每个人都将图像从侧面加载到内存中,而计算机并不擅长检测侧面图像中的物体或面部。数码相机如何自动旋转图像当您拍照时,相机会检测您的倾斜方向。当您在另一个程序中查看照片时,它们会以正确的方向显示。但棘手的部分是您的相机实际上并没有旋转它保存到磁盘的文件中的图像数据。因为数码相机中的图像传感器是逐行读取的,最终汇集了连续的像素信息流。这使得相机更容易保存像素数据,因为它总是以相同的顺序保存,无论相机的姿势如何。事实上,照片是否会以正确的方向显示完全取决于图像查看器应用程序。除了图像数据,相机还保存每张照片的元数据——相机设置、位置数据,当然还有相机的旋转。图像查看器应该使用此信息来正确显示图像。图像元数据最常见的格式是Exif(可交换图像文件格式“可交换图像文件格式”的缩写)。Exif格式的元数据放在相机保存的jpeg文件中。您不能直接从图像本身读取此Exif数据,但可以使用任何知道如何读取此数据的程序。这是上面使用Exiftool读取的鹅照片的Exif元数据:注意方向数据元素。它指示图像查看器程序在屏幕上显示图像之前将图像顺时针旋转90度。如果程序忘记这样做,图像将横向显示。为什么这会使许多Python计算机视觉应用程序表现不佳?Exif元数据不是jpeg文件格式的原生部分。jpeg文件格式在TIFF文件格式使用它之后添加了这个元数据。它保持与老一代图像查看器的向后兼容性,但这也意味着某些程序根本不需要解析Exif数据。大多数用于处理图像数据的Python库,如numpy、scipy、TensorFlow、Keras等,都将自己视为研??究一般数据数组的人的科学工具。所以他们并不关心消费层面的问题,比如“图像自动旋转”——即使目前所有的相机都需要这个操作来拍照。这几乎意味着当您使用任何Python库加载图像时,您将获得未经旋转的原始图像数据。现在猜猜当您将侧向或倒置的图像输入人脸识别或物体检测模型时会发生什么?检测器将失败,因为您提供了错误的数据。您可能会认为此问题仅限于新手或学生编写的Python脚本,但事实并非如此。即使是Google的旗舰VisionAPI演示也无法正确处理Exif方向:Google的VisionAPI演示无法旋转标准手机肖像图像。虽然谷歌的视觉技术能够成功检测到某些动物在侧面图像中的存在,但它只提供了一个非特定的“动物”标签。这是因为该模型检测侧向的鹅比检测面向前方的鹅要困难得多。如果在输入之前正确旋转它,Google的VisionAPI会执行以下操作:当图像方向正确时,Google的检测结果更加具体——不仅正确给出了“鹅”标签,而且置信度得分也更高,这好多了。如果您可以看到图像像本演示中那样是横向的,那么问题就更加明显了。但问题是你一般是看不到的。今天计算机上的典型程序以正确旋转的形式显示图像,而不是实际存储在磁盘上的图像。因此,当您查看图像以了解您的模型为何无法正常工作时,图像查看器会以正确的方向显示它,让您无法了解您的模型为何表现不佳。Mac上的Finder始终显示应用了Exif旋转的图像,因此无法看到文件中的图像数据实际上是横向的。这不可避免地导致人们在GitHub上报告问题,说他们使用的开源项目根本不起作用或者模型不够准确。但事情的本质很简单——他们输入的图像是横着的,甚至是倒着的!这个问题的解决方案是每次使用Python程序加载图像时执行Exif方向元数据检查,并在必要时旋转它。这很简单,尽管很难在网上找到针对所有方向正确执行旋转的示例代码。下面是是任意图像正确的方向方向后将将numpynumpy数:importpil.imageimportpil.imageimportpil.imageopsimportyasnumpyasnumpyasnumpyasnumpyasnpdexif_t??ranspose(img):img)._getexif(),dict)andexif_orientation_taginimg._getexif():exif_data=img._getexif()orientation=exif_data[exif_orientation_tag]#HandleEXIFOrientationiforientation==1:#Normalimage-nothingtodo!passeliforientation==2:#Mirroredlefttorightimg=img.transpose(PIL.Image.FLIP_LEFT_RIGHT)eliforientation==3:#Rotated180degreesimg=img.rotate(180)eliforientation==4:#Mirroredtoptobottomimg=img.rotate(180).transpose(PIL.Image.FLIP_LEFT_RIGHT)eliforientation==5:#Mirroredalongtop-leftdiagonalimg=img.rotate(-90,expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT)eliforientation==6:#Rotated90degreesimg=img.rotate(-90,expand=True)eliforientation==7:#Mirroredalongtop-rightdiagonalimg=img.rotate(90,expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT)eliforientation==8:#Rotated270degreesimg=img.rotate(90,expand=True)returnimgdefload_image_file(file,mode='RGB'):#LoadtheimagewithPILimg=PIL.Image.open(file)ifhasattr(PIL.ImageOps,'exif_t??ranspose'):#VeryrecentversionsofPILcandoexittransposeinternallyimg=PIL.ImageOps.exif_t??ranspose(img)else:#Otherwise,dotheexiftransposeourselfsimg=exif_t??ranspose(img)img=img.convert(mode)returnnp.array(img)之后,就可以将这个图像数据数组传递给所有需要的标准Python机器学习库,例如Keras和TensorFlow因为这个问题太常见了,我把它做了一个名为image_to_numpy的pip库,你可以这样安装它:pip3installimage_to_numpy你可以在任何Python程序中使用它来实现正确的图片加载,例如:importmatplotlib.pyplotaspltimportimage_to_numpy#Loadyourimagefileimg=image_to_numpy.load_image_file("my_file.jpg")#Showitonthescreen(orwhateveryouwanttodo)plt.imshow(img)plt.show()