Upgraded simulations using numba
This commit is contained in:
parent
aba3257de6
commit
92c30cdf65
|
@ -6,7 +6,7 @@ Created on Tue Dec 20 2022
|
|||
@author: Emilio Martínez <emilio.martinez@fing.edu.uy>
|
||||
|
||||
Script that reads all images in folder and simulates HDMI tempest capture
|
||||
if there is no subfolder with it's name.
|
||||
|
||||
"""
|
||||
|
||||
#%%
|
||||
|
@ -20,7 +20,7 @@ import numpy as np
|
|||
from skimage.io import imread
|
||||
from scipy import signal
|
||||
from PIL import Image
|
||||
from utils.DTutils import TMDS_encoding, TMDS_serial
|
||||
from utils.DTutils import TMDS_encoding_original, TMDS_serial
|
||||
import sys
|
||||
|
||||
#%%
|
||||
|
@ -39,14 +39,14 @@ def get_subfolders_names_from_folder(folder):
|
|||
def image_transmition_simulation(I, blanking=False):
|
||||
|
||||
# Encode image for TMDS
|
||||
I_TMDS = TMDS_encoding (I, blanking = blanking)
|
||||
I_TMDS = TMDS_encoding_original (I, blanking = blanking)
|
||||
|
||||
# Serialize pixel bits and sum channel signals
|
||||
I_TMDS_Tx = TMDS_serial(I_TMDS)
|
||||
|
||||
return I_TMDS_Tx, I_TMDS.shape
|
||||
|
||||
def image_capture_simulation(I_Tx, h_total, v_total, N_harmonic, noise_std,
|
||||
def image_capture_simulation(I_Tx, h_total, v_total, N_harmonic, noise_std=0,
|
||||
fps=60):
|
||||
|
||||
# Compute pixelrate and bitrate
|
||||
|
@ -54,7 +54,7 @@ def image_capture_simulation(I_Tx, h_total, v_total, N_harmonic, noise_std,
|
|||
bit_rate = 10*px_rate
|
||||
|
||||
# Continuous samples (interpolate)
|
||||
interpolator = 1
|
||||
interpolator = int(np.ceil(N_harmonic/5)) # Condition for sampling rate and
|
||||
sample_rate = interpolator*bit_rate
|
||||
Nsamples = interpolator*(10*h_total*v_total)
|
||||
if interpolator > 1:
|
||||
|
@ -115,67 +115,40 @@ def main():
|
|||
foldername = sys.argv[-1]
|
||||
|
||||
# Get images and subfolders names
|
||||
images_tmp = get_images_names_from_folder(foldername)
|
||||
old_subfolders = get_subfolders_names_from_folder(foldername)
|
||||
images = get_images_names_from_folder(foldername)
|
||||
|
||||
# Keep images without dedicated folders only
|
||||
images = []
|
||||
new_subfolders = []
|
||||
for image in images_tmp:
|
||||
image_name = image.split('.')[0]
|
||||
if image_name not in old_subfolders:
|
||||
images.append(image)
|
||||
new_subfolders.append(image_name)
|
||||
simulations_folder = foldername+'/simulations/'
|
||||
|
||||
os.mkdir(simulations_folder)
|
||||
|
||||
# timestamp for simulation starting
|
||||
t1_image = time.time()
|
||||
|
||||
for image, subfolder in zip(images,new_subfolders):
|
||||
|
||||
|
||||
# Create new directory for simulations
|
||||
subfolder_path = foldername+'/'+subfolder
|
||||
os.mkdir(subfolder_path)
|
||||
|
||||
# timestamp for simulation starting
|
||||
t1_image = time.time()
|
||||
for image in images:
|
||||
|
||||
# Read image
|
||||
image_path = foldername+'/'+image
|
||||
imagename = image.split('.')[0]
|
||||
I = imread(image_path)
|
||||
|
||||
# TMDS coding and bit serialization
|
||||
I_Tx, resolution = image_transmition_simulation(I)
|
||||
v_res, h_res, _ = resolution
|
||||
|
||||
# Possible std dev noise simulation values
|
||||
noise_stds = np.array([ 0, 5, 10, 15, 20, 25, 40, 50])
|
||||
|
||||
|
||||
# Make five simulations for each image
|
||||
for i in range(5):
|
||||
|
||||
# Choose random pixelrate harmonic number
|
||||
N_harmonic = np.random.randint(1,10)
|
||||
|
||||
# Choose random SNR value (SNR=0 for no noise)
|
||||
noise_std = np.random.choice(noise_stds)
|
||||
|
||||
I_capture = image_capture_simulation(I_Tx, h_res, v_res, N_harmonic, noise_std)
|
||||
|
||||
path = subfolder_path+'/'+imagename+'_'+str(N_harmonic)+'harm_'+str(noise_std)+"std.png"
|
||||
|
||||
save_simulation_image(I_capture,path)
|
||||
|
||||
if i==0:
|
||||
# timestamp for simulation ending
|
||||
t2_image = time.time()
|
||||
|
||||
|
||||
# Choose random pixelrate harmonic number
|
||||
N_harmonic = np.random.randint(1,10)
|
||||
|
||||
I_capture = image_capture_simulation(I_Tx, h_res, v_res, N_harmonic)
|
||||
|
||||
t_image = t2_image-t1_image
|
||||
path = simulations_folder+image
|
||||
|
||||
print('Tiempo de la primer simulación de '+image+':','{:.2f}'.format(t_image)+'s\n')
|
||||
save_simulation_image(I_capture,path)
|
||||
|
||||
# timestamp for simulation ending
|
||||
t2_image = time.time()
|
||||
|
||||
t_images = t2_images-t1_images
|
||||
|
||||
print('\nTiempo total de las '+str(len(images))+' simulaciones:','{:.2f}'.format(t_images)+'s\n')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,7 +1,6 @@
|
|||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from scipy import signal
|
||||
from scipy.fft import fft, ifft, fftshift
|
||||
from numba import jit, uint8, int8, prange
|
||||
|
||||
def autocorr(x):
|
||||
"""Compute autocorrelation function of 1-D array
|
||||
|
@ -50,6 +49,108 @@ def binarray_to_uint(binarray):
|
|||
|
||||
return num
|
||||
|
||||
def TMDS_pixel_rare (pix):
|
||||
"""8bit pixel TMDS coding
|
||||
|
||||
Inputs:
|
||||
- pix: 8-bit pixel
|
||||
- cnt: 0's and 1's balance. Default in 0 (balanced)
|
||||
|
||||
Outputs:
|
||||
- pix_out: TDMS coded 16-bit pixel (only 10 useful)
|
||||
- cnt: 0's and 1's balance updated with new pixel coding
|
||||
|
||||
"""
|
||||
# Convert 8-bit pixel to binary list D
|
||||
D = uint8_to_binarray(pix)
|
||||
|
||||
# Initialize output q
|
||||
qm = [D[0]]
|
||||
|
||||
# 1's unbalanced condition at current pixel
|
||||
N1_D = np.sum(D)
|
||||
|
||||
if N1_D>4 or (N1_D==4 and not(D[0])):
|
||||
|
||||
# XNOR of consecutive bits
|
||||
for k in range(1,8):
|
||||
qm.append( not(qm[k-1] ^ D[k]) )
|
||||
qm.append(0)
|
||||
|
||||
else:
|
||||
# XOR of consecutive bits
|
||||
for k in range(1,8):
|
||||
qm.append( qm[k-1] ^ D[k] )
|
||||
qm.append(1)
|
||||
|
||||
qm.append(np.random.choice([0,1]))
|
||||
|
||||
|
||||
|
||||
# Return the TMDS coded pixel as uint and 0's y 1's balance
|
||||
return binarray_to_uint(qm)
|
||||
|
||||
@jit(nopython=True)
|
||||
def TMDS_pixel_numba(pix:uint8, cnt:int8)->tuple:
|
||||
|
||||
D = np.zeros(8, dtype=np.uint8)
|
||||
for i in range(8):
|
||||
D[i] = (pix >> i) & 1
|
||||
|
||||
qm = np.zeros(9, dtype=np.uint8)
|
||||
qm[0] = D[0]
|
||||
|
||||
N1_D = np.sum(D)
|
||||
|
||||
if N1_D > 4 or (N1_D == 4 and not D[0]):
|
||||
for k in range(1, 8):
|
||||
qm[k] = not (qm[k-1] ^ D[k])
|
||||
qm[8] = 0
|
||||
else:
|
||||
for k in range(1, 8):
|
||||
qm[k] = qm[k-1] ^ D[k]
|
||||
qm[8] = 1
|
||||
|
||||
qout = np.zeros(10, dtype=np.uint8)
|
||||
N1_qm = np.sum(qm[:8])
|
||||
N0_qm = 8 - N1_qm
|
||||
|
||||
if cnt == 0 or N1_qm == 4:
|
||||
|
||||
qout[9] = not(qm[8])
|
||||
qout[8] = qm[8]
|
||||
if qm[8]:
|
||||
qout[:8] = qm[:8]
|
||||
else:
|
||||
qout[:8] = np.logical_not(qm[:8])
|
||||
|
||||
if not qm[8]:
|
||||
cnt += N0_qm - N1_qm
|
||||
else:
|
||||
cnt += N1_qm - N0_qm
|
||||
|
||||
else:
|
||||
|
||||
if (cnt > 0 and N1_qm > 4) or (cnt < 0 and N1_qm < 4):
|
||||
qout[9] = 1
|
||||
qout[8] = qm[8]
|
||||
qout[:8] = np.logical_not(qm[:8])
|
||||
cnt += 2*qm[8] + N0_qm - N1_qm
|
||||
else:
|
||||
qout[9] = 0
|
||||
qout[8] = qm[8]
|
||||
qout[:8] = qm[:8]
|
||||
cnt += -2*(not(qm[8])) + N1_qm - N0_qm
|
||||
|
||||
# Convert binary array to unsigned int
|
||||
pix_tmds = 0
|
||||
for bit in qout[::-1]:
|
||||
pix_tmds = (pix_tmds << 1) | bit
|
||||
|
||||
# Return the TMDS coded pixel as uint and 0's y 1's balance
|
||||
return pix_tmds, cnt
|
||||
|
||||
|
||||
def TMDS_pixel (pix,cnt=0):
|
||||
"""8bit pixel TMDS coding
|
||||
|
||||
|
@ -118,6 +219,7 @@ def TMDS_pixel (pix,cnt=0):
|
|||
# Return the TMDS coded pixel as uint and 0's y 1's balance
|
||||
return binarray_to_uint(qout), cnt
|
||||
|
||||
@jit(parallel=True)
|
||||
def TMDS_encoding_original (I, blanking = False):
|
||||
"""TMDS image coding
|
||||
|
||||
|
@ -132,9 +234,10 @@ def TMDS_encoding_original (I, blanking = False):
|
|||
|
||||
# Create "ghost dimension" if I is gray-scale image (not RGB)
|
||||
if len(I.shape)!= 3:
|
||||
I = np.repeat(I[:, :, np.newaxis], 3, axis=2).astype('uint8')
|
||||
|
||||
chs = 3
|
||||
I = I[:, :, np.newaxis]
|
||||
chs = 1
|
||||
else:
|
||||
chs = 3
|
||||
|
||||
# Get image resolution
|
||||
v_in, h_in = I.shape[:2]
|
||||
|
@ -158,13 +261,13 @@ def TMDS_encoding_original (I, blanking = False):
|
|||
I_c = np.zeros((v_in,h_in,chs)).astype('uint16')
|
||||
|
||||
# Iterate over channels and pixels
|
||||
for c in range(chs):
|
||||
for c in prange(chs):
|
||||
for i in range(v_in):
|
||||
cnt=[0,0,0]
|
||||
for j in range(h_in):
|
||||
# Get pixel and code it TMDS between blanking
|
||||
pix = I[i,j,c]
|
||||
I_c[i + v_diff//2 , j + h_diff//2, c], cnt[c] = TMDS_pixel (pix,cnt[c])
|
||||
I_c[i + v_diff//2 , j + h_diff//2, c], cnt[c] = TMDS_pixel_numba (pix,cnt[c])
|
||||
|
||||
return I_c
|
||||
|
||||
|
@ -242,6 +345,7 @@ def TMDS_pixel_cntdiff (pix,cnt=0):
|
|||
byte_range = np.arange(256)
|
||||
# Initialize pixel coding and cnt-difference arrays
|
||||
TMDS_pix_table = np.zeros((256,3),dtype='uint16')
|
||||
TMDS_rare_pix_table = np.zeros((256),dtype='uint16')
|
||||
TMDS_cntdiff_table = np.zeros((256,3),dtype='int8')
|
||||
|
||||
for byte in byte_range:
|
||||
|
@ -253,6 +357,8 @@ for byte in byte_range:
|
|||
TMDS_cntdiff_table[byte,1] = p_null[1]
|
||||
TMDS_cntdiff_table[byte,2] = p1[1]
|
||||
|
||||
TMDS_rare_pix_table[byte] = TMDS_pixel_rare(byte)
|
||||
|
||||
def pixel_fastencoding(pix,cnt_prev=0):
|
||||
"""8bit pixel TMDS fast coding
|
||||
|
||||
|
@ -314,11 +420,9 @@ def TMDS_encoding (I, blanking = False):
|
|||
|
||||
# Create "ghost dimension" if I is gray-scale image (not RGB)
|
||||
if len(I.shape)!= 3:
|
||||
# Gray-scale image
|
||||
I = np.repeat(I[:, :, np.newaxis], 3, axis=2).astype('uint8')
|
||||
I = I[:, :, np.newaxis]
|
||||
chs = 1
|
||||
else:
|
||||
# RGB image
|
||||
else:
|
||||
chs = 3
|
||||
|
||||
# Get image resolution
|
||||
|
@ -343,7 +447,8 @@ def TMDS_encoding (I, blanking = False):
|
|||
# Assuming the blanking corresponds to 10bit number
|
||||
# [0, 0, 1, 0, 1, 0, 1, 0, 1, 1] (LSB first) for channels R and G
|
||||
I_c = 852*np.ones((v,h,chs)).astype('uint16')
|
||||
I_c[:,:,2] = TMDS_blanking(h_total=h, v_total=v, h_active=h_in, v_active=v_in,
|
||||
if chs==3:
|
||||
I_c[:,:,2] = TMDS_blanking(h_total=h, v_total=v, h_active=h_in, v_active=v_in,
|
||||
h_front_porch=h_front_porch, v_front_porch=v_front_porch, h_back_porch=h_back_porch, v_back_porch=v_back_porch)
|
||||
|
||||
else:
|
||||
|
@ -362,6 +467,61 @@ def TMDS_encoding (I, blanking = False):
|
|||
|
||||
return I_c
|
||||
|
||||
def TMDS_encoding_rare (I, blanking = False):
|
||||
"""TMDS image coding
|
||||
|
||||
Inputs:
|
||||
- I: 2-D image array
|
||||
- blanking: Boolean that specifies if horizontal and vertical blanking is applied
|
||||
|
||||
Output:
|
||||
- I_c: TDMS coded 16-bit image (only 10 useful)
|
||||
|
||||
"""
|
||||
|
||||
# Create "ghost dimension" if I is gray-scale image (not RGB)
|
||||
if len(I.shape)!= 3:
|
||||
I = I[:, :, np.newaxis]
|
||||
chs = 1
|
||||
else:
|
||||
chs = 3
|
||||
|
||||
# Get image resolution
|
||||
v_in, h_in = I.shape[:2]
|
||||
|
||||
# Get image resolution
|
||||
v_in, h_in = I.shape[:2]
|
||||
|
||||
if blanking:
|
||||
# Get blanking resolution for input image
|
||||
|
||||
v = (v_in==1080)*1125 + (v_in==720)*750 + (v_in==600)*628 + (v_in==480)*525
|
||||
h = (h_in==1920)*2200 + (h_in==1280)*1650 + (h_in==800)*1056 + (h_in==640)*800
|
||||
|
||||
vdiff = v - v_in
|
||||
hdiff = h - h_in
|
||||
|
||||
# Create image with blanking and change type to uint16
|
||||
# Assuming the blanking corresponds to 10bit number [0, 0, 1, 0, 1, 0, 1, 0, 1, 1] (LSB first)
|
||||
|
||||
I_c = 852*np.ones((v,h,chs)).astype('uint16')
|
||||
|
||||
else:
|
||||
v_diff = 0
|
||||
h_diff = 0
|
||||
I_c = I.copy()
|
||||
I_c = I_c.astype('uint16')
|
||||
|
||||
# Iterate over channels and pixels
|
||||
for c in range(chs):
|
||||
for i in range(v_in):
|
||||
for j in range(h_in):
|
||||
# Get pixel and code it TMDS between blanking
|
||||
pix = I[i,j,c]
|
||||
I_c[i + v_diff//2 , j + h_diff//2, c] = TMDS_rare_pix_table[pix]
|
||||
|
||||
return I_c
|
||||
|
||||
def DecTMDS_pixel (pix):
|
||||
"""10-bit pixel TMDS decoding
|
||||
|
||||
|
|
Loading…
Reference in New Issue