Python을 사용하여 OpenCV에서 이미지를 자르는 방법


233

OpenCV를 사용하여 이전에 PIL에서 한 것처럼 이미지를 자르는 방법

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

매우 간단합니다. numpy 슬라이싱을 사용하십시오.

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
흠 ... 그러나 어떻게 자르기 이미지를 변수로 저장할 수 있습니까?
Nolik

56
x와 y가 뒤집힌다는 것을 기억하십시오 . 나는 이것을 놓쳤다.
markroxor

10
또는 작물 마진을 정의한 경우 다음을 수행 할 수 있습니다.crop_img = img[margin:-margin, margin:-margin]
Rufus

39
crop_img를 변경하면 img가 변경됩니다. 그렇지 않으면 crop_img = img [y : y + h, x : x + w] .copy ()
user1270710

1
@javadba numpy 구현 세부 사항. Numpy는 col 대신 row, col 표기법을 사용합니다
Froyo

121

나는이 질문을했고 여기에 다른 대답을 찾았습니다. 관심있는 복사 영역

(0,0)을 이미지의 왼쪽 상단으로 간주하면 im왼쪽에서 오른쪽으로 x 방향으로, 위에서 아래로 y 방향으로 호출 됩니다. 그리고 우리는 (x1, y1)을 왼쪽 위 꼭지점으로하고 (x2, y2)를 그 이미지 내 사각형 영역의 오른쪽 아래 꼭지점으로합니다.

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

다음은 이미지의 일부를 자르는 것과 같은 것들에 대해 더 많이 알 수있는 numpy 배열 인덱싱 및 슬라이싱 에 대한 포괄적 인 리소스입니다 . 이미지는 opencv2에 numpy 배열로 저장됩니다.

:)


안녕하세요, 당신의 상황에서`roi = im [y1 : y2 + 1, x1 : x2 + 1]이되어서는 안됩니까? numpy는 제외 영역을 사용하여 슬라이스하기 때문입니다.
Scott Yang

@ samkhan13,이 수식을 사용하여 자르면 모든 자르기의 모양이 (0, 너비, 채널)입니다. 즉. 나는 전혀 치수를 얻지 못하고있다
mLstudent33

@ mLstudent33 이미지 im가 올바르게 읽히지 않았거나 비어있을 수 있습니다. 중단 점과 함께 IDE를 사용하여 코드를 단계별로 진단 해보십시오. 당신이 사용할 수있는 구글 colab을 코드 블록을 작성하고 당신의 jupytor 노트북을 공유 할 수 있습니다 유래 파이썬 대화방 누군가의 도움을받을 수 있습니다.
samkhan13

@ samkhan13 실제로 Github Opencv에 게시 된 이상한 문제가 있습니다 .github.com / opencv / opencv / issues / 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.

이를 찾는 후, 나는 깨달았다 의견 중 하나 에 의해 user1270710 언급을하지만 (즉, 디버깅 등)를 찾아 나에게 꽤 시간이 걸렸습니다. 따라서 언급 할 가치가 있다고 생각합니다.



메모리 공간이 차지한다는 점에서 관심 영역을 복사하는 것이 최선의 방법이라는 것을 이해하지만 시간이 많이 걸리는 것은 무엇입니까? copy()슬라이싱과 비교하여 ROI를 수행 하면 결과는 무엇입니까? 또한 tmp컴퓨터에서로드 한 각 그림을 저장할 변수 가있는 경우 슬라이싱이 메모리에 나쁜 영향을 미치지 않아야합니다. 설명하는 문제는 모든 이미지를로드 한 다음 원본과 ROI가 모두있는 ROI를 다시 저장할 때 발생하는 문제와 만 관련이 있습니다 . 내가 제대로 이해했다면 알려주십시오.
Cătălina Sîrbu

내가 말한 경우 복사는 무시할 수있는 시간이 될 것입니다. 큰 이미지를 여러 번 복사하지 않으면 시간 차이가 없습니다. 내 코드에서 효과는 자르기 당 1ms 미만입니다. 문제는 큰 이미지와 포인터 (몇 바이트에 불과한 ROI)를 저장하거나 작은 이미지를 메모리에 저장하는 것입니다 (필자의 경우). 이 작업을 몇 번하면 괜찮습니다. 그러나이 작업을 수천 번 수행하면 메모리 사용량이 슬라이싱에 미치게됩니다. 슬라이스를하면 수천 장의 이미지를로드 한 후 전체 메모리를 채우는 것처럼. MB 인 경우 내 코드는 여전히 주문 상태입니다.
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입니까? numpy 배열의 101 번째 행입니까? 너비는 x = 0의 오른쪽에 200 픽셀입니까?
mLstudent33'8

4

아래는 이미지를 자르는 방법입니다.

image_path : 편집 할 이미지의 경로

coords : x / y 좌표의 튜플 (x1, y1, x2, y2) [mspaint에서 이미지를 열고 좌표를 보려면보기 탭에서 "ruler"를 확인하십시오.]

saved_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 = bboxTypeError: 'int' object is not iterable
Sabah

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(imageheight, imagewidth, 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
)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.