Python之PIL【持续更新填补】

PIL

Image

  • 坐标值:整型int或浮点型float

  • size:Image是(宽,高),np.shape是(高, 宽)

    • Image.open(img).size=(宽, 高)

    • cv2.imread(img).shape=(高, 宽, 通道数)

    • imageio.v3.imread(uri=img).shape=(高, 宽, 通道数)

    • Image转Array

      from PIL import Image
      import cv2
      import numpy as np
      # 打开PIL图像
      img_pil = Image.open('image.jpg')
      # 将PIL图像转换为NumPy数组
      imgread = np.array(img_pil)
      # 如果PIL图像是RGBA模式(包含透明通道),需要将其转换为RGB模式
      if imgread.shape[2] == 4:
          imgread = cv2.cvtColor(imgread, cv2.COLOR_RGBA2BGR)
      cv2.imwrite('new_image.jpg', imgread)
      
      from PIL import Image
      # 打开图像
      img_pil = Image.open(image_file)  # 或 请求url -> 获取`图片二进制字节流` -> BytesIO将二进制字节流转为io流 = Image.open(BytesIO(requests.get(url).content))
      # 创建图像 Image.new(mode, size, color=0)
      # w=100,h=100的黑色三通道的图片
      img_new = Image.new(mode="RGB", size=(100, 100), color=0)
      # 显示图像
      img_pil.show()
      # 保存图像
      img_pil.save(image_file)
      
      • Image.open(fp, mode="r", formats=None):读取图片。打开并标识给定的映像文件。

        • fp:路径对象或文件对象。
        • mode:模式。必须是“r”。
        • img_pil.convert(mode=None, matrix=None, dither=None, palette=Palette.WEB, colors=256):

          • mode:请求的模式。PIL有九种不同模式: 1,L,P,RGB,RGBA,CMYK,YCbCr,I,F。

            • img_pil.convert('1'):二值图像,非黑即白。每个像素用8个bit表示,0表示黑,255表示白。

            • img_pil.convert('L'):灰度图像,每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。

              模式RGB到L的转换公式:L = R * 0.299 + G * 0.587 + B * 0.114。 
            • img_pil.convert('P'):8位彩色图像,它的每个像素用8个bit表示,其对应的彩色值是按照调色板查询出来的。

            • img_pil.convert("RGBA"):32位彩色图像,它的每个像素用32个bit表示,其中24bit表示红色、绿色和蓝色三个通道,另外8bit表示alpha通道,即透明通道

            • img_pil.convert("CMYK"):32位彩色图像,它的每个像素用32个bit表示。模式“CMYK”就是印刷四分色模式,它是彩色印刷时采用的一种套色模式,利用色料的三原色混色原理,加上黑色油墨,共计四种颜色混合叠加,形成所谓“全彩印刷”。四种标准颜色是:C:Cyan = 青色,又称为天蓝色或湛蓝;M:Magenta = 品红色,又称为洋红色;Y:Yellow = 黄色;K:Key Plate(blacK) = 定位套版色(黑色)。

            • img_pil.convert("YCbCr"):为24位彩色图像,它的每个像素用24个bit表示。YCbCr其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。人的肉眼对视频的Y分量更敏感,因此在通过对色度分量进行子采样来减少色度分量后,肉眼将察觉不到的图像质量的变化。

              模式RGB到YCbCr的转换公式:\\ 
              Y = 0.257R+0.504G+0.098B+16 \\ Cb = -0.148R-0.291G+0.439B+128 \\ Cr = 0.439R-0.368G-0.071*B+128 ```
              
            • img_pil.convert("I"):32位整型灰色图像,它的每个像素用32个bit表示,0表示黑,255表示白,(0,255)之间的数字表示不同的灰度。

              模式RGB到I的转换公式:I = R * 0.299 + G * 0.587 + B * 0.114 
            • img_pil.convert("F"):32位浮点灰色图像,它的每个像素用32个bit表示,0表示黑,255表示白,(0,255)之间的数字表示不同的灰度。

              模式RGB到F的转换公式:F = R * 0.299 + G * 0.587 + B * 0.114 
            • matrix:可选的转换矩阵。如果给定,则应为包含浮点值的4元组或12元组。

            • dither:抖动方法,在模式“RGB”转换为“P”或“ RGB”或“ L”转换为“1”时使用。可用的方法有:data:NONE或data:FLOYDSTEINBERG(默认)。请注意,在提供matrix参数时不使用此选项。

            • palette:从模式“RGB”转换为“P”时使用的调色板。可用的调色板是WEB或ADAPTIVE。

            • colors:用于“ ADAPTIVE”调色板的颜色数。 默认值为256。

            • img_pil.size:获取图片尺寸(x, y),即(w, h)

            • img_pil.width:获取图片宽度

            • img_pil.height:获取图片高度

            • img_pil.getbands():获取通道。len(img_pil.getbands())获取通道数。

              # 获取通道数len(img_pil.getbands()) -> len(('R', 'G', 'B')) / len(('L'))
              print(len(img_pil.getbands())) 
            • img_pil.rotate(angle, resample=Resampling.NEAREST, expand=0, center=None, translate=None, fillcolor=None):旋转图片。angle表示要旋转的角度,角度制。

            • img_pil.resize(size, resample=None, box=None, reducing_gap=None):调整图像大小。

            • img_pil.paste(im, box=None, mask=None):将paste方法中,传入的图像粘贴在原图像上。

              obj.paste(im, box=None, mask=None)
              # 将一张图img2粘贴到另一张图像img1上, box=(左上x, 左上y, 右下x, 右下y)
              img1 = Image.new("RGB",(100, 100))
              img2 = Image.new("RGB",(20, 20, 40, 40),"red")
              img1.paste(img2,(20, 20, 40, 40))
              # 将图像img中的box改成color色
              img = Image.new("RGB", (100,100)).paste(color="red", box=(10,10,30,30))
              # 不是很清楚mask的使用:利用mask,首先将被paste图像先与mask图像“相乘”,规定0为黑色,1为白色,再将“乘积”paste到目标图像
              
            • img_pil.show():显示图片。

            • img_pil.save(fp, format=None, **params):保存图片。

              • fp:路径对象或文件对象。
                img_pil.save("to.jpg")
                

                ImageFont

                load()

                ImageFont.load(file)
                
                • 从指定的文件中加载一种字体,该函数返回对应的字体对象。如果该函数失败,将产生IOError异常。

                  load_path()

                  ImageFont.load_path(file)
                  
                  • 和函数load()一样,但是如果没有指定当前路径的话,会从sys.path开始查找指定的字体文件。

                    truetype()

                    ImageFont.truetype(file,size, encoding=value)
                    
                    • 加载一个TrueType或者OpenType字体文件,并且创建一个字体对象。

                      • file:字体路径

                      • size:字号

                      • encoding:指定编码方式创建一个字体对象。通常的编码方式有“unic”(Unicode),“symb”(Microsoft Symbol),“ADOB”(Adobe Standard),“ADBE”(Adobe Expert)和“armn”(Apple Roman)

                        font = ImageFont.truetype("symbol.ttf", 16, encoding="symb")
                        draw.text((0, 0), unichr(0xF000 + 0xAA))
                        

                        ImageFont对象的方法

                        obj.getsize()
                        font.getsize(text)
                        
                        • 返回给定文本的宽度和高度(width, height),返回值为2元组。
                          obj.getmask()
                          font.getmask(text,mode="")
                          
                          • 为给定的文本返回一个位图(Image object)。这个位图是PIL内部存储内存的实例(为Image.core接口模块定义);
                          • 如果字体使用了抗锯齿,位图的模式为“L”,且其最大值为255。否则,它的模式为“1”;
                          • 可选参数mode用于一些显卡驱动指定自己喜欢的模式;如果为空,渲染器可能会返回任意模式。注意:该模式总是一个字符串。

                            ImageDraw

                            from PIL import ImageDraw
                            # 绘制图片
                            draw = ImageDraw.Draw(img_pil)  # 或 draw = ImageDraw.ImageDraw(img_pil)
                            
                            • 参数

                              • fill:线条或中间区域填充颜色,‘red’、(255,0,0)、‘#FF0000’、125等。ImageColor 中的颜色表达方式。方法可设置outline参数时,fill为内部区域填充颜色,否则为线条颜色。

                              • outline:轮廓线条颜色。

                              • width:线条宽度,单位pixels。

                                ● 圆/圆弧

                                draw.arc(xy, start, end, fill=None, width=1)
                                # todo 在给定的边界(左,上,右,下)内绘制圆弧。圆弧使用 fill颜色 和 width宽度的线条从start角度开始到end角度结束
                                # xy:定义边界(圆弧满圆外接矩形框左上xy右下xy)的坐标[(x0, y0), (x1, y1)] or [x0, y0, x1, y1]
                                # start:起始角度,单位度。 角度从水平位置右方开始,顺时针方向为正。
                                # end:结束角度。
                                # (0, 0, 100, 100)矩形框的内切圆,圆心(50, 50),半径为50,起始角度即起始点为(100, 50)顺时针旋转360度
                                draw.arc((0, 0, 100, 100), start=0, end=360, fill='red', width=3)
                                

                                ● 位图

                                draw.bitmap(xy, bitmap, fill=None)
                                # todo 根据bitmap为蒙版从(x,y)位置开始填充fill颜色。与Image.paste(im, box, mask) 方法类似,paste 填充图像,bitmap 填充颜色。
                                # xy:起始坐标,( x , y )
                                # bitmap:作为蒙版的图像。必须是含透明图 mode=1 或能做遮罩的 mode (L or RGBA)
                                

                                ● 弦

                                draw.chord(xy, start, end, fill=None, outline=None, width=1)
                                # 类似draw.arc(),但会将绘制的弧两个端点链接起来,outline是线的颜色,fill是内部填充颜色
                                draw.chord(xy=(0, 0, 100, 100), start=0, end=90, fill=None, outline="#FF0000")
                                

                                ● 椭圆

                                draw.ellipse(xy, fill=None, outline=None, width=1)
                                # xy:椭圆外接矩形框的左上xy右下xy,**画圆形(外接矩形w==h)**
                                # 画椭圆
                                draw.ellipse(xy=(x1, y1, x2, y2), fill="yellow", outline="red", width=1)
                                # 画圆
                                draw.ellipse(xy=(x1, y1, x1+3, y1+3), fill=(255, 255, 0), outline=(255, 0, 0), width=1)
                                

                                ● 线/折线

                                draw.line(xy, fill=None, width=0, joint=None)
                                # todo 在xy列表中的坐标之间绘制一条线。
                                # xy:点与点之间坐标,[(x0, y0), (x1, y1),...] or [x0, y0, x1, y1,....]
                                # joint:线之间的联合类型,如:curve圆角。
                                # 画一个以差值为10的白色网格
                                diff_value = 10
                                for next_diff_value in range(diff_value, img_pil.width, diff_value):  # 竖线
                                    fill = "black" if next_diff_value / 100 == next_diff_value // 100 else "white"  # 每满100线条改为黑色
                                    draw.line(xy=[(next_diff_value, 0), (next_diff_value, img_pil.height)], fill=fill, width=0, joint=None)
                                for next_diff_value in range(diff_value, img_pil.height, diff_value):  # 横线
                                    fill = "black" if next_diff_value / 100 == next_diff_value // 100 else "white"
                                    draw.line(xy=[(0, next_diff_value), (img_pil.width, next_diff_value)], fill=fill, width=0, joint=None)
                                # 画折线
                                draw.line(xy=[(10, 10), (20, 20), (30, 30), (20, 40)], fill="red", width=3, joint="curve")
                                

                                ● 形状

                                draw.shape(shape, fill=None, outline=None)
                                

                                ● 扇形图

                                draw.pieslice(xy, start, end, fill=None, outline=None, width=1)
                                # xy:定义边界的坐标[(x0, y0), (x1, y1)] or [x0, y0, x1, y1],
                                # start:起始角度,单位度。 角度从水平位置右方开始,顺时针方向为正。
                                # end:结束角度
                                draw.pieslice((0, 0) + obj.size , start=0, end=300, outline="#FF0000",fill='#FFFFFF',width=1)
                                

                                ● 点

                                draw.point(xy, fill=None)
                                draw.point(xy=(x1, y1), fill="red")
                                

                                ● 多边形/正多边形

                                # 多边形
                                draw.polygon(xy, fill=None, outline=None, width=1)
                                # 正多边形
                                draw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None)
                                

                                ● 矩形/圆角矩形

                                # 矩形
                                draw.rectangle(xy, fill=None, outline=None, width=1)
                                # 圆角矩形
                                draw.rounded_rectangle(xy, radius=0, fill=None, outline=None, width=1)
                                

                                ● 文本

                                draw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False, *args, **kwargs)
                                # xy:文字的左上角位置
                                # text:文本. 如果包含换行符需将文本传递给 multiline_text()
                                # fill:文字颜色
                                # font:字体,需要时 ImageFont 实例。
                                # spacing:如果文本传递给multiline_text(),则为行之间的像素数。
                                # align:对齐方式,如果文本传递给multiline_text(), 值有"left","center","right"。
                                # direction:文字方向。'rtl'(从右到左),'ltr'(从左到右)或'ttb'(从上到下)
                                # features:在文本布局期间使用的OpenType字体功能列表。通常用于打开默认情况下未启用的可选字体功能,如'dlig'或'ss01';也可用于关闭默认字体功能,例如'-liga'以禁用连字或'-kern'禁用字距调整。参阅:https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
                                # 设置字体,将字体库复制到当前目录
                                font = ImageFont.truetype('font_path', 32)  # 字体路径,字号
                                # 填充文字
                                draw.text(xy=(10,10), text="text", font=font, fill="#FFFFFF")
                                draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False)
                                # 绘制带换行的文字
                                draw.multiline_text((10,10), "Hello,nwoodman", font=font, fill="#FFFFFF")
                                draw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
                                # 获取文字的大小,(width, height)
                                # text:文本. 如果包含换行符需将文本传递给 multiline_textsize()
                                # font:字体,需要时 ImageFont 实例。
                                # spacing:如果文本传递给multiline_textsize(),则为行之间的像素数。
                                # direction:文字方向。'rtl'(从右到左),'ltr'(从左到右)或'ttb'(从上到下)
                                # features:在文本布局期间使用的OpenType字体功能列表。通常用于打开默认情况下未启用的可选字体功能,如'dlig'或'ss01';也可用于关闭默认字体功能,例如'-liga'以禁用连字或'-kern'禁用字距调整。
                                draw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
                                draw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False)
                                draw.textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
                                draw.multiline_textbbox(xy, text, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, embedded_color=False)
                                

                                ImageStat

                                • 获取图片亮度值的5种方式,返回值互不相同
                                  def get_image_light_mean(dst_src):
                                      # 转换图像到灰度,返回平均像素亮度
                                      im = Image.open(dst_src).convert('L')
                                      stat = ImageStat.Stat(im)
                                      return stat.mean[0]
                                  def get_image_light_rms(dst_src):
                                      # 转换图像到灰度,返回RMS像素亮度
                                      im = Image.open(dst_src).convert('L')
                                      stat = ImageStat.Stat(im)
                                      return stat.rms[0]
                                  def get_image_light_mean_sqrt(dst_src):
                                      # 平均像素,然后转换为“可感知的亮度”
                                      im = Image.open(dst_src)
                                      stat = ImageStat.Stat(im)
                                      r, g, b = stat.mean
                                      return math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2))
                                  def get_image_light_rms_sqrt(dst_src):
                                      # 像素的均方根,然后转换为“感知亮度”
                                      im = Image.open(dst_src)
                                      stat = ImageStat.Stat(im)
                                      r, g, b = stat.rms
                                      return math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2))
                                  def get_image_light_gs(dst_src):
                                      # 计算像素的“感知亮度”,然后返回平均值
                                      im = Image.open(dst_src)
                                      stat = ImageStat.Stat(im)
                                      gs = (math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2))
                                            for r, g, b in im.getdata())
                                      return sum(gs) / stat.count[0]