Как сделать маску изображения, которая меняет часть изображения на градацию серого цвета?

Нужно наложить на изображение круг, за пределами которого изображение будет менять цвет на градацию серого цвета, а внутри круга цвет изображения не будет меняться.

В итоге, получилось только сделать так, чтобы цвет изображения за кругом менялся в негатив:

import matplotlib.cbook as cbook
import matplotlib.pyplot as plt
import numpy as np
import skimage.io as io
from skimage import color
import cv2
from skimage.color import rgb2gray
import matplotlib.image as mpimg

fig = plt.figure(figsize=(16,12))

ax_1 = fig.add_subplot(231)
ax_1.set(xticks=[], yticks=[])
ax_1.Img = io.imread ('Testphoto.jpg')

mask = ax_1.Img <87
l_x, l_y = ax_1.Img.shape[0], ax_1.Img.shape[1]
X, Y = np.ogrid[:l_x, :l_y]
outer_disk_mask = (X - l_x / 2)**2 + (Y - l_y/2)**2 > (l_x / 3)**2

img1 = ax_1.Img[outer_disk_mask]
ax_1.Img[outer_disk_mask] = img1-41


ax_1.axis('off')
ax_1.imshow(ax_1.Img)

Кто знает, как это можно реализовать?

Первая картинка на изображении:

Зона за кругом должна быть в градации серого.


Ответы (2 шт):

Автор решения: MaxU

Предлагаю решение с использованием OpenCV и Numpy:

import numpy as np
import cv2


def get_circle_mask(img, center=None, radius=None):
    height, width, depth = img.shape
    if center is None:
        center = (width // 2, height // 2)
    if radius is None:
        radius = min(height, width) // 4
    circle_mask = np.zeros((height, width), np.uint8)
    return cv2.circle(circle_mask, center, radius, 1, thickness=-1)
    
def mask_grayscale(img, mask, alpha=1.0, beta=1.0):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # convert grayscale to 3-channel grayscale image
    img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
    # invert mask, to select the area outside of a circle
    inv_mask = (mask == 0).astype("int8")
    # overlay - black out all pixels outside of a circle
    overlay = cv2.bitwise_and(img, img, mask=mask)
    # background - black out all pixels inside of a circle
    background = cv2.bitwise_and(img_gray, img_gray, mask=inv_mask)
    out = background.copy()
    return cv2.addWeighted(overlay, alpha, out, beta, 0, out)
    

# fn = r"D:\download\mountain.jpg"
# img = cv2.imread(fn)

url = "https://www.alpinschule-ortler.com/uploads/tx_bh/titel_32.jpg"
cap = cv2.VideoCapture(url)
ret,img = cap.read()

mask = get_circle_mask(img)
out = mask_grayscale(img, mask)
cv2.imshow("result", out)
cv2.imwrite(r"c:\temp\out.jpg", out)

введите сюда описание изображения


UPDATE: используя функцию mask_grayscale() можно подсветить или затемнить каждую из частей изображения:

im_small = cv2.resize(img, (0,0), fx=0.5, fy=0.5)
mask = get_circle_mask(im_small)

cv2.imshow("", mask_grayscale(im_small, mask, alpha=1.3, beta=0.6))

введите сюда описание изображения

cv2.imshow("", mask_grayscale(im_small, mask, alpha=0.6, beta=1.5))

введите сюда описание изображения

→ Ссылка
Автор решения: Alex Alex

Еще вариант:

import cv2
url = "https://www.alpinschule-ortler.com/uploads/tx_bh/titel_32.jpg"
cap = cv2.VideoCapture(url)
ret,img = cap.read()
height, width, depth = img.shape
center = (width // 2, height // 2)
radius = min(height, width) // 4
hsv=cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v=cv2.split(hsv)
mask=s.copy()
mask[:]=0
mask=cv2.circle(mask, center, radius, 255, thickness=-1)
s=cv2.bitwise_and(s, mask)
cv2.merge((h,s,v), dst=hsv)
img2=cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

cv2.imshow('sample', img2)

результат:

введите сюда описание изображения

→ Ссылка