Materiał poświęcony jest sposobom pozyskiwania obrazu i dźwięku u urządzeń zewnętrznych wewnątrz środowiska Python. Jeżeli macie problemy z urządzeniami polecam zajrzeć do FAQ
Przechwytywanie obrazu
Najłatwiejszym dla nas rozwiązaniem jest wykorzystanie w tym celu biblioteki OpenCV. Słów kilka na temat tego procesu. Podstawowa informacja jak otwieramy strumienie wideo, ze pomocą OpenCV. Nie zależnie od tego czy będzie to strumień pochodzący z pliku, adresu internetowego czy kamery internetowej.
import cv2
= cv2.VideoCapture(0) #przechwytywanie z kamery o id -> 0
cap = cv2.VideoCapture("nazwa.plik") #przechwytywanie z pliku cap
Teraz napiszmy mały program służący do wyświetlania obrazu. Poniższy program wyświetli nam obraz z domyślnego źródła obrazu przekształcony do obrazu w skali odcieni szarości. Program można wyłączyć naciskając klawisz q
na klawiaturze. Uprzedzając pytania: tak w konsoli będą prawdopodobnie pojawiały się komunikaty i ostrzeżenia.
if not cap.isOpened():
print("Cannot open camera")
exit()while True:
# Capture frame-by-frame
= cap.read()
ret, frame # if frame is read correctly ret is True
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# Our operations on the frame come here
= cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray # Display the resulting frame
'frame', gray)
cv2.imshow(if cv2.waitKey(1) == ord('q'):
break
# When everything done, release the capture
cap.release() cv2.destroyAllWindows()
Domyślny rozmiar klatki możemy odczytać lub zmienić za pomocą odpowiednich poleceń set
oraz get
:
cap.get(cv2.CAP_PROP_FRAME_WIDTH)
cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
set(cv2.CAP_PROP_FRAME_WIDTH,1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,720) cap.
W jaki sposób zapisać nasz strumień wideo do pliku? Na początku tworzymy odpowiednią funkcję zapisu wybierając odpowiedni kodek (DIVX
, XVID
, MJPG
, X264
) i przypisując go do odpowiedniego strumienia wyjściowego.
= cv2.VideoWriter_fourcc(*'X264')
fourcc = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) out
Potem wewnątrz pętli należy przekazać klatkę do zapisu.
out.write(frame)
A na końcu pamiętać, żeby zamknąć strumień wyjściowy.
out.release()
Przechwytywanie audio
Do pracy z tą częścią materiały wykorzystamy bibliotekę PyAudio
. Ty mogą być pewne trudności z instalacja dlatego podaję kilka sposobów na wykonanie tego:
# metoda 1
pip install pipwin
pipwin install pyaudio
# metoda 2
conda install PyAudio
Jeżeli obie nie będą działać to można jeszcze spróbować tej metody
Po instalacji zacznijmy od sprawdzenia dostępnych urządzeń, z których możemy skorzystać.
= pyaudio.PyAudio()
audio = audio.get_device_count()
numdevices for i in range(0, numdevices):
print(audio.get_device_info_by_index(i))
Jak znamy już urządzenia jakie mamy podłączone urządzenia możemy przygotować się na przechwytywanie. Na początek przygotujmy sobie ustawienia:
= pyaudio.paInt16
FORMAT = 1
CHANNELS = 44100
FS = 1024 CHUNK
Na ich podstawie możemy utworzyć strumień danych do przechwytywania.
= audio.open(input_device_index =0,
stream format=FORMAT,
=CHANNELS,
channels=FS,
rateinput=True,
=CHUNK
frames_per_buffer )
Przykład 1
Teraz mamy kilka możliwości jak przechwycić i obsłużyć sygnał zaczniemy od pierwszego przykładu źródło, który na bieżąco wyświetla dane zbierane z mikrofonu i zapisuje je do bufora. Na początek trochę operacji przygotowawczych.
global keep_going
=0
i= plt.subplots(2)
f,ax
# Prepare the Plotting Environment with random starting values
= np.arange(10000)
x = np.random.randn(10000)
y
# Plot 0 is for raw audio data
= ax[0].plot(x, y)
li, 0].set_xlim(0,1000)
ax[0].set_ylim(-5000,5000)
ax[0].set_title("Raw Audio Signal")
ax[# Plot 1 is for the FFT of the audio
= ax[1].plot(x, y)
li2, 1].set_xlim(0,5000)
ax[1].set_ylim(-100,100)
ax[1].set_title("Fast Fourier Transform")
ax[# Show the plot, but without blocking updates
0.01)
plt.pause(
plt.tight_layout()
= []
frames = True keep_going
Oraz funkcji którą będziemy wywoływać:
def plot_data(in_data):
# get and convert the data to float
= np.frombuffer(in_data, dtype=np.int16)
audio_data = 10.*np.log10(abs(np.fft.rfft(audio_data)))
dfft len(audio_data)))
li.set_xdata(np.arange(
li.set_ydata(audio_data)len(dfft))*10.)
li2.set_xdata(np.arange(
li2.set_ydata(dfft)
frames.extend(audio_data.tolist())
0.01)
plt.pause(if keep_going:
return True
else:
return False
Następnie uruchamiamy naszą pętlę (wyłączmy ją zamykając okno a następnie naciskając kombinację klawiszy Ctrl+C
w konsoli). Uwaga program nie zadziała jeżeli wyświetlamy ploty wewnątrz IDE, należy wymusić wyświetlanie ich w nowym oknie.
stream.start_stream()print("\n+---------------------------------+" )
print("| Press Ctrl+C to Break Recording |")
print("+---------------------------------+\n")
# Loop so program doesn't end while the stream callback's
# itself for new data
while keep_going:
try:
=stream.read(CHUNK)
frame
plot_data(frame)except KeyboardInterrupt:
=False
keep_goingexcept:
pass
stream.stop_stream()
stream.close()
audio.terminate()
Zapisywanie danych do pliku
Do zapisania danych na podstawie naszych danych do pliku wave możemy wykorzystać poniższą funkcję. Będzie ona działać dla danych pozyskanych zarówno w pierwszym jak i drugim przykładzie, ale do drugiego przykładu jest też dedykowany sposób.
= wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf
wf.setnchannels(CHANNELS)
wf.setsampwidth(audio.get_sample_size(FORMAT))
wf.setframerate(FS)b''.join(frames))
wf.writeframes( wf.close()
Przykład 2
Druga metoda czyli przechwytywanie jako proces działający w tle przy wykorzystaniu funkcji callback
. Na początek zadeklarujmy sobie funkcję która zrealizuje nam ten process bezpośredniego przetwarzania danych:
def process_data(in_data, frame_count, time_info, flag):
global Frame_buffer,frame_idx
= np.frombuffer(in_data, dtype=np.int16)
in_audio_data +CHUNK),0]=in_audio_data
Frame_buffer[frame_idx:(frame_idx################################
## Do something wih data
= in_audio_data
out_audio_data ################################
+CHUNK),1]=out_audio_data
Frame_buffer[frame_idx:(frame_idx= out_audio_data.tobytes()
out_data +=CHUNK
frame_idxreturn out_data, pyaudio.paContinue
Mamy tu jak widać miejsce na przechwytywanie danych tak jak w poprzednim przykładzie jak również miejsce na ich przetwarzanie. Dane pobrane z mikrofonu będą w zmiennej in_data
a dane przekazywane do waszych głośników będą zwracane w returnie w zmiennej out_data
. Kolejnym korkiem jest modyfikacja naszego strumienia i przerobienie naszego strumienia wejściowego na wejściowo-wyjściowe. UWAGA Trzeba ustalić odpowiednia urządzenia wejścia i wyjścia - urządzenie o indeksie \(0\) nie będzie na pewno jednym i drugim.
= audio.open(input_device_index =0,
stream =0,
output_device_indexformat=FORMAT,
=CHANNELS,
channels=FS,
rateinput=True,
=True,
output=CHUNK,
frames_per_buffer=process_data
stream_callback )
Teraz deklaracja potrzebnych zmiennych globalnych oraz nowa pętla działająca przez \(10\) sekund (można sterować parametrem):
global Frame_buffer,frame_idx
=10
N= np.zeros(((N+1)*FS,2))
Frame_buffer =0
frame_idx
stream.start_stream()while stream.is_active():
time.sleep(N)
stream.stop_stream() stream.close()
Sprawdzanie danych i zapis do pliku
Zastosowanie buforu w postaci dwukolumnowej tablicy numpy
daje nam lepszą możliwość porównywania wejścia i wyjścia naszego callbacka
. To możemy go zarówno zapisać do pliku i sprawdzić wyniki w zewnętrznym programie do obórki dźwięku lub wyświetlić sobie oba sygnały (wywołanie uzupełnić o odpowiednią metodę wygenerowania wartości czasu dla osi OX).
## zapis do pliku
'nazwa.wav', Frame_buffer.astype(np.int16), FS)
sf.write(
## wyświetlanie niepełne
2,1,1)
plt.subplot(0])
plt.plot(?,Frame_buffer[:,2,1,2)
plt.subplot(1]) plt.plot(?,Frame_buffer[:,