Як обрізати зображення у 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.