使用 jgrapht 图库进行图形处理
最编程
2024-07-15 09:20:46
...
在 了解数据结构图 (graph) 的学习中,我们了解到了图的基本概念,并通过学习 迪杰斯特拉 ( Dijkstra ) 最短路径算法 和 DFS(深度优先遍历) 以及 BFS(广度优先遍历) ,可以了解到怎么求图中的最短路径。为什么学习图呢?在调度任务中,大量使用了图的概念,处理任务的依赖关系,所以,为了彻底弄懂原理,有必要系统粗略地学习下。除此之外,在机器学习中例如知识图谱、推荐系统等也大量使用了图。
接下来,我们通过 jgrapht 库,对图进行操作。 jgrapht 库提供了在 java 中进行图运算的能力,开源地址:https://github.com/jgrapht/jgrapht 。下面我们将通过一些小栗子,学习该库的使用。
例子1:基本的API
在 图 中,有一些基本的概念,例如 顶点Vertex、边edge、度degree、有环、无环。如果不了解,可以在上面提到的文章中了解。好了,下面实操实操。
代码中,描述了下图所示中的有向图
@Test
public void testOtherAPI() {
String a = "A";
String b = "B";
String c = "C";
String d = "D";
String e = "E";
String f = "F";
// 构建图
Graph<String, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
// 添加顶点
g.addVertex(a);
g.addVertex(b);
g.addVertex(c);
g.addVertex(d);
g.addVertex(e);
g.addVertex(f);
// 添加边
g.addEdge(a, b);
g.addEdge(a, c);
g.addEdge(b, d);
g.addEdge(c, e);
g.addEdge(d, f);
g.addEdge(e, f);
// 度
System.out.println(g.degreeOf(a)); // 出度 output: 2
System.out.println(g.inDegreeOf(a)); // 入度 output: 0
System.out.println(g.outDegreeOf(a)); // 入度 output: 2
// Graphs 工具类
// 当前顶点的后续节点列表 output: [B, C]
System.out.println(Graphs.successorListOf(g, a));
// 当前顶点的后续节点列表 output: [D, E]
System.out.println(Graphs.predecessorListOf(g, f));
// 图中“环”的操作
CycleDetector<String, DefaultEdge> cycleDetector = new CycleDetector<>(g);
// 探测图中是否有环 output: false
System.out.println(cycleDetector.detectCycles());
// 找出图中的环 output: []
System.out.println(cycleDetector.findCycles());
}
例子2:有向图
@Test
public void testDirectedGraph() {
// create a graph based on URL objects
Graph<URL, DefaultEdge> hrefGraph = createHrefGraph();
// note directed edges are printed as: (<v1>,<v2>)
// 打印所有的路径,默认是第一个添加的edge是起始点顶点
// 前面的 [] 是显示顶点数;第二个 [] 是路径
// 打印([http://www.amazon.com, http://www.yahoo.com, http://www.ebay.com], [(http://www.yahoo.com,http://www.amazon.com), (http://www.yahoo.com,http://www.ebay.com)])
System.out.println(hrefGraph.toString());
}
private static Graph<URL, DefaultEdge> createHrefGraph() {
Graph<URL, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
try {
URL amazon = new URL("http://www.amazon.com");
URL yahoo = new URL("http://www.yahoo.com");
URL ebay = new URL("http://www.ebay.com");
// add the vertices
// 顶点
g.addVertex(amazon);
g.addVertex(yahoo);
g.addVertex(ebay);
// 边缘
// add edges to create linking structure
g.addEdge(yahoo, amazon);
g.addEdge(yahoo, ebay);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return g;
}
例子3:广度优先和深度优先算法遍历有向无环图
/**
* 测试广度优先遍历
*
* ([v1, v2, v3, v4, v5], [(v1,v5), (v5,v3), (v3,v4), (v1,v2), (v4,v2)])
* []
* [v2, v3, v4, v5]
* v1
* v5
* v2
* v3
* v6
* v4
*/
@Test
public void testBreadthFirstIterator() {
DirectedAcyclicGraph<String, DefaultEdge> dag = createGraph();
System.out.println(dag);
System.out.println(dag.getAncestors("v1"));
System.out.println(dag.getDescendants("v1"));
BreadthFirstIterator<String, DefaultEdge> bfi = new BreadthFirstIterator<>(dag, "v1");
while (bfi.hasNext()) {
System.out.println(bfi.next());
}
}
/**
([v1, v2, v3, v4, v5], [(v1,v5), (v5,v3), (v3,v4), (v1,v2), (v4,v2)])
[]
[v2, v3, v4, v5]
v1
v2
v6
v5
v3
v4
* 测试深度优先遍历
*/
@Test
public void testDepthFirstIterator() {
DirectedAcyclicGraph<String, DefaultEdge> dag = createGraph();
System.out.println(dag);
System.out.println(dag.getAncestors("v1"));
System.out.println(dag.getDescendants("v1"));
DepthFirstIterator<String, DefaultEdge> dfi = new DepthFirstIterator<>(dag, "v1");
while (dfi.hasNext()) {
System.out.println(dfi.next());
}
}
/**
* DAG有向无环图
*/
private static DirectedAcyclicGraph<String, DefaultEdge> createGraph() {
DirectedAcyclicGraph<String, DefaultEdge> g = new DirectedAcyclicGraph<>(DefaultEdge.class);
String v1 = "v1";
String v2 = "v2";
String v3 = "v3";
String v4 = "v4";
String v5 = "v5";
String v6 = "v6";
// add the vertices
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
g.addVertex(v5);
g.addVertex(v6);
// 如果出现了环状,抛异常 IllegalArgumentException
// add edges to create a circuit
g.addEdge(v1, v5);
g.addEdge(v5, v3);
g.addEdge(v3, v4);
g.addEdge(v1, v2);
g.addEdge(v2, v6);
return g;
}
例子4:Dijkstra算法获取最短路径
@Test
public void testShortestPath() {
Graph<String, DefaultEdge> g2 = new DefaultDirectedGraph<>(DefaultEdge.class);
String v1 = "v1";
String v2 = "v2";
String v3 = "v3";
// add the vertices
g2.addVertex(v1);
g2.addVertex(v2);
g2.addVertex(v3);
// v1 -> v2 -> v3
// add edges to create a circuit
g2.addEdge(v1, v2);
g2.addEdge(v2, v3);
DijkstraShortestPath shortestPath = new DijkstraShortestPath(g2);
ShortestPathAlgorithm.SingleSourcePaths paths = shortestPath.getPaths(v2);
System.out.println(paths.toString());
GraphPath<Integer, DefaultWeightedEdge> shortestPath1 = shortestPath.getPath(v1, v3);
GraphPath<Integer, DefaultWeightedEdge> shortestPath2 = shortestPath.getPath(v1, v2);
GraphPath<Integer, DefaultWeightedEdge> shortestPath3 = shortestPath.getPath(v1, v1);
GraphPath<Integer, DefaultWeightedEdge> shortestPath4 = shortestPath.getPath(v2, v1);
GraphPath<Integer, DefaultWeightedEdge> shortestPath5 = shortestPath.getPath(v2, v3);
// [(v1 : v2), (v2 : v3)]
System.out.println(shortestPath1.toString());
// [(v1 : v2)]
System.out.println(shortestPath2.toString());
// [v1]
System.out.println(shortestPath3.toString());
//路径没有联通,所有返回的是null
Assert.assertNull(shortestPath4);
// [(v2 : v3)]
System.out.println(shortestPath5.toString());
}
例子5:有向加权图
获取从v0到v7的最短路径
@Test
public void testDirectedWeightedGraph() {
SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> graph = createGraph();
printShortestPath(graph);
}
/**
* 创建一个有向带权图
*/
public SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> createGraph() {
String[] str = {"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"};
int[] startPoint = {0, 0, 0, 1, 1, 3, 3, 3, 3, 2, 4, 5, 5, 4, 6};
int[] endPoint = {1, 3, 2, 4, 3, 4, 5, 6, 2, 6, 5, 6, 7, 7, 7};
double[] weights = {2, 8, 1, 1, 6, 5, 1, 2, 7, 9, 3, 4, 6, 9, 3};
SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> directedGraph =
new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class);
//添加顶点
for (String s : str) {
directedGraph.addVertex(s);
}
// 起点
DefaultWeightedEdge[] edgeSources = new DefaultWeightedEdge[startPoint.length];
// 终点
DefaultWeightedEdge[] edgeTargets = new DefaultWeightedEdge[startPoint.length];
for (int i = 0; i < endPoint.length; i++) {
edgeSources[i] = directedGraph.addEdge(str[startPoint[i]], str[endPoint[i]]);
edgeTargets[i] = directedGraph.addEdge(str[endPoint[i]], str[startPoint[i]]);
directedGraph.setEdgeWeight(edgeSources[i], weights[i]);
directedGraph.setEdgeWeight(edgeTargets[i], weights[i]);
}
return directedGraph;
}
/**
* 根据得到的带权有向图,Dijkstra计算最短路径,并保存他们的路径权值之和到数组Dij中
*/
public static void printShortestPath(SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> graph) {
DijkstraShortestPath<String, DefaultWeightedEdge> dijkstraAlg = new DijkstraShortestPath<>(graph);
//获取从u0到u7的最短路径
System.out.println("Shortest path from v1 to v8:");
ShortestPathAlgorithm.SingleSourcePaths<String, DefaultWeightedEdge> sourcePaths = dijkstraAlg.getPaths("v0");
GraphPath<String, DefaultWeightedEdge> u0ToU7ShortestPath = sourcePaths.getPath("v7");
//打印路径
List<String> pathsList = u0ToU7ShortestPath.getVertexList();
StringBuilder sb = new StringBuilder();
boolean firstElement = true;
for (String s : pathsList) {
if (firstElement) {
sb.append(s);
firstElement = false;
} else {
sb.append("->").append(s);
}
}
System.out.println(sb);
System.out.println("Shortest path weight:" + u0ToU7ShortestPath.getWeight());
}
输出:
Shortest path from v0 to v7:
v0->v1->v4->v7
Shortest path weight:12.0
上一篇: 在 Windows 上使用 pybind11 的教程
下一篇: Ubuntu pybind11 教程
推荐阅读
-
Python 知识:如何使用 Python 工具使用 OpenCV 进行图像处理
-
Python 知识:如何使用 Flink 和 Python 进行实时数据处理
-
优化性能:通过使用parted命令对分区进行对齐处理
-
Java 8新特性探究(十三)JavaFX 8新特性以及开发2048游戏-JavaFX历史## 跟java在服务器端和web端成绩相比,桌面一直是java的软肋,于是Sun公司在2008年推出JavaFX,弥补桌面软件的缺陷,请看下图JavaFX一路走过来的改进 从上图看出,一开始推出时候,开发者需使用一种名为JavaFX Script的静态的、声明式的编程语言来开发JavaFX应用程序。因为JavaFX Script将会被编译为Java bytecode,程序员可以使用Java代码代替。 JavaFX 2.0之后的版本摒弃了JavaFX Script语言,而作为一个Java API来使用。因此使用JavaFX平台实现的应用程序将直接通过标准Java代码来实现。 JavaFX 2.0 包含非常丰富的 UI 控件、图形和多媒体特性用于简化可视化应用的开发,WebView可直接在应用中嵌入网页;另外 2.0 版本允许使用 FXML 进行 UI 定义,这是一个脚本化基于 XML 的标识语言。 从JDK 7u6开始,JavaFx就与JDK捆绑在一起了,JavaFX团队称,下一个版本将是8.0,目前所有的工作都已经围绕8.0库进行。这是因为JavaFX将捆绑在Java 8中,因此该团队决定跳过几个版本号,迎头赶上Java 8。 ##JavaFx8的新特性 ## ###全新现代主题:Modena 新的Modena主题来替换原来的Caspian主题。不过在Application的start方法中,可以通过setUserAgentStylesheet(STYLESHEET_CASPIAN)来继续使用Caspian主题。 参考http://fxexperience.com/2013/03/modena-theme-update/ ###JavaFX 3D 在JavaFX8中提供了3D图像处理API,包括Shape3D (Box, Cylinder, MeshView, Sphere子类),SubScene, Material, PickResult, LightBase (AmbientLight 和PointLight子类),SceneAntialiasing等。Camera类也得到了更新。从JavaDoc中可以找到更多信息。 ###富文本 强化了富文本的支持 ###TreeTableView ###日期控件DatePicker 增加日期控件 ###用于 CSS 结构的公共 API
-
14-傅里叶变换的代码实现-一、numpy实现傅里叶变换和逆傅里叶变换 1.numpy实现傅里叶变换numpy.fft.fft2实现傅里叶变换,返回一个复数数组(complex ndarray),也就是频谱图像numpy.fft.fftshift将零频率分量移到频谱中心(将左上角的低频区域,移到中心位置) 20*np.log(np.abs(fshift))设置频谱的范围。可以理解为,之前通过傅里叶变换得到复数的数组,是不能通过图像的方法展示出来的,需要转换为灰度图像(映射到[0,255]区间)需要注意的是1> 傅里叶得到低频、高频信息,针对低频、高频处理能够实现不同的目的2> 傅里叶过程是可逆的,图像经过傅里叶变换、逆傅里叶变换后,能够恢复到原始图像3> 在频域对图像进行处理,在频域的处理会反映在逆变换图像上 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) # 移动中心位置 fshift = np.fft.fftshift(f) # 调整值范围 result = 20*np.log(np.abs(fshift)) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(result,cmap=plt.cm.gray) plt.title("result") plt.axis("off") plt.show 傅里叶变换的频谱图像: 2.numpy实现逆傅里叶变换numpy.fft.ifft2实现逆傅里叶变换,返回一个复数数组(complex ndarray)numpy.fft.ifftshiftfftshift函数的逆函数,将中心位置的低频,重新移到左上角iimg = np.abs(逆傅里叶变化结果)设置值的范围,映射到[0,255]区间 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\boat.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) fshift = np.fft.fftshift(f) # 逆傅里叶变换 ishift = np.fft.ifftshift(fshift) iimg = np.fft.ifft2(ishift) iimg = np.abs(iimg) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("iimg") plt.axis("off") plt.show 将一副图像,进行傅里叶变换和逆傅里叶变换后,进行对比(一样的) 实例:通过numpy实现高通滤波,保留图像的边缘信息 获取图像的形状rows,cols = img.shape获取图像的中心点crow,ccol = int(rows/2),int(cols/2)将频谱图像的中心区域(低频区域)设置为0(黑色)fshift[crow-30:crow+30,ccol-30:ccol+30] = 0 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\boat.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 f = np.fft.fft2(img) fshift = np.fft.fftshift(f) # 高通滤波 rows,cols = img.shape crow,ccol = int(rows/2),int(cols/2) fshift[crow-30:crow+30,ccol-30:ccol+30] = 0 # 逆傅里叶变换 ishift = np.fft.ifftshift(fshift) iimg = np.fft.ifft2(ishift) iimg = np.abs(iimg) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("iimg") plt.axis("off") plt.show 使用numpy实现高通滤波的实验结果: 二、opencv实现傅里叶变换和逆傅里叶变换 1.opencv实现傅里叶变换 返回结果 = cv2.dft(原始图像,转换标识)1> 返回结果:是双通道的,第一个通道是结果的实数部分,第二个通道是结果的虚数部分2> 原始图像:输入图像要首先转换成np.float32(img)格式3> 转换标识:flags = cv2.DFT_COMPLEX_OUTPUT,输出一个复数阵列numpy.fft.fftshift将零频率分量移到频谱中心(将左上角的低频区域,移到中心位置)调整频谱的范围,将上面频谱图像的复数数组,转换为可以显示的灰度图像(映射到[0,255]区间)返回值 = 20*np.log(cv2.magnitude(参数1,参数2))1> 参数1:浮点型X坐标值,也就是实部2> 参数2:浮点型Y坐标值,也就是虚部 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) # 移动中心位置 dftShift = np.fft.fftshift(dft) # 调整频谱的范围 result = 20*np.log(cv2.magnitude(dftShift[:,:,0],dftShift[:,:,1])) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(result,cmap=plt.cm.gray) plt.title("result") plt.axis("off") plt.show 傅里叶变换的频谱图像: 2.opencv实现逆傅里叶变换返回结果 = cv2.idft(原始数据)1> 返回结果:取决于原始数据的类型和大小2> 原始数据:实数或者复数均可numpy.fft.ifftshiftfftshift函数的逆函数,将中心位置的低频,重新移到左上角调整频谱的范围,映射到[0,255]区间返回值 = cv2.magnitude(参数1,参数2)1> 参数1:浮点型X坐标值,也就是实部2> 参数2:浮点型Y坐标值,也就是虚部 # 将绘制的图显示在窗口 %matplotlib qt5 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r"image\lena.bmp",cv2.IMREAD_GRAYSCALE) # 傅里叶变换 dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) dftShift = np.fft.fftshift(dft) # 逆傅里叶变换 ishift = np.fft.ifftshift(dftShift) iimg = cv2.idft(ishift) iimg = cv2.magnitude(iimg[:,:,0],iimg[:,:,1]) plt.subplot(1,2,1) plt.imshow(img,cmap=plt.cm.gray) plt.title("original") plt.axis("off") plt.subplot(1,2,2) plt.imshow(iimg,cmap=plt.cm.gray) plt.title("inverse") plt.axis("off") plt.show 将一副图像,进行傅里叶变换和逆傅里叶变换后,进行对比(一样的) 实例:通过opencv实现低通滤波,模糊一副图像
-
在Windows操作系统下,使用bat批处理脚本进行telnet批量检测远程端口的小结
-
【NLP】使用LSTM进行命名实体识别的自然语言处理技术
-
使用jQuery进行表单提交并处理返回的JSON数据
-
使用 Node.js 的 gm 工具对图片进行处理和编辑
-
使用JavaCPP和FFmpeg进行流媒体编码与图像处理