asjdf

一只在杭电摸鱼的小火鸡

快速照片扫描方法记录
Aug 1, 2021
2 minutes read

假期找了点时间把家里的老照片都扫成了电子版,下面记录一下简单快捷的照片扫描方法

法1

因为不太清楚photoshop有自动裁切的功能,所以我最开始花了一个小时多写了一个小程序帮我自动裁切扫描出来的影像的白边。

原图:

image-20210803020139131

裁切后:

image-20210803020336359

额外预留的一些白边,总体效果还行。

程序用到了 python + opencv + numpy,基本可以满足我个人需求。

总体思路:

  1. 先将图片从 RGB 空间转为 HSV 空间
  2. 通过调整阈值找到图片所在的大致位置(兴趣区域),并二值化
  3. 开运算(先腐蚀再膨胀)以去除毛刺和噪点
  4. 找出二值化后图形的边缘
  5. 找出包裹最大面积的边缘
  6. 找出可包住上步面积的最小的方框
  7. 计算方框倾斜角度
  8. 根据方框倾斜角度矫正图片倾斜
  9. 计算旋转后的方框范围并切割图片

下面是我的程序:

import cv2
import numpy
import os

def cv_imread(file_path):
    cv_img = cv2.imdecode(numpy.fromfile(file_path, dtype=numpy.uint8), -1)
    return cv_img

def cv_imwrite(path ,img):
    suffix = os.path.splitext(path)[-1]
    cv2.imencode(suffix, img)[1].tofile(path)

Path = r"C:/Users/24385/Pictures/ControlCenter4/Scan/"

# 参数 根据自己的情况更改
kernelSize = 150
hsv_low = numpy.array([0, 0, 0])
hsv_high = numpy.array([255, 255, 245])

for fileName in os.listdir(Path):
    img = cv_imread(Path + fileName)
    padding = int(img.shape[1]/25)
    img = cv2.copyMakeBorder(img, padding, padding, padding, padding,
        cv2.BORDER_CONSTANT, value=[255, 255, 255])
        
    dst = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # BGR转HSV
    dst = cv2.inRange(dst, hsv_low, hsv_high) # 通过HSV的高低阈值,提取图像部分区域

    # 开运算 先腐蚀再膨胀 去除毛刺
    kernel = numpy.ones((kernelSize, kernelSize), dtype=numpy.uint8)
    dst = cv2.morphologyEx(dst, cv2.MORPH_OPEN, kernel, 3)

    img_debug = img.copy()
    line_width = int(img.shape[1]/100)

    # 找边缘
    contours, hierarchy = cv2.findContours(dst, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    # 面积最大的区域
    c = max(contours, key = cv2.contourArea)

    
    # 用蓝色画出边缘
    cv2.drawContours(img_debug, contours, -1, (255, 0, 0), line_width)

    # 找出可包住面积最大区域的方框
    x, y, w, h = cv2.boundingRect(c)
    # 用绿色绘制方框
    cv2.rectangle(img_debug,(x, y), (x + w, y + h), (0, 255, 0), line_width)

    # 找出可包住面积最大区域的最小方框 红色绘制
    rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rect)
    box = numpy.int0(box)
    cv2.drawContours(img_debug, [box], 0, (0, 0, 255), line_width)

    # 计算图片倾斜角度
    angle = rect[2]
    if angle < -45:
        angle = 90 + angle

    # 设定红框中心为旋转轴心 防止照片位置偏太多导致转出图片
    w = box[0][0] + box[1][0] + box[2][0] + box[3][0]
    h = box[0][1] + box[1][1] + box[2][1] + box[3][1]
    center = (w // 4, h // 4)

    # 计算旋转矩阵
    M = cv2.getRotationMatrix2D(center, angle, 1.0)


    # 旋转图片
    (h, w) = img.shape[:2]
    rotated = cv2.warpAffine(img_debug, M, (w, h),
        flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT)
    img_final = cv2.warpAffine(img, M, (w, h),
        flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT)

    # 旋转红框坐标
    pts = numpy.int0(cv2.transform(numpy.array([box]), M))[0]

    # 计算旋转后红框的范围
    y_min = min(pts[0][0], pts[1][0], pts[2][0], pts[3][0])
    y_max = max(pts[0][0], pts[1][0], pts[2][0], pts[3][0])
    x_min = min(pts[0][1], pts[1][1], pts[2][1], pts[3][1])
    x_max = max(pts[0][1], pts[1][1], pts[2][1], pts[3][1])

    # 裁切图片
    img_crop = rotated[x_min:x_max, y_min:y_max]
    img_final = img_final[x_min-20:x_max+20, y_min-20:y_max+20]
    # cv2.namedWindow('show',0)
    # cv2.imshow('show', img_final)
    # cv2.waitKey(0)
    cv_imwrite(Path+"Cut_"+fileName,img_final)

法2

首先打开 -》窗口 -》 动作

image-20210803022011203

先录制一套宏:

image-20210803022118354

点记录,然后做一整套操作,再点击白色方形结束录制,完成后如图:

image-20210803023146817

之后在 Photoshop 中 -》 文件 -》 自动 -》 批处理

image-20210803021447598

然后按如下图配置:

image-20210803022705587

记住要勾选“覆盖动作中的打开命令”和“覆盖动作中的储存为命令”

文件命名部分根据个人喜好进行命名,这是我自己个人的喜好,仅供参照。

完成设置后点击确定就好

比较

Photoshop 的自动化固然更加小白,但是我个人使用起来的话还是自己客制化的程序更加好用(参数可以根据照片的特征进行调整),裁切也较为准确

Photoshop在这种老照片的裁切中还是存在切歪的情况(在大多数情况下裁切还是很准确的,下面是我的程序切不好的几张图片拿给 Photoshop 切的效果):

image-20210803023420941


Back to posts