参考博客https://blog.csdn.net/qq_40456669/article/details/93375709与https://blog.csdn.net/weixin_43181409/article/details/104778084
import cv2
import numpy as np
import os
import json
def show(img_name, img): # 显示图像
cv2.namedWindow(str(img_name), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)
cv2.resizeWindow(str(img_name), 960, 1440)
cv2.imshow(str(img_name), img)
cv2.waitKey(0)
def boxes_extract(img_1, img_2, name): # 提取ROI
image_ref_rgb = cv2.imread(img_1) # 带红色标注框的参考图像
image_ori_rgb = cv2.imread(img_2) # 原图篡改图像
ROI = np.zeros(image_ref_rgb.shape, np.uint8) # 创建与原图同尺寸的空numpy数组,用来保存ROI信息
image_1_hsv = cv2.cvtColor(image_ref_rgb, cv2.COLOR_BGR2HSV) # HSV空间内分离颜色
low_hsv = np.array([0, 43, 46])
high_hsv = np.array([10, 255, 255])
binary = cv2.inRange(image_1_hsv, lowerb=low_hsv, upperb=high_hsv) # 根据红色hsv上下限转为二值图像
binary = cv2.blur(binary, (3, 3)) # KEY STEP: 模糊处理减少瑕疵点
# show('target zones', binary)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, # EXTERNAL选择最外框
cv2.CHAIN_APPROX_SIMPLE) # 查找所有轮廓信息并保存于contours数组中
# print(len(contours))
left_up_corner_set = {} # 创建字典{‘轮廓标号’:‘左上角点坐标’} 保存裁剪区域左上角点集合
corners_json = {}
if not os.path.exists('./log'):
os.mkdir('./log')
for i in range(len(contours)): # 基于轮廓数量处理每个轮廓
epsilon = 0.5 * cv2.arcLength(contours[i], True) # arcLength计算轮廓周长,True表示目标轮廓是闭合的
# epsilon为准确率参数,表示实际轮廓到近似轮廓的最大距离(调参)
approx = cv2.approxPolyDP(contours[i], epsilon, True) # 把轮廓形状近似为边数较少的形状,边数由指定的epsilon决定
mm = cv2.moments(contours[i])
if mm['m00'] != 0:
cx = int(mm['m10'] / mm['m00'])
cy = int(mm['m01'] / mm['m00']) # 计算轮廓重心位置坐标
color = image_ref_rgb[cy][cx]
color_str = "(" + str(color[0]) + ", " + str(color[1]) + ", " + str(color[2]) + ")"
p = cv2.arcLength(contours[i], True)
area = cv2.contourArea(contours[i]) # 计算轮廓面积
# 分析几何形状
corners = len(approx)
if corners <= 4 and (color[2] >= 10 or color[0] >= 10) and area > 1000: # 判定条件根据ROI特点进行调整
rect = cv2.minAreaRect(contours[i]) # 找到最小外接矩形,该矩形可能有方向
box = cv2.boxPoints(rect) # box是四个点的坐标
# x, y, w, h = cv2.boundingRect(contours[i]) #寻找正矩形 返回x,y是矩阵左上点的坐标,w,h是矩阵的宽和高
# box = [(x, y), (x + w, y), (x + w, y + h), (x + h, y)]
# cv2.rectangle(ROI, (x, y), (x + w, y + h), (0, 255, 0), 2)
box = np.array([np.array(i, np.int) for i in box])
rbx = max(box[i][0] for i in range(4))
rby = max(box[i][1] for i in range(4))
lux = min(box[i][0] for i in range(4))
luy = min(box[i][1] for i in range(4))
crop = image_ori_rgb[luy:rby, lux:rbx] # 裁剪篡改区域
if not os.path.exists('./crop/' + name):
os.mkdir('./crop/' + name)
cv2.imwrite('./crop/' + name + '/crop' + str(i).zfill(4) + ".jpg", crop) # 保存裁剪区域
left_up_corner_set[str(i).zfill(4)] = (lux, luy) # 将左上角点坐标写入json
corners_json[str(i).zfill(4)] = (float(lux), float(luy))
cv2.drawContours(ROI, [box], 0, (255, 255, 255),
-1) # 在ROI空画布上画出轮廓,并填充白色(最后的参数为轮廓线条宽度,如果为负数则直接填充区域)
# if not os.path.exists('./mask/' + name):
# os.mkdir('./mask/' + name)
# cv2.imwrite("./mask/" + str(name) + "/img_mask" + str(i).zfill(4) + ".jpg", ROI)
# imgroi = ROI & image_ori_rgb # ROI和原图进行与运算,筛出原图中的ROI区域
# cv2.imwrite("./mask/" + str(name) + "/img_roi" + str(i).zfill(4) + ".jpg", imgroi)
# show("ROI", imgroi)
with open("./log/" + name + ".json", 'w') as f:
json.dump(corners_json, f)
return name, image_ori_rgb, left_up_corner_set
if __name__ == "__main__":
ref_list = os.listdir('./reference')
fake_list = os.listdir('./reals')
for i in range(len(ref_list)):
img_ref = './reference/' + ref_list[i]
img_fake = './reals/' + fake_list[i]
name = ref_list[i][:-4]
name, image_ori_rgb, left_up_corner_set = boxes_extract(img_ref, img_fake, name) # 篡改