Question: I have this input image (feel free to download it and try your solution, please):  I need to find points A and B that are closest to the left down and right upper corner. And than I would like to cut of the image. See desired output:  So far I have this function, but it does not find points A, B correctly:  def CheckForLess(list1, val): return(all(x < val for x in list1)) def find_corner_pixels(img): # Get image dimensions height, width = img.shape[:2] # Find the first non-black pixel closest to the left-down and right-up corners nonempty = [] for i in range(height): for j in range(width): # Check if the current pixel is non-black if not CheckForLess(img[i, j], 10): nonempty.append([i, 1080 - j]) return min(nonempty) , max(nonempty)  Can you help me please?  The solution provided by Achille works on one picture, but if I change input image to this:  It gives wrong output: 

Answer: I noticed that your image has an alpha mask that already segment the foreground. This imply using the flag cv.IMREAD_UNCHANGED when reading the image with openCV (cv.imread(filename, cv.IMREAD_UNCHANGED)). If this is the case you can have a try to the following: import sys from typing import Tuple import cv2 as cv import numpy as np class DetectROI: def __init__(self, alpha_threshold: int = 125, display: bool = False, gaussian_sigma: float = 1., gaussian_window: Tuple[int, int] = (3, 3), relative_corner: float = 0.25, relative_line_length: float = 0.25, relative_max_line_gap: float = 0.02, working_size: Tuple[int, int] = (256, 256)): self.alpha_threshold = alpha_threshold self.display = display self.working_size = working_size self.gaussian_sigma = gaussian_sigma self.gaussian_window = gaussian_window self.relative_line_length = relative_line_length self.relative_max_line_gap = relative_max_line_gap self.relative_corner = relative_corner self._origin: Tuple[int, int] = (0, 0) self._src_shape: Tuple[int, int] = (0, 0) def __call__(self, src): # get cropped contour cnt_img = self.get_cropped_contour(src) left_lines, right_lines = self.detect_lines(cnt_img) x, y, w, h = self.get_bounding_rectangle(left_lines + right_lines) # top_left = (x, y) top_right = (x + w, y) # bottom_right = (x + w, y + h) bottom_left = (x, y + h) if self.display: src = cv.rectangle(src, bottom_left, top_right, (0, 0, 255, 255), 3) cv.namedWindow("Source", cv.WINDOW_KEEPRATIO) cv.imshow("Source", src) cv.waitKey() return bottom_left, top_right def get_cropped_contour(self, src): self._src_shape = tuple(src.shape[:2]) msk = np.uint8((src[:, :, 3] > self.alpha_threshold) * 255) msk = cv.resize(msk, self.working_size) cnt, _ = cv.findContours(msk, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) cnt_img = cv.drawContours(np.zeros_like(msk), cnt, 0, (255,)) cnt = cnt[0] x, y, w, h = cv.boundingRect(np.array(cnt)) top_left = (x, y) # top_right = (x + w, y) bottom_right = (x + w, y + h) # bottom_left = (x, y + h) self._origin = top_left cnt_img = cnt_img[self._origin[1]:bottom_right[1], self._origin[0]:bottom_right[0]] if self.display: cv.namedWindow("Contours", cv.WINDOW_KEEPRATIO) cv.imshow("Contours", cnt_img) return cnt_img def detect_lines(self, img): img = cv.GaussianBlur(img, self.gaussian_window, self.gaussian_sigma) lines = cv.HoughLinesP(img, 1, np.pi / 180, 50, 50, int(self.relative_line_length*img.shape[0]), int(self.relative_max_line_gap*img.shape[0])) if self.display: lines_img = np.repeat(img[:, :, None], 3, axis=2) if lines is not None: for i in range(0, len(lines)): l = lines[i][0] cv.line(lines_img, (l[0], l[1]), (l[2], l[3]), (255, 0, 0), 2, cv.LINE_AA) # keep lines close to bottom left and bottom right images corner = self.relative_corner left_lines = [] right_lines = [] if lines is not None: # left side for i in range(0, len(lines)): l = lines[i][0] if (l[1] > (1 - corner) * img.shape[1] and l[0] < corner * img.shape[0]) \ or (l[3] > (1 - corner) * img.shape[1] and l[2] < corner * img.shape[0]): left_lines.append(l) elif (l[1] > (1 - corner) * img.shape[1] and l[0] > (1 - corner) * img.shape[0]) \ or (l[3] > (1 - corner) * img.shape[1] and l[2] > (1 - corner) * img.shape[0]): right_lines.append(l) if self.display: if lines is not None: for l in left_lines + right_lines: cv.line(lines_img, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 2, cv.LINE_AA) cv.namedWindow("Contours", cv.WINDOW_KEEPRATIO) cv.imshow("Contours", lines_img) return left_lines, right_lines def get_bounding_rectangle(self, lines): cnt = sum(([(l[0], l[1]), (l[2], l[3])] for l in lines), []) x, y, w, h = cv.boundingRect(np.array(cnt)) x += self._origin[0] y += self._origin[1] y = np.int32(np.round(y * self._src_shape[0] / self.working_size[0])) h = np.int32(np.round(h * self._src_shape[0] / self.working_size[0])) x = np.int32(np.round(x * self._src_shape[1] / self.working_size[1])) w = np.int32(np.round(w * self._src_shape[1] / self.working_size[1])) return x, y, w, h def main(argv): default_file = r'book.png' filename = argv[0] if len(argv) > 0 else default_file src = cv.imread(filename, cv.IMREAD_UNCHANGED) detector = DetectROI(display=True) return detector(src) if __name__ == "__main__": print("bottom_left: {}, top_right: {}".format(*main(sys.argv[1:])))  The underlying idea is the following:  threshold the alpha mask to get the foreground compute the contour of the alpha mask detect the lines (assuming the right and left border to be rather strait) keep the lines that start from the bottom left and the bottom right of the image (drawn in red)   Here is the obtained result  I hope this is robust enough