When the convolution process is performed with [-1, 1], the rising part becomes a value exceeding 0 and the falling part becomes less than 0. Using this, an ascending flag and a descending flag are created. The point where the flags overlap is the apex.

```
import math
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import PIL.Image
import random
import scipy.ndimage
#One-dimensional vertex detection
x = np.array([0, 0, 1, 0, 0])
print("x", x, "Target array")
conv1 = np.convolve(x, [1, -1], mode="full")
print("conv1", conv1, "[-1, 1]Make marks for rising and falling points with the filter of")
flag1 = (conv1 > 0).astype(int)
print("flag1", flag1, "Flag of rising point")
flag2 = (conv1 <= 0).astype(int)
print("flag2", flag2, "Flag of descent point")
flag1 = flag1[:-1]
print("flag1", flag1, "Cut one end of the rising flag to match the length")
flag2 = flag2[1:]
print("flag2", flag2, "Remove one head of the descending flag to match the apex and align the length.")
flag3 = flag1 & flag2
print("flag3", flag3, "The result of ANDing the ascending flag and descending flag is the apex.")
```

```
x [0 0 1 0 0]Target array
conv1 [ 0 0 1 -1 0 0] [-1, 1]Make marks for rising and falling points with the filter of
flag1 [0 0 1 0 0 0]Flag of rising point
flag2 [1 1 0 1 1 1]Flag of descent point
flag1 [0 0 1 0 0]Cut one end of the rising flag to match the length
flag2 [1 0 1 1 1]Remove one head of the descending flag to match the apex and align the length.
flag3 [0 0 1 0 0]The result of ANDing the ascending flag and descending flag is the apex.
```

```
cycle = 4
data = np.zeros(100)
cycleWidth = len(data) / cycle
unit = math.pi / cycleWidth * 2
for i in range(cycle):
for j in range(int(cycleWidth)):
data[i * int(cycleWidth) + j] = math.cos(unit * float(j))
plt.plot(data)
plt.show()
```

```
#Functionize one-dimensional vertex detection
def detectPeak1D(x):
conv1 = np.convolve(x, [1, -1], mode="full")
flag1 = (conv1 > 0).astype(int)
flag2 = (conv1 <= 0).astype(int)
flag1 = flag1[:-1]
flag2 = flag2[1:]
flag3 = flag1 & flag2
return flag3
peaks = detectPeak1D(data)
plt.plot(data)
plt.plot(peaks)
plt.show()
```

```
data[data > 0.7] = 0.7
peaks = detectPeak1D(data)
print("If the vertices are horizontal, the rise is detected as a vertex")
plt.plot(data)
plt.plot(peaks)
plt.show()
```

Two-dimensional data is the result of ANDing the two "flags performed for all rows (two-dimensional array)" and "flags performed for all columns (two-dimensional array)" for one-dimensional vertex detection. To be the apex of

```
#Two-dimensional vertex detection
x = np.array([
[0, 0, 1, 0, 0],
[0, 2, 3, 2, 0],
[1, 3, 5, 3, 1],
[0, 2, 3, 2, 0],
[0, 0, 1, 0, 0],
])
print(x, "Target data")
#Perform vertex detection on every row
peaks1 = []
for ix in x:
peak = detectPeak1D(ix)
peaks1.append(peak)
peaks1 = np.array(peaks1)
print(peaks1, "Horizontal vertex detection flag")
#Perform vertex detection on all columns
peaks2 = []
for ix in x.transpose():
peak = detectPeak1D(ix)
peaks2.append(peak)
peaks2 = np.array(peaks2).transpose() #Execute detection by transpose and return to the original
print(peaks2, "Vertical vertex detection flag")
peaks3 = (peaks1 & peaks2).astype(int)
print(peaks3, "The remaining flag after ANDing the row and column detection flags becomes the two-dimensional vertex flag.")
```

```
[[0 0 1 0 0]
[0 2 3 2 0]
[1 3 5 3 1]
[0 2 3 2 0]
[0 0 1 0 0]]Target data
[[0 0 1 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 1 0 0]]Horizontal vertex detection flag
[[0 0 0 0 0]
[0 0 0 0 0]
[1 1 1 1 1]
[0 0 0 0 0]
[0 0 0 0 0]]Vertical vertex detection flag
[[0 0 0 0 0]
[0 0 0 0 0]
[0 0 1 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]The remaining flag after ANDing the row and column detection flags becomes the two-dimensional vertex flag.
```

```
#Functionize two-dimensional vertex detection
def detectPeak2D(x):
peaks1 = []
for ix in x:
peak = detectPeak1D(ix)
peaks1.append(peak)
peaks1 = np.array(peaks1)
peaks2 = []
for ix in x.transpose():
peak = detectPeak1D(ix)
peaks2.append(peak)
peaks2 = np.array(peaks2).transpose()
flag = (peaks1 & peaks2).astype(int)
return flag
```

```
#Create test data for 2D vertex detection, rotate data with 1D vertices
random.seed(1)
data2d = np.zeros((200, 200))
pattern = np.array([1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1])
for i in range(10):
ox, oy = random.randrange(200), random.randrange(200)
for j in range(50):
rad = (math.pi / 50) * j
for ix, v in enumerate(pattern):
pos = ix - len(pattern) / 2
x = ox + math.cos(rad) * pos
y = oy + math.sin(rad) * pos
if x < 0: continue
if x >= 200: continue
if y < 0: continue
if y >= 200: continue
data2d[int(x)][int(y)] = v
plt.figure(figsize=(10,4),dpi=200)
plt.imshow(data2d)
plt.show()
```

```
peaks = detectPeak2D(data2d)
print("Since all rising edges are recognized as vertices, points where the same value is continuous are recognized as vertices.")
plt.figure(figsize=(10,4),dpi=200)
plt.imshow(peaks)
plt.show()
```

```
data2dGaussian = scipy.ndimage.gaussian_filter(data2d, sigma=1)
peaks = detectPeak2D(data2dGaussian)
print("Vertices can be detected normally by smoothing and eliminating parallel parts as much as possible.")
plt.figure(figsize=(10,4),dpi=200)
plt.imshow(peaks)
plt.show()
```

There is no pre-processing or processing, and it is a process that assumes data that has a beautiful mountain shape. Basically, I think that it often does not work as expected unless it is smoothed, so use it as it is It doesn't matter if it is a moving average or a Gaussian filter, so use it after smoothing.

that's all.