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://xiahunao.cn/news/3031628.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

HC-05的简介与使用

第一部分*********************&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;蓝牙模块HC-05 蓝牙概述 蓝牙&#xff08;Bluetooth&#xff09;是一种用于无线通信的技术标准&#xff0c;允许设备在短距离内进行数据交换…

*****水上飞机:继承,虚函数,虚继承

一题目 请设计以下航行器、飞机、船、水上飞机等 4 个类。 CRAFT 为航行器类&#xff0c;是公共基类&#xff0c;提供航行器的基本特性。包括&#xff1a; 一个保护数据成员&#xff1a;speed(速度)。 三个公有成员函数&#xff1a;构造函数(初始化速度)、析构函数和 Show 函数…

算法学习笔记(一)-快速幂

#问题的引入-对于幂次方的求解我们怎么可以最大限度的降低时间复杂度呢 #对于一个基本的幂次运算&#xff0c;c代码如下示例 long long int myPower(int base,int power) {long long int result 1 ;for (int i 1 ; i < power ; i){result * base ;}return result ; } #…

LLMs应被视为一种文字计算器?

编者按&#xff1a;当前&#xff0c;大语言模型已经成为自然语言处理领域的热点。LLMs 是否真的“智能”&#xff1f;它们又为我们带来了哪些启发&#xff1f;针对这些问题&#xff0c;Darveen Vijayan 为我们带来了这篇引人深思的文章。 作者主要阐释了两个观点&#xff1a;第…

linux上用Jmter进行压测

在上一篇中安装好了Jmeter环境&#xff0c;在这一篇中将主要分享如何使用jmeter在linux中进行单机压测。 1.项目部署 在这里我们先简单部署一下测试环境&#xff0c;所用到的项目环境是个jar包&#xff0c;先在linux上home目录下新建app目录&#xff0c;然后通过rz命令将项目ja…

2万字干货:如何从0到1搭建一套会员体系(2)

2.用户等级 还是一样&#xff0c;我们为什么要搭建用户等级&#xff1f; 一个国家有几亿人口的时候你怎么来管理&#xff1f;老祖宗秦始皇给出了我们答案&#xff1a;郡县制。发展到现在则演进成了省-市-区县-乡镇(街道)-村(社区)5层行政治理结构。 产品同理&#xff0c;当你…

Flume 的安装和使用方法(Spark-2.1.0)

一、Flume的安装 1.下载压缩包 https://www.apache.org/dyn/closer.lua/flume/1.7.0/apache-flume-1.7.0-bin.tar.gz 2.上传到linux中 3.解压安装包 cd #进入加载压缩包目录sudo tar -zxvf apache-flume-1.7.0-bin.tar.gz -C /usr/local # 将 apache-flume-1.7.0-bin.tar.g…

文旅行业| 某景区导游培养和管理项目成功案例纪实

——整合导游资源并进行统一管理&#xff0c;构建完善的培养与管理机制&#xff0c;发挥景区导游价值 【客户行业】文旅行业&#xff1b;景区&#xff1b;文旅企业 【问题类型】人才培养&#xff1b;人员管理 【客户背景】 南方某5A级景区&#xff0c;作为国内极具代表性和特…

经常睡不好觉?试试用上华为手环9新升级的睡眠监测功能

睡眠问题是不是经常困扰着你呢&#xff1f;听说&#xff0c;华为手环9的睡眠监测功能升级了&#xff0c;无论是入睡前、睡眠中还是睡醒后&#xff0c;都能够帮助我们改善睡眠&#xff0c;让我们告别糟糕的睡眠质量&#xff01; 睡觉前&#xff0c;打开华为手环9的睡眠模式&…

寻找最大价值的矿堆 - 矩阵

系列文章目录 文章目录 系列文章目录前言一、题目描述二、输入描述三、输出描述四、Java代码五、测试用例 前言 本人最近再练习算法&#xff0c;所以会发布一些解题思路&#xff0c;希望大家多指教 一、题目描述 给你一个由’0’(空地)、‘1’(银矿)、‘2’(金矿)组成的地图…

自动化测试基础 --- Jmeter

前置环境安装 首先我们需要知道如何下载Jmeter 这里贴上下载网站Apache JMeter - Download Apache JMeter 我们直接解压,然后在bin目录下找到jemter.bat即可启动使用 成功打开之后就是这个界面 每次打开可以用这种方式切换成简体中文 或者直接修改properties文件修改对应的语言…

第七届精武杯部分wp

第一部分&#xff1a;计算机和手机取证 1.请综合分析计算机和手机检材&#xff0c;计算机最近一次登录的账户名是 答案&#xff1a;admin 创建虚拟机时直接给出了用户名 2. 请综合分析计算机和手机检材&#xff0c;计算机最近一次插入的USB存储设备串号是 答案&#xff1a…

01面向类的讲解

指针指向类成员使用 代码&#xff1a; #include<iostream> using namespace std;class Test { public:void func() { cout << "call Test::func" << endl; }static void static_func();int ma;static int mb; //不依赖对象 }; void Test::static…

探索GitHub上的GPTs项目:泄露和被破解的GPT提示

GPTs项目是一个在GitHub上由用户linexjlin发起的开源项目&#xff0c;专注于提供泄露的GPT&#xff08;生成式预训练转换器&#xff09;提示。这些提示用于指导和优化AI模型的输出&#xff0c;进而提升代码生成的质量和效率。项目页面提供了丰富的功能和资源&#xff0c;旨在帮…

全套停车场管理系统报价多少钱?停车场管理系统由哪些设备组成?

随着城市化进程的加快&#xff0c;汽车保有量的不断攀升&#xff0c;停车场的管理和运营成为城市基础设施建设的重要组成部分。一个高效、智能的停车场收费系统不仅能提升停车效率&#xff0c;还能增强用户体验&#xff0c;对城市的交通管理起到关键作用。本文将为您详细介绍全…

mac 讨厌百度网盘怎么办

一、别拦我 首先请允许我泄个愤&#xff0c;tmd百度网盘下个1g的文件下载速度竟然超不过200k&#xff0c;只要不放在所有已打开软件的最前面&#xff0c;它就给你降到10k以内&#xff0c;关键是你慢就慢了&#xff0c;我也不是很着急&#xff0c;关键是你日常下载失败并且总是…

AI代理和AgentOps生态系统的剖析

1、AI代理的构成&#xff1a;AI代理能够根据用户的一般性指令自行做出决策和采取行动。 主要包含四个部分&#xff1a; &#xff08;1&#xff09;大模型&#xff08;LLM&#xff09; &#xff08;2&#xff09;工具&#xff1a;如网络搜索、代码执行等 &#xff08;3&#x…

在Qt工具栏上实现矩阵并排的按钮效果源码

如果这个要用MFC去实现头皮都得掉一层&#xff0c;建议大家以后要写GUI方面的小工具尽量转QT或其他吧&#xff0c;MFC真不适合搞这种花里胡哨的界面. 在Qt工具栏上实现矩阵并排的按钮效果源码如下&#xff1a; #include "mainwindow.h" #include "ui_mainwind…

初识指针(4)<C语言>

前言 前面的文章&#xff0c;已经对指针的基础概念以及运用有了初步了解&#xff0c;我们可以进一步探究指针比较深入的知识&#xff0c;下文将主要介绍&#xff1a;使用指针数组模拟二维数组、字符指针变量、数组指针、二维数组传参的本质、函数指针、typedef关键字等。 目录…

RustDesk 自建服务器部署和使用教程

RustDesk 是一个强大的开源远程桌面软件&#xff0c;是中国开发者的作品&#xff0c;它使用 Rust 编程语言构建&#xff0c;提供安全、高效、跨平台的远程访问体验。可以说是目前全球最火的开源远程桌面软件了&#xff0c;GitHub 星星数量达到了惊人的 64k&#xff01; 与 Team…