使用OpenCV裁剪黑边

8hhllhi2  于 2023-08-06  发布在  其他
关注(0)|答案(9)|浏览(101)

我认为这应该是一个非常简单的问题,但我无法找到解决方案或有效的关键字搜索。
我只是有这个形象。


的数据
黑色的边缘是无用的,使我想削减他们,只留下Windows图标(和蓝色背景)。
我不想计算Windows图标的坐标和大小。GIMP和Photoshop都有自动裁剪功能。OpenCV没有一个?

t5zmwmid

t5zmwmid1#

我不知道你的照片是不是都是这样的。但是对于这张图片,下面是一个简单的python-opencv代码来裁剪它。
第一个导入库:

import cv2
import numpy as np

字符串
读取图像,将其转换为灰度,并使二值图像的阈值为1。

img = cv2.imread('sofwin.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_,thresh = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)


现在找到它的轮廓。只有一个对象,所以找到它的边界矩形。

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)


现在裁剪图像,并将其保存到另一个文件中。

crop = img[y:y+h,x:x+w]
cv2.imwrite('sofwinres.png',crop)


下面是结果:
x1c 0d1x的数据

qv7cva1a

qv7cva1a2#

我觉得这个答案更简洁:

def crop(image):
    y_nonzero, x_nonzero, _ = np.nonzero(image)
    return image[np.min(y_nonzero):np.max(y_nonzero), np.min(x_nonzero):np.max(x_nonzero)]

字符串

67up9zun

67up9zun3#

import numpy as np

def autocrop(image, threshold=0):
    """Crops any edges below or equal to threshold

    Crops blank image to 1x1.

    Returns cropped image.

    """
    if len(image.shape) == 3:
        flatImage = np.max(image, 2)
    else:
        flatImage = image
    assert len(flatImage.shape) == 2

    rows = np.where(np.max(flatImage, 0) > threshold)[0]
    if rows.size:
        cols = np.where(np.max(flatImage, 1) > threshold)[0]
        image = image[cols[0]: cols[-1] + 1, rows[0]: rows[-1] + 1]
    else:
        image = image[:1, :1]

    return image

字符串

jucafojl

jucafojl4#

好了,为了完整起见,我实现了上面的每一个建议,添加了递归算法的迭代版本(一旦纠正),并进行了一组性能测试。
TLDR:递归可能是最适合一般情况的(但使用下面的一个-OP有几个bug),自动裁剪是最适合您期望几乎为空的图像的。
一般调查结果:1.上面的递归算法中有几个1的错误。更正后的版本在下面。2. cv2.findContours函数在处理非矩形图像时存在问题,实际上在各种情况下甚至会修剪掉一些图像。我添加了一个使用cv2.CHAIN_APPROX_NONE的版本,看看它是否有帮助(它没有帮助)。3.自动裁剪实现对于稀疏图像是很好的,但是对于密集图像是很差的,这是递归/迭代算法的逆。

import numpy as np
import cv2

def trim_recursive(frame):
  if frame.shape[0] == 0:
    return np.zeros((0,0,3))

  # crop top
  if not np.sum(frame[0]):
    return trim_recursive(frame[1:])
  # crop bottom
  elif not np.sum(frame[-1]):
    return trim_recursive(frame[:-1])
  # crop left
  elif not np.sum(frame[:, 0]):
    return trim_recursive(frame[:, 1:])
    # crop right
  elif not np.sum(frame[:, -1]):
    return trim_recursive(frame[:, :-1])
  return frame

def trim_contours(frame):
  gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
  _,thresh = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)
  _, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  if len(contours) == 0:
    return np.zeros((0,0,3))
  cnt = contours[0]
  x, y, w, h = cv2.boundingRect(cnt)
  crop = frame[y:y + h, x:x + w]
  return crop

def trim_contours_exact(frame):
  gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
  _,thresh = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)
  _, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
  if len(contours) == 0:
    return np.zeros((0,0,3))
  cnt = contours[0]
  x, y, w, h = cv2.boundingRect(cnt)
  crop = frame[y:y + h, x:x + w]
  return crop

def trim_iterative(frame):
  for start_y in range(1, frame.shape[0]):
    if np.sum(frame[:start_y]) > 0:
      start_y -= 1
      break
  if start_y == frame.shape[0]:
    if len(frame.shape) == 2:
      return np.zeros((0,0))
    else:
      return np.zeros((0,0,0))
  for trim_bottom in range(1, frame.shape[0]):
    if np.sum(frame[-trim_bottom:]) > 0:
      break

  for start_x in range(1, frame.shape[1]):
    if np.sum(frame[:, :start_x]) > 0:
      start_x -= 1
      break
  for trim_right in range(1, frame.shape[1]):
    if np.sum(frame[:, -trim_right:]) > 0:
      break

  end_y = frame.shape[0] - trim_bottom + 1
  end_x = frame.shape[1] - trim_right + 1

  # print('iterative cropping x:{}, w:{}, y:{}, h:{}'.format(start_x, end_x - start_x, start_y, end_y - start_y))
  return frame[start_y:end_y, start_x:end_x]

def autocrop(image, threshold=0):
  """Crops any edges below or equal to threshold

  Crops blank image to 1x1.

  Returns cropped image.

  """
  if len(image.shape) == 3:
    flatImage = np.max(image, 2)
  else:
    flatImage = image
  assert len(flatImage.shape) == 2

  rows = np.where(np.max(flatImage, 0) > threshold)[0]
  if rows.size:
    cols = np.where(np.max(flatImage, 1) > threshold)[0]
    image = image[cols[0]: cols[-1] + 1, rows[0]: rows[-1] + 1]
  else:
    image = image[:1, :1]

  return image

字符串
为了测试它,我做了这个简单的函数:

import datetime
import numpy as np
import random

ITERATIONS = 10000

def test_image(img):
  orig_shape = img.shape
  print ('original shape: {}'.format(orig_shape))
  start_time = datetime.datetime.now()
  for i in range(ITERATIONS):
    recursive_img = trim_recursive(img)
  print ('recursive shape: {}, took {} seconds'.format(recursive_img.shape, (datetime.datetime.now()-start_time).total_seconds()))
  start_time = datetime.datetime.now()
  for i in range(ITERATIONS):
    contour_img = trim_contours(img)
  print ('contour shape: {}, took {} seconds'.format(contour_img.shape, (datetime.datetime.now()-start_time).total_seconds()))
  start_time = datetime.datetime.now()
  for i in range(ITERATIONS):
    exact_contour_img = trim_contours(img)
  print ('exact contour shape: {}, took {} seconds'.format(exact_contour_img.shape, (datetime.datetime.now()-start_time).total_seconds()))
  start_time = datetime.datetime.now()
  for i in range(ITERATIONS):
    iterative_img = trim_iterative(img)
  print ('iterative shape: {}, took {} seconds'.format(iterative_img.shape, (datetime.datetime.now()-start_time).total_seconds()))
  start_time = datetime.datetime.now()
  for i in range(ITERATIONS):
    auto_img = autocrop(img)
  print ('autocrop shape: {}, took {} seconds'.format(auto_img.shape, (datetime.datetime.now()-start_time).total_seconds()))

def main():
  orig_shape = (10,10,3)

  print('Empty image--should be 0x0x3')
  zero_img = np.zeros(orig_shape, dtype='uint8')
  test_image(zero_img)

  print('Small image--should be 1x1x3')
  small_img = np.zeros(orig_shape, dtype='uint8')
  small_img[3,3] = 1
  test_image(small_img)

  print('Medium image--should be 3x7x3')
  med_img = np.zeros(orig_shape, dtype='uint8')
  med_img[5:8, 2:9] = 1
  test_image(med_img)

  print('Random image--should be full image: 100x100')
  lg_img = np.zeros((100,100,3), dtype='uint8')
  for y in range (100):
    for x in range(100):
      lg_img[y,x, 0] = random.randint(0,255)
      lg_img[y, x, 1] = random.randint(0, 255)
      lg_img[y, x, 2] = random.randint(0, 255)
  test_image(lg_img)

main()


……结果……

Empty image--should be 0x0x3
original shape: (10, 10, 3)
recursive shape: (0, 0, 3), took 0.295851 seconds
contour shape: (0, 0, 3), took 0.048656 seconds
exact contour shape: (0, 0, 3), took 0.046273 seconds
iterative shape: (0, 0, 3), took 1.742498 seconds
autocrop shape: (1, 1, 3), took 0.093347 seconds
Small image--should be 1x1x3
original shape: (10, 10, 3)
recursive shape: (1, 1, 3), took 1.342977 seconds
contour shape: (0, 0, 3), took 0.048919 seconds
exact contour shape: (0, 0, 3), took 0.04683 seconds
iterative shape: (1, 1, 3), took 1.084258 seconds
autocrop shape: (1, 1, 3), took 0.140886 seconds
Medium image--should be 3x7x3
original shape: (10, 10, 3)
recursive shape: (3, 7, 3), took 0.610821 seconds
contour shape: (0, 0, 3), took 0.047263 seconds
exact contour shape: (0, 0, 3), took 0.046342 seconds
iterative shape: (3, 7, 3), took 0.696778 seconds
autocrop shape: (3, 7, 3), took 0.14493 seconds
Random image--should be full image: 100x100
original shape: (100, 100, 3)
recursive shape: (100, 100, 3), took 0.131619 seconds
contour shape: (98, 98, 3), took 0.285515 seconds
exact contour shape: (98, 98, 3), took 0.288365 seconds
iterative shape: (100, 100, 3), took 0.251708 seconds
autocrop shape: (100, 100, 3), took 1.280476 seconds

jei2mxaa

jei2mxaa5#

Abid Rahman K的回答:
cv2.boundingRect可以在不找到外部轮廓的情况下完成工作,如下所示

_,thresh = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)
x,y,w,h = cv2.boundingRect(thresh)

字符串
(this功能可能添加后阿比德的答案和运行更快)

j2datikz

j2datikz6#

一个光滑的小递归函数怎么样?

import cv2
import numpy as np
def trim(frame):
    #crop top
    if not np.sum(frame[0]):
        return trim(frame[1:])
    #crop bottom
    elif not np.sum(frame[-1]):
        return trim(frame[:-2])
    #crop left
    elif not np.sum(frame[:,0]):
        return trim(frame[:,1:]) 
    #crop right
    elif not np.sum(frame[:,-1]):
        return trim(frame[:,:-2])    
    return frame

字符串
加载图像并设置阈值,以确保暗区为黑色:

img = cv2.imread("path_to_image.png")   
thold = (img>120)*img


然后调用递归函数

trimmedImage = trim(thold)

nafvub8i

nafvub8i7#

为了帮助任何人,我对@wordsforthewise的replacement进行了调整,以获得基于PIL的解决方案:

bw = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rows, cols = bw.shape

non_empty_columns = np.where(bw.max(axis=0) > 0)[0]
non_empty_rows = np.where(bw.max(axis=1) > 0)[0]
cropBox = (min(non_empty_rows) * (1 - padding),
            min(max(non_empty_rows) * (1 + padding), rows),
            min(non_empty_columns) * (1 - padding),
            min(max(non_empty_columns) * (1 + padding), cols))

return img[cropBox[0]:cropBox[1]+1, cropBox[2]:cropBox[3]+1 , :]

字符串
(It这是一个调整,因为原始代码期望裁剪掉白色背景而不是黑色背景。)

x3naxklr

x3naxklr8#

Python 3.6版本
裁剪图像并插入到'CropedImages'文件夹

import cv2
import os

arr = os.listdir('./OriginalImages')

for itr in arr:
    img = cv2.imread(itr)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    _,thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
    _, contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    cnt = contours[0]
    x,y,w,h = cv2.boundingRect(cnt)
    crop = img[y:y+h,x:x+w]
    cv2.imwrite('CropedImages/'+itr,crop)

字符串

更改数字120到其他在第9行,并尝试为您的图像,它会工作

b1payxdu

b1payxdu9#

这里用到的PIL代码适配在openCV中,也就是说比较通用。比PIL快多了

def trim_opencv(im):
    # sensitivity of the crop
    threshold = 128
    
    # Converts image to gray and does stuff described above
    gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
    bg = np.full_like(gray, gray[0,0])
    diff = abs(gray - bg) - threshold
    _,thresh = cv2.threshold(diff,diff[0,0],255,cv2.THRESH_BINARY)
    
    # finds bounding box and crops
    x,y,w,h = cv2.boundingRect(thresh)
    crop = im[y:y+h,x:x+w]
    
    return crop

字符串

相关问题