Як обрізати зображення у OpenCV за допомогою Python


233

Як я можу обрізати зображення, як я це робив раніше в PIL, використовуючи OpenCV.

Робочий приклад на PIL

im = Image.open('0.png').convert('L')
im = im.crop((1, 1, 98, 33))
im.save('_0.png')

Але як я можу це зробити на OpenCV?

Це те, що я спробував:

im = cv.imread('0.png', cv.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, im_bw) = cv.threshold(im, 128, 255, cv.THRESH_OTSU)
im = cv.getRectSubPix(im_bw, (98, 33), (1, 1))
cv.imshow('Img', im)
cv.waitKey(0)

Але це не працює.

Я думаю, що я неправильно використав getRectSubPix. Якщо це так, будь ласка, поясніть, як я можу правильно використовувати цю функцію.

Відповіді:


527

Це дуже просто. Скористайтеся нарізкою.

import cv2
img = cv2.imread("lenna.png")
crop_img = img[y:y+h, x:x+w]
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)

9
Хм ... Але як я можу зберегти обрізати зображення в змінну?
Нолик

56
пам'ятайте, що x і y перевернуті. Я пропустив це.
markroxor

10
Крім того, якщо ви визначили запас урожаю, ви можете зробити цеcrop_img = img[margin:-margin, margin:-margin]
Руфус,

39
Це чудово, просто майте на увазі, що зміна crop_img змінить img. В іншому випадку вам слід crop_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
@javadba numpy деталі реалізації. Numpy використовує рядок, позначення col замість col, рядок
Froyo

121

У мене було це питання і тут я знайшов іншу відповідь: копіювати область інтересу

Якщо ми розглядаємо (0,0) як верхній лівий кут зображення, який називається imліворуч праворуч у напрямку x, а зверху вниз як напрямок y. і маємо (x1, y1) як верхню ліву вершину і (x2, y2) як нижню праву вершину прямокутника в межах цього зображення, то:

roi = im[y1:y2, x1:x2]

Ось вичерпний ресурс щодо індексації та нарізки масивів, який може розповісти більше про речі, такі як обрізання частини зображення. зображення зберігатимуться як нумерований масив у opencv2.

:)


Привіт, чи не повинен бути "roi = im [y1: y2 + 1, x1: x2 + 1]" за ваших обставин? Оскільки numpy використовує виключену область для зрізу.
Скотт Ян

@ samkhan13, коли я обрізаю за цією формулою, усі мої культури мають форму (0, ширина, канали). Тобто Я взагалі не отримую жодного виміру
mLstudent33

@ mLstudent33 ймовірно, що зображення imбуло прочитано неправильно та порожнє. спробуйте використовувати IDE з точками переривання, щоб діагностувати код покроково. ви можете використовувати google colab для створення блоків коду, а також можете поділитися своєю зошитом jupytor на чаті stackoverflow python, щоб отримати допомогу.
samkhan13

@ samkhan13 Насправді у мене є дивна проблема, яку я розмістив на Github Opencv Issues: github.com/opencv/opencv/isissue/15406 Я також перевіряю чат. Дякую!
mLstudent33

16

Слід зазначити, що зображення нарізка не створює копію , cropped imageале створюючи pointerв roi. Якщо ви завантажуєте стільки зображень, обрізаючи відповідні частини зображень нарізкою, а потім додаючи їх до списку, це може бути величезним марнотратством.

Припустимо, ви завантажуєте кожне N зображень, >1MPі вам потрібна лише 100x100область з верхнього лівого кута.

Slicing:

X = []
for i in range(N):
    im = imread('image_i')
    X.append(im[0:100,0:100]) # This will keep all N images in the memory. 
                              # Because they are still used.

Крім того, ви можете скопіювати відповідну частину за допомогою .copy(), тому сміттєзбірник буде видалений im.

X = []
for i in range(N):
    im = imread('image_i')
    X.append(im[0:100,0:100].copy()) # This will keep only the crops in the memory. 
                                     # im's will be deleted by gc.

Дізнавшись про це, я зрозумів, що один із коментарів користувача1270710 згадав це, але це знадобило мені досить багато часу (тобто, налагодження тощо). Отже, я вважаю, що це варто згадати.



У розумінні займаного простору пам’яті я розумію, що скопіювати цікавить область найкраще, але що робити з часом? Якби я зробив copy()рентабельність інвестицій, порівняно з нарізкою, який би був результат ?. Крім того, якщо у мене є змінна, tmpв якій я зберігаю кожну картинку, яку завантажую з комп'ютера, нарізка не повинна погано впливати на мою пам'ять, правда? Описана вами проблема пов’язана лише з тим, що відбувається, коли ви завантажуєте всі зображення, а потім знову зберігаєте їх рентабельність інвестицій, маючи як оригінали, так і рентабельність інвестицій . Будь ласка, дайте мені знати, чи правильно я зрозумів.
Cătălina Sîrbu

Копіювання буде мізерним часом у випадку, про який я сказав. Якщо ви не копіюєте великі зображення стільки разів, у вас не буде різниці у часі. У моєму коді ефект буде меншим за 1 мс на обрізку. Проблема полягає в тому, що ви або зберігаєте велике зображення та покажчик (ROI, який становить лише кілька байтів), або ви зберігаєте невелике зображення в пам'яті (в моєму випадку). Якщо ви зробите це кілька разів, це добре. Однак якщо ви зробите це тисячі разів, використання пам'яті буде з розуму від нарізки. Як би ви заповнили всю пам'ять через пару, якщо завантажуєте тисячу зображень, якщо ви робите нарізку. Тоді як мій код все ще буде замовлений, якщо МБ
smttsp

12

цей код обрізає зображення з позиції x = 0, y = 0 до h = 100, w = 200

import numpy as np
import cv2

image = cv2.imread('download.jpg')
y=0
x=0
h=100
w=200
crop = image[y:y+h, x:x+w]
cv2.imshow('Image', crop)
cv2.waitKey(0) 

@hatami, тому висота становить 100 пікселів "внизу" y = 0 правда? Це 101-й рядок масиву numpy? А ширина 200 пікселів праворуч від x = 0 правильна?
mLstudent33

4

Нижче наведено спосіб обрізати зображення.

image_path: шлях до редагування зображення

coords: Набір координат x / y (x1, y1, x2, y2) [відкрийте зображення в mspaint і перевірте "лінійку" на вкладці перегляду, щоб побачити координати]

save_location : шлях до збереження обрізаного зображення

from PIL import Image
    def crop(image_path, coords, saved_location:
        image_obj = Image.open("Path of the image to be cropped")
            cropped_image = image_obj.crop(coords)
            cropped_image.save(saved_location)
            cropped_image.show()


if __name__ == '__main__':
    image = "image.jpg"
    crop(image, (100, 210, 710,380 ), 'cropped.jpg')

3

Надійна обрізка з функцією рамки копіювання opencv:

def imcrop(img, bbox):
   x1, y1, x2, y2 = bbox
   if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
        img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
   return img[y1:y2, x1:x2, :]

def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
    img = cv2.copyMakeBorder(img, - min(0, y1), max(y2 - img.shape[0], 0),
                            -min(0, x1), max(x2 - img.shape[1], 0),cv2.BORDER_REPLICATE)
   y2 += -min(0, y1)
   y1 += -min(0, y1)
   x2 += -min(0, x1)
   x1 += -min(0, x1)
   return img, x1, x2, y1, y2

Чи можете ви поясніть, що тут є bbox, і що ми повинні дати йому значення, оскільки яке б значення я не намагався передати, воно дає мені помилку x1,y1,x2,y2 = bbox , кажучи:TypeError: 'int' object is not iterable
Сабах,

3

ось код для більш надійного imcrop (трохи як у matlab)

def imcrop(img, bbox): 
    x1,y1,x2,y2 = bbox
    if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
        img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
    return img[y1:y2, x1:x2, :]

def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
    img = np.pad(img, ((np.abs(np.minimum(0, y1)), np.maximum(y2 - img.shape[0], 0)),
               (np.abs(np.minimum(0, x1)), np.maximum(x2 - img.shape[1], 0)), (0,0)), mode="constant")
    y1 += np.abs(np.minimum(0, y1))
    y2 += np.abs(np.minimum(0, y1))
    x1 += np.abs(np.minimum(0, x1))
    x2 += np.abs(np.minimum(0, x1))
    return img, x1, x2, y1, y2

1

Крім того, ви можете використовувати tensorflow для обрізання та openCV для створення масиву із зображення.

import cv2
img = cv2.imread('YOURIMAGE.png')

Тепер imgце масив форми (висота зображення, ширина зображення, 3). Обрізати масив tensorflow:

import tensorflow as tf
offset_height=0
offset_width=0
target_height=500
target_width=500
x = tf.image.crop_to_bounding_box(
    img, offset_height, offset_width, target_height, target_width
)

Повторно зібрати зображення за допомогою tf.keras, щоб ми могли переглянути його, якщо він працював:

tf.keras.preprocessing.image.array_to_img(
    x, data_format=None, scale=True, dtype=None
)

Це виводить картинку в зошит (перевірена в Google Colab).


Весь код разом:

import cv2
img = cv2.imread('YOURIMAGE.png')

import tensorflow as tf
offset_height=0
offset_width=0
target_height=500
target_width=500
x = tf.image.crop_to_bounding_box(
    img, offset_height, offset_width, target_height, target_width
)

tf.keras.preprocessing.image.array_to_img(
    x, data_format=None, scale=True, dtype=None
)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.