A. Points in Segments

本篇博客介绍了一道算法题目,任务是在给定的一系列区间中找出所有不属于这些区间的整数点。通过遍历每个区间并标记已覆盖的点,最终输出所有未被标记的点。该方法适用于较小的数据规模。

题目来源:https://codeforces.com/problemset/problem/1015/A
You are given a set of n segments on the axis Ox, each segment has integer endpoints between 1 and m inclusive. Segments may intersect, overlap or even coincide with each other. Each segment is characterized by two integers li and ri (1≤li≤ri≤m) — coordinates of the left and of the right endpoints.
Consider all integer points between 1 and m inclusive. Your task is to print all such points that don’t belong to any segment. The point x belongs to the segment [l;r] if and only if l≤x≤r.
Input
The first line of the input contains two integers n and m (1≤n,m≤100) — the number of segments and the upper bound for coordinates.
The next n lines contain two integers each li and ri (1≤li≤ri≤m) — the endpoints of the i-th segment. Segments may intersect, overlap or even coincide with each other. Note, it is possible that li=ri, i.e. a segment can degenerate to a point.
Output
In the first line print one integer k — the number of points that don’t belong to any segment.
In the second line print exactly k integers in any order — the points that don’t belong to any segment. All points you print should be distinct.
If there are no such points at all, print a single integer 0 in the first line and either leave the second line empty or do not print it at all.

Examples
Input
3 5
2 2
1 2
5 5
Output
2
3 4
Input
1 7
1 7
Output
0

Note
In the first example the point 1 belongs to the second segment, the point 2 belongs to the first and the second segments and the point 5 belongs to the third segment. The points 3 and 4 do not belong to any segment.

In the second example all the points from 1 to 7 belong to the first segment.

在轴Ox上给出一组n个段,每个段具有介于1和m之间的整数端点。段可以相互交叉,重叠或甚至重合。每个段的特征在于两个整数li和ri(1≤li≤ri≤m) - 左端点和右端点的坐标。
考虑1到m之间的所有整数点。您的任务是打印所有不属于任何细分的点。当且仅当l≤x≤r时,点x属于段[l; r]。
输入
输入的第一行包含两个整数n和m(1≤n,m≤100) - 段的数量和坐标的上限。
接下来的n行包含两个整数,每个li和ri(1≤li≤ri≤m) - 第i个分段的端点。段可以相互交叉,重叠或甚至重合。注意,有可能li = ri,即一个段可以退化到一个点。
产量
在第一行中打印一个整数k - 不属于任何段的点数。
在第二行中,以任何顺序精确打印k个整数 - 不属于任何段的点。您打印的所有点都应该是不同的。
如果根本没有这样的点,则在第一行中打印一个整数0,并将第二行留空或根本不打印。
注意
在第一示例中,点1属于第二段,点2属于第一段和第二段,点5属于第三段。第3点和第4点不属于任何细分。
在第二个示例中,从1到7的所有点都属于第一个段。
题意:给你一组n个段,每个段左右坐标给出,让你找出不属于如何段的点;
解题思路:将输入的每一个段包含的点都标记为1,就可以找出不属于所有线段的点了;其实数据比较小直接搜就可以过;

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
int main()
{
    int a,b,n,m,sum=0;
    cin>>a>>b;
    int s[1500];
    memset(s,0,sizeof(s));
    for(int i=0;i<a;i++)
    {
        scanf("%d%d",&n,&m);
        for(int i=n;i<=m;i++)//输入一组就标记一组;
        {
            s[i]=1;
        }
    }
    for(int i=1;i<=b;i++)//它是从1,开始到b结束;
    {
        if(s[i]==1)//标记过就计算它标记的点;
        {
            sum++;
        }
    }
    if(sum==b)
    {
        printf("0");
    }
    else//按格式打印;
    {
        printf("%d\n",b-sum);
        for(int i=1;i<=b;i++)
        {
            if(s[i]==0&&i<b)
                printf("%d ",i);
            if(s[i]==0&&i==b)
                printf("%d",i);
        }
    }
    return 0;
}
import numpy as np from scipy.spatial import distance, Voronoi, cKDTree from matplotlib.path import Path as MplPath import matplotlib.pyplot as plt from concurrent.futures import ThreadPoolExecutor from functools import lru_cache from typing import List, Tuple, Dict class EnhancedRechargeableTSP: """ 增强型可充电TSP求解器 功能特性: - Voronoi图辅助充电决策 - 滚动时域控制优化 - 动态能量管理 - 多阶段路径修复 """ def __init__(self, tasks: List[Tuple[float, float]], recharges: List[Tuple[float, float]], warehouse: Tuple[float, float], max_range: float, w1: float = 0.7, w2: float = 0.3): # 节点坐标整合 self.warehouse = np.array(warehouse) self.points = np.vstack([ self.warehouse, np.array(tasks), np.array(recharges) ]) # 节点索引配置 self.num_tasks = len(tasks) self.num_recharges = len(recharges) self.warehouse_idx = 0 self.task_indices = list(range(1, 1 + self.num_tasks)) self.recharge_indices = list(range(1 + self.num_tasks, 1 + self.num_tasks + self.num_recharges)) # 系统参数 self.max_range = max_range self.w1 = w1 # 路径长度权重 self.w2 = w2 # 充电次数权重 # 预计算数据 self.dist_matrix = distance.cdist(self.points, self.points) self._build_voronoi() self.recharge_tree = cKDTree(self.points[self.recharge_indices]) def _build_voronoi(self): """构建充电站Voronoi图""" self.vor = Voronoi(self.points[self.recharge_indices]) self.voronoi_regions = {} for idx, region_id in enumerate(self.vor.point_region): vertices = self.vor.regions[region_id] if -1 not in vertices and vertices: self.voronoi_regions[self.recharge_indices[idx]] = [ self.vor.vertices[i] for i in vertices ] # ----------------- 核心算法流程 ----------------- def generate_initial_path(self) -> List[int]: """生成初始路径(Christofides改进版)""" path = [self.warehouse_idx] unvisited = set(self.task_indices) # 贪婪算法构造初始路径 current = self.warehouse_idx while unvisited: next_node = min(unvisited, key=lambda x: self.dist_matrix[current][x]) path.append(next_node) unvisited.remove(next_node) current = next_node path.append(self.warehouse_idx) return path def insert_recharges(self, path: List[int]) -> List[int]: """插入充电站主方法""" return self._rhc_optimize(path) def optimize_path(self, path: List[int]) -> List[int]: """路径优化方法""" return self._post_process(path) def validate_and_repair_full_path(self, path: List[int]) -> List[int]: """验证并修复完整路径""" repaired = [] energy = self.max_range i = 0 while i < len(path)-1: from_node = path[i] to_node = path[i+1] if self.dist_matrix[from_node][to_node] > energy: # 查找最近可达充电站 candidates = [ r for r in self.recharge_indices if self.dist_matrix[from_node][r] <= energy ] if not candidates: candidates = [self.warehouse_idx] best_recharge = min(candidates, key=lambda r: self.dist_matrix[r][to_node]) repaired.append(from_node) repaired.append(best_recharge) energy = self.max_range continue repaired.append(from_node) energy -= self.dist_matrix[from_node][to_node] i += 1 repaired.append(path[-1]) return repaired # ----------------- 优化核心方法 ----------------- def _rhc_optimize(self, path: List[int]) -> List[int]: """滚动时域控制优化""" optimized = [self.warehouse_idx] energy = self.max_range ptr = 0 while ptr < len(path)-1: window, is_last = self._get_rhc_window(path, ptr, energy) candidates = self._generate_candidates(optimized[-1], window, energy) if not candidates: raise RuntimeError("无可行路径") best = self._select_best_candidate(candidates) optimized.extend(best['path'][1:]) energy = best['remaining_energy'] ptr += best['progress'] return optimized def _get_rhc_window(self, path: List[int], ptr: int, energy: float) -> Tuple[List[int], bool]: """动态获取优化窗口""" max_window = min(5, len(path)-ptr-1) energy_used = 0 window_end = ptr for i in range(ptr, ptr + max_window): segment_energy = self.dist_matrix[path[i]][path[i+1]] if energy_used + segment_energy > energy * 0.8: break energy_used += segment_energy window_end = i+1 return path[ptr:window_end+1], (window_end >= len(path)-1) def _generate_candidates(self, current: int, window: List[int], energy: float) -> List[Dict]: """生成候选路径""" candidates = [] with ThreadPoolExecutor() as executor: # 不充电策略 futures = [executor.submit(self._simulate_no_charge, current, window, energy)] # 立即充电策略 futures.append(executor.submit(self._simulate_precharge, current, window, energy)) # 插入充电策略 for i in range(1, len(window)): futures.append(executor.submit( self._simulate_insert_charge, current, window, i, energy )) for f in futures: result = f.result() if result['valid']: candidates.append(result) return candidates def _simulate_no_charge(self, current: int, window: List[int], energy: float) -> Dict: """模拟不充电策略""" path = [current] remaining = energy safety_violations = 0 for node in window[1:]: cost = self.dist_matrix[path[-1]][node] if cost > remaining: return {'valid': False} # 安全余量检查 safe_energy = self._min_safe_return(node) if (remaining - cost) < safe_energy: safety_violations += 1 path.append(node) remaining -= cost return { 'type': 'no_charge', 'path': path, 'remaining_energy': remaining, 'safety': 1 - safety_violations/len(window), 'recharge_count': 0, 'progress': len(window)-1, 'valid': True } # ----------------- 工具方法 ----------------- def _path_cost(self, path: List[int]) -> float: """计算路径成本""" length = sum(self.dist_matrix[i][j] for i,j in zip(path, path[1:])) recharges = sum(1 for n in path if n in self.recharge_indices) return self.w1 * length + self.w2 * recharges def print_segment_distances(self, path: List[int]): """打印分段路径分析""" print("\n分段路径分析:") segments = self._split_segments(path) for i, seg in enumerate(segments, 1): dist = sum(self.dist_matrix[a][b] for a,b in zip(seg, seg[1:])) valid = "有效" if self._validate_segment(seg) else "无效" nodes = '→'.join(map(str, seg)) print(f"段{i}: 距离={dist:.2f} {valid} 节点序列: {nodes}") def debug_invalid_segments(self, path: List[int]): """调试无效路径段""" print("\n能量检查跟踪:") energy = self.max_range for i in range(1, len(path)): a, b = path[i-1], path[i] cost = self.dist_matrix[a][b] after_energy = energy - cost status = "✔ 有效" if after_energy >=0 else f"❌ 无效 (剩余{after_energy:.1f})" print(f"{a}→{b}: 需要{cost:.1f} 剩余前:{energy:.1f} | 状态: {status}") energy = self.max_range if b in self.recharge_indices else after_energy def visualize(self, path: List[int], title: str = "路径"): """可视化路径""" plt.figure(figsize=(10,8)) plt.scatter(self.points[self.task_indices,0], self.points[self.task_indices,1], c='b', label='任务点') plt.scatter(self.points[self.recharge_indices,0], self.points[self.recharge_indices,1], c='g', marker='s', label='充电站') plt.scatter(*self.warehouse, c='r', marker='*', s=200, label='仓库') path_points = self.points[path] plt.plot(path_points[:,0], path_points[:,1], 'r--', alpha=0.5) plt.title(f"{title}\n总成本: {self._path_cost(path):.1f}") plt.legend() plt.grid(True) plt.show() # ----------------- 测试用例 ----------------- def test_case(): np.random.seed(42) warehouse = (25, 25) tasks = np.random.rand(40, 2) * 50 recharges = np.random.rand(10, 2) * 50 max_range = 100 solver = EnhancedRechargeableTSP( tasks=tasks, recharges=recharges, warehouse=warehouse, max_range=max_range ) print("生成初始路径...") init_path = solver.generate_initial_path() solver.visualize(init_path, "Initial Path") print("\n插入充电站...") recharge_path = solver.insert_recharges(init_path) solver.visualize(recharge_path, "After Recharge Insertion") print("\n优化路径...") final_path = solver.optimize_path(recharge_path) # 最终修复验证 final_path = solver.validate_and_repair_full_path(final_path) solver.visualize(final_path, "Final Optimized Path") print(f"\n初始成本: {solver._path_cost(init_path):.1f}") print(f"插入充电后成本: {solver._path_cost(recharge_path):.1f}") print(f"最终优化成本: {solver._path_cost(final_path):.1f}") solver.print_segment_distances(final_path) solver.debug_invalid_segments(final_path) if __name__ == "__main__": test_case()帮我逐行解释一下代码
05-29
给出平均每家临街距离The plots, courtyards, and rooms include many variants. To be able to generate the variants, in the generation process the application of some rules could be altered by each other. The Hutong Neighbourhood Grammar is a parametric shape grammar. Focusing on the plan view, this grammar is defined in the Cartesian product of algebras: U12 × (V02 × V12 × V22). Shapes in algebras U12 include lines to represent the boundaries of Siheyuan plots and alleys. Labels are alphanumeric characters attached to a shape to represent additional information about the shape, which are employed to identify the available rules to be applied to the shape (Stiny, 1980). Shapes in algebras V02 include labelled points to represent the central point of a room plan and the midpoint of an edge of a room plan or a courtyard plan; shapes in algebras V12 include labelled line segments to represent the edge of Siheyuan plot, courtyard, and room; and shapes in algebras V22 include labelled plane segments to represent the plan of Siheyuan plot, courtyard, and room. In the rules, variables are introduced to control the generation of variants. For clarity, labels are used in the form of points, lines, or planes to mark the geometries in assistance in the derivation process, which are deleted once the generation is finished. The grammar describes the iteration of plots from Yuan to Qing dynasties and the generation of Siheyuan on the Qianlong Capital Map in a top-down fashion by dividing and iterating shapes representing the plots and courtyards and adding shapes representing rooms and gates. Corresponding to the historical planning of Hutong neighbourhoods, the generation process followed the steps: 1) generating the Hutong neighbourhood and dividing the neighbourhood into Siheyuan plots, 2) iterating Siheyuan plots, 3) dividing plots into courtyards and defining their types, 4) iterating courtyard, 5) defining room layout pattern and locating rooms and walls. Fig. 3 demonstrates the workflow of the grammar, the rules that could be applied in each step, and the shapes for this grammar. It is noted there are branches in some steps, which case variants of Siheyuan. In the grammar, the rectangles represent neighbourhoods, plots, sub-plots, courtyards, and rooms. The letters in the rectangles are employed to distinguish types of sub-plots and courtyards, and colours of rectangles are used to distinguish types of rooms. The black solid line segments represent the Siheyuan walls and the black dash line segments represent the courtyard walls. Fig. 3 Download: Download high-res image (905KB) Download: Download full-size image Fig. 3. The workflow and the shapes of the grammar. 3.1. Generation of Hutong neighbourhoods plan and Siheyuan plots As recorded in ancient literature such as Kao Gong Ji, an ideal Chinese city should be planned in a square shape to conform to the concept of “round sky and square earth (tian yuan di fang)”. When rebuilt by the Mongols in 1264, the scheme of Beijing was planned in a rectangular shape, which was close to the set forth by the Chinese philosophers. On this rectangular city plan, the urban neighbourhoods were also rectangular, enclosed by the south-north and east-west oriented streets, which caused an orthogonal grid system. This urban system passed through Yuan, Ming, and Qing dynasties and survived into the last century (Steinhardt, 1990). Zhao (1972) pointed out that, when Beijing was rebuilt in the Yuan dynasty, each neighbourhood's depth (length in south–north orientation) was planned to be 67.76 m and its width (length in east-west orientation) was 677.6 m. The Hutong alley between two adjacent neighbourhoods was 9.24 m wide. These key urban elements and dimensions are determining the initial shapes of the Hutong grammar. Each neighbourhood was averagely divided into ten plots aligned in an east–west orientation. We defined these neighbourhoods as “Type A Neighbourhood” (TAN). It is noted that some TANs were replanned in the Qing dynasty. Specifically, in the south-north orientation, two adjacent neighbourhoods were combined as a whole and were divided into three new neighbourhoods, with an alley between each of the two adjacent new neighbourhoods. We defined these new neighbourhoods as “Type B Neighborhoods” (TBN). The neighbourhoods planned in the Yuan dynasty (TAN) were divided into 67.67 m × 67.67 m Siheyuan plots. Although the development of neighbourhoods in the Qing dynasty changed the depth of both Siheyuan plots and neighbourhoods, the width remained. Two 67.76 × 677.6 m2 neighbourhood plans, represented by two rectangles are placed on a two-dimensional (XY) coordinate system, which is defined as the initial shape. On the XY coordinate system, the X-axis is defined as the east-west orientation and the Y-axis is defined as the north-south orientation, and the origin (0, 0) is on the southwest vertex of the south neighbourhood plan. Rule R1 describes the transformation of neighbourhoods from two TANs with one alley to three TBNs with two alleys. Variables associated with the rules are the neighbourhood depth (Nd) and the alley width (Aw), whose values are smaller than the original neighbourhood depth and alley width. Rules R2 describe the division of a TAN or a TBN neighbourhood into Siheyuan plots, represented by squares. Rules R1–R2 are shown in Fig. 4. Fig. 4 Download: Download high-res image (242KB) Download: Download full-size image Fig. 4. Rule R1 transforms TAN to TBN and Rule R2 divides a TAN or TBN into plots. 3.2. Siheyuan plots iteration It is noted the plots experienced iterations in various modes in Yuan, Ming, and Qing dynasties, which result in Siheyuan variants shown on the Qianlong Capital Map. 3.2.1. Division of plots into sub-plots A Siheyuan plot in the Yuan dynasty could be divided into two to four sub-plots in the east-west orientation. Each sub-plot could include several courtyards aligned in south-north orientation (Type A sub-plot, TASP) or empty space (Type B sub-plot, TBSP). We categorized six dividing patterns of a plot, by which the plot is divided into one or two sub-plot types. We found that the width of a TASP is normally around 12–35 m, while most TBSPs are close to or narrower than TASPs but wider than 5 m. Therefore, for simplification, we define the value of the parameter TASPw, the width of a TASP, which ranges from 12 to 35 m, and the value of the parameter TASPw, the width of a TBSP, from 5 to 12 m. The six division patterns of the plots are defined as Rules R3a to R3f respectively. In Fig. 5, Rule R3a divides a plot into two TASPs, and R3b divides a plot into three TASPs. Rule R3c and R3e describe three modes of dividing a plot into two TASPs and one TBSP. The difference between them is the location of the TBSP, which are in the east, west, and mid respectively. Rule 3f describes that a plot is divided into four sub-plots: two TASPs in the mid and a TBSP in the east and west respectively. In these rules, north-south orientated line segments are inserted into the plots to generate new rectangles on the XY plane to represent sub-plots. To locate these line segments, the variables, width of TASP (TASPw) and width of TBSP (TBSPw), are used for description. Fig. 5 Download: Download high-res image (479KB) Download: Download full-size image Fig. 5. Rule R3 divides a plot into sub-plots. Fig. 6 Download: Download high-res image (120KB) Download: Download full-size image Fig. 6. Rule R4 combines sub-plots to become a new sub-plot. 3.2.2. Sub-plot combination Some TASPs and TBSPs were combined to recreate new sub-plots. It is noted some sub-plots were additionally developed into new forms. As the historical material (E, 1739) recorded, to relieve the stress of the increase of the population of Beijing in the Qing dynasty, the government advocated using empty spaces to construct dwellings. It is noted that, since the width of TBSPs was usually too narrow to construct Siheyuan and many Siheyuans on TASPs were dilapidated or derelict due to the change of dynasty, some TASPs and TBSPs were combined to recreate new sub-plots. According to Liu's (2019) investigation of selected Siheyuan examples shown on the Qianlong Capital Map, we inferred the principles that one TASP could be combined with one adjacent TBSP to become a combined sub-plot, called Type AB sub-plot (TABSP). Then the TABSP could be used to construct a Siheyuan with a side courtyard. Since the TABSPs are wider than other types, it enables a Siheyuan to be built on the plot with two courtyards in the east-west orientation. Normally, on a TABSP, a set of courtyards are aligned in a south-north orientation, with a side courtyard next to these courtyards in parallel and connecting the south and north sub-plot boundaries. Rule R4a and R4b describe how a TASP and a TBSP combine to become a TABSP, as shown in Fig. 6. The difference between them is the locations of the TASP and the TBSP. On the right side of the rule, the line segment between the two sub-plots is removed and two rectangles are merged to become a new rectangle, representing the TABSP. The summation of the variables TASPw and TBSPw is the width of TABSP (TABSPw). 3.2.3. Sub-plot disaggregation and recombination The Siheyuan housings with a side courtyard were rare and usually belonged to upper-class owners in ancient times. In many cases, the TABSP disaggregates into many smaller sub-plots, called a Type ABd sub-plot (TABdSP), on which a one-courtyard Siheyuan is constructed. The disaggregation may also happen to some TASPs, whose disaggregated sub-plot is called a Type Ad sub-plot (TAdSP). Two TABdSPs or two TAdSPs adjacent in north-south orientation may be recombined to become a new sub-plot, called Type Ad2 sub-plot (TAd2SP) and Type ABd2 sub-plot (TABd2SP) respectively. In the disaggregation, the number of generated TAdSPs or TABdSPs in a north-south orientation, constrained by the depth of the neighbourhood, is 3 or 4 in most cases. For simplification, we assume it is 4 in TANs and 3 in TBNs. The number in the east-west orientation, constrained by the width of the sub-plot, is 2 or 3 mostly. We define it as 3 if the width is more than 20 m, otherwise 2. These Siheyuans built on disaggregated sub-plots are clustered, in which some Siheyuans are not on street. To create access to these Siheyuans, the west and east edges of disaggregated sub-plots that are adjacent in the east-west orientation, shrink to give space to generate a small south-north oriented alley. The alley might, or might not, cross the neighbourhood to connect the alleys on the south and north side of the neighbourhood. To simplify, we assume that an alley crosses the neighbourhood in this grammar. For 2 × 3 and 2 × 4 disaggregated sub-plots, an alley is generated by shrinking the sub-plot edges. 3 × 3 and 3 × 4 cases could be considered subplots in three rows aligned in parallel in the north-south orientation. For the pattern of three rows, it is unusual to generate an alley between every two adjacent rows. Alternatively, one small alley is generated between two rows in the way the same as the rules of 2 × 3 or 2 × 4 disaggregated sub-plots. For the third row of sub-plots that are not adjacent to the alley, if there are four sub-plots in the rest row, they will recombine to become two TAd2SPs/TABd2SPs, and if there are three sub-plots, two adjacent sub-plots will recombine to become a TAd2SPs/TABd2SP, both of which ensure each sub-plot to have access to the urban fabric. In the shrinkage, the movement distance of each sub-plot edge is slightly different, which makes the alley geometrically irregular. Except for the above disaggregation, there was another mode that a TASP separates into two sub-plots. Historically, a TASP was usually used to construct a three-courtyard Siheyuan or a four-courtyard Siheyuan, whose courtyards were aligned in a row in the north-south orientation. In this mode, the TASP separates by dividing the boundary of the second and the third courtyard, in which the original type of each courtyard remains. We call the separated sub-plot on the south side as the Type ASS sub-plot (TASSSP) and the one on the north as the Type ASN sub-plot (TASNSP). Rule R5 defines the disaggregation of a TASP and a TABSP. Considering the variants caused by neighbourhood depth and width, the rule includes variants Rule R5a-d. Line segments are introduced to indicate the edges of new sub-plots after the disaggregation of a TASP or a TABSP. In Rule R5a, two east-west orientated line segments and one north-south line segment are inserted to divide the TASP into 2 × 3 TAdSPs. In Rule R5b, there is one more east-west orientated line segment, enabling the generation of 2 × 4 TAdSPs. In Rule R5c, two east-west segments and two north-south segments are inserted to divide the plot into 3 × 3 TABdSPs. And in Rule R5d, one more east-west orientated segment than in R5c is introduced to divide the plot into 3 × 4 TABdSPs. The location of each line segment is important since they determine the size of each generated subplot. To describe them, including the inserted north-south orientated line segments and the line segments representing the east and west edges of the plot, the distances between each two of them are defined as w1, w2, and w3 from west to east. And the distances between each two east-west orientated line segments, including the ones representing the north and south edges of the plot and the inserted ones, are defined as d1, d2, d3, and d4 from south to north. These distances are variables to control the disaggregation. Rule R6 describes the generation of a small alley between two north-south orientated rows, which includes two variations. In Rule R6a, six TAdSPs or TABdSPs are clustered in two north-south orientated rows (2 × 3 mode) on the left of the rule. The shared edges of each two adjacent sub-plots in the west and east are labelled as thick line segments. On the right of the rule, for each segment, two copies of it are generated by offsetting itself in the east and west in the mirror using the segment as an axis, which are the boundaries between sub-plots and the small alley edge fragment. The distances describing the width of the small alley fragments (SAw1, SAw2, and SAw3) are variables in this rule. In Rule R6b, there are eight TAdSPs or TABdSPs clustered in two north-south orientated rows (2 × 4 mode). The generation of each small alley fragment is the same. The small alley fragments may move in the east-west orientation. Rule R7 is introduced to simulate the movement. In Rule R7, each pair of line segments moves in the east-west orientation using its mirror axis as an indicator. The displacement is a variable, (SAm1, SAm2, SAm3, and SAm4) in the east-west orientation, and the movement toward the east is positive (+x) and toward the west is negative (-x). The same as Rule R6, Rule R7 has two variations corresponding to the two clustered sub-plot modes. Rule R8 introduces the recombination of two TABdSPs or two TAdSPs, which normally happen to sub-plots that have no access to the urban fabric. In this rule, the shared edge of the two sub-plots adjacent in north-south orientation is removed and the two sub-plots are merged. Considering the disaggregation patterns and sub-plots locations, it includes three variations. Rule R8a-R8c. Rule R9 describes the division of a TASP into a TASNSP and a TASSSP. In the rule, an east-west line segment is inserted into the rectangle, whose variable is the depth of the TASSSP. Rules R5-R9 are shown in Fig. 7. Fig. 7 Download: Download high-res image (722KB) Download: Download full-size image Fig. 7. Rules R5-R9 define the disaggregation and recombination of sub-plots.
最新发布
06-25
import open3d as o3d import numpy as np import os import laspy # 导入 laspy 库 def interpolate_missing_points(pcd, k=10, search_radius=0.1): """ 基于KDTree的近邻插值方法补全缺失的点云 :param pcd: 输入点云 :param k: 近邻点数量 :param search_radius: 搜索半径 :return: 补全后的点云 """ pcd_tree = o3d.geometry.KDTreeFlann(pcd) points = np.asarray(pcd.points) colors = np.asarray(pcd.colors) if np.asarray(pcd.colors).size > 0 else None for i in range(len(points)): _, idx, _ = pcd_tree.search_radius_vector_3d(points[i], search_radius) if len(idx) < k: # 如果近邻点数量不足,进行插值 near_points = points[idx] if colors is not None: near_colors = colors[idx] else: near_colors = None new_point = np.mean(near_points, axis=0) points[i] = new_point if colors is not None: new_color = np.mean(near_colors, axis=0) colors[i] = new_color pcd.points = o3d.utility.Vector3dVector(points) if colors is not None: pcd.colors = o3d.utility.Vector3dVector(colors) return pcd def process_point_cloud(pcd_file_path, output_dir="output"): os.makedirs(output_dir, exist_ok=True) # 1. 数据读取与格式转换(适配 PCD) print("=== 数据读取与格式转换 ===") try: pcd = o3d.io.read_point_cloud(pcd_file_path) # 直接读取 PCD 文件 print(f"成功读取点云,点数:{len(pcd.points)}") # 检查颜色信息是否存在 if np.asarray(pcd.colors).size == 0: print("警告:PCD 文件未包含颜色信息,显示可能为白色") else: print("点云包含颜色信息,正常显示") # 先进行点云插值补全缺失点 print("=== 点云插值补全缺失点 ===") pcd = interpolate_missing_points(pcd, k=10, search_radius=0.1) # 添加可视化代码,显示插值补全后的点云 o3d.visualization.draw_geometries([pcd]) except Exception as e: print(f"数据读取失败:{e}") raise # 2. 混合滤波处理(统计滤波+半径滤波) print("=== 混合滤波处理 ===") # 统计滤波:移除离群点(基于邻近点数量和标准差) pcd_filtered = pcd.remove_statistical_outlier(nb_neighbors=10, std_ratio=3.0)[0] # 半径滤波:移除低密度区域点(基于球形邻域内点数量) pcd_filtered = pcd_filtered.remove_radius_outlier(nb_points=10, radius=0.03)[0] print(f"滤波后点数:{len(pcd_filtered.points)}") # 3. 平面拟合 print("=== 平面拟合 ===") plane_model, inliers = pcd_filtered.segment_plane(distance_threshold=0.005, ransac_n=3, num_iterations=1000) [a, b, c, d] = plane_model print(f"平面方程: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0") print(f"平面内点的数量: {len(inliers)}") # 4. 法向量估计 print("=== 法向量估计 ===") pcd_filtered.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)) # 5. 全局体素下采样(降低点云密度) print("=== 全局体素下采样 ===") voxel_size = 0.0001 # 体素大小(可根据需求调整,数值越大下采样越剧烈) pcd_down = pcd_filtered.voxel_down_sample(voxel_size) # 保存为 PCD 格式 pcd_path = os.path.join(output_dir, "final_processed_pcd.pcd") o3d.io.write_point_cloud(pcd_path, pcd_down) print(f"下采样后点数:{len(pcd_down.points)},已保存至 {pcd_path}") # 将点云转换为 laspy 格式并保存 las_path = os.path.join(output_dir, "final_processed.las") points = np.asarray(pcd_down.points) header = laspy.LasHeader(point_format=3, version="1.4") # 设置点格式和版本 header.point_count = len(points) las = laspy.LasData(header) las.x = points[:, 0] las.y = points[:, 1] las.z = points[:, 2] # 如果点云有颜色信息,保存颜色 if np.asarray(pcd_down.colors).size > 0: colors = np.asarray(pcd_down.colors) * 255 las.red = colors[:, 0].astype(np.uint16) las.green = colors[:, 1].astype(np.uint16) las.blue = colors[:, 2].astype(np.uint16) las.write(las_path) print(f"已将点云保存为 {las_path}") # 可视化处理后的点云 o3d.visualization.draw_geometries([pcd_down]) if __name__ == "__main__": # 指定文件路径(Windows 路径直接使用反斜杠,无需转义) PCD_FILE_PATH = r"D:\ShuJu\SLAM\hebing1.pcd" # 你的 PCD 文件路径 OUTPUT_DIR = "preprocessed_data" # 输出目录(自动创建) process_point_cloud(PCD_FILE_PATH, OUTPUT_DIR)。可以优化一下代码吗,我的点云数据是凉亭,这个代码运行出来的效果不好
05-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值