[英]How to check whether a jpeg image is color or grayscale using only Python PIL? (not using OpenCV)
[英]How to check whether a jpeg image is color or gray scale using only Python stdlib
我必须在 python 中编写一个测试用例来检查 jpg 图像是彩色还是灰度。 任何人都可以让我知道是否有任何方法可以不安装像 opencv 这样的额外库吗?
可以按如下方式进行:
from scipy.misc import imread, imsave, imresize
image = imread(f_name)
if(len(image.shape)<3):
print 'gray'
elif len(image.shape)==3:
print 'Color(RGB)'
else:
print 'others'
您可以检查每个像素以查看它是否为灰度(R == G == B)
import Image
def is_grey_scale(img_path):
img = Image.open(img_path).convert('RGB')
w, h = img.size
for i in range(w):
for j in range(h):
r, g, b = img.getpixel((i,j))
if r != g != b:
return False
return True
为了更快的处理,最好避免在每个像素上循环,使用 ImageChops,(还要确保图像是真正的灰度,我们需要比较每个像素上的颜色,不能只使用总和):
from PIL import Image,ImageChops
def is_greyscale(im):
"""
Check if image is monochrome (1 channel or 3 identical channels)
"""
if im.mode not in ("L", "RGB"):
raise ValueError("Unsuported image mode")
if im.mode == "RGB":
rgb = im.split()
if ImageChops.difference(rgb[0],rgb[1]).getextrema()[1]!=0:
return False
if ImageChops.difference(rgb[0],rgb[2]).getextrema()[1]!=0:
return False
return True
使用 numpy 功能和 opencv 有更多 pythonic 方式:
import cv2
def isgray(imgpath):
img = cv2.imread(imgpath)
if len(img.shape) < 3: return True
if img.shape[2] == 1: return True
b,g,r = img[:,:,0], img[:,:,1], img[:,:,2]
if (b==g).all() and (b==r).all(): return True
return False
快速结果的性能增强:由于许多图像具有黑色或白色边框,您希望通过从 im 中采样一些随机 i,j 点并测试它们来更快地终止? 或者使用模运算来遍历图像行。 首先我们采样(-无替换)说 100 个随机 i,j 点; 在不太确定的情况下,我们会对其进行线性扫描。
使用自定义迭代器 iterpixels(im)。 我没有安装 PIL,所以我无法测试这个,这是大纲:
import Image
def isColor(r,g,b): # use tuple-unpacking to unpack pixel -> r,g,b
return (r != g != b)
class Image_(Image):
def __init__(pathname):
self.im = Image.open(pathname)
self.w, self.h = self.im.size
def iterpixels(nrand=100, randseed=None):
if randseed:
random.seed(randseed) # For deterministic behavior in test
# First, generate a few random pixels from entire image
for randpix in random.choice(im, n_rand)
yield randpix
# Now traverse entire image (yes we will unwantedly revisit the nrand points once)
#for pixel in im.getpixel(...): # you could traverse rows linearly, or modulo (say) (im.height * 2./3) -1
# yield pixel
def is_grey_scale(img_path="lena.jpg"):
im = Image_.(img_path)
return (any(isColor(*pixel)) for pixel in im.iterpixels())
(我的原话也是如此,首先你检查 JPEG 标头,偏移量 6:分量数(1 = 灰度,3 = RGB)。如果它是 1 = 灰度,你已经知道答案,而无需检查单个像素。)
我遇到了类似的情况,我尝试了以下方法:
IMREAD_UNCHANGED
读取并检查 image.shape这两种方法在我的数据集中只有 53% 的准确率。 我不得不放宽检查不同通道中像素的条件,并创建一个比率以将其分类为灰色或彩色。 通过这种方法,我能够在我的数据集上获得 87.3% 的准确率。
这是对我有用的逻辑:
import cv2
import numpy as np
###test image
img=cv2.imread('test.jpg')
### splitting b,g,r channels
b,g,r=cv2.split(img)
### getting differences between (b,g), (r,g), (b,r) channel pixels
r_g=np.count_nonzero(abs(r-g))
r_b=np.count_nonzero(abs(r-b))
g_b=np.count_nonzero(abs(g-b))
### sum of differences
diff_sum=float(r_g+r_b+g_b)
### finding ratio of diff_sum with respect to size of image
ratio=diff_sum/img.size
if ratio>0.005:
print("image is color")
else:
print("image is greyscale")
为什么我们不使用 ImageStat 模块?
from PIL import Image, ImageStat
def is_grayscale(path="image.jpg")
im = Image.open(path).convert("RGB")
stat = ImageStat.Stat(im)
if sum(stat.sum)/3 == stat.sum[0]:
return True
else:
return False
stat.sum为我们提供了列表视图中所有像素的总和 = [R, G, B] 例如 [568283302.0, 565746890.0, 559724236.0]。 对于灰度图像,列表的所有元素都是相等的。
在灰度图像的情况下,某个像素中的所有通道都是相等的(如果您只有一个通道,那么您没有问题)。 所以基本上如果你列出所有像素的 3 个通道值,你会假设每个像素的 3 个通道都相等。
Image.getcolors()
返回一个未排序的(计数,像素)值列表。
im = Image.open('path_to_image.whatever')
color_count = im.getcolors()
如果len(color_count)
超过 256,则此方法返回 None - 意味着您的像素列表中有超过 256 种颜色选项,因此它是彩色图像(在灰度的情况下,您只能有 256 种颜色(0,0, 0) 到 (255,255,255))。 所以在那之后你只需要:
if color_count:
# your image is grayscale
else:
# your images is colored
请注意,这仅在使用getcolors()
的默认参数值getcolors()
。
文档: https : //pillow.readthedocs.io/en/3.0.x/reference/Image.html#PIL.Image.Image.getcolors
老问题,但我需要一个不同的解决方案。 有时 3 通道图像(例如 RGB)可能几乎是灰度,而 3 通道中的每个像素都不相同。 这会检查每个像素,但如果需要,您也可以对图像进行子采样。 我在这里使用了斜率,但您可以对回归中的大部分参数进行检查。 由于内部矩阵乘法解,线性回归通常非常快。
import glob
import scipy
import cv2
THRESH = 0.01
BASEDIR = 'folder/*.jpg'
files = glob.glob(BASEDIR)
for file in files:
img = cv2.imread(file)
slope1, intercept1, r1, p1, se1 = scipy.stats.linregress(img[:,:,0].flatten(),img[:,:,1].flatten())
slope2, intercept2, r2, p2, se2 = scipy.stats.linregress(img[:,:,0].flatten(),img[:,:,2].flatten())
if abs(slope1 - 1) > THRESH or abs(slope2 - 1) > THRESH:
print(f'{file} is colour')
else:
print(f'{file} is close to grey scale')
这是使用 PIL.image 而不是 cv2 的 Alexey Antonenko 答案的一个版本。 如果你有浮动图像,我认为使用np.allclose
function 更安全。
from PIL import Image
import numpy as np
def isgray(imgpath):
img_pil = Image.open(imgpath)
img = np.asarray(img_pil)
if len(img.shape) < 3: return True
if img.shape[2] == 1: return True
r,g,b = img[:,:,0], img[:,:,1], img[:,:,2]
if np.allclose(r,g) and np.allclose(r,b): return True
return False
正如您可能是对的,OpenCV 可能对这项任务来说太过分了,但为此使用 Python 图像库 (PIL) 应该没问题。 以下应该对您有用:
import Image
im = Image.open("lena.jpg")
编辑正如 Mark 和 JRicardo000 所指出的,您可以迭代每个像素。 您还可以在此处使用 im.split() 函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.