Rozdzielczość bitowa

Rozdzielczość bitowa określa dokładność, z jaką zapisana jest jedna próbka danych, określając ilość dostępnych dla niej bitów. Najpopularniejsze aktualnie formaty danych dźwiękowych opierają się na 16- lub 24- bitowych próbkach. Rzadziej spotykane są formaty 8- lub 32- bitowe. Na zajęciach będzie się zdarzać, że będziemy pracować i prowadzić badania na sztucznie tworzonych formatach o mniejszej ilości bitów, która nie będzie obsługiwana domyślnie przez biblioteki, a to będzie wymagało pewnych dodatkowych operacji.

Rozdzielczość bitowa określa zakres wartości, jakie mogą wystąpić we wczytywanym pliku. Owszem można wymusić zawsze jednolity tryb wczytywania danych (np. dtype=np.float32), ale nie zmienia to, z jaką dokładnością zapisane są dane oraz z jakim domyślnym typem są one wczytane. Domyślnie dla poszczególnych formatów dane wczytane powinny być w poniższych typach:

  • 8-bitowe - typ uint8 - Uwaga! wartość neutralna sygnału w tym przypadku oscyluje w okolicach 128 nie 0
  • 16-bitowe - typ int16
  • 24-bitowe i 32-bitowe - typ int32 lub float32

Jeżeli w jakimś przypadku macie problem z odtworzeniem lub zapisaniem dźwięku, bo przykładowo słychać tylko szumy, to prawdopodobnie jest problem z interpretacją typu i trzeba wymusić inny typ danych na zmiennej .astype().

Zmiana typów danych

W przypadku obrazu głównie będziemy operować na typach danych bez znaku (kategoria \(B\)):

  • uint8 - tutaj zakres będzie \(\in <0,255>\),
  • uint16 - tutaj zakres będzie \(\in <0,65535>\),
  • float32 - tutaj zakres będzie \(\in <0,1>\).

Żeby między nimi się przemieszczać, wystarczy podzielić lub pomnożyć przez maksymalną wartość. Czyli żeby z uint8 przejść na float32, należy wartości podzielić przez \(255\) (pierwszy wzór), a dla operacji odwrotnej pomnożyć przez tyle (drugi wzór).

W przypadku dźwięku sytuacja jest trochę bardziej skomplikowana, ponieważ tu częściej spotykamy się z większą ilością typów danych i nie są one tak konsekwentne:

  • uint8 - tutaj zakres będzie \(\in <0,255>\),
  • int8 - tutaj zakres będzie \(\in <-128,127>\),
  • int16 - tutaj zakres będzie \(\in <-32768,32767>\),
  • int32 - tutaj zakres będzie \(\in <-2147483648,2147483647>\),
  • float32 - tutaj zakres będzie \(\in <-1,1>\).

Jeżeli będziecie potrzebować kiedyś informacjo o typie zmiennych i zakresach ich wartości można je sprawdzić za pomocą funkcji np.iinfo.

Jeżeli chodzi o przechodzenie pomiędzy zakresami tutaj najważniejsze jest na samym początku z zakresu typu \(C\) (wszystkie poza uint8) na zakres typu \(B\) (a najlepiej \(A\)). Zauważcie, że środek zakresu wartości dla liczb całkowitych nie wypada idealnie na \(0\) i jeżeli będziecie starali się dokonywać operacji w ten sposób, będzie prowadziło to do różnego rodzaju błędów. To przesunięcie najłatwiej dokonać poprzez dodanie minimalnej wartości. W każdym z tych przypadków dalej możemy stosować oryginalne wzory.

Przykład przejść z float32 na int16 i na odwrót

Zaczynamy ze zmienną \(F_{<-1,1>}\) na float32. W pierwszym kroku musimy przejść z zakresu \(\in <-1,1>\) na zakres \(\in <0,1>\). Wykonujemy to przy użyciu równania pierwszego równania: \[Z_A=\frac{Z_C-m}{n-m}\] \(Z_c\) będzie w naszym przypadku \(F_{<-1,1>}\), czyli nasze \(m=-1\), a \(n=1\). Całość przypiszemy sobie do zmiennej \(F_{<0,1>}\), żeby pamiętać na jakim zakresie wartości jesteśmy. Nasza operacja powinna wyglądać mniej więcej tak: \[F_{<0,1>}=\frac{F_{<-1,1>}-(-1)}{1-(-1)}=\frac{F+1}{2}\] Teraz musimy przejść na docelowy zakres int16, stosując trzeci wzór: \[ Z_C=[Z_A*(n-m)]+m \] Nasze nowe \(m=-32768\), \(n=32767\), a zmienną nazwijmy \(F_{<-32768,32767>}\), czyli nasza operacja powinna wyglądać tak: \[ F_{<-32768,32767>}=[F_{<0,1>}*(32767-(-32768))]+(-32768) = F_{<0,1>} *65535 - 32768\] Teraz wystarczy przejść z float32 na int16, czyli zmienić typ: \[I_{<-32768,32767>}=F_{<-32768,32767>}\] Żeby wykonać operację odwrotną należ wykonać, wszystkie kroki w odwrotnej kolejności. Czyli zaczynamy od przejścia z int16 na float32: \[F_{<-32768,32767>}=I_{<-32768,32767>}\] Teraz przechodzimy z zakresu \(F_{<-32768,32767>}\) na zakres \(F_{<0,1>}\). \[F_{<0,1>}=\frac{F_{<-32768,32767>}-(-32768)}{32767-(-32768)}=\frac{F_{<-32768,32767>}+32768}{65535}\] Kolejny krok to przejście z zakresu \(F_{<0,1>}\) na \(F_{<-1,1>}\). \[F_{<-1,1>}=[F_{<0,1>} *(1-(-1))]+(-1) = F_{<0,1>}* 2 - 1\]