- Introduction π
- Image Representation π
- Generating the Image π
- Header π
- Chunks π
- The Image Header Chunk (IHDR) π
- The Data Chunk (IDAT) π
- PNG Output π
- References π
- ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ Π² Python. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Pillow
- Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Pillow
- ΠΠ°ΡΠ°Π»ΠΎ ΡΠ°Π±ΠΎΡΡ Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ
- ΠΠ±ΡΠ΅Π·ΠΊΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
- ΠΠΎΠ²ΠΎΡΠΎΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
- ΠΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅ΠΌ ΠΈΠ· JPG Π² PNG Ρ ΠΏΠΎΠΌΠΎΡΡΡ Pillow
- ΠΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
- ΠΠΈΡΠ΅ΠΌ ΡΠ΅ΠΊΡΡ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡ
- ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΏΡΡΡΠΎΠ³ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
- ΠΡΠ²ΠΎΠ΄
Introduction π
PNG is the most common lossless image format on the web. As I am fascinated by the most mundane building blocks of the web, I thought it would be interesting to go through a demonstration of how to generate a PNG image file from scratch in Python.
I will be covering only the very basics on how to produce some output, and as the format is simple, there is no need to use anything but the standard Python library.
Image Representation π
Letβs figure out what we want to visualise in our PNG image. As this is not a painting class, we will only generate a simple checkerboard pattern and spend the rest of our time focusing on writing that to a PNG file.
First of all, we need to decide how we represent a pixel. A common way to do this is to store the pixel as a sequence of 3 values, one for each of the RGB (red, green, blue) components. Letβs define that as a Python type alias:
from typing import Tuple Pixel = Tuple[int, int, int]
What pixels do we want to store? As we will be generating a checkerboard pattern, we only need black and white pixels. Letβs go ahead and define them as constants:
BLACK_PIXEL: Pixel = (0, 0, 0) WHITE_PIXEL: Pixel = (255, 255, 255)
We are representing the intensity of each colour as a value in the range of 0-255, which thankfully fits in one byte (3 bytes per pixel).
Now that we have pixels, how do we represent the image? For simplicityβs sake, we will use a list of pixel lists, where each pixel list is a row in the image, and the number of pixel lists is the height of the image. We will make an assumption that all pixel lists have the same length (i.e. every row has the same width). Letβs define the image as another type alias:
from typing import List Image = List[List[Pixel]]
Generating the Image π
Now letβs write a simple function to generate our checkerboard pattern and return it as our image representation:
def generate_checkerboard_pattern(width: int, height: int) -> Image: out = [] for i in range(height): # Generate a single row of white/black pixels row = [] for j in range(width): if (i + j) % 2 == 0: row.append(WHITE_PIXEL) else: row.append(BLACK_PIXEL) out.append(row) return out
Note: As the checkerboard pattern consists of just two colours, we donβt need all of the RGB values. We couldβve stored this as a greyscale image and saved some space, or as an indexed-colour image with a palette and saved even more space. However, as most images on the web have colours, we will keep it as a colour image and stick to the RGB values.
Now letβs work out how to write everything to a PNG file.
Header π
A PNG file begins with a header. Letβs define it as a constant:
We will later write it out to our file.
Chunks π
The header is followed by a number of chunks. A chunk is a named data block that consists of:
- a 4-byte length field
- a 4-byte chunk type field
- the data
- a 4-byte checksum
The length field holds the size of the data field, and the chunk type is a special name defined in the PNG spec that represents what kind of data this chunk holds. Next comes the actual data, and finally a checksum.
The checksum is computed from the chunk type and the data using the CRC algorithm. The length field is not included in the checksum.
We will implement a function that writes out a data chunk, but first letβs begin with a function that returns the checksum:
import zlib def get_checksum(chunk_type: bytes, data: bytes) -> int: checksum = zlib.crc32(chunk_type) checksum = zlib.crc32(data, checksum) return checksum
Fortunately we donβt need to implement the CRC algorithm ourselves, as Python conveniently exposes it via the crc32 function from the zlib library. We make two calls to crc32 in order to compute a running checksum on two inputs: the chunk type and the data.
We can now implement a function that can write any type of a PNG chunk to a file:
import struct from typing import BinaryIO def chunk(out: BinaryIO, chunk_type: bytes, data: bytes) -> None: out.write(struct.pack('>I', len(data))) out.write(chunk_type) out.write(data) checksum = get_checksum(chunk_type, data) out.write(struct.pack('>I', checksum))
Note: The PNG format requires us to output integers as big endian, which is what we use struct.pack for. More specifically ‘>I’ indicates a 4-byte big endian unsigned integer.
The Image Header Chunk (IHDR) π
The first mandatory chunk we need to write after the PNG header is IHDR (the image header). It has the following format:
- 4-byte width
- 4-byte height
- 1-byte bit depth
- 1-byte colour type
- 1-byte compression method
- 1-byte filter method
- 1-byte interlace method
For simplicity, we can ignore the compression method, filter method and interlace method, and set them to 0. As for the rest, letβs define a function that generates the data for this chunk.
def make_ihdr(width: int, height: int, bit_depth: int, color_type: int) -> bytes: return struct.pack('>2I5B', width, height, bit_depth, color_type, 0, 0, 0)
Here we use struct.pack to pack the data into a byte string (namely as per the IHDR spec, we write 2 unsigned integers and 5 bytes).
The Data Chunk (IDAT) π
After the image header comes the main data chunk. Weβll write a function that coverts our image format to something we can output as part of the chunk.
We are representing pixel data as a list of pixel lists, where each pixel itself is an RGB triple. We now need to encode this data into scanlines. A scanline is just a continuous row of bytes where every byte holds a colour value from our RGB triples. Every scanline begins with a filter type byte. We are not doing any fancy filtering here so we will use 0 as filter type.
The function to encode the data will then look like this:
def encode_data(img: Image) -> List[int]: ret = [] for row in img: ret.append(0) color_values = [ color_value for pixel in row for color_value in pixel ] ret.extend(color_values) return ret
Note: A more sophisticated implementation could perform different filtering for every scanline, based on the contents of the scanline. This is why every row starts with its own filter type.
The PNG format also requires us to compress the data using the zlib compression method. Here we will again use the Python standard zlib library. Letβs write a function for compression:
def compress_data(data: List[int]) -> bytes: data_bytes = bytearray(data) return zlib.compress(data_bytes)
With these two functions we can generate the data field for the IDAT data chunk:
def make_idat(img: Image) -> bytes: encoded_data = encode_data(img) compressed_data = compress_data(encoded_data) return compressed_data
Note: PNG files can have multiple IDAT chunks, but for demonstration purposes we will only write one.
PNG Output π
Letβs put all of this together and write a function to output the whole PNG image. We will start out by outputting the PNG header:
def dump_png(out: BinaryIO, img: Image) -> None: out.write(HEADER) # start by writing the header
Now letβs consolidate all we need for the Image Header IHDR chunk.
The width and the height are just the dimensions of the image. Bit depth refers to the number of bits used to represent each channel (not pixel). In our case, each value in an RGB triple is represented by 1 byte (0-255), so the bit depth will be 8. Colour type describes the colour model of the image. An image defined by RGB triples with no alpha channel has colour type 2.
assert len(img) > 0 # assume we were not given empty image data width = len(img[0]) height = len(img) bit_depth = 8 # bits per pixel color_type = 2 # pixel is RGB triple ihdr_data = make_ihdr(width, height, bit_depth, color_type) chunk(out, b'IHDR', ihdr_data)
After IHDR comes the data IDAT chunk:
compressed_data = make_idat(img) chunk(out, b'IDAT', data=compressed_data)
Finally, the PNG format requires us to output an IEND chunk that marks the end of the PNG image. This chunk does not hold any data:
Letβs write a function that actually opens a binary file and writes out the data:
def save_png(img: Image, filename: str) -> None: with open(filename, 'wb') as out: dump_png(out, img)
To conclude the demonstration, letβs generate the checkerboard pattern and call the function above to save it to a file:
width = 100 height = 100 img = generate_checkerboard_pattern(width, height) save_png(img, 'out.png')
References π
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ Π² Python. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Pillow
ΠΠ΅ΡΠ΅Π΄ΠΊΠΎ Π½Π°ΠΌ Π½ΡΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ ΡΠ°Π·ΠΌΠ΅Ρ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ, ΠΊΡΠΎΠΏΠ½ΡΡΡ Π΅Π΅, Π½Π°Π»ΠΎΠΆΠΈΡΡ ΡΠ΅ΠΊΡΡ ΠΈ ΡΠ°ΠΊ Π΄Π°Π»Π΅Π΅. ΠΡΠ΅ ΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Python ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ Pillow.
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Pillow
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡΡ ΡΠ΅ΡΠ΅Π· pip3. ΠΠ²ΠΎΠ΄ΠΈΠΌ Π² ΠΊΠΎΠ½ΡΠΎΠ»Ρ:
ΠΠ°ΡΠ°Π»ΠΎ ΡΠ°Π±ΠΎΡΡ Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ
ΠΠ»Ρ Π½Π°ΡΠ°Π»Π° Π½Π°ΠΌ Π½ΡΠΆΠ½ΠΎ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ Π² Π½Π°ΡΠ΅ΠΌ ΡΠΊΡΠΈΠΏΡΠ΅:
Π ΠΏΠ°ΠΏΠΊΡ ΡΠΎ ΡΠΊΡΠΈΠΏΡΠΎΠΌ ΠΏΠΎΠΌΠ΅ΡΡΠΈΠΌ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ»ΡΠ½ΠΎΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅. Π€Π°ΠΉΠ» Π½Π°Π·ΠΎΠ²Π΅ΠΌ test.jpg.
Π’Π΅ΠΏΠ΅ΡΡ ΠΎΡΠΊΡΠΎΠ΅ΠΌ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π· ΠΌΠ΅ΡΠΎΠ΄ Π² Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅:
from PIL import Image img = Image.open('test.jpg') img.show()
ΠΠ° ΡΠΊΡΠ°Π½Π΅ Ρ Π½Π°Ρ ΠΎΡΠΎΠ±ΡΠ°Π·ΠΈΠ»ΠΎΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΡ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ Π² ΠΏΠ°ΠΏΠΊΡ:
ΠΠ»Ρ ΠΏΡΠΎΡΠΌΠΎΡΡΠ° ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎΠ± ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Pillow ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ:
from PIL import Image img = Image.open('test.jpg') print(img.format) # ΠΡΠΎΡΠΌΠΎΡΡ ΡΠΎΡΠΌΠ°ΡΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ. ΠΡΠ²Π΅Π΄Π΅Ρ 'JPEG' print(img.mode) # ΠΡΠΎΡΠΌΠΎΡΡ ΡΠΈΠΏΠ° ΡΠ²Π΅ΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π°. ΠΡΠ²Π΅Π΄Π΅Ρ 'RGB' print(img.size) # ΠΡΠΎΡΠΌΠΎΡΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ. ΠΡΠ²Π΅Π΄Π΅Ρ (568, 305) print(img.filename) # ΠΡΠΎΡΠΌΠΎΡΡ ΠΈΠΌΠ΅Π½ΠΈ ΡΠ°ΠΉΠ»Π°. ΠΡΠ²Π΅Π΄Π΅Ρ 'test.jpg' r, g, b = img.split() histogram = img.histogram() print(histogram) # ΠΡΠΎΡΠΌΠΎΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ RGB ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ. ΠΡΠ²Π΅Π΄Π΅Ρ 1750, 255, 267, 237, 276, 299β¦
ΠΠ±ΡΠ΅Π·ΠΊΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
Π Pillow Π΅ΡΡΡ ΠΌΠ΅ΡΠΎΠ΄Ρ Π΄Π»Ρ ΠΊΡΠΎΠΏΠ° (ΠΎΠ±ΡΠ΅Π·ΠΊΠΈ) ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ. ΠΠ±ΡΠ΅ΠΆΠ΅ΠΌ Π½Π°ΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΠΌΠ΅ΡΠΎΠ΄Π° crop(), ΠΏΠ΅ΡΠ΅Π΄Π°Π² Π² ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΎΠ±ΡΠ΅Π·ΠΊΠΈ:
from PIL import Image img = Image.open('test.jpg') cropped = img.crop((0, 0, 100, 200)) cropped.save('cropped_test.jpg') img = Image.open('cropped_test.jpg') img.show()
ΠΠΎΡΠ»Π΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°, ΠΏΠΎΠ»ΡΡΠΈΠΌ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅:
ΠΠΎΠ²ΠΎΡΠΎΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
Π‘ ΠΏΠΎΠΌΠΎΡΡΡ ΠΌΠ΅ΡΠΎΠ΄Π° Image.rotate() ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°ΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΠΊΠ°ΠΊ Π½Π°ΠΌ ΡΠ³ΠΎΠ΄Π½ΠΎ. Π ΡΠΊΠΎΠ±ΠΊΠ°Ρ ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π³ΡΠ°Π΄ΡΡΠΎΠ², Π½Π° ΠΊΠΎΡΠΎΡΠΎΠ΅ Π½ΡΠΆΠ½ΠΎ ΠΏΠΎΠ²Π΅ΡΠ½ΡΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅. Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ΅:
from PIL import Image img = Image.open('test.jpg') rotated = img.rotate(180) rotated.save('rotated_test.jpg') img = Image.open('rotated_test.jpg') img.show()
ΠΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅ΠΌ ΠΈΠ· JPG Π² PNG Ρ ΠΏΠΎΠΌΠΎΡΡΡ Pillow
ΠΠ»Ρ ΠΊΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΠΈ Π½Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ ΠΎΡΠΊΡΡΡΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΠΈ ΠΏΡΠΎΡΡΠΎ ΡΠΎΡ ΡΠ°Π½ΠΈΡΡ Π΅Π³ΠΎ Π² Π΄ΡΡΠ³ΠΎΠΌ ΡΠΎΡΠΌΠ°ΡΠ΅. Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ΅:
from PIL import Image img = Image.open('test.jpg') img.save('test_png.png', 'png')
ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ ΡΠ°ΠΊΠΎΠ΅ ΠΆΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅, Π½ΠΎ Π² ΡΠΎΡΠΌΠ°ΡΠ΅ PNG.
ΠΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΉ
Π§ΡΠΎΠ±Ρ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ ΡΠ°Π·ΠΌΠ΅Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ resize(). Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΡΠΎ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ΅:
from PIL import Image img = Image.open('test.jpg') img = img.resize((170, 100), Image.ANTIALIAS) img.save('test_text.jpg') img = Image.open('test_text.jpg') img.show()
ΠΠΈΡΠ΅ΠΌ ΡΠ΅ΠΊΡΡ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡ
ΠΠ»Ρ Π½Π°Π»ΠΎΠΆΠ΅Π½ΠΈΡ ΡΠ΅ΠΊΡΡΠ° Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π² Pillow ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ ΠΌΠ΅ΡΠΎΠ΄ text(), Π½ΠΎ Π΄Π»Ρ Π½Π°ΡΠ°Π»Π° Π½ΡΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΡΡΠΈΡΡ. Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ΅:
from PIL import Image, ImageDraw, ImageFont img = Image.open('test.jpg') font = ImageFont.truetype("arial.ttf", size=20) idraw = ImageDraw.Draw(img) idraw.text((25, 25), 'TEST test TeSt', font=font) img.save('test_text.jpg') img = Image.open('test_text.jpg') img.show()
ΠΠΎΠ»ΡΡΠΈΠΌ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Ρ ΡΠ΅ΠΊΡΡΠΎΠΌ:
ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΏΡΡΡΠΎΠ³ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
ΠΠ»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΡΡΡΠΎΠ³ΠΎ Ρ ΠΎΠ»ΡΡΠ° (ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ) ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ Image.new(). Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΏΡΠΈΠΌΠ΅ΡΠ΅:
from PIL import Image, ImageDraw, ImageFont img = Image.new('RGB', (200, 200), 'black') img.save('test1.jpg') img = Image.open('test1.jpg') img.show()
Π’Π΅ΠΏΠ΅ΡΡ Π΄Π°Π²Π°ΠΉΡΠ΅ Π½Π°ΡΠΈΡΡΠ΅ΠΌ Π½Π° ΡΡΠΎΠΌ ΠΊΠ²Π°Π΄ΡΠ°ΡΠ΅ Π±Π΅Π»ΡΠΉ ΠΏΡΡΠΌΠΎΡΠ³ΠΎΠ»ΡΠ½ΠΈΠΊ:
from PIL import Image, ImageDraw, ImageFont img = Image.new('RGB', (200, 200), 'black') idraw = ImageDraw.Draw(img) idraw.rectangle((0, 0, 100, 100), fill='white') img.save('test1.jpg') img = Image.open('test1.jpg') img.show()
ΠΡΠ²ΠΎΠ΄
ΠΡ ΡΠ°Π·ΠΎΠ±ΡΠ°Π»ΠΈ ΠΎΡΠ½ΠΎΠ²Π½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ Pillow Π² Python: Π½Π°ΡΡΠΈΠ»ΠΈΡΡ ΠΏΠΈΡΠ°ΡΡ ΡΠ΅ΠΊΡΡ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡ , ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ ΡΠ°Π·ΠΌΠ΅Ρ, ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°ΡΡ ΠΈΡ ΠΈ Π΄Π°ΠΆΠ΅ ΠΎΠ±ΡΠ΅Π·Π°ΡΡ.
ΠΠ°Π΄Π΅ΡΡΡ, ΡΡΠ°ΡΡΡ Π±ΡΠ»Π° ΠΏΠΎΠ»Π΅Π·Π½Π° Π΄Π»Ρ Π²Π°Ρ. Π£Π΄Π°ΡΠΈ!