So beschneiden Sie ein Bild in OpenCV mit Python

232

Wie kann ich mit OpenCV Bilder zuschneiden, wie ich es zuvor in PIL getan habe?

Arbeitsbeispiel zu PIL

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

Aber wie kann ich das auf OpenCV machen?

Folgendes habe ich versucht:

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)

Aber es funktioniert nicht.

Ich glaube ich habe falsch verwendet getRectSubPix. Wenn dies der Fall ist, erklären Sie bitte, wie ich diese Funktion richtig verwenden kann.

Nolik
quelle

Antworten:

524

Es ist sehr einfach. Verwenden Sie Numpy Slicing.

import cv2
img = cv2.imread("lenna.png")
crop_img = img[y:y+h, x:x+w]
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)
Froyo
quelle
9
Hmm ... Aber wie kann ich das Zuschneidebild in einer Variablen speichern?
Nolik
55
Denken Sie daran, dass x und y umgedreht sind. Das habe ich verpasst.
Markroxor
10
Alternativ, wenn Sie eine Erntemarge definiert haben, können Sie tuncrop_img = img[margin:-margin, margin:-margin]
Rufus
39
Das ist großartig, sei dir nur bewusst, dass das Ändern von crop_img img ändert. Andernfalls sollten Sie crop_img = img [y: y + h, x: x + w] .copy ()
user1270710
1
@javadba numpy Implementierungsdetail. Numpy verwendet Zeile, Spalte Notation anstelle von Spalte, Zeile
Froyo
121

Ich hatte diese Frage und fand hier eine andere Antwort: Region von Interesse kopieren

Wenn wir (0,0) als obere linke Ecke des Bildes betrachten, wird es imvon links nach rechts als x-Richtung und von oben nach unten als y-Richtung aufgerufen . und wir haben (x1, y1) als den oberen linken Scheitelpunkt und (x2, y2) als den unteren rechten Scheitelpunkt eines Rechteckbereichs innerhalb dieses Bildes, dann:

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

Hier finden Sie eine umfassende Ressource zum Indizieren und Schneiden von Numpy-Arrays, die Ihnen mehr über das Zuschneiden eines Teils eines Bildes erzählen kann. Bilder würden als Numpy-Array in opencv2 gespeichert.

:) :)

samkhan13
quelle
Hallo, sollte es unter Ihren Umständen nicht "roi = im [y1: y2 + 1, x1: x2 + 1]" sein? Weil numpy den ausgeschlossenen Bereich zum Schneiden verwendet.
Scott Yang
@ samkhan13, wenn ich mit dieser Formel beschneide, haben alle meine Ernten eine Form (0, Breite, Kanäle). Dh. Ich
bekomme
@ mLstudent33 Es ist wahrscheinlich, dass das Bild imnicht richtig gelesen wurde und leer ist. Versuchen Sie, eine IDE mit Haltepunkten zu verwenden, um Ihren Code Schritt für Schritt zu diagnostizieren. Sie können Google Colab verwenden , um Codeblöcke zu erstellen, und Ihr Jupytor-Notizbuch im Stackoverflow-Python-Chatroom freigeben, um Hilfe von jemandem zu erhalten.
Samkhan13
@ samkhan13 Eigentlich habe ich ein seltsames Problem, das ich auf Github Opencv Issues gepostet habe: github.com/opencv/opencv/issues/15406 Ich werde auch den Chat überprüfen. Vielen Dank!
mLstudent33
16

Beachten Sie, dass die Bild Slicing nicht eine Kopie der Erstellung , cropped imagesondern eine Schaffung von pointerauf die roi. Wenn Sie so viele Bilder laden, die relevanten Teile der Bilder mit Slicing beschneiden und dann in eine Liste einfügen, kann dies eine enorme Speicherverschwendung sein.

Angenommen, Sie laden jeweils N Bilder >1MPund benötigen nur den 100x100Bereich in der oberen linken Ecke.

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.

Alternativ können Sie das relevante Teil von kopieren .copy(), damit der Garbage Collector entfernt wirdim .

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.

Nachdem ich dies herausgefunden hatte, stellte ich fest, dass einer der Kommentare von user1270710 dies erwähnte, aber es dauerte einige Zeit, bis ich es herausfand (dh Debuggen usw.). Ich denke, es ist erwähnenswert.

smttsp
quelle
Schauen Sie sich das an: stackoverflow.com/q/60359398/7644562
Abdul Rehman
Ich verstehe, dass das Kopieren des interessierenden Bereichs das Beste ist, aber was ist mit dem Zeitaufwand? Wenn ich copy()den ROI im Vergleich zum Schneiden mache , was wäre das Ergebnis? Wenn ich eine Variable habe, tmpin der ich jedes Bild speichere, das ich von meinem Computer lade, sollte das Schneiden keinen negativen Einfluss auf mein Gedächtnis haben, oder? Das von Ihnen beschriebene Problem hängt nur damit zusammen, was passiert, wenn Sie alle Bilder laden und dann den ROI erneut speichern, wobei sowohl die Originale als auch der ROI berücksichtigt werden . Bitte lassen Sie mich wissen, ob ich richtig verstanden habe.
Cătălina Sîrbu
Das Kopieren wird in dem Fall, den ich sagte, vernachlässigbar sein. Wenn Sie nicht so oft große Bilder kopieren, haben Sie keinen Zeitunterschied. In meinem Code beträgt der Effekt weniger als 1 ms pro Zuschneiden. Das Problem ist, dass Sie entweder das große Bild und einen Zeiger (ROI, der nur wenige Bytes beträgt) oder ein kleines Bild im Speicher speichern (in meinem Fall). Wenn Sie dies einige Male tun, ist es in Ordnung. Wenn Sie dies jedoch tausende Male tun, wird die Speichernutzung durch das Schneiden verrückt. Als ob Sie den gesamten Speicher nach ein paar, wenn tausend Bilder geladen werden, wenn Sie schneiden. Während mein Code noch in der Bestellung ist, wenn MBs
smttsp
12

Dieser Code schneidet ein Bild von der Position x = 0, y = 0 auf 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) 
m.hatami
quelle
@hatami, also ist die Höhe 100 Pixel "unter" y = 0 richtig? Es ist die 101. Zeile des Numpy-Arrays? Und Breite ist 200 Pixel rechts von x = 0 richtig?
mLstudent33
4

Unten sehen Sie, wie Sie ein Bild zuschneiden.

Bildpfad: Der Pfad zum zu bearbeitenden Bild

Koordinaten:Koordinaten Ein Tupel von x / y-Koordinaten (x1, y1, x2, y2) [Öffnen Sie das Bild in mspaint und überprüfen Sie das "Lineal" auf der Registerkarte "Ansicht", um die Koordinaten anzuzeigen.]

saved_location : Pfad zum Speichern des zugeschnittenen Bildes

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')
Sanyal
quelle
3

Robustes Zuschneiden mit OpenCV-Kopierrahmenfunktion:

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
Belgraviton
quelle
Können Sie bitte erklären, was bbox hier ist und was wir in seinem Wert geben sollen, denn welcher Wert auch immer ich zu übergeben versuche, es gibt mir Fehler, x1,y1,x2,y2 = bbox wenn ich sage:TypeError: 'int' object is not iterable
Sabah
3

Hier ist ein Code für robusteres Imcrop (ein bisschen wie in 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
Dan Erez
quelle
1

Alternativ können Sie Tensorflow zum Zuschneiden und openCV zum Erstellen eines Arrays aus dem Bild verwenden.

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

Jetzt imgist ein (Bildhöhe, Bildbreite, 3) Formarray. Beschneiden Sie das Array mit 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
)

Setzen Sie das Bild mit tf.keras wieder zusammen, damit wir es uns ansehen können, wenn es funktioniert hat:

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

Dadurch wird das Bild in einem Notizbuch ausgedruckt (getestet in Google Colab).


Der ganze Code zusammen:

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
)
zabop
quelle