
PIL in Python from easy to hard
To get to the complex processing algorithms, it is worth analyzing standard schemes, with which I suggest starting.
For processing examples, an image with different sets of colors will be used:

To start, we need two library modules:
Set up tools for comfortable future work:
We will process images in RGB format. PIL also supports working with formats 1, L, P, RGB, RGBA, CMYK, YCbCr, LAB, HSV, I, F.
Pixel values in the image are specified in the format: (x, y), (red, green, blue) , where x, y are the coordinates, and the numerical values of RGB are in the range from 0 to 255. That is, we work with an 8-bit image.
A gray tint appears in the case of equality of all color palettes, so we need to get the arithmetic mean value in all three points:

Inversion is obtained by subtracting the current color from 255:

Combining the two previous algorithms, you can write the following code:

For this algorithm, you need to determine the threshold value, which I will take for 100:

In the following articles, I would like to talk about how to more locally approach image filtering by dividing it into areas, as well as show interesting possibilities of DFS in image processing algorithms
For processing examples, an image with different sets of colors will be used:

To start, we need two library modules:
from PIL import Image, ImageDraw
Set up tools for comfortable future work:
image = Image.open('test.jpg') # Открываем изображение
draw = ImageDraw.Draw(image) # Создаем инструмент для рисования
width = image.size[0] # Определяем ширину
height = image.size[1] # Определяем высоту
pix = image.load() # Выгружаем значения пикселей
Let's get started
We will process images in RGB format. PIL also supports working with formats 1, L, P, RGB, RGBA, CMYK, YCbCr, LAB, HSV, I, F.
Pixel values in the image are specified in the format: (x, y), (red, green, blue) , where x, y are the coordinates, and the numerical values of RGB are in the range from 0 to 255. That is, we work with an 8-bit image.
Grayscale
A gray tint appears in the case of equality of all color palettes, so we need to get the arithmetic mean value in all three points:
for x in range(width):
for y in range(height):
r = pix[x, y][0] #узнаём значение красного цвета пикселя
g = pix[x, y][1] #зелёного
b = pix[x, y][2] #синего
sr = (r + g + b) // 3 #среднее значение
draw.point((x, y), (sr, sr, sr)) #рисуем пиксель
image.save("result.jpg", "JPEG") #не забываем сохранить изображение

Slight nuance
Inversion
Inversion is obtained by subtracting the current color from 255:
for x in range(width):
for y in range(height):
r = pix[x, y][0]
g = pix[x, y][1]
b = pix[x, y][2]
draw.point((x, y), (255 - r, 255 - g, 255 - b))

Grayscale Inversion
Combining the two previous algorithms, you can write the following code:
for x in range(width):
for y in range(height):
r = pix[x, y][0]
g = pix[x, y][1]
b = pix[x, y][2]
sr = (r + g + b) // 3
draw.point((x, y), (255 - sr, 255 - sr, 255 - sr))

Selective Grayscale Inversion
For this algorithm, you need to determine the threshold value, which I will take for 100:
for x in range(width):
for y in range(height):
r = pix[x, y][0]
g = pix[x, y][1]
b = pix[x, y][2]
if (r+g+b)>100: #если сумма значений больше 100 , то используем инверисю
sr = (r + g + b) // 3
draw.point((x, y), (255-sr, 255-sr, 255-sr))
else: #иначе обычный оттенок серого
sr = (r + g + b) // 3
draw.point((x, y), (sr, sr, sr))

Conclusion
In the following articles, I would like to talk about how to more locally approach image filtering by dividing it into areas, as well as show interesting possibilities of DFS in image processing algorithms