kinemotion 0.11.4__py3-none-any.whl → 0.11.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kinemotion might be problematic. Click here for more details.
- kinemotion/cmj/cli.py +10 -50
- kinemotion/core/cli_utils.py +80 -1
- kinemotion/dropjump/cli.py +4 -67
- {kinemotion-0.11.4.dist-info → kinemotion-0.11.5.dist-info}/METADATA +1 -1
- {kinemotion-0.11.4.dist-info → kinemotion-0.11.5.dist-info}/RECORD +8 -8
- {kinemotion-0.11.4.dist-info → kinemotion-0.11.5.dist-info}/WHEEL +0 -0
- {kinemotion-0.11.4.dist-info → kinemotion-0.11.5.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.11.4.dist-info → kinemotion-0.11.5.dist-info}/licenses/LICENSE +0 -0
kinemotion/cmj/cli.py
CHANGED
|
@@ -10,16 +10,15 @@ from typing import Any
|
|
|
10
10
|
import click
|
|
11
11
|
import numpy as np
|
|
12
12
|
|
|
13
|
-
from ..core.auto_tuning import (
|
|
14
|
-
AnalysisParameters as AutoTunedParams,
|
|
15
|
-
)
|
|
16
13
|
from ..core.auto_tuning import (
|
|
17
14
|
QualityPreset,
|
|
18
15
|
analyze_video_sample,
|
|
19
16
|
auto_tune_parameters,
|
|
20
17
|
)
|
|
21
18
|
from ..core.cli_utils import (
|
|
19
|
+
apply_expert_param_overrides,
|
|
22
20
|
determine_initial_confidence,
|
|
21
|
+
print_auto_tuned_params,
|
|
23
22
|
smooth_landmark_sequence,
|
|
24
23
|
track_all_frames,
|
|
25
24
|
)
|
|
@@ -299,50 +298,6 @@ def cmj_analyze( # NOSONAR(S107) - Click CLI requires individual parameters for
|
|
|
299
298
|
sys.exit(1)
|
|
300
299
|
|
|
301
300
|
|
|
302
|
-
def _apply_expert_param_overrides(
|
|
303
|
-
params: AutoTunedParams, expert_params: AnalysisParameters
|
|
304
|
-
) -> AutoTunedParams:
|
|
305
|
-
"""Apply expert parameter overrides to auto-tuned parameters."""
|
|
306
|
-
if expert_params.smoothing_window is not None:
|
|
307
|
-
params.smoothing_window = expert_params.smoothing_window
|
|
308
|
-
if expert_params.velocity_threshold is not None:
|
|
309
|
-
params.velocity_threshold = expert_params.velocity_threshold
|
|
310
|
-
if expert_params.min_contact_frames is not None:
|
|
311
|
-
params.min_contact_frames = expert_params.min_contact_frames
|
|
312
|
-
if expert_params.visibility_threshold is not None:
|
|
313
|
-
params.visibility_threshold = expert_params.visibility_threshold
|
|
314
|
-
return params
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
def _print_auto_tuned_params(
|
|
318
|
-
video: VideoProcessor,
|
|
319
|
-
quality_preset: QualityPreset,
|
|
320
|
-
params: AutoTunedParams,
|
|
321
|
-
countermovement_threshold: float,
|
|
322
|
-
) -> None:
|
|
323
|
-
"""Print auto-tuned parameters in verbose mode."""
|
|
324
|
-
click.echo("\n" + "=" * 60, err=True)
|
|
325
|
-
click.echo("AUTO-TUNED PARAMETERS", err=True)
|
|
326
|
-
click.echo("=" * 60, err=True)
|
|
327
|
-
click.echo(f"Video FPS: {video.fps:.2f}", err=True)
|
|
328
|
-
click.echo(f"Quality preset: {quality_preset.value}", err=True)
|
|
329
|
-
click.echo("\nSelected parameters:", err=True)
|
|
330
|
-
click.echo(f" smoothing_window: {params.smoothing_window}", err=True)
|
|
331
|
-
click.echo(f" polyorder: {params.polyorder}", err=True)
|
|
332
|
-
click.echo(f" velocity_threshold: {params.velocity_threshold:.4f}", err=True)
|
|
333
|
-
click.echo(
|
|
334
|
-
f" countermovement_threshold: {countermovement_threshold:.4f}", err=True
|
|
335
|
-
)
|
|
336
|
-
click.echo(f" min_contact_frames: {params.min_contact_frames}", err=True)
|
|
337
|
-
click.echo(f" visibility_threshold: {params.visibility_threshold}", err=True)
|
|
338
|
-
click.echo(f" detection_confidence: {params.detection_confidence}", err=True)
|
|
339
|
-
click.echo(f" tracking_confidence: {params.tracking_confidence}", err=True)
|
|
340
|
-
click.echo(f" outlier_rejection: {params.outlier_rejection}", err=True)
|
|
341
|
-
click.echo(f" bilateral_filter: {params.bilateral_filter}", err=True)
|
|
342
|
-
click.echo(f" use_curvature: {params.use_curvature}", err=True)
|
|
343
|
-
click.echo("=" * 60 + "\n", err=True)
|
|
344
|
-
|
|
345
|
-
|
|
346
301
|
def _get_foot_position(frame_landmarks: dict | None, last_position: float) -> float:
|
|
347
302
|
"""Extract average foot position from frame landmarks."""
|
|
348
303
|
if not frame_landmarks:
|
|
@@ -419,7 +374,7 @@ def _process_single(
|
|
|
419
374
|
landmarks_sequence, video.fps, video.frame_count
|
|
420
375
|
)
|
|
421
376
|
params = auto_tune_parameters(characteristics, quality_preset)
|
|
422
|
-
params =
|
|
377
|
+
params = apply_expert_param_overrides(params, expert_params)
|
|
423
378
|
|
|
424
379
|
# Calculate countermovement threshold (FPS-adjusted)
|
|
425
380
|
# Base: +0.015 at 30fps (POSITIVE for downward motion in normalized coords)
|
|
@@ -429,8 +384,13 @@ def _process_single(
|
|
|
429
384
|
|
|
430
385
|
# Show parameters if verbose
|
|
431
386
|
if verbose:
|
|
432
|
-
|
|
433
|
-
video,
|
|
387
|
+
print_auto_tuned_params(
|
|
388
|
+
video,
|
|
389
|
+
quality_preset,
|
|
390
|
+
params,
|
|
391
|
+
extra_params={
|
|
392
|
+
"countermovement_threshold": countermovement_threshold
|
|
393
|
+
},
|
|
434
394
|
)
|
|
435
395
|
|
|
436
396
|
# Apply smoothing
|
kinemotion/core/cli_utils.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Any, Protocol
|
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
-
from .auto_tuning import AutoTunedParams, QualityPreset
|
|
7
|
+
from .auto_tuning import AutoTunedParams, QualityPreset, VideoCharacteristics
|
|
8
8
|
from .pose import PoseTracker
|
|
9
9
|
from .smoothing import smooth_landmarks, smooth_landmarks_advanced
|
|
10
10
|
from .video_io import VideoProcessor
|
|
@@ -15,6 +15,10 @@ class ExpertParameters(Protocol):
|
|
|
15
15
|
|
|
16
16
|
detection_confidence: float | None
|
|
17
17
|
tracking_confidence: float | None
|
|
18
|
+
smoothing_window: int | None
|
|
19
|
+
velocity_threshold: float | None
|
|
20
|
+
min_contact_frames: int | None
|
|
21
|
+
visibility_threshold: float | None
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
def determine_initial_confidence(
|
|
@@ -79,6 +83,81 @@ def track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list,
|
|
|
79
83
|
return frames, landmarks_sequence
|
|
80
84
|
|
|
81
85
|
|
|
86
|
+
def apply_expert_param_overrides(
|
|
87
|
+
params: AutoTunedParams, expert_params: ExpertParameters
|
|
88
|
+
) -> AutoTunedParams:
|
|
89
|
+
"""Apply expert parameter overrides to auto-tuned parameters.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
params: Auto-tuned parameters
|
|
93
|
+
expert_params: Expert overrides
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Modified params object (mutated in place)
|
|
97
|
+
"""
|
|
98
|
+
if expert_params.smoothing_window is not None:
|
|
99
|
+
params.smoothing_window = expert_params.smoothing_window
|
|
100
|
+
if expert_params.velocity_threshold is not None:
|
|
101
|
+
params.velocity_threshold = expert_params.velocity_threshold
|
|
102
|
+
if expert_params.min_contact_frames is not None:
|
|
103
|
+
params.min_contact_frames = expert_params.min_contact_frames
|
|
104
|
+
if expert_params.visibility_threshold is not None:
|
|
105
|
+
params.visibility_threshold = expert_params.visibility_threshold
|
|
106
|
+
return params
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def print_auto_tuned_params(
|
|
110
|
+
video: VideoProcessor,
|
|
111
|
+
quality_preset: QualityPreset,
|
|
112
|
+
params: AutoTunedParams,
|
|
113
|
+
characteristics: VideoCharacteristics | None = None,
|
|
114
|
+
extra_params: dict[str, Any] | None = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""Print auto-tuned parameters in verbose mode.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
video: Video processor
|
|
120
|
+
quality_preset: Quality preset
|
|
121
|
+
params: Auto-tuned parameters
|
|
122
|
+
characteristics: Optional video characteristics (for tracking quality display)
|
|
123
|
+
extra_params: Optional extra parameters to display (e.g., countermovement_threshold)
|
|
124
|
+
"""
|
|
125
|
+
click.echo("\n" + "=" * 60, err=True)
|
|
126
|
+
click.echo("AUTO-TUNED PARAMETERS", err=True)
|
|
127
|
+
click.echo("=" * 60, err=True)
|
|
128
|
+
click.echo(f"Video FPS: {video.fps:.2f}", err=True)
|
|
129
|
+
|
|
130
|
+
if characteristics:
|
|
131
|
+
click.echo(
|
|
132
|
+
f"Tracking quality: {characteristics.tracking_quality} "
|
|
133
|
+
f"(avg visibility: {characteristics.avg_visibility:.2f})",
|
|
134
|
+
err=True,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
click.echo(f"Quality preset: {quality_preset.value}", err=True)
|
|
138
|
+
click.echo("\nSelected parameters:", err=True)
|
|
139
|
+
click.echo(f" smoothing_window: {params.smoothing_window}", err=True)
|
|
140
|
+
click.echo(f" polyorder: {params.polyorder}", err=True)
|
|
141
|
+
click.echo(f" velocity_threshold: {params.velocity_threshold:.4f}", err=True)
|
|
142
|
+
|
|
143
|
+
# Print extra parameters if provided
|
|
144
|
+
if extra_params:
|
|
145
|
+
for key, value in extra_params.items():
|
|
146
|
+
if isinstance(value, float):
|
|
147
|
+
click.echo(f" {key}: {value:.4f}", err=True)
|
|
148
|
+
else:
|
|
149
|
+
click.echo(f" {key}: {value}", err=True)
|
|
150
|
+
|
|
151
|
+
click.echo(f" min_contact_frames: {params.min_contact_frames}", err=True)
|
|
152
|
+
click.echo(f" visibility_threshold: {params.visibility_threshold}", err=True)
|
|
153
|
+
click.echo(f" detection_confidence: {params.detection_confidence}", err=True)
|
|
154
|
+
click.echo(f" tracking_confidence: {params.tracking_confidence}", err=True)
|
|
155
|
+
click.echo(f" outlier_rejection: {params.outlier_rejection}", err=True)
|
|
156
|
+
click.echo(f" bilateral_filter: {params.bilateral_filter}", err=True)
|
|
157
|
+
click.echo(f" use_curvature: {params.use_curvature}", err=True)
|
|
158
|
+
click.echo("=" * 60 + "\n", err=True)
|
|
159
|
+
|
|
160
|
+
|
|
82
161
|
def smooth_landmark_sequence(landmarks_sequence: list, params: AutoTunedParams) -> list:
|
|
83
162
|
"""Apply smoothing to landmark sequence.
|
|
84
163
|
|
kinemotion/dropjump/cli.py
CHANGED
|
@@ -12,17 +12,15 @@ import click
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
|
|
14
14
|
from ..api import VideoConfig, VideoResult, process_videos_bulk
|
|
15
|
-
from ..core.auto_tuning import (
|
|
16
|
-
AnalysisParameters as AutoTunedParams,
|
|
17
|
-
)
|
|
18
15
|
from ..core.auto_tuning import (
|
|
19
16
|
QualityPreset,
|
|
20
|
-
VideoCharacteristics,
|
|
21
17
|
analyze_video_sample,
|
|
22
18
|
auto_tune_parameters,
|
|
23
19
|
)
|
|
24
20
|
from ..core.cli_utils import (
|
|
21
|
+
apply_expert_param_overrides,
|
|
25
22
|
determine_initial_confidence,
|
|
23
|
+
print_auto_tuned_params,
|
|
26
24
|
smooth_landmark_sequence,
|
|
27
25
|
track_all_frames,
|
|
28
26
|
)
|
|
@@ -260,67 +258,6 @@ def dropjump_analyze( # NOSONAR(S107) - Click CLI requires individual parameter
|
|
|
260
258
|
)
|
|
261
259
|
|
|
262
260
|
|
|
263
|
-
def _apply_expert_param_overrides(
|
|
264
|
-
params: AutoTunedParams, expert_params: AnalysisParameters
|
|
265
|
-
) -> AutoTunedParams:
|
|
266
|
-
"""Apply expert parameter overrides to auto-tuned parameters.
|
|
267
|
-
|
|
268
|
-
Args:
|
|
269
|
-
params: Auto-tuned parameters
|
|
270
|
-
expert_params: Expert overrides
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
Modified params object (mutated in place)
|
|
274
|
-
"""
|
|
275
|
-
if expert_params.smoothing_window is not None:
|
|
276
|
-
params.smoothing_window = expert_params.smoothing_window
|
|
277
|
-
if expert_params.velocity_threshold is not None:
|
|
278
|
-
params.velocity_threshold = expert_params.velocity_threshold
|
|
279
|
-
if expert_params.min_contact_frames is not None:
|
|
280
|
-
params.min_contact_frames = expert_params.min_contact_frames
|
|
281
|
-
if expert_params.visibility_threshold is not None:
|
|
282
|
-
params.visibility_threshold = expert_params.visibility_threshold
|
|
283
|
-
return params
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def _print_auto_tuned_params(
|
|
287
|
-
video: VideoProcessor,
|
|
288
|
-
characteristics: VideoCharacteristics,
|
|
289
|
-
quality_preset: QualityPreset,
|
|
290
|
-
params: AutoTunedParams,
|
|
291
|
-
) -> None:
|
|
292
|
-
"""Print auto-tuned parameters in verbose mode.
|
|
293
|
-
|
|
294
|
-
Args:
|
|
295
|
-
video: Video processor
|
|
296
|
-
characteristics: Video characteristics
|
|
297
|
-
quality_preset: Quality preset
|
|
298
|
-
params: Auto-tuned parameters
|
|
299
|
-
"""
|
|
300
|
-
click.echo("\n" + "=" * 60, err=True)
|
|
301
|
-
click.echo("AUTO-TUNED PARAMETERS", err=True)
|
|
302
|
-
click.echo("=" * 60, err=True)
|
|
303
|
-
click.echo(f"Video FPS: {video.fps:.2f}", err=True)
|
|
304
|
-
click.echo(
|
|
305
|
-
f"Tracking quality: {characteristics.tracking_quality} "
|
|
306
|
-
f"(avg visibility: {characteristics.avg_visibility:.2f})",
|
|
307
|
-
err=True,
|
|
308
|
-
)
|
|
309
|
-
click.echo(f"Quality preset: {quality_preset.value}", err=True)
|
|
310
|
-
click.echo("\nSelected parameters:", err=True)
|
|
311
|
-
click.echo(f" smoothing_window: {params.smoothing_window}", err=True)
|
|
312
|
-
click.echo(f" polyorder: {params.polyorder}", err=True)
|
|
313
|
-
click.echo(f" velocity_threshold: {params.velocity_threshold:.4f}", err=True)
|
|
314
|
-
click.echo(f" min_contact_frames: {params.min_contact_frames}", err=True)
|
|
315
|
-
click.echo(f" visibility_threshold: {params.visibility_threshold}", err=True)
|
|
316
|
-
click.echo(f" detection_confidence: {params.detection_confidence}", err=True)
|
|
317
|
-
click.echo(f" tracking_confidence: {params.tracking_confidence}", err=True)
|
|
318
|
-
click.echo(f" outlier_rejection: {params.outlier_rejection}", err=True)
|
|
319
|
-
click.echo(f" bilateral_filter: {params.bilateral_filter}", err=True)
|
|
320
|
-
click.echo(f" use_curvature: {params.use_curvature}", err=True)
|
|
321
|
-
click.echo("=" * 60 + "\n", err=True)
|
|
322
|
-
|
|
323
|
-
|
|
324
261
|
def _extract_positions_and_visibilities(
|
|
325
262
|
smoothed_landmarks: list,
|
|
326
263
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
@@ -458,11 +395,11 @@ def _process_single(
|
|
|
458
395
|
landmarks_sequence, video.fps, video.frame_count
|
|
459
396
|
)
|
|
460
397
|
params = auto_tune_parameters(characteristics, quality_preset)
|
|
461
|
-
params =
|
|
398
|
+
params = apply_expert_param_overrides(params, expert_params)
|
|
462
399
|
|
|
463
400
|
# Show parameters if verbose
|
|
464
401
|
if verbose:
|
|
465
|
-
|
|
402
|
+
print_auto_tuned_params(video, quality_preset, params, characteristics)
|
|
466
403
|
|
|
467
404
|
# Apply smoothing
|
|
468
405
|
smoothed_landmarks = smooth_landmark_sequence(landmarks_sequence, params)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.5
|
|
4
4
|
Summary: Video-based kinematic analysis for athletic performance
|
|
5
5
|
Project-URL: Homepage, https://github.com/feniix/kinemotion
|
|
6
6
|
Project-URL: Repository, https://github.com/feniix/kinemotion
|
|
@@ -3,25 +3,25 @@ kinemotion/api.py,sha256=M6KqzdspOvtJ9Wl555HGe3ITzvRFhPK4xeqcX7IA98s,31463
|
|
|
3
3
|
kinemotion/cli.py,sha256=cqYV_7URH0JUDy1VQ_EDLv63FmNO4Ns20m6s1XAjiP4,464
|
|
4
4
|
kinemotion/cmj/__init__.py,sha256=Ynv0-Oco4I3Y1Ubj25m3h9h2XFqeNwpAewXmAYOmwfU,127
|
|
5
5
|
kinemotion/cmj/analysis.py,sha256=4HYGn4VDIB6oExAees-VcPfpNgWOltpgwjyNTU7YAb4,18263
|
|
6
|
-
kinemotion/cmj/cli.py,sha256=
|
|
6
|
+
kinemotion/cmj/cli.py,sha256=3CQPJOC3N00dFz_mrEgSHq9pIA-RyOynZoNUIk8RzD0,17040
|
|
7
7
|
kinemotion/cmj/debug_overlay.py,sha256=ELrSYQ9LmLV81bJS5w9i2c4VwRS0EYAUnMehMHU7VGc,18724
|
|
8
8
|
kinemotion/cmj/joint_angles.py,sha256=8ucpDGPvbt4iX3tx9eVxJEUv0laTm2Y58_--VzJCogE,9113
|
|
9
9
|
kinemotion/cmj/kinematics.py,sha256=Xl_PlC2OqMoA-zOc3SRB_GqI0AgLlJol5FTPe5J_qLc,7573
|
|
10
10
|
kinemotion/core/__init__.py,sha256=3yzDhb5PekDNjydqrs8aWGneUGJBt-lB0SoB_Y2FXqU,1010
|
|
11
11
|
kinemotion/core/auto_tuning.py,sha256=cvmxUI-CbahpOJQtR2r5jOx4Q6yKPe3DO1o15hOQIdw,10508
|
|
12
|
-
kinemotion/core/cli_utils.py,sha256=
|
|
12
|
+
kinemotion/core/cli_utils.py,sha256=iI1bHZxgbzxW5-2X3tTHEy2mqR49o3oIlwFptZ6gCco,6726
|
|
13
13
|
kinemotion/core/filtering.py,sha256=f-m-aA59e4WqE6u-9MA51wssu7rI-Y_7n1cG8IWdeRQ,11241
|
|
14
14
|
kinemotion/core/pose.py,sha256=Wfd1RR-2ZznYpWeQUbySwcV3mvReqn8n3XO6S7pGq4M,8390
|
|
15
15
|
kinemotion/core/smoothing.py,sha256=FON4qKtsSp1-03GnJrDkEUAePaACn4QPMJF0eTIYqR0,12925
|
|
16
16
|
kinemotion/core/video_io.py,sha256=z8Z0qbNaKbcdB40KnbNOBMzab3BbgnhBxp-mUBYeXgM,6577
|
|
17
17
|
kinemotion/dropjump/__init__.py,sha256=yc1XiZ9vfo5h_n7PKVSiX2TTgaIfGL7Y7SkQtiDZj_E,838
|
|
18
18
|
kinemotion/dropjump/analysis.py,sha256=HfJt2t9IsMBiBUz7apIzdxbRH9QqzlFnDVVWcKhU3ow,23291
|
|
19
|
-
kinemotion/dropjump/cli.py,sha256
|
|
19
|
+
kinemotion/dropjump/cli.py,sha256=-iBgHNwW_dijHe6_JIEGSBUzvFb6tZV0aopbPd-9jC8,22402
|
|
20
20
|
kinemotion/dropjump/debug_overlay.py,sha256=GMo-jCl5OPIv82uPxDbBVI7CsAMwATTvxZMeWfs8k8M,8701
|
|
21
21
|
kinemotion/dropjump/kinematics.py,sha256=RM_O8Kdc6aEiPIu_99N4cu-4EhYSQxtBGASJF_dmQaU,19081
|
|
22
22
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
kinemotion-0.11.
|
|
24
|
-
kinemotion-0.11.
|
|
25
|
-
kinemotion-0.11.
|
|
26
|
-
kinemotion-0.11.
|
|
27
|
-
kinemotion-0.11.
|
|
23
|
+
kinemotion-0.11.5.dist-info/METADATA,sha256=gsLG_2Z3qbeG2UpWqp7mfyqWDXj2SpwrsXG5cgh8oEo,18990
|
|
24
|
+
kinemotion-0.11.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
25
|
+
kinemotion-0.11.5.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
26
|
+
kinemotion-0.11.5.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
27
|
+
kinemotion-0.11.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|