Poisson_Image-Editing

1.算法介绍

        快速泊松图像编辑(Fast Poisson Image Editing)是一种图像处理算法,用于将源图像的某个区域无缝地嵌入到目标图像中。它基于泊松方程的性质,通过求解离散化的泊松方程来实现图像的融合。该算法的核心思想是,在目标图像中构造一个与源图像区域相同大小的梯度场,并根据梯度场和源图像区域的边界条件求解离散化的泊松方程。通过求解泊松方程,可以得到一个平滑的修复结果,使得源图像区域与目标图像无缝衔接。

        为了提高算法的效率,快速泊松图像编辑采用了一些优化技巧。例如,可以使用多重网格方法加速泊松方程的求解过程,以减少迭代次数。还可以利用并行计算和矩阵运算等技术,提高算法的计算速度。

        快速泊松图像编辑在许多图像处理任务中具有广泛的应用,如图像修复、图像合成、图像拼接等。它能够有效地将源图像的特定内容融合到目标图像中,产生逼真的合成结果。

2.基本原理

        Fast Poisson Image Editing 的核心是通过求解泊松方程来进行图像修复。该方程涉及图像的梯度和掩码信息。

        修复过程中,算法通过迭代方法不断更新图像像素的值,以减小梯度的误差,达到图像平滑的效果。

2.1 PIE 算法

        Fast Poisson Image Editing 采用了 PIE(Poisson Image Editing)算法,该算法使用了 Jacobi 方法对泊松方程进行求解。

        PIE 算法将图像修复任务分解为多个网格,通过并行计算提高了求解效率。

2.2 程序结构

        Fast Poisson Image Editing 的实现涉及多个 Python 文件,包括 process.py 和 taichi_solver.py。

        process.py 中包含了图像处理器的定义,通过调用 Taichi 求解器进行图像修复。

        taichi_solver.py 中包含了基于 Taichi 的 Jacobi 方法求解器的定义,提供了对泊松方程的求解和图像修复的功能。

2.3 图像处理流程

        图像处理流程主要包括初始化、重置状态、执行多次迭代等步骤。

        使用 PIE 算法,通过不断迭代更新图像像素值,最终得到修复后的图像。

2.4 核心类和方法

        GridProcessor 类负责执行 PIE 算法中的网格处理,通过调用 Taichi 求解器进行图像修复。

        EquSolver 和 GridSolver 类分别实现了基于 Taichi 的 Jacobi 方法的方程求解器和网格求解器。

2.5 Taichi 求解器

        Taichi 求解器通过 Jacobi 方法对泊松方程进行求解,包含了初始化、重置状态、执行迭代等方法。

        使用 Taichi 字段来存储误差、系数矩阵、常数项、未知变量等信息。

2.6 功能总结

        Fast Poisson Image Editing 通过泊松方程求解,实现了图像的高效修复。

        通过并行计算和优化的算法结构,提高了图像修复的速度和效果。

3.代码实现

示例1

原图:

4f7014ca83fc4a318b6b3127bbca9b92.png1592bd6f637f444e9f97253e4e2fd6a5.png

 效果图:

29a73a6339f546f78377b0c4d829a9d2.png

 

示例2

基于GUI后端自定义框输出编辑图像结果:

6417882057c54534a141d08ea3021cef.png

 

4.代码解析

4.tests文件下

4.1.1.data.py

        这部分代码能实现文件下载的功能,通过 download() 函数,可以从给定的链接列表中下载文件。根据下载的图片可以对图片进行编辑;还有通过 square() 函数和circle() 函数对正方形图像生成和圆形图像生成的功能,使用该方法创建出的图像可以进行测试和可视化;其中代码还利用 OpenCV 库进行图像处理和操作系统交互,以及使用 NumPy 库进行科学计算和数据处理。代码通过函数和循环结构实现了模块化和可扩展的设计,使得功能的实现更加灵活和易于维护。

#!/usr/bin/env python3import os # 提供与操作系统交互的功能
import sys # 提供与python解释器和运行时环境交互的功能
from typing import List, Tupleimport cv2 # OpenCV库的Python接口,用于图像处理和计算机视觉
import numpy as np # 导入numpy库并命名为np,用于科学计算和数据处理# ->None表示该函数不返回任何内容
def download(links: List[Tuple[str, str]]) -> None: # 输入参数为包含元组的列表,列表中的元素都是一个包含两个字符串的元组for link, filename in links: # 遍历列表每个元素中元组的link和filename。if not os.path.exists(filename): # 判断filename是否存在os.system(f"wget {link} -O {filename}") # 如果不存在,则使用os.system()函数调用系统命令来下载
# -O选项用于指定下载文件的保存路径和文件名def square(x: int) -> None: # 该函数接受一个整数参数xr = int((4**x)**.5 + 2) # 根据接受的x计算出正方形的边长rimg = np.zeros([r, r, 3], np.uint8) + 255 # 元素初始化为0时,像素为黑色,然后将每个元素加上255,表示为白色cv2.imwrite(f"square{x}.png", img) # 使用OpenCV的imwrite()函数将图像保存为PNG文件。def circle(x: int) -> None: # 接受一个整型参数xr = int(((4**x) * 4 / np.pi)**.5 + 2) # 根据给定的x计算对于的半径img = np.zeros([r, r, 3], np.uint8) # 创建一个大小为[r,r,3]的三维数组img用于表示图像的高度、宽度和通道数img = cv2.circle( # 再调用OpenCV的circle()函数在图像上绘制一个圆形img, (int(r / 2), int(r / 2)), int(r / 2), (255, 255, 255), -1) # 指定圆心的位置,半径和颜色。参数-1表示填充整个圆形cv2.imwrite(f"circle{x}.png", img) # 使用OpenCV的imwrite()函数将图像保存为PNG文件。if __name__ == "__main__":if sys.argv[-1] != "benchmark": # 判断命令行参数的最后一个元素是否为字符串”benchmark”links = [i.split() for i in open("data1.txt").read().splitlines()] # 如果不是,则读取data1.txt文件中的内容,并将其分割成列表,再对每一行,将文件内容分割成一个字符串列表download(links) # 将拆分后的link和filename列表作为参数进行下载for i in range(6, 11):square(i) # 生成一个正方形图像circle(i) # 生成一个圆形图像

4.fpei文件下

该包中的代码与解析如下:

6d8f1f0161434d059c34bab58a4fecd2.png

4.3 args.py

        本段代码用于解析命令行参数并提供一些选项和参数供用户在命令行中指定和操作,使用parser.add_argument()方法添加各种命令行选项,每个选项都有不同的短选项形式(如 -v)和长选项形式(如 --version),以及相应的参数类型、默认值和帮助信息。这些选项包括版本信息、后端选择、CPU 数量、CUDA 块大小、并行计算方法、源图像文件名、掩膜图像文件名、目标图像文件名、输出图像文件名、掩膜在源图像和目标图像中的位置、梯度计算方式、迭代次数等。可以根据用户的输入生成一个包含解析后参数值的argparse.Namespac对象,以便后续使用这些参数进行泊松图像编辑的相关操作。

import argparse # 用于解析命令行参数
import os # 提供了与操作系统交互的功能import fpie # 自定义的模块
from fpie.process import ALL_BACKEND, CPU_COUNT, DEFAULT_BACKEND
# 从 fpie.process 中导入的变量,可能是用于指定不同后端选项的常量或配置信息def get_args(gen_type: str) -> argparse.Namespace: # 返回的是一个argparse.Namespace对象parser = argparse.ArgumentParser() # 创建该对象用于解析命令行参数,将其赋值给名为parser的变量可以使用parser对象来添加命令行选项、设置默认值、指定参数类型等。# parser.add_argument 为添加命令行选项# help为展示对应的信息parser.add_argument( # 添加命令行选项"-v", "--version", action="store_true", help="show the version and exit") # 当用户输入"-v", "--version"时,action的参数值被设置为True,help的值为展示版本信息 parser.add_argument( # 添加命令行选项"--check-backend", action="store_true", help="print all available backends" ) # 当用户输入"--check-backend"时,args.check_backends将被设置为True,可查看后端信息 帮助信息中提示该选项用于打印所有可用的后端信息if gen_type == "gui" and "mpi" in ALL_BACKEND:# gui doesn't support MPI backendALL_BACKEND.remove("mpi") # gui不支持mpi后端,所以需移除parser.add_argument("-b", # 短选项形式"--backend", # 长选项  都用于在命令行中指定后端选项type=str, # 指定了该选项的值类型为字符串choices=ALL_BACKEND, # 指定了可选的后端选项为 ALL_BACKEND 列表中的元素。default=DEFAULT_BACKEND, # 指定了选项的默认值为DEFAULT_BACKENDhelp="backend choice", # 提供对该选项的描述信息)parser.add_argument("-c", # 短选项形式"--cpu", # 长选项形式  都用于在命令行中指定CPU数量type=int, # 指定该选项的值类型为整数default=CPU_COUNT, # 默认值即如果未指定该选项,则将使用默认值 CPU_COUNThelp="number of CPU used", # 获取用户在命令行中指定的CPU数量)parser.add_argument("-z", # 短选项形式"--block-size", # 长选项形式,都用于在命令行中指定CUDA块大小type=int, # 指定该选项的值类型为整数default=1024, # 默认值为1024help="cuda block size (only for equ solver)", #显示)parser.add_argument( "--method", # 长选项形式type=str, # 指定该选项的值的类型为字符串choices=["equ", "grid"], # 两个可选值default="equ", # 默认为equhelp="how to parallelize computation", # 显示如何并行计算)# 添加指定源图像的文件名的命令parser.add_argument("-s", "--source", type=str, help="source image filename") # -s和—source都是可选命令行选项,用于指定源图像的文件名,参数类型为字符串。例如,如果用户在命令行中输入--source image.jpg,则源图像的文件名将被设置为"image.jpg"。帮助信息,提示用户该选项可用于指定源图像的文件名if gen_type == "cli": # 如果gen_type的值为”cli”parser.add_argument( #设置命令行选项"-m", # 短选项形式"--mask", # 长选项形式,都用于在命令行中指定掩膜图像文件名type=str, # 指定该选项的值类型为字符串help="mask image filename (default is to use the whole source image)", # 帮助信息,提示用户可以通过设置参数来指定mask图像的文件名,还提到如果用户没有指定文件名,则默认使用整个源图像进行处理default="", # 默认为空字符串)#  添加指定目标图像文件名的命令parser.add_argument("-t", "--target", type=str, help="target image filename") # 使用-t和—target选项来指定目标图像的文件名,参数类型为字符串。例如,如果用户在命令行中输入--target output.jpg,则目标图像的文件名将被设置为"output.jpg" 。帮助信息,用于提示用户该选项用于指定目标图像的文件名# 添加指定输出图像的文件名的命令parser.add_argument("-o", "--output", type=str, help="output image filename") # -o和—output选项用于指定输出图像的文件名,参数类型为字符串,例如,如果用户在命令行中输入--output result.jpg,则输出图像的文件名将被设置为"result.jpg。帮助信息,提示用户该选项用于指定输出图像的文件名。if gen_type == "cli":  # 如果gen_type的值为”cli”parser.add_argument("-h0", type=int, help="mask position (height) on source image", default=0) # 用于在命令行中指定掩膜在源图像中的位置(高度)parser.add_argument("-w0", type=int, help="mask position (width) on source image", default=0) # 用于在命令行中指定掩膜在源图像中的位置(宽度)parser.add_argument("-h1", type=int, help="mask position (height) on target image", default=0) # 用于在命令行中指定掩膜在目标图像中的位置(高度)parser.add_argument("-w1", type=int, help="mask position (width) on target image", default=0)  # 用于在命令行中指定掩膜在目标图像中的位置(宽度)parser.add_argument("-g",   # 这是一个可选的命令行短选项,用于指定梯度计算"--gradient", # 这是一个可选的命令行长选项,用于指定梯度计算type=str,   # 指定参数的类型为字符串choices=["max", "src", "avg"], # 指定了参数的可选项,只能从该列表的元素中选择。default="max",     # 选择的参数默认为maxhelp="how to calculate gradient for PIE",   # 提供了关于如何计算梯度的帮助信息的选项)parser.add_argument("-n", # 用于在命令行中指定迭代次数的选项type=int, # 指定了参数的类型为整型help="how many iteration would you perfer, the more the better", # 帮助信息,提示用户迭代次数越多,算法的性能会更好default=5000,   # 默认迭代次数为5000)if gen_type == "cli":     # 如果gen_type的值为cliparser.add_argument(    # 用于在命令行中指定每隔多少次迭代输出结果"-p", type=int, help="output result every P iteration", default=0  # -p选项用来指定每隔多少迭代输出结果,参数类型为整型,help选项表达的意思是,告诉用户可以用过设置参数p来指定来输出结果的频率。) if "mpi" in ALL_BACKEND:  # 如果字符串”mpi”在后端列表中parser.add_argument( # 添加一个命令行选项"--mpi-sync-interval", # 命令行,用于指定mpi同步迭代间隔type=int, # 参数类型为整型help="MPI sync iteration interval", # 帮助信息,用于提示用户可以通过设置参数来指定mpi同步的频率default=100,# 设置默认参数为100,即每隔100次迭代就会进行一次mpi同步操作,这样可以确保不同进程之间的数据同步和通信) #parser.add_argument( # 添加命令行选项"--grid-x", type=int, help="x axis stride for grid solver", default=8) # --grid-x 选项来指定网格求解器的 x 轴步长,参数类型为整型,帮助信息选项,提示用户可以通过设置参数来指定在网络求解器中沿着x轴步长大小 默认x轴步长为8# 其中,sync是synchronization的缩写,表示协调多个并发操作或进程之间执行顺序和数据一致性parser.add_argument( # 添加命令行选项"--grid-y", type=int, help="y axis stride for grid solver", default=8) # --grid-y 选项来指定网格求解器的 y 轴步长,参数类型为整型,帮助信息选项,用于提示用户可以通过设置参数来指定网络求解器中沿着y轴的步长大小。该步长决定了在计算过程中沿着y轴方向进行离散化间隔。 默认y轴步长为8# axis(轴):用于描述数据在特定维度上的变化或操作。args = parser.parse_args() # 该方法用于解析命令行参数,并将解析结果存储在变量 args 中if args.version: # 检查 args.version 是否为真  即用户是否在命令行中指定了 -v 或 --versionprint(fpie.__version__) # 输出exit(0) # 退出程序if args.check_backend: # 如果args对象中check_backend的属性为真print(ALL_BACKEND) # 则打印后端所有信息exit(0) # 退出if not os.path.exists(args.source): # 检查源图像文件是否存在print(f"Source image {args.source} not found.") # 输出提示exit(1) # 用于指示程序在遇到错误或异常情况时以非零的退出码结束if not os.path.exists(args.target): # 检查目标图像文件是否存在print(f"Target image {args.target} not found.") # 输出提示exit(1) # 用于指示程序在遇到错误或异常情况时以非零的退出码结束args.mpi_sync_interval = getattr(args, "mpi_sync_interval", 0) # 获取对象 args 的属性 "mpi_sync_interval" 的值return args

4.4 cli.py

        用于执行处理图像。该代码中导入所需的模块和类来解析命令行参数,通过解析命令行参数获取相关配置信息,选择合适的处理器进行图像处理,进行迭代处理并输出结果图像。

import time # 导入time模块,用于处理时间相关的操作,例如等待或获取当前时间
from fpie.args import get_args # 从fpie.args模块中导入get_args类。该类用于获取命令行参数并进行解析
from fpie.io import read_images, write_image # 从fpie.io中导入了read_images和write_image类,用于读取和写入图像文件
from fpie.process import BaseProcessor, EquProcessor, GridProcessor
# 从fpie.process模块中导入BaseProcessor、EquProcessor和GridProcessor类。这些类可能用于图像处理的不同算法
def main() -> None:args = get_args("cli") # 调用 get_args("cli") 函数来获取命令行参数,并将解析后的结果赋值给变量 argsproc: BaseProcessorif args.method == "equ": # 如果 args.method 的值是 "equ",则使用 EquProcessor 类来实例化proc = EquProcessor( # 将实例化后的处理器赋值给变量 proc# 传递相应的参数如下:args.gradient, # 命令行参数中指定的梯度值,用于均衡化处理args.backend,  # 命令行参数中指定的后端值,用于选择特定的处理后端args.cpu,    # 命令行参数中指定的CPU使用的值,用于控制处理器在CPU上的运行args.mpi_sync_interval, # 命令行参数值中指定的mpi同步间隔值,用于在分布式环境中控制mpi进程之间的同步操作args.block_size,  # 命令行参数中指定的块大小值,用于划分图像处理)else: # 如果 args.method 的值不是 "equ",则使用 GridProcessor 类来实例化# 并传递相应的参数proc = GridProcessor(args.gradient, # 命令行参数中指定的梯度值,用于均衡化处理args.backend, # 命令行参数中指定的后端值,用于选择特定的处理后端args.cpu, # 命令行参数中指定的CPU使用的值,用于控制处理器在CPU上的运行args.mpi_sync_interval, #  命令行参数中指定的MPI同步间隔值,用于在分布式环境中控制MPI进程之间的同步操作args.block_size, # # 命令行参数中指定的块大小值,用于划分图像处理args.grid_x, # 命令行参数中指定的网格求解器的x轴步长值,用于在网格处理器中控制沿x轴方向的离散化间隔args.grid_y, # 命令行参数中指定的网格求解器的y轴步长值,用于在网格处理器中控制沿y轴方向的离散化间隔)if proc.root: # 检查当前处理器是否为根节点print(f"Successfully initialize PIE {args.method} solver " # 打印初始化信息f"with {args.backend} backend" # 是一个使用了f-string的字符串格式化表达式。在这个表达式中,{args.backend}会被替换为args.backend变量的值)# 调用 proc.reset() 方法对处理器进行重置,传递源图像、掩膜图像、目标图像以及感兴趣区域的坐标范围参数src, mask, tgt = read_images(args.source, args.mask, args.target) # 调用read_images函数,从指定的源图像、遮罩图像和目标图像文件中读取图像数据,并将其分别赋值给变量src、mask和tgtn = proc.reset(src, mask, tgt, (args.h0, args.w0), (args.h1, args.w1)) # 调用处理器对象proc的reset方法,传递源图像、遮罩图像、目标图像以及感兴趣区域的起始坐标和结束坐标作为参数print(f"# of vars: {n}") # 使用f-string格式化字符串的方式打印出变量n的值,即处理器中的变量数量。proc.sync() # 调用处理器对象proc的sync方法,用于进行同步操作if proc.root: # 检查当前处理器是否为根节点result = tgt # 初始化结果变量 result 为目标图像(tgt)t = time.time() # 使用 time.time() 记录当前时间,并将其赋值给变量 t,以便计算总共花费的时间if args.p == 0:  # 如果参数args.p为0args.p = args.n # ,将 args.p 的值设置为 args.n 的值,以确保每次迭代处理的默认步长等于总迭代次数 args.nfor i in range(0, args.n, args.p):  # 该循环用于进行图像处理的迭代步骤if proc.root: # 如果是根节点result, err = proc.step(args.p)  # type: ignoreprint(f"Iter {i + args.p}, abs error {err}") # 调用处理器对象proc的step方法,传递步长args.p作为参数。该方法可能执行一次迭代的图像处理,并返回处理后的结果和误差if i + args.p < args.n: # 检查是否还有剩余的迭代步骤write_image(f"iter{i + args.p:05d}.png", result) # 将当前迭代的结果图像result写入文件,文件名以迭代次数命名else: # 如果当前处理器不是根节点proc.step(args.p) # 调用处理器对象proc的step方法,传递步长args.p作为参数if proc.root: # 如果是根节点dt = time.time() - t # 计算总共花费的时间,即当前时间减去起始时间,得到时间差,并将其赋值给变量dtprint(f"Time elapsed: {dt:.4f}s") # 使用f-string格式化字符串的方式打印总共花费的时间,保留4位小数write_image(args.output, result) # 将最终结果图像result写入文件,文件名由参数args.output指定print(f"Successfully write image to {args.output}") # 使用f-string格式化字符串的方式打印成功写入图像文件的信息

4.5 gui.py

        该代码用于实现一个简单的图像处理GUI,其中包含三个窗口:源图像窗口、目标图像窗口和结果图像窗口。它允许用户通过鼠标选择合适的区域,并通过图像处理算法生成结果图像。

import time # 导入了Python标准库中的time模块,用于处理时间相关的操作,例如等待或获取当前时间
from typing import Any # 从typing模块中导入了Any类型,用于在类型注解中表示任意类型import cv2 # 导入了OpenCV库,用于图像处理和计算机视觉任务
import numpy as np # 导入了NumPy库,用于高性能的数值计算和数组操作from fpie.cli import get_args # 从fpie.cli模块中导入了get_args类,用于获取命令行参数并进行解析
from fpie.io import read_image, write_image # 从fpie.io模块中导入了read_image和write_image类,用于读取和写入图像文件
from fpie.process import BaseProcessor, EquProcessor, GridProcessor
# 从fpie.process模块中导入了BaseProcessor、EquProcessor和GridProcessor类。
# 这些类用于图像处理的不同算法
class GUI(object):"""A simple GUI implementation.3 windows:1. src with rect bbox;2. tgt with single point;3. result with fix rate refresh."""'''以下代码实现了一个简单的图像显示和交互界面,允许用户在源图像和目标图像上进行操作,并在退出时保存结果图像'''
# 一个类的构造函数__init__(),proc是一个实现BaseProcessor接口的处理器对象。
# src表示源图像的路径,tgt表示目标图像的路径,out表示输出图像的路径,n表示迭代次数def __init__(self, proc: BaseProcessor, src: str, tgt: str, out: str, n: int):super().__init__() # 调用父类的构造函数,确保正确初始化继承自父类的属性self.xt, self.yt = 0, 0 # 初始化图像的坐标self.src = read_image(src) # 从指定路径读取源图像,并将其赋值给self.src属性self.tgt = read_image(tgt) # 从指定路径读取目标图像,并将其赋值给self.tgt属性。self.x0, self.y0 = 0, 0 # 初始化图像坐标# self.src.shape 返回一个元组,包含了图像的形状信息即图像的高度、宽度和通道数(对于彩色图像)# 使用切片操作 [:2]即只保留前两个元素,即高度和宽度self.y1, self.x1 = self.src.shape[:2] # 度赋值给 self.y1,将宽度赋值给 self.x1self.out = out # 将输出路径赋值给self.out属性self.n = n # 将迭代次数赋值给self.n属性self.proc = proc # 将处理器对象赋值给self.proc属性cv2.namedWindow("source") # 创建名为"source"的窗口cv2.setMouseCallback("source", self.source_callback) # 设置鼠标回调函数cv2.namedWindow("target") # 创建名为"target"的窗口cv2.setMouseCallback("target", self.target_callback) # 设置鼠标回调函数
# 回调函数是在用户与窗口中的图像进行交互时被调用的函数# 分别将源图像 (self.src)、目标图像 (self.tgt)# 和结果图像 (self.tgt) 复制给 self.gui_src、self.gui_tgt 和 self.gui_out,以便在 GUI 中显示self.gui_src = self.src.copy() # 创建了一个源图像的副本,并将其赋值给self.gui_src属性self.gui_tgt = self.tgt.copy() # 创建了一个目标图像的副本,并将其赋值给self.gui_tgt属性self.gui_out = self.tgt.copy() # 创建了一个目标图像的副本,并将其赋值给self.gui_out属性self.on_source = False  # 初始化了一个布尔值变量 self.on_source,表示当前是否在源图像上操作while True: # 不断调用 cv2.imshow 显示源图像、目标图像和结果图像的窗口cv2.imshow("source", self.gui_src) # 将名为"source"的窗口打开,并在该窗口中显示self.gui_src属性所代表的源图像cv2.imshow("target", self.gui_tgt) # 将名为"target"的窗口打开,并在该窗口中显示self.gui_tgt属性所代表的目标图像cv2.imshow("result", self.gui_out) # 将名为"result"的窗口打开,并在该窗口中显示self.gui_out属性所代表的输出图像key = cv2.waitKey(30) & 0xFF # 等待按键输入,并将按键的 ASCII 码存储在变量 key 中if key == 27: # 如果按下 ESC 键(ASCII 码为 27)break   # 则跳出循环,停止显示图像write_image(self.out, self.gui_out) # 将结果图像保存到指定的输出路径cv2.destroyAllWindows() # 关闭所有窗口'''这段代码的作用是在源图像窗口中实现了鼠标交互操作,允许用户通过拖动鼠标选择一个矩形区域,并将该选择框的坐标保存在相应的属性中。'''def source_callback(# 该回调函数接受几个参数:event(表示事件类型),x 和 y(表示鼠标点击位置的坐标)# flags(表示鼠标事件的附加标志),以及 param(用户定义的额外参数)self, event: int, x: int, y: int, flags: int, param: Any) -> None:if event == cv2.EVENT_LBUTTONDOWN: # 如果点击鼠标左键self.on_source = True   # 则标志为Trueself.x0, self.y0 = x, y # 并记录鼠标按下时的坐标elif event == cv2.EVENT_MOUSEMOVE: # 如果拖动鼠标if self.on_source: # 并且鼠标左键已按下self.gui_src = self.src.copy() # 则复制源图像到self.gui_srccv2.rectangle( # 并在self.gui_src 上绘制一个矩形框self.gui_src, (self.x0, self.y0), (x, y), (255, 255, 255), 1) # 框选从(x0, y0)到当前鼠标位置(x, y)的区域并以白色表示elif event == cv2.EVENT_LBUTTONUP: # 如果释放鼠标左键self.on_source = False # 将标志设置为Falseself.x1, self.y1 = x, y # 记录下鼠标释放时的坐标self.gui_src = self.src.copy() # 复制源图像到self.gui_srccv2.rectangle(self.gui_src, (self.x0, self.y0), (x, y), (255, 255, 255), 1) # 框选从(x0, y0)到当前鼠标位置(x, y)的区域并以白色表示# 确保左上角坐标为 (self.x0, self.y0),右下角坐标为 (self.x1, self.y1)self.x0, self.x1 = min(self.x0, self.x1), max(self.x0, self.x1) # 通过取最小值和最大值来规范化选择框的坐标self.y0, self.y1 = min(self.y0, self.y1), max(self.y0, self.y1) # 通过取最小值和最大值来规范化选择框的坐标'''这段代码的作用是在目标图像窗口中实现了鼠标交互操作'''def target_callback( # 用于处理目标图像的鼠标事件self, event: int, x: int, y: int, flags: int, param: Any) -> None:if event == cv2.EVENT_LBUTTONDOWN: # 当按下鼠标左键时self.gui_tgt = self.tgt.copy() # 复制目标图像 self.tgt 到 self.gui_tgtmask_x = min(self.x1 - self.x0, self.tgt.shape[1] - x) # 选择框在x方向上的宽度,确保选择框不会超出目标图像的宽度范围mask_y = min(self.y1 - self.y0, self.tgt.shape[0] - y) # 选择框在 y 方向上的高度,确保选择框不会超出目标图像的高度范围cv2.rectangle( # 在 self.gui_tgt 上绘制一个矩形框self.gui_tgt,(x, y),(x + mask_x, y + mask_y), # 从 (x, y) 开始,到 (x + mask_x, y + mask_y) 结束(255, 255, 255), # 以白色边框显示1,)mask = np.zeros([mask_y, mask_x], np.uint8) + 255 # 创建一个大小为 (mask_y, mask_x) 的二值化掩码图像 mask,并将所有像素值设置为 255t = time.time() # 记录当前时间 t# 然后调用处理器对象的 reset 方法,将源图像、掩码、目标图像以及选择框的坐标作为参数传入self.proc.reset(self.src, mask, self.tgt, (self.y0, self.x0), (y, x))# 调用处理器的 step 方法进行指定次数的迭代处理,并将结果保存到 self.gui_out 中。同时,将返回的误差值赋值给变量 errself.gui_out, err = self.proc.step(self.n)  # type: ignoret = time.time() - t # 计算处理时间 tprint( # 打印相关信息,如经过的时间、掩码大小、误差值以及一些参数f"Time elapsed: {t:.4f}s, mask size {mask.shape}, abs Error: {err}\t"f"Args: -n {self.n} -h0 {self.y0} -w0 {self.x0} -h1 {y} -w1 {x}")def main() -> None:args = get_args("gui") # 调用get_args("gui")获取命令行参数,并将返回值赋给 args 变量proc: BaseProcessor # 声明一个类型为BaseProcessor的变量proc,用于存储处理器对象if args.method == "equ": # 如果方法是 "equ"proc = EquProcessor( # 创建一个 EquProcessor 对象,传递相应的参数args.gradient, # 命令行参数中指定的梯度值,用于均衡化处理args.backend, # 命令行参数中指定的后端值,用于选择特定的处理后端args.cpu, # 命令行参数中指定的CPU使用值,用于控制处理器在CPU上的运行args.mpi_sync_interval, # 命令行参数中指定的MPI同步间隔值,用于在分布式环境中控制MPI进程之间的同步操作args.block_size, # 命令行参数中指定的块大小值,用于划分图像处理的块)else: # 否则,创建一个 GridProcessor 对象,传递相应的参数proc = GridProcessor(args.gradient, # 命令行参数中指定的梯度值,用于均衡化处理args.backend, # 命令行参数中指定的后端值,用于选择特定的处理后端args.cpu, # 命令行参数中指定的CPU使用值,用于控制处理器在CPU上的运行args.mpi_sync_interval,  # 命令行参数中指定的MPI同步间隔值,用于在分布式环境中控制MPI进程之间的同步操作args.block_size, # 命令行参数中指定的块大小值,用于划分图像处理的块args.grid_x, # 是命令行参数中指定的网格求解器的x轴步长值,用于在网格处理器中控制沿x轴方向的离散化间隔args.grid_y, # 命令行参数中指定的网格求解器的y轴步长值,用于在网格处理器中控制沿y轴方向的离散化间隔)print( # 打印一条成功初始化处理器的消息,显示所选方法和后端f"Successfully initialize PIE {args.method} solver "f"with {args.backend} backend" # 使用f-string格式化字符串的方式,将方法和后端的值插入到字符串中)
# 创建一个GUI对象,传递处理器对象、源图像路径、目标图像路径、输出路径和迭代次数作为参数。
# 这样就开始执行图像编辑的 GUI 界面GUI(proc, args.source, args.target, args.output, args.n)

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/675664.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

给网站网页PHP页面设置密码访问代码

将MkEncrypt.php文件上传至你网站根目录下或者同级目录下。 MkEncrypt.php里面添加代码&#xff0c;再将调用代码添加到你需要加密的页进行调用 MkEncrypt(‘123456’);括号里面123456修改成你需要设置的密码。 密码正确才能进去页面&#xff0c;进入后会存下cookies值&…

一览函数式编程

文章目录 一、 什么是函数式编程1.1 编程范式1.1.1 命令式编程(Imperative Programming)范式1.1.2 声明式编程(Declarative Programming)范式1.1.3 函数式编程(Functional Programming)范式1.1.4 面向对象编程(Object-Oriented Programming)范式1.1.5 元编程(Metaprogramming)范…

【自动驾驶|毫米波雷达】逻辑化讲解测角全流程

第一次更新&#xff1a;2024/5/7 目录 一. 引入 基础概念 二. 测角原理 1. 接收天线不同位置 2. 角度几何关系 3. 角度正负规定 4. 角度测量 5. 最大不模糊角 三. 角度分辨率 1. 相位变化量 2. 角度表示 3. 角度变化量 三. 测角算法 1. 三维快速傅里叶变换 (3D-FFT&…

湖仓一体 - Apache Arrow的那些事

湖仓一体 - Apache Arrow的那些事 Arrow是高性能列式内存格式标准。它的优势&#xff1a;高效计算&#xff1a;所有列存的通用优势&#xff0c;CPU缓存友好、SIMD向量化计算友好等&#xff1b;零序列化/反序列化&#xff1a;arrow的任何数据结构都是一段连续的内存&#xff0c;…

什么是电脑监控软件?哪些监控软件好用?

电脑监控软件是一种用于监控和管理计算机系统和数据的工具。它可以对计算机的使用情况进行实时监控&#xff0c;记录用户的操作行为&#xff0c;并及时发出警报&#xff0c;以防止数据泄露、违规操作和其他安全问题的发生。在当今信息时代&#xff0c;保护企业和个人信息安全变…

2022 年全国职业院校技能大赛高职组云计算赛项试卷(容器云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

IDEA中向Data Sources导入sql文件

IDEA中向Data Sources导入sql文件 开篇 在学习黑马的课程时&#xff0c;时常需要向数据库中导入sql文件生成数据库表&#xff0c;每次都会忘记导入步骤&#xff0c;折腾许久&#xff0c;于是将过程记录下来。 步骤 在Database中选择你要导入的数据库源&#xff0c;如图我想…

【陀螺仪JY61P维特智能】通过单片机修改波特率和角度参考的方法

根据官方文档&#xff1a; 修改波特率 1.解锁:FF AA 69 88 B5 1.1延时200ms 2.修改波特率:FF AA 04 06 00 2.1切换已修改的波特率然后重新发送解锁和保存指令 2.2解锁:FF AA 69 88 B5 2.3延时200ms 4.保存: FF AA 00 00 00 XY轴角度参考 角度参考是以传感器当前的实际位置&…

Terraform资源

资源是Terraform中最核心的部分&#xff0c;使用Terraform的目的就是用于管理资源。 在Terraform中&#xff0c;资源使用resource块定义。 一个resource可以定义一个或多个基础设施资源对象&#xff0c;如&#xff1a;VPC&#xff0c;虚拟机&#xff0c;DNS记录&#xff0c;Con…

C++ Primer 总结索引 | 第十四章:重载运算与类型转换

1、C语言定义了 大量运算符 以及 内置类型的自动转换规则 当运算符 被用于 类类型的对象时&#xff0c;C语言允许我们 为其指定新的含义&#xff1b;也能自定义类类型之间的转换规则 例&#xff1a;可以通过下述形式输出两个Sales item的和&#xff1a; cout << item1 …

【自动驾驶|毫米波雷达】逻辑化讲清快时间与慢时间傅里叶变换

碎碎念&#xff1a;实习过程中发现在进行雷达知识交流时&#xff0c;大部分同事都会用英文简称代替中文的一些称呼&#xff0c;比如Chirp、FFT等等。起初我觉得是因为很多英伟达、TI芯片的开发教程都是英文的&#xff0c;所以看得多了大家都习惯这样称呼&#xff0c;后来在和指…

微信小程序15: 小程序组件

创建组件 ①在项目的根目录中&#xff0c;鼠标右键&#xff0c;创建components -> test文件夹 ②在新建的components -> test文件夹上&#xff0c;鼠标右键&#xff0c;点击“新建Component‘ ③键入组件的名称之后回车&#xff0c;会自动生成组件对应的4个文件&#…