491 lines
21 KiB
Python
491 lines
21 KiB
Python
![]() |
import os
|
|||
|
from datetime import datetime
|
|||
|
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, DateTime,TIMESTAMP
|
|||
|
import psycopg2
|
|||
|
import cv2
|
|||
|
import numpy as np
|
|||
|
from numpy.fft import fft2, fftshift, ifft2, ifftshift
|
|||
|
import matplotlib.pyplot as plt
|
|||
|
from PIL import Image, ImageOps
|
|||
|
import random
|
|||
|
from skimage.measure import label, regionprops
|
|||
|
import math
|
|||
|
import json
|
|||
|
|
|||
|
# 创建连接引擎
|
|||
|
engine = create_engine('postgresql://postgres:!1q2w3E*@8.134.85.73:5432/postgres')
|
|||
|
# 连接数据库
|
|||
|
connection = engine.connect()
|
|||
|
print("Connection to PostgreSQL DB successful")
|
|||
|
# 声明表结构
|
|||
|
metadata = MetaData()
|
|||
|
qrcode_data = Table('qrcode_data', metadata,
|
|||
|
Column('key', String),
|
|||
|
Column('circles', String),
|
|||
|
# createdate 时间类型
|
|||
|
Column('createdate', TIMESTAMP)
|
|||
|
)
|
|||
|
|
|||
|
def create_filter_with_only_circles_and_generate(size, cutoff_radius, num_circles, circle_radius):
|
|||
|
"""
|
|||
|
创建一个仅包含指定数量圆形的滤波器。
|
|||
|
|
|||
|
参数:
|
|||
|
size: 滤波器的大小(宽度和高度相同)。
|
|||
|
cutoff_radius: 低频区域的半径,确保所有圆都在此区域内。
|
|||
|
num_circles: 滤波器中要生成的圆的数量。
|
|||
|
circle_radius: 圆的半径。
|
|||
|
|
|||
|
返回值:
|
|||
|
一个二进制滤波器数组,其中圆的区域值为1,其他区域值为0。
|
|||
|
"""
|
|||
|
filter = np.zeros((size, size)) # 初始化滤波器数组
|
|||
|
center_x, center_y = size // 2, size // 2 # 计算滤波器中心点坐标
|
|||
|
circles = [] # 存储已生成圆的列表
|
|||
|
max_attempts = 1000 # 设置最大尝试次数,以避免无限循环
|
|||
|
|
|||
|
while len(circles) < num_circles and max_attempts > 0: # 循环,直到生成足够数量的不重叠圆
|
|||
|
# 随机生成圆心位置,确保圆完全位于低频区域内
|
|||
|
angle = random.uniform(0, 2 * np.pi)
|
|||
|
r = random.uniform(0, cutoff_radius - circle_radius)
|
|||
|
x = int(center_x + r * np.cos(angle))
|
|||
|
y = int(center_y + r * np.sin(angle))
|
|||
|
|
|||
|
# 检查新生成的圆是否与已存在的圆重叠
|
|||
|
overlapping = False
|
|||
|
for cx, cy, cr in circles:
|
|||
|
if np.sqrt((x - cx) ** 2 + (y - cy) ** 2) < (circle_radius + cr):
|
|||
|
overlapping = True
|
|||
|
break
|
|||
|
|
|||
|
if not overlapping: # 如果新圆不与任何已存在圆重叠,则添加到列表中
|
|||
|
circles.append((x, y, circle_radius))
|
|||
|
max_attempts -= 1
|
|||
|
|
|||
|
# 为每个生成的圆,在滤波器数组中设置对应区域的值为1
|
|||
|
for (x, y, radius) in circles:
|
|||
|
for i in range(-radius, radius + 1):
|
|||
|
for j in range(-radius, radius + 1):
|
|||
|
if np.sqrt(i ** 2 + j ** 2) <= radius:
|
|||
|
filter[x + i, y + j] = 1 # 设置圆形区域的值为1
|
|||
|
|
|||
|
return filter
|
|||
|
|
|||
|
def generate_circles_in_frequency_domain(size, cutoff_radius, num_circles, circle_radius):
|
|||
|
"""
|
|||
|
生成指定数量的圆形,用于频域操作。
|
|||
|
|
|||
|
参数:
|
|||
|
size: 频域的大小(宽度和高度相同)。
|
|||
|
cutoff_radius: 低频区域的半径,确保所有圆都在此区域内。
|
|||
|
num_circles: 要生成的圆的数量。
|
|||
|
circle_radius: 圆的半径。
|
|||
|
|
|||
|
返回值:
|
|||
|
一个列表,包含每个圆的 (中心x坐标, 中心y坐标, 半径) 元组。
|
|||
|
"""
|
|||
|
center_x, center_y = size // 2, size // 2 # 计算频域中心点坐标
|
|||
|
circles = [] # 存储已生成圆的列表
|
|||
|
max_attempts = 1000 # 设置最大尝试次数,以避免无限循环
|
|||
|
|
|||
|
while len(circles) < num_circles and max_attempts > 0: # 循环,直到生成足够数量的不重叠圆
|
|||
|
# 随机生成圆心位置,确保圆完全位于低频区域内
|
|||
|
angle = random.uniform(0, 2 * np.pi)
|
|||
|
r = random.uniform(0, cutoff_radius - circle_radius)
|
|||
|
x = int(center_x + r * np.cos(angle))
|
|||
|
y = int(center_y + r * np.sin(angle))
|
|||
|
|
|||
|
# 检查新生成的圆是否与已存在的圆重叠
|
|||
|
overlapping = False
|
|||
|
for cx, cy, cr in circles:
|
|||
|
if np.sqrt((x - cx) ** 2 + (y - cy) ** 2) < (circle_radius + cr):
|
|||
|
overlapping = True
|
|||
|
break
|
|||
|
|
|||
|
if not overlapping: # 如果新圆不与任何已存在圆重叠,则添加到列表中
|
|||
|
circles.append((x, y, circle_radius))
|
|||
|
max_attempts -= 1
|
|||
|
|
|||
|
return circles
|
|||
|
|
|||
|
def draw_circles_on_frequency_domain(size, circles):
|
|||
|
"""
|
|||
|
在频域图中绘制圆形。
|
|||
|
|
|||
|
参数:
|
|||
|
size: 频域的大小(宽度和高度相同)。
|
|||
|
circles: 包含每个圆的 (中心x坐标, 中心y坐标, 半径) 元组的列表。
|
|||
|
|
|||
|
返回值:
|
|||
|
一个二维数组,表示频域图,其中圆形区域的值为1,其他区域的值为0。
|
|||
|
"""
|
|||
|
frequency_domain_image = np.zeros((size, size)) # 初始化频域图数组
|
|||
|
|
|||
|
# 遍历所有圆并在频域图中绘制它们
|
|||
|
for (x, y, radius) in circles:
|
|||
|
for i in range(-radius, radius + 1):
|
|||
|
for j in range(-radius, radius + 1):
|
|||
|
if i**2 + j**2 <= radius**2: # 判断点是否在圆内
|
|||
|
# 检查绘制点是否在图像范围内
|
|||
|
if 0 <= x + i < size and 0 <= y + j < size:
|
|||
|
frequency_domain_image[x + i, y + j] = 1
|
|||
|
|
|||
|
return frequency_domain_image
|
|||
|
|
|||
|
def find_circles_in_filter(filter_array):
|
|||
|
"""
|
|||
|
从滤波器数组中识别圆形区域,提取每个圆的中心和半径。
|
|||
|
|
|||
|
参数:
|
|||
|
filter_array (ndarray): 一个二维数组,其中圆形区域的值为1,其他为0。
|
|||
|
|
|||
|
返回:
|
|||
|
list: 每个圆的 (中心x坐标, 中心y坐标, 半径) 元组列表。
|
|||
|
"""
|
|||
|
labeled_image = label(filter_array) # 标记连通区域
|
|||
|
regions = regionprops(labeled_image) # 提取区域属性
|
|||
|
circles = []
|
|||
|
for region in regions:
|
|||
|
if region.area >= 5: # 排除太小的区域,假定它们不是我们要找的圆
|
|||
|
radius = np.sqrt(region.area / np.pi) # 计算半径
|
|||
|
circles.append((region.centroid[1], region.centroid[0], radius)) # x, y顺序要反转,因为centroid给出的是(row, col)
|
|||
|
return circles
|
|||
|
|
|||
|
def distribute_points_on_circles(f_shifted, circles, num_points=240):
|
|||
|
"""
|
|||
|
将点均匀分布在每个圆周上。
|
|||
|
|
|||
|
参数:
|
|||
|
f_shifted: 平移处理的函数图像
|
|||
|
circles (list of tuples): 每个元组包含一个圆的 (中心x坐标, 中心y坐标, 半径)
|
|||
|
num_points (int): 每个圆上的点数量,默认为240
|
|||
|
|
|||
|
返回:
|
|||
|
list: 每个圆的点坐标列表,每个元素为一个包含240个点的列表,每个点是(x, y)坐标的元组
|
|||
|
"""
|
|||
|
radians_per_step = 2 * np.pi / num_points # 计算每个步长的弧度值
|
|||
|
print('弧度:', radians_per_step)
|
|||
|
all_circle_points = [] # 存储所有圆的点坐标
|
|||
|
|
|||
|
for center_x, center_y, radius in circles:
|
|||
|
# print('中心坐标:', center_x, center_y)
|
|||
|
circle_points = [] # 存储单个圆的点坐标
|
|||
|
for i in range(num_points):
|
|||
|
theta = i * radians_per_step # 计算当前点的角度弧度
|
|||
|
x = center_x + radius * np.cos(theta) # 极坐标转笛卡尔坐标(x)
|
|||
|
y = center_y + radius * np.sin(theta) # 极坐标转笛卡尔坐标(y)
|
|||
|
#通过笛卡尔坐标计算回角度
|
|||
|
angle_radians = math.atan2(y - center_y, x - center_x)
|
|||
|
angle_degrees = math.degrees(angle_radians)
|
|||
|
#保留angle_degrees小数点后1位,四舍五入
|
|||
|
angle_degrees = round(angle_degrees, 1)
|
|||
|
#计算x,y坐标点的幅度
|
|||
|
amplitude = np.abs(f_shifted[int(x), int(y)])
|
|||
|
#计算x,y坐标点的相位
|
|||
|
phase = np.angle(f_shifted[int(x), int(y)])
|
|||
|
#将当前点的坐标,幅度以及相位打印出来
|
|||
|
# print(f"x: {x}, y: {y}, angle: {angle_degrees}, amplitude: {amplitude}, phase: {phase}")
|
|||
|
# 将当前点的坐标,幅度以及相位添加到列表
|
|||
|
circle_points.append((int(x), int(y), angle_degrees, amplitude, phase)) # 添加当前点的坐标到列表
|
|||
|
all_circle_points.append(circle_points) # 添加该圆的所有点坐标到主列表
|
|||
|
#打印出circle_points中最大的幅度
|
|||
|
# print(f"最大幅度:{max(circle_points, key=lambda x: x[3])[3]}")
|
|||
|
|
|||
|
# 将all_circle_points每个元素的点坐标根据amplitude进行降序排序
|
|||
|
# for i in range(len(all_circle_points)):
|
|||
|
# all_circle_points[i].sort(key=lambda x: x[3], reverse=True)
|
|||
|
|
|||
|
packed_circle_points = []
|
|||
|
|
|||
|
for circle_points in all_circle_points:
|
|||
|
x, y, angles, amplitudes, phase = zip(*circle_points)
|
|||
|
packed_circle_points.append((x, y, angles, amplitudes, phase))
|
|||
|
|
|||
|
return packed_circle_points
|
|||
|
|
|||
|
def degrees_to_radians(degrees):
|
|||
|
"""
|
|||
|
将角度转换为弧度。
|
|||
|
|
|||
|
参数:
|
|||
|
degrees (float): 输入的角度值。
|
|||
|
|
|||
|
返回:
|
|||
|
float: 转换后的弧度值。
|
|||
|
"""
|
|||
|
# 将角度乘以π/180,完成角度到弧度的转换
|
|||
|
radians = degrees * (math.pi / 180)
|
|||
|
return radians
|
|||
|
|
|||
|
# 将水印序列编码为二进制字符串
|
|||
|
def encode_watermark_to_binary(watermark_sequence):
|
|||
|
binary_sequence = []
|
|||
|
for number in watermark_sequence:
|
|||
|
# 初始化一个100位全为'0'的二进制字符串
|
|||
|
binary_100_bit = ['0'] * 100
|
|||
|
# 在数字对应的位置设置'1'
|
|||
|
if 0 <= number < 100:
|
|||
|
binary_100_bit[number-1] = '1'
|
|||
|
# 连接这些位,形成一个100位的二进制字符串
|
|||
|
binary_sequence.append(''.join(binary_100_bit))
|
|||
|
return binary_sequence
|
|||
|
|
|||
|
#解析图片水印
|
|||
|
def process_image_b_component(image_path, circles, watermarks):
|
|||
|
num_points = 240
|
|||
|
img = Image.open(image_path)
|
|||
|
img = np.array(img)
|
|||
|
# 将img缩放到 256*256
|
|||
|
# img = cv2.resize(img, (339,339))
|
|||
|
# img = cv2.resize(img, (500, 500))
|
|||
|
img = cv2.resize(img, (255, 255))
|
|||
|
b_channel = img[:, :, 2]
|
|||
|
# 计算二维DFT并移位,使零频率分量在中心
|
|||
|
f_transform = fft2(b_channel)
|
|||
|
f_shifted = fftshift(f_transform)
|
|||
|
radians_per_step = 2 * np.pi / num_points # 计算每个步长的弧度值
|
|||
|
all_circle_points = [] # 存储所有圆的点坐标
|
|||
|
|
|||
|
for center_x, center_y, radius in circles:
|
|||
|
# print('中心坐标:', center_x, center_y)
|
|||
|
circle_points = [] # 存储单个圆的点坐标
|
|||
|
for i in range(num_points):
|
|||
|
theta = i * radians_per_step # 计算当前点的角度弧度
|
|||
|
x = center_x + radius * np.cos(theta) # 极坐标转笛卡尔坐标(x)
|
|||
|
y = center_y + radius * np.sin(theta) # 极坐标转笛卡尔坐标(y)
|
|||
|
# 通过笛卡尔坐标计算回角度
|
|||
|
angle_radians = math.atan2(y - center_y, x - center_x)
|
|||
|
angle_degrees = math.degrees(angle_radians)
|
|||
|
# 保留angle_degrees小数点后1位,四舍五入
|
|||
|
angle_degrees = round(angle_degrees, 1)
|
|||
|
# 计算x,y坐标点的幅度
|
|||
|
amplitude = np.abs(f_shifted[int(x), int(y)])
|
|||
|
# 计算x,y坐标点的相位
|
|||
|
phase = np.angle(f_shifted[int(x), int(y)])
|
|||
|
# 将当前点的坐标,幅度以及相位打印出来
|
|||
|
print(f"x: {int(x)}, y: {int(y)}, angle: {angle_degrees}, amplitude: {amplitude}, phase: {phase}")
|
|||
|
# 将当前点的坐标,幅度以及相位添加到列表
|
|||
|
circle_points.append((int(x), int(y), angle_degrees, amplitude, phase)) # 添加当前点的坐标到列表
|
|||
|
all_circle_points.append(circle_points) # 添加该圆的所有点坐标到主列表
|
|||
|
|
|||
|
watermark_result = []
|
|||
|
#遍历all_circle_points
|
|||
|
for i in range(len(all_circle_points)):
|
|||
|
print(f"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 第{i + 1}个圆")
|
|||
|
w = watermarks[i]
|
|||
|
isOk = False
|
|||
|
# 将all_circle_points每个元素的点坐标根据amplitude进行降序排序
|
|||
|
all_circle_points[i].sort(key=lambda x: x[3], reverse=True)
|
|||
|
# 打印all_circle_points[i]中每个元素
|
|||
|
for j in range(16): # 只考虑每个圆的前四个最大幅度
|
|||
|
point = all_circle_points[i][j]
|
|||
|
print(f"幅度排名 {j + 1}: x: {point[0]}, y: {point[1]}, 角度: {point[2]}, 幅度: {point[3]}, 相位: {point[4]}")
|
|||
|
# 处理角度,使其在0到360度范围内
|
|||
|
adjusted_angle = point[2] if point[2] >= 0 else point[2] + 360
|
|||
|
# 计算并打印当前角度是圆上的第几份
|
|||
|
fraction_index = adjusted_angle / 1.5 + 1
|
|||
|
if w == fraction_index:
|
|||
|
print(f"水印匹配成功!角度:{adjusted_angle} , 编码:{fraction_index}")
|
|||
|
# 将当前fraction_index添加到结果列表中,保留整数位
|
|||
|
watermark_result.append(int(fraction_index))
|
|||
|
isOk = True
|
|||
|
break
|
|||
|
if isOk == False:
|
|||
|
watermark_result.append(0)
|
|||
|
|
|||
|
return watermark_result, img
|
|||
|
|
|||
|
def calculate_psnr(image1, image2):
|
|||
|
mse = np.mean((image1 - image2) ** 2)
|
|||
|
if mse == 0:
|
|||
|
return float('inf')
|
|||
|
max_pixel = 255.0
|
|||
|
psnr = 20 * np.log10(max_pixel / np.sqrt(mse))
|
|||
|
return psnr
|
|||
|
|
|||
|
def perspective_transform(img_path):
|
|||
|
if not os.path.exists(img_path):
|
|||
|
raise FileNotFoundError(f"Image file '{img_path}' not found.")
|
|||
|
# 通过cv2加载图片
|
|||
|
img = cv2.imread(img_path)
|
|||
|
if img is None:
|
|||
|
raise FileNotFoundError(f"Image file '{img_path}' not found or could not be read.")
|
|||
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|||
|
# 复制图片
|
|||
|
img_copy = img.copy()
|
|||
|
# 将图片转灰度图
|
|||
|
img_copy = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
|
|||
|
# 黑白颜色反转
|
|||
|
img_copy = cv2.bitwise_not(img_copy)
|
|||
|
wechatqr = cv2.wechat_qrcode.WeChatQRCode("wechat_qrcode/detect.prototxt", "wechat_qrcode/detect.caffemodel",
|
|||
|
"wechat_qrcode/sr.prototxt", "wechat_qrcode/sr.caffemodel")
|
|||
|
res, points = wechatqr.detectAndDecode(img_copy)
|
|||
|
# 在原图上标记二维码的角点
|
|||
|
if len(res) > 0:
|
|||
|
# for point in points[0]:
|
|||
|
# cv2.circle(img, tuple(int(i) for i in point), 10, (255, 0, 0), -1) # 红色圆点,半径为10
|
|||
|
point = points[0]
|
|||
|
width = max(np.linalg.norm(point[0] - point[1]), np.linalg.norm(point[2] - point[3]))
|
|||
|
height = max(np.linalg.norm(point[1] - point[2]), np.linalg.norm(point[3] - point[0]))
|
|||
|
# 不添加边距
|
|||
|
points_dst = np.array([[0, 0], [width, 0], [width, height], [0, height]], dtype=point.dtype)
|
|||
|
# 生成变换矩阵
|
|||
|
matrix = cv2.getPerspectiveTransform(point, points_dst)
|
|||
|
# 应用透视变换,纠正图像
|
|||
|
corrected_image = cv2.warpPerspective(img, matrix, (int(width), int(height)))
|
|||
|
|
|||
|
return corrected_image, img, res[0] # 返回校正后的图像和标记后的原图
|
|||
|
|
|||
|
return None, img, None # 如果没有检测到二维码,仅返回原图
|
|||
|
|
|||
|
|
|||
|
def exec_image_result(img_path):
|
|||
|
# 设置图像大小和截止频率半径
|
|||
|
cutoff_radius = 80
|
|||
|
# 在低通滤波器中添加8个随机圆形区域
|
|||
|
circle_radius = 15 # 每个圆的半径
|
|||
|
num_circles = 8 # 圆形数量
|
|||
|
alpha = 1.5
|
|||
|
# 加载图片
|
|||
|
# img_path = 'qr8_1000.png'
|
|||
|
# img = Image.open(img_path)
|
|||
|
# img = np.array(img)
|
|||
|
corrected_image, img, qr_res = perspective_transform(img_path)
|
|||
|
# 复制一份原图
|
|||
|
img_copy = corrected_image.copy()
|
|||
|
# 获取img_copy的size
|
|||
|
N, M, _ = img_copy.shape
|
|||
|
print(f"img_copy size: {json.dumps(img_copy.shape)}")
|
|||
|
# 提取B通道
|
|||
|
b_channel = corrected_image[:, :, 2]
|
|||
|
N, M = b_channel.shape
|
|||
|
# 计算二维DFT并移位,使零频率分量在中心
|
|||
|
f_transform = fft2(b_channel)
|
|||
|
f_shifted = fftshift(f_transform)
|
|||
|
# watermark_sequence = [77, 23, 51, 48, 44, 55, 22, 66] # 示例数字
|
|||
|
watermark_sequence = [int(qr_res[i:i + 2]) for i in range(0, len(qr_res), 2)]
|
|||
|
encoded_watermark = encode_watermark_to_binary(watermark_sequence)
|
|||
|
# 展开所有的二进制编码成一个长字符串
|
|||
|
flattened_encoded_watermark = ''.join(encoded_watermark)
|
|||
|
print(flattened_encoded_watermark)
|
|||
|
# 生成了指定数量的位于频域中的圆
|
|||
|
circles = generate_circles_in_frequency_domain(b_channel.shape[0], cutoff_radius, num_circles, circle_radius)
|
|||
|
# 绘制圆形
|
|||
|
frequency_domain_image = draw_circles_on_frequency_domain(b_channel.shape[0], circles)
|
|||
|
# 将circles转成 json
|
|||
|
circles_json = json.dumps(circles)
|
|||
|
print(circles_json)
|
|||
|
# 获取当前时间
|
|||
|
print(datetime.now())
|
|||
|
# 保存数据
|
|||
|
new_qr_data = qrcode_data.insert().values(key=json.dumps(watermark_sequence), circles=json.dumps(circles),
|
|||
|
createdate=datetime.now())
|
|||
|
connection.execute(new_qr_data)
|
|||
|
connection.commit()
|
|||
|
# connection.close()
|
|||
|
distributed_points = distribute_points_on_circles(f_shifted, circles, 240)
|
|||
|
# 总幅度
|
|||
|
f_amplitudes = np.abs(f_shifted)
|
|||
|
# 总相位
|
|||
|
f_phase = np.angle(f_shifted)
|
|||
|
for circle_index, (x, y, angles, amplitudes, phase) in enumerate(distributed_points):
|
|||
|
# 根据指定的圆圈索引,从编码后的水印中获取对应的元素
|
|||
|
cw = encoded_watermark[circle_index]
|
|||
|
# 获取amplitudes的最大值
|
|||
|
max_amplitude = max(amplitudes)
|
|||
|
print(f"圆 {circle_index + 1} 的数据: 编码:{cw} ,最大幅度:{max_amplitude}")
|
|||
|
# 这里我们可以访问每个圆的所有点的坐标、角度和幅度
|
|||
|
for i in range(len(x)): # 假设每个圆有相同数量的点,例如240个
|
|||
|
# print(f"点 {i + 1}: x = {int(x[i])}, y = {int(y[i])}, 角度 = {angles[i]}, 幅度 = {amplitudes[i]}, 相位 = {phase[i]}")
|
|||
|
if i < len(cw): # 确保不会超出水印信息的长度
|
|||
|
bit = cw[i] # 根据索引获取水印信息的当前位
|
|||
|
if bit == '1':
|
|||
|
original_amplitude = f_amplitudes[x[i], y[i]]
|
|||
|
f_amplitudes[x[i], y[i]] = max_amplitude * alpha
|
|||
|
f_amplitudes[N - x[i], M - y[i]] = max_amplitude * alpha
|
|||
|
modified_amplitude = f_amplitudes[x[i], y[i]] # alpha是定义的强度因子
|
|||
|
# print(f"点 {i + 1}: x = {int(x[i])}, y = {int(y[i])}, 角度 = {angles[i]}, 幅度 = {amplitudes[i]}, 相位 = {phase[i]}")
|
|||
|
# print(f"原始幅度:{original_amplitude}")
|
|||
|
# 打印嵌入水印信息后幅度
|
|||
|
# print(f"嵌入水印信息后幅度: {modified_amplitude}")
|
|||
|
else:
|
|||
|
modified_amplitude = f_amplitudes[x[i], y[i]]
|
|||
|
# 重新构造复数值并更新频域数据
|
|||
|
# f_shifted[x[i], y[i]] = modified_amplitude * np.exp(1j * phase[i])
|
|||
|
f_shifted = f_amplitudes * np.exp(1j * f_phase)
|
|||
|
# 频域图像转换回空间域
|
|||
|
img_lowpass_multi_circular = np.real(ifft2(ifftshift(f_shifted)))
|
|||
|
b_channel = np.clip(img_lowpass_multi_circular, 0, 255).astype(np.uint16)
|
|||
|
# 合并修改后的通道回到一个新的图像数组中
|
|||
|
# merged_img = np.stack((r_channel, g_channel, b_channel), axis=-1)
|
|||
|
corrected_image[:, :, 2] = b_channel
|
|||
|
final_image = Image.fromarray(corrected_image)
|
|||
|
# 添加边框
|
|||
|
final_image_copy = final_image.copy()
|
|||
|
border_size = 30
|
|||
|
# border_color = (73, 116, 165)
|
|||
|
# 设置border_color 为rgb 白色
|
|||
|
border_color = (255, 255, 255)
|
|||
|
border = (border_size, border_size, border_size, border_size) # (左, 上, 右, 下)
|
|||
|
img_with_border = ImageOps.expand(final_image_copy, border=border, fill=border_color)
|
|||
|
# 将merged_img保存到文件,文件是为当前时间的字符串
|
|||
|
filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}.tiff"
|
|||
|
# cv2.imwrite(filename, merged_img)
|
|||
|
img_with_border.save(filename, format='TIFF')
|
|||
|
# 显示结果
|
|||
|
plt.figure(figsize=(12, 12))
|
|||
|
plt.subplot(221)
|
|||
|
plt.title("Original Image")
|
|||
|
plt.imshow(img_copy, cmap='gray')
|
|||
|
plt.subplot(222)
|
|||
|
plt.title("Frequency Spectrum")
|
|||
|
plt.imshow(np.log1p(np.abs(f_shifted)), cmap='gray')
|
|||
|
plt.subplot(223)
|
|||
|
plt.title("Multi Circular Lowpass Filter (50 Points per Circle)")
|
|||
|
plt.imshow(frequency_domain_image, cmap='gray')
|
|||
|
plt.subplot(224)
|
|||
|
plt.title("Filtered Image (Multi Circular Low Frequencies, 50 Points per Circle)")
|
|||
|
plt.imshow(final_image)
|
|||
|
plt.tight_layout()
|
|||
|
plt.show()
|
|||
|
return filename
|
|||
|
|
|||
|
|
|||
|
def validate_qr(filename, watermark_sequence):
|
|||
|
watermarks = [int(watermark_sequence[i:i + 2]) for i in range(0, len(watermark_sequence), 2)]
|
|||
|
# 根据 key读取数据库圆形信息
|
|||
|
key = json.dumps(watermarks)
|
|||
|
circles = []
|
|||
|
# 查询第一条数据
|
|||
|
qr = qrcode_data.select().where(qrcode_data.c.key == key)
|
|||
|
result = connection.execute(qr).first()
|
|||
|
if result:
|
|||
|
# 将json字符串转换为列表
|
|||
|
circles = json.loads(result[1])
|
|||
|
print(circles)
|
|||
|
# 验证水印结果
|
|||
|
result, img2 = process_image_b_component(filename, circles, watermarks)
|
|||
|
print(result)
|
|||
|
else:
|
|||
|
print("未找到数据")
|
|||
|
# psnr = calculate_psnr(img_copy, img2)
|
|||
|
# print(f"PSNR: {psnr}")
|
|||
|
# # PSNR > 40 dB:通常被认为是高质量图像
|
|||
|
# # 30 dB < PSNR < 40 dB:图像质量是良好
|
|||
|
# if psnr > 40:
|
|||
|
# print("图像质量是高质量")
|
|||
|
# elif 35 <= psnr < 40:
|
|||
|
# print("图像质量是良好")
|
|||
|
# else:
|
|||
|
# print("图像质量是差")
|
|||
|
|
|||
|
# img_path = './temp/tmpb_1ffsve.png'
|
|||
|
# exec_image_result(img_path)
|
|||
|
|
|||
|
img_path = "corrected_image.png"
|
|||
|
qr = "9918314391322762"
|
|||
|
validate_qr(img_path, qr)
|
|||
|
|
|||
|
# perspective_transform(img_path)
|
|||
|
|