วิธีการครอบตัดรูปภาพใน OpenCV โดยใช้ Python


234

ฉันจะครอบตัดรูปภาพอย่างที่เคยทำใน 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ผมคิดว่าผมใช้ไม่ถูกต้อง หากเป็นกรณีนี้โปรดอธิบายวิธีที่ฉันสามารถใช้ฟังก์ชั่นนี้ได้อย่างถูกต้อง

คำตอบ:


528

มันง่ายมาก ใช้การแบ่ง 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]
รูฟัส

39
นี่เยี่ยมมากโปรดทราบว่าการเปลี่ยน crop_img จะเปลี่ยน img มิฉะนั้นคุณควร crop_img = img [y: y + h, x: x + w] .copy ()
user1270710

1
@javadba รายละเอียดการใช้งาน numpy Numpy ใช้ row, col
Froyo

121

ฉันมีคำถามนี้และพบคำตอบอื่นที่นี่: คัดลอกภูมิภาคที่น่าสนใจ

หากเราพิจารณา (0,0) เป็นมุมบนซ้ายของภาพที่เรียกว่าimมีซ้ายไปขวาเป็นทิศทาง x และจากบนลงล่างเป็นทิศทาง y และเรามี (x1, y1) เป็นจุดสุดยอดมุมซ้ายบนและ (x2, y2) เป็นจุดสุดยอดมุมขวาล่างของพื้นที่สี่เหลี่ยมผืนผ้าภายในภาพนั้นจากนั้น:

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

นี่คือทรัพยากรที่ครอบคลุมเกี่ยวกับการจัดทำดัชนีอาร์เรย์และการแบ่งส่วนซึ่งสามารถบอกคุณเพิ่มเติมเกี่ยวกับสิ่งต่าง ๆ เช่นการครอบตัดส่วนของรูปภาพ ภาพจะถูกเก็บไว้เป็นอาร์เรย์ numpy ใน opencv2

:)


สวัสดีมันควรจะเป็น `roi = im [y1: y2 + 1, x1: x2 + 1]` ภายใต้สถานการณ์ของคุณ? เพราะมีผู้ใช้จำนวนมากใช้พื้นที่ที่ถูกแยกเพื่อตัด
Scott Yang

@ samkhan13 เมื่อฉันครอบตัดโดยใช้สูตรนี้พืชทั้งหมดของฉันมีรูปร่าง (0, ความกว้าง, ช่อง) กล่าวคือ ฉันไม่ได้รับมิติใด ๆ เลย
mLstudent33

@ mLstudent33 เป็นไปได้ว่าภาพimไม่ได้อ่านอย่างถูกต้องและว่างเปล่า ลองใช้ IDE พร้อมเบรกพอยต์เพื่อวิเคราะห์รหัสของคุณทีละขั้นตอน คุณสามารถใช้google colabเพื่อสร้างบล็อคโค้ดและสามารถแชร์สมุดบันทึก jupytor ของคุณบนห้องแชทหลาม stackoverflowเพื่อรับความช่วยเหลือ
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.

หลังจากค้นพบสิ่งนี้ฉันได้ตระหนักถึงความคิดเห็นอย่างหนึ่งของผู้ใช้1270710 ที่กล่าวถึงว่า แต่ฉันใช้เวลาพอสมควรในการค้นหา ดังนั้นฉันคิดว่ามันควรค่าแก่การกล่าวขวัญ



ในความหมายของพื้นที่หน่วยความจำที่ถูกครอบครองฉันเข้าใจว่าการคัดลอกพื้นที่ที่น่าสนใจเป็นสิ่งที่ดีที่สุดที่จะทำ แต่จะใช้เวลานานแค่ไหน? ถ้าฉันทำcopy()ROI เปรียบเทียบกับการแบ่งส่วนผลจะเป็นอย่างไร นอกจากนี้หากฉันมีตัวแปรtmpที่ฉันจัดเก็บรูปภาพแต่ละรูปที่ฉันโหลดจากคอมพิวเตอร์ของฉันการแบ่งส่วนข้อมูลไม่ควรส่งผลเสียต่อหน่วยความจำของฉันใช่ไหม ปัญหาที่คุณอธิบายจะเกี่ยวข้องเฉพาะกับสิ่งที่เกิดขึ้นเมื่อคุณโหลดภาพทั้งหมดแล้วคุณเก็บอีกครั้งพวกเขาผลตอบแทนการลงทุนมีทั้งต้นฉบับและผลตอบแทนการลงทุน โปรดแจ้งให้เราทราบหากฉันเข้าใจถูกต้อง
CătălinaSî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 ใช่ไหม มันเป็นแถวที่ 101 ของแถว numpy หรือไม่? และความกว้างคือ 200 พิกเซลทางด้านขวาของ x = 0 ถูกต้อง?
mLstudent33

4

ด้านล่างเป็นวิธีการครอบตัดรูปภาพ

image_path:เส้นทางไปยังรูปภาพที่จะแก้ไข

coords: tuple ของพิกัด 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คืออาเรย์ (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.