Cách cắt hình ảnh trong OpenCV bằng Python


234

Làm cách nào tôi có thể cắt hình ảnh, giống như tôi đã thực hiện trước đây trong PIL, bằng cách sử dụng OpenCV.

Ví dụ hoạt động trên PIL

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

Nhưng làm thế nào tôi có thể làm điều đó trên OpenCV?

Đây là những gì tôi đã cố gắng:

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)

Nhưng nó không hoạt động.

Tôi nghĩ rằng tôi sử dụng không chính xác getRectSubPix. Nếu đây là trường hợp, xin vui lòng giải thích làm thế nào tôi có thể sử dụng chính xác chức năng này.

Câu trả lời:


528

Nó rất đơn giản. Sử dụng numpy cắt.

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
Hmm ... Nhưng làm thế nào tôi có thể lưu hình ảnh crop thành biến?
Nolik

56
hãy nhớ rằng x và y được lật. Tôi đã bỏ lỡ điều này.
markroxor

10
Ngoài ra, nếu bạn đã xác định biên độ cắt, bạn có thể làmcrop_img = img[margin:-margin, margin:-margin]
Rufus

39
Điều này thật tuyệt, chỉ cần lưu ý rằng việc thay đổi crop_img sẽ thay đổi img. Nếu không, bạn nên crop_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
@javadba chi tiết thực hiện numpy. Numpy sử dụng hàng, ký hiệu col thay vì col, hàng
Froyo

121

tôi đã có câu hỏi này và tìm thấy một câu trả lời khác ở đây: sao chép khu vực quan tâm

Nếu chúng ta coi (0,0) là góc trên cùng bên trái của hình ảnh được gọi imvới hướng từ trái sang phải là hướng x và từ trên xuống dưới là hướng y. và chúng ta có (x1, y1) là đỉnh trên cùng bên trái và (x2, y2) là đỉnh dưới cùng bên phải của một vùng hình chữ nhật trong hình ảnh đó, sau đó:

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

đây là một nguồn tài nguyên toàn diện về lập chỉ mục và cắt mảng numpy có thể cho bạn biết thêm về những thứ như cắt xén một phần của hình ảnh. hình ảnh sẽ được lưu trữ dưới dạng một mảng numpy trong opencv2.

:)


Xin chào, không phải là `roi = im [y1: y2 + 1, x1: x2 + 1]` trong hoàn cảnh của bạn? Bởi vì numpy sử dụng vùng bị loại trừ để cắt.
Scott Yang

@ samkhan13, khi tôi sử dụng công thức này, tất cả các loại cây trồng của tôi đều có hình dạng (0, chiều rộng, kênh). I E. Tôi hoàn toàn không nhận được kích thước nào
mLstudent33

@ mLstudent33 có khả năng hình ảnh imchưa được đọc chính xác và trống. hãy thử sử dụng IDE với các điểm dừng để chẩn đoán mã của bạn từng bước. bạn có thể sử dụng google colab để tạo các khối mã và có thể chia sẻ sổ ghi chép jupytor của mình trên phòng trò chuyện python stackoverflow để nhờ ai đó giúp đỡ.
samkhan13

@ samkhan13 thực sự tôi có một vấn đề kỳ lạ mà tôi đã đăng trên Github Opencv Các vấn đề: github.com/opencv/opencv/issues/15406 Tôi cũng sẽ kiểm tra trò chuyện. Cảm ơn!
mLstudent33

16

Lưu ý rằng, hình ảnh cắt không tạo ra một bản sao của cropped imagenhưng tạo pointercho roi. Nếu bạn đang tải quá nhiều hình ảnh, cắt xén các phần có liên quan của hình ảnh bằng cách cắt, sau đó thêm vào danh sách, đây có thể là một sự lãng phí bộ nhớ rất lớn.

Giả sử bạn tải N hình ảnh mỗi >1MPvà bạn chỉ cần 100x100khu vực từ góc trên bên trái.

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.

Ngoài ra, bạn có thể sao chép phần có liên quan bằng cách .copy(), vì vậy trình thu gom rác sẽ loại bỏ 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.

Sau khi tìm hiểu điều này, tôi nhận ra một trong những bình luận của user1270710 đã đề cập đến điều đó nhưng tôi đã mất khá nhiều thời gian để tìm hiểu (ví dụ, gỡ lỗi, v.v.). Vì vậy, tôi nghĩ rằng nó đáng được đề cập.



Trong ý nghĩa của không gian bộ nhớ bị chiếm dụng, tôi hiểu rằng sao chép vùng quan tâm là điều tốt nhất để làm, nhưng còn tốn thời gian thì sao? Nếu tôi làm copy()ROI, so với cắt lát, kết quả sẽ thế nào? Ngoài ra, nếu tôi có một biến tmptrong đó tôi lưu trữ mỗi hình ảnh tôi tải từ máy tính của mình, thì việc cắt lát không ảnh hưởng xấu đến bộ nhớ của tôi, phải không? Vấn đề bạn mô tả chỉ liên quan đến những gì xảy ra khi bạn tải tất cả hình ảnh và sau đó bạn lưu lại ROI, có cả bản gốc và ROI . Xin vui lòng cho tôi biết nếu tôi đã hiểu đúng.
Cătălina Sîrbu

Sao chép sẽ là thời gian không đáng kể trong trường hợp tôi nói. Trừ khi bạn sao chép hình ảnh lớn rất nhiều lần, bạn sẽ không có chênh lệch múi giờ. Trong mã của tôi, hiệu ứng sẽ giống như ít hơn 1ms mỗi lần cắt. Vấn đề là bạn lưu trữ hình ảnh lớn và một con trỏ (ROI chỉ có vài byte) hoặc bạn lưu trữ một hình ảnh nhỏ trong bộ nhớ (trong trường hợp của tôi). Nếu bạn làm điều này một vài lần, nó là tốt. Tuy nhiên, nếu bạn làm điều này hàng ngàn lần, việc sử dụng bộ nhớ sẽ trở nên điên rồ với việc cắt lát. Giống như bạn lấp đầy toàn bộ bộ nhớ sau một vài lần tải hình ảnh nếu bạn cắt lát. Trong khi đó mã của tôi vẫn sẽ được đặt hàng nếu MBs
smttsp

12

mã này cắt một hình ảnh từ vị trí x = 0, y = 0 đến 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, vậy chiều cao là 100 pixel "bên dưới" y = 0 phải không? Đó là hàng thứ 101 của mảng numpy? Và chiều rộng là 200 pixel ở bên phải của x = 0 có đúng không?
mLstudent33

4

Dưới đây là cách để cắt một hình ảnh.

image_path: Đường dẫn đến hình ảnh cần chỉnh sửa

coords: Một tuple của tọa độ x / y (x1, y1, x2, y2) [mở hình ảnh trong mspaint và kiểm tra "thước đo" trong tab xem để xem tọa độ]

yet_location : Đường dẫn để lưu hình ảnh đã cắt

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

Cây trồng mạnh mẽ với chức năng biên giới sao chép 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

Bạn có thể vui lòng giải thích bbox ở đây là gì và chúng ta phải cho giá trị của nó là gì bởi vì bất kỳ giá trị nào tôi đang cố gắng vượt qua, nó đều gây ra lỗi cho tôi x1,y1,x2,y2 = bbox trong khi nói:TypeError: 'int' object is not iterable
Sabah

3

Đây là một số mã cho imcrop mạnh mẽ hơn (giống như trong 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

Ngoài ra, bạn có thể sử dụng tenorflow cho crop và openCV để tạo một mảng từ hình ảnh.

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

Bây giờ imglà một mảng hình ảnh (imageheight, hình ảnh, 3). Cắt mảng với tenorflow:

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
)

Lắp lại hình ảnh với tf.keras, vì vậy chúng tôi có thể xem nó nếu nó hoạt động:

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

Điều này in ra pic trong một máy tính xách tay (được thử nghiệm trong Google Colab).


Toàn bộ mã với nhau:

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
)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.