title: An Audio Bayeux Tapestry
abstract: With the power of technology, the Bayeux tapestry is now avaiable for the blind.
keywords: programming,audio
date: 16/12/2024

# An Audio Bayeux Tapestry

Hello!

With the power of technology, the Bayeux tapestry is now avaiable for the blind.

<audio src="https://blog.ozva.co.uk/bayeux/bayeux.mp3" controls />

In this post I'll show you how this was achived.

## The image

![image](/bayeux/bayeux1.png "Before ajustment")

The scan available online[^1] is not centered, unless we want an unclear or ragged edge on our audio, we must first straighten and strectch the tapestry flat.

I did this with a simple python script.

```
for (i, pixel) in enumerate(line):
    value = np.mean(pixel) # Get the brightness
    if value < THRESHHOLD:
        front_index = i # Set the index of our in-point
        break
```

As you can see, by scrolling our way across each line and noting where the pixel value is greater than our threshold (set at 225), we can figure out the bounds of that particular line of the tapestry.

```
trim_line = np.array([line[front_index:back_index]], dtype=np.uint8)
new_image.append(cv.resize(trim_line, (image_width, 1))[0])
```

We then stretch the line to the total width of the image.

This produces a far more regular image.

![image](/bayeux/bayeux2.png "After ajustment")

This is very stretched, but this is no problem. Since it will end up as a spectrogram whose dimentions we don't care much for we will skip the step of fixing this.

## The spectrogram

The image can then be turned into a spectrogram with the power of the FFT.

```
data = np.fft.irfft(image, h*2, axis=1).reshape((w*h*2)) # Reshape and get the FFT output
data -= np.average(data) # Remove DC offset
data *= (2**15-1.)/np.amax(data) # Normalize
```

As a side effect of the jump from one side of the image to the other, there is a sample impulse every 1,500 samples, we record this high point and subtract it from all following samples.

```
high_point = data[1500]
for i in range(0, len(data), 1500):
    data[i] -= high_point
```

If we split the image in half and process seperately, we can then combine them over the right and left audio channels to get a more resolute image.

This solves another issue:

Say you have an image which is not so rectangular and is intended to have a "silent" background. As this image rises to the top of the frame, hovering around 20,000kHz it can become quite unplesant. Splitting the sound across right and left not only produces some sence of space, but also gives you a wholy wider and more plesent range of frequencies.

![image](/bayeux/bayeux3.png "Finished spectrogram")

[^1]: Link: <https://en.wikipedia.org/wiki/File:Tapisserie_de_Bayeux_31109.jpg>
