From 8711529c5f2de3552decd44e2bec4546935ca54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Mart=C3=ADnez?= Date: Sat, 25 Feb 2023 12:21:59 -0300 Subject: [PATCH] Added to tempest module utilities functions --- .../grc/tempest_TMDS_image_source.block.yml | 7 +- gr-tempest/python/CMakeLists.txt | 1 + gr-tempest/python/DTutils.py | 465 ++++++++++++++++++ gr-tempest/python/TMDS_image_source.py | 336 ++----------- 4 files changed, 503 insertions(+), 306 deletions(-) create mode 100644 gr-tempest/python/DTutils.py diff --git a/gr-tempest/grc/tempest_TMDS_image_source.block.yml b/gr-tempest/grc/tempest_TMDS_image_source.block.yml index 4b7d77a..ca2937a 100644 --- a/gr-tempest/grc/tempest_TMDS_image_source.block.yml +++ b/gr-tempest/grc/tempest_TMDS_image_source.block.yml @@ -4,7 +4,7 @@ category: '[Tempest]' templates: imports: import tempest - make: tempest.TMDS_image_source(${image_file}, ${mode}) + make: tempest.TMDS_image_source(${image_file}, ${mode}, ${blanking}) # Make one 'parameters' list entry for every parameter you want settable from the GUI. # Keys include: @@ -22,6 +22,11 @@ parameters: options: ['1', '2','3','4'] option_labels: ['Repeat with TMDS', 'Repeat, no TMDS','No repeat with TMDS','No repeat, no TMDS'] +- id: blanking + label: Use blanking + dtype: bool + options: ['True', 'False'] + option_labels: ['True', 'False'] # Make one 'inputs' list entry per input and one 'outputs' list entry per output. # Keys include: diff --git a/gr-tempest/python/CMakeLists.txt b/gr-tempest/python/CMakeLists.txt index c0defbb..ae28028 100644 --- a/gr-tempest/python/CMakeLists.txt +++ b/gr-tempest/python/CMakeLists.txt @@ -36,6 +36,7 @@ GR_PYTHON_INSTALL( message_to_var.py tempest_msgbtn.py TMDS_image_source.py + DTutils.py TMDS_decoder.py DESTINATION ${GR_PYTHON_DIR}/tempest ) diff --git a/gr-tempest/python/DTutils.py b/gr-tempest/python/DTutils.py new file mode 100644 index 0000000..a99743b --- /dev/null +++ b/gr-tempest/python/DTutils.py @@ -0,0 +1,465 @@ +import numpy as np +from matplotlib import pyplot as plt +from scipy import signal +from scipy.fft import fft, ifft, fftshift + +def autocorr(x): + """Compute autocorrelation function of 1-D array + + Input: + x: 1-D array + + Output: + autocorr: autocorrelation function of x + """ + + # Use FFT method, which has more computing efectiveness for 1-D numpy arrays + autocorr = signal.correlate(x,x,mode='full', method= 'fft') + + # Fix some shifts due FFT + half_idx =int(autocorr.size/2) + max_ind = np.argmax(autocorr[half_idx:])+half_idx + autocorr = autocorr[max_ind:] + # Normalise output + return autocorr/autocorr[0] + + +def uint8_to_binarray(integer): + """Convert integer into fixed-length 8-bit binary array. LSB in [0]. + Extended and modified code from https://github.com/projf/display_controller/blob/master/model/tmds.py + """ + + b_array = [int(i) for i in reversed(bin(integer)[2:])] + b_array += [0]*(8-len(b_array)) + return b_array + +def uint16_to_binarray(integer): + """Convert integer into fixed-length 16-bit binary array. LSB in [0]. + Extended and modified code from https://github.com/projf/display_controller/blob/master/model/tmds.py + """ + b_array = [int(i) for i in reversed(bin(integer)[2:])] + b_array += [0]*(16-len(b_array)) + return b_array + +def binarray_to_uint(binarray): + + array = binarray[::-1] + num = array[0] + for n in range(1,len(binarray)): + num = (num << 1) + array[n] + + return num + +def TMDS_pixel (pix,cnt=0): + """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) + + # Initialize output qout + qout = qm.copy() + + # Unbalanced condition with previous and current pixels + N1_qm = np.sum(qm[:8]) + N0_qm = 8 - N1_qm + + if cnt==0 or N1_qm==4: + + qout.append(not(qm[8])) + qout[8] = qm[8] + qout[:8]=qm[:8] if qm[8] else 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.append(1) + qout[8] = qm[8] + qout[:8] = np.logical_not(qm[:8]) + cnt += 2*qm[8] + N0_qm - N1_qm + else: + qout.append(0) + qout[8] = qm[8] + qout[:8] = qm[:8] + cnt += -2*(not(qm[8])) + N1_qm - N0_qm + + # Return the TMDS coded pixel as uint and 0's y 1's balance + return binarray_to_uint(qout), cnt + +def TMDS_encoding_original (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 = np.repeat(I[:, :, np.newaxis], 3, axis=2).astype('uint8') + + chs = 3 + + # 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 = np.zeros((v_in,h_in,chs)).astype('uint16') + + # Iterate over channels and pixels + for c in range(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]) + + return I_c + +def TMDS_pixel_cntdiff (pix,cnt=0): + """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) + - cntdiff: balance difference given by the actual coded pixel + + """ + # 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 pixelo + 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) + + # Initialize output qout + qout = qm.copy() + + # Unbalanced condition with previous and current pixels + N1_qm = np.sum(qm[:8]) + N0_qm = 8 - N1_qm + + if cnt==0 or N1_qm==4: + + qout.append(not(qm[8])) + qout[8]=qm[8] + qout[:8]=qm[:8] if qm[8] else [not(val) for val in qm[:8]] + + if not(qm[8]): + cnt_diff = N0_qm - N1_qm + else: + cnt_diff = N1_qm - N0_qm + + else: + + if (cnt>0 and N1_qm>4) or (cnt<0 and N1_qm<4): + qout.append(1) + qout[8]=qm[8] + qout[:8] = [not(val) for val in qm[:8]] + cnt_diff = 2*qm[8] +N0_qm -N1_qm + else: + qout.append(0) + qout[8]=qm[8] + qout[:8] = qm[:8] + cnt_diff = -2*(not(qm[8])) + N1_qm - N0_qm + + # Return the TMDS coded pixel as uint and 0's y 1's balance difference + uint_out = binarray_to_uint(qout) + return uint_out, cnt_diff + + +### Create TMDS LookUp Tables for fast encoding (3 times faster than the other implementation) +byte_range = np.arange(256) +# Initialize pixel coding and cnt-difference arrays +TMDS_pix_table = np.zeros((256,3),dtype='uint16') +TMDS_cntdiff_table = np.zeros((256,3),dtype='int8') + +for byte in byte_range: + p0,p_null, p1 = TMDS_pixel_cntdiff(byte,-1),TMDS_pixel_cntdiff(byte,0),TMDS_pixel_cntdiff(byte,1) # 0's and 1's unbalance respect. + TMDS_pix_table[byte,0] = p0[0] + TMDS_pix_table[byte,1] = p_null[0] + TMDS_pix_table[byte,2] = p1[0] + TMDS_cntdiff_table[byte,0] = p0[1] + TMDS_cntdiff_table[byte,1] = p_null[1] + TMDS_cntdiff_table[byte,2] = p1[1] + +def pixel_fastencoding(pix,cnt_prev=0): + """8bit pixel TMDS fast 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 + + """ + balance_idx = int(np.sign(cnt_prev))+1 + pix_out = TMDS_pix_table[pix,balance_idx] + cnt = cnt_prev + TMDS_cntdiff_table[pix,balance_idx] + + return pix_out, cnt + +def TMDS_blanking (h_total, v_total, h_active, v_active, h_front_porch, v_front_porch, h_back_porch, v_back_porch): + + # Initialize blanking image + img_blank = np.zeros((v_total,h_total)) + + # Get the total blanking on vertical an horizontal axis + h_blank = h_total - h_active + v_blank = v_total - v_active + + # (C1,C0)=(0,0) region + img_blank[:v_front_porch,:h_front_porch] = 0b1101010100 + img_blank[:v_front_porch,h_blank-h_back_porch:] = 0b1101010100 + img_blank[v_blank-v_back_porch:v_blank,:h_front_porch] = 0b1101010100 + img_blank[v_blank-v_back_porch:v_blank,h_blank-h_back_porch:] = 0b1101010100 + img_blank[v_blank:,:h_blank] = 0b1101010100 + + # (C1,C0)=(0,1) region + img_blank[:v_front_porch,h_front_porch:h_blank-h_back_porch] = 0b0010101011 + img_blank[v_blank-v_back_porch:,h_front_porch:h_blank-h_back_porch] = 0b0010101011 + + # (C1,C0)=(1,0) region + img_blank[v_front_porch:v_blank-v_back_porch,:h_front_porch] = 0b0101010100 + img_blank[v_front_porch:v_blank-v_back_porch,h_blank-h_back_porch:] = 0b0101010100 + + # (C1,C0)=(1,1) region + img_blank[v_front_porch:v_blank-v_back_porch,v_front_porch:h_blank-h_back_porch] = 0b1010101011 + + return(img_blank) + +def TMDS_encoding (I, blanking = False): + """TMDS image coding + + Inputs: + - I: 2D/3D image array (v_size, h_size, channels) + - blanking: Boolean that specifies if horizontal and vertical blanking is applied or not + + Output: + - I_c: 3D TDMS coded 16-bit (only 10 useful) image array + + """ + + # 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') + chs = 1 + else: + # RGB image + chs = 3 + + # 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==900)*1000 + (v_in==720)*750 + (v_in==600)*628 + (v_in==480)*525 + h = (h_in==1920)*2200 + (h_in==1600)*1800 + (h_in==1280)*1650 + (h_in==800)*1056 + (h_in==640)*800 + + v_diff = v - v_in + h_diff = h - h_in + + v_front_porch = (v_in==1080)*4 + (v_in==900)*1 + (v_in==720)*5 + (v_in==600)*1 + (v_in==480)*2 + v_back_porch = (v_in==1080)*36 + (v_in==900)*96 + (v_in==720)*20 + (v_in==600)*23 + (v_in==480)*25 + + h_front_porch = (h_in==1920)*88 + (h_in==1600)*24 + (h_in==1280)*110 + (h_in==800)*40 + (h_in==640)*8 + h_back_porch = (h_in==1920)*148 + (h_in==1600)*96 + (h_in==1280)*220 + (h_in==800)*88 + (h_in==640)*40 + + # 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) 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, + 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: + v_diff = 0 + h_diff = 0 + I_c = np.zeros((v_in,h_in,chs)).astype('uint16') + + # Iterate over channels and pixels + for c in range(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, j + h_diff, c], cnt[c] = pixel_fastencoding (pix,cnt[c]) + + return I_c + +def DecTMDS_pixel (pix): + """10-bit pixel TMDS decoding + + Inputs: + - pix: 16-bit pixel (only 10 first bits useful) + + Output: + - pix_out: 8-bit TMDS decoded pixel + + """ + + + D = uint16_to_binarray(pix)[:10] + + if D[9]: + D[:8] = np.logical_not(D[:8]) + + Q = D.copy()[:8] + + if D[8]: + for k in range(1,8): + Q[k] = D[k] ^ D[k-1] + else: + for k in range(1,8): + Q[k] = not(D[k] ^ D[k-1]) + + # Return pixel as uint + return binarray_to_uint(Q) + +def TMDS_decoding (Ic): + """Image TMDS decoding + + Inputs: + - Ic: TMDS coded image + + Output: + - Idec: 8-bit decoded image + + """ + + # Create "ghost dimension" if gray-scale image (not RGB) + if len(Ic.shape)!= 3: + Ic = Ic.reshape(Ic.shape[0],Ic.shape[1],1) + + Idec = Ic.copy() + # Get image dimensions + Nx, Ny, Nz = Ic.shape + + # Iterate over channels and pixels + for c in np.arange(Nz): + for i in np.arange(Nx): + for j in np.arange(Ny): + + # Get pixel and use TMDS decoding + pix = Ic[i,j,c] + Idec[i,j,c] = DecTMDS_pixel (pix) + + return Idec + + +def TMDS_serial(I): + ''' + Serialize an image as an 1D binary array given a 10bit pixel value. + + Inputs: + - I: TMDS image to serialize. Pixel values must be between 0 and 1023 + + Output: + - Iserials: 1D binary array per image channel which represents + the voltage value to be transmitted + + ''' + assert np.min(I)>=0 and np.max(I)<= 1023, "Pixel values must be between 0 and 1023" + + # Initialize lists per channel + Iserials = [] + n_rows,n_columns, n_channels = I.shape + + # Iterate over pixel + for c in range(n_channels): + channel_list = [] + for i in range(n_rows): + for j in range(n_columns): + # Get pixel value and cast it as binary string + binstring = bin(I[i,j,c])[2:] + # Fill string with 0's to get length 10 + binstring = '0'*(10-len(binstring))+binstring + # Re-order string for LSB first + binstring = binstring[::-1] + binarray = list(binstring) + # Extend the bit stream + channel_list.extend(binarray) + + Iserials.append(channel_list) + + # Digital to analog value mapping: [0,1]-->[-A,A] (A=1) + Iserials = np.sum(2*np.array(Iserials,dtype='int32') - 1, axis=0) + + del(channel_list) + + return Iserials diff --git a/gr-tempest/python/TMDS_image_source.py b/gr-tempest/python/TMDS_image_source.py index bbf96ca..6bfea56 100644 --- a/gr-tempest/python/TMDS_image_source.py +++ b/gr-tempest/python/TMDS_image_source.py @@ -26,296 +26,10 @@ import numpy as np -from skimage.io import imread -# from DTutils import TMDS_pix_table, TMDS_cntdiff_table, pixel_fastencoding, TMDS_encoding +from PIL import Image +from tempest.DTutils import TMDS_pix_table, TMDS_cntdiff_table, pixel_fastencoding, TMDS_encoding from gnuradio import gr -def uint8_to_binarray(integer): - """Convert integer into fixed-length 8-bit binary array. LSB in [0]. - Extended and modified code from https://github.com/projf/display_controller/blob/master/model/tmds.py - """ - - b_array = [int(i) for i in reversed(bin(integer)[2:])] - b_array += [0]*(8-len(b_array)) - return b_array - -def uint16_to_binarray(integer): - """Convert integer into fixed-length 16-bit binary array. LSB in [0]. - Extended and modified code from https://github.com/projf/display_controller/blob/master/model/tmds.py - """ - b_array = [int(i) for i in reversed(bin(integer)[2:])] - b_array += [0]*(16-len(b_array)) - return b_array - -def binarray_to_uint(binarray): - - array = binarray[::-1] - num = array[0] - for n in range(1,len(binarray)): - num = (num << 1) + array[n] - - return num - -def TMDS_pixel (pix,cnt=0): - """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) - - # Initialize output qout - qout = qm.copy() - - # Unbalanced condition with previous and current pixels - N1_qm = np.sum(qm[:8]) - N0_qm = 8 - N1_qm - - if cnt==0 or N1_qm==4: - - qout.append(not(qm[8])) - qout[8] = qm[8] - qout[:8]=qm[:8] if qm[8] else 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.append(1) - qout[8] = qm[8] - qout[:8] = np.logical_not(qm[:8]) - cnt += 2*qm[8] + N0_qm - N1_qm - else: - qout.append(0) - qout[8] = qm[8] - qout[:8] = qm[:8] - cnt += -2*(not(qm[8])) + N1_qm - N0_qm - - # Return the TMDS coded pixel as uint and 0's y 1's balance - return binarray_to_uint(qout), cnt - - -def TMDS_pixel_cntdiff (pix,cnt=0): - """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) - - cntdiff: balance difference given by the actual coded pixel - - """ - # 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 pixelo - 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) - - # Initialize output qout - qout = qm.copy() - - # Unbalanced condition with previous and current pixels - N1_qm = np.sum(qm[:8]) - N0_qm = 8 - N1_qm - - if cnt==0 or N1_qm==4: - - qout.append(not(qm[8])) - qout[8]=qm[8] - qout[:8]=qm[:8] if qm[8] else [not(val) for val in qm[:8]] - - if not(qm[8]): - cnt_diff = N0_qm - N1_qm - else: - cnt_diff = N1_qm - N0_qm - - else: - - if (cnt>0 and N1_qm>4) or (cnt<0 and N1_qm<4): - qout.append(1) - qout[8]=qm[8] - qout[:8] = [not(val) for val in qm[:8]] - cnt_diff = 2*qm[8] +N0_qm -N1_qm - else: - qout.append(0) - qout[8]=qm[8] - qout[:8] = qm[:8] - cnt_diff = -2*(not(qm[8])) + N1_qm - N0_qm - - # Return the TMDS coded pixel as uint and 0's y 1's balance difference - uint_out = binarray_to_uint(qout) - return uint_out, cnt_diff - - -### Create TMDS LookUp Tables for fast encoding (3 times faster than the other implementation) -byte_range = np.arange(256) -# Initialize pixel coding and cnt-difference arrays -TMDS_pix_table = np.zeros((256,3),dtype='uint16') -TMDS_cntdiff_table = np.zeros((256,3),dtype='int8') - -for byte in byte_range: - p0,p_null, p1 = TMDS_pixel_cntdiff(byte,-1),TMDS_pixel_cntdiff(byte,0),TMDS_pixel_cntdiff(byte,1) # 0's and 1's unbalance respect. - TMDS_pix_table[byte,0] = p0[0] - TMDS_pix_table[byte,1] = p_null[0] - TMDS_pix_table[byte,2] = p1[0] - TMDS_cntdiff_table[byte,0] = p0[1] - TMDS_cntdiff_table[byte,1] = p_null[1] - TMDS_cntdiff_table[byte,2] = p1[1] - -def pixel_fastencoding(pix,cnt_prev=0): - """8bit pixel TMDS fast 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 - - """ - balance_idx = int(np.sign(cnt_prev))+1 - pix_out = TMDS_pix_table[pix,balance_idx] - cnt = cnt_prev + TMDS_cntdiff_table[pix,balance_idx] - - return pix_out, cnt - - -def TMDS_blanking (h_total, v_total, h_active, v_active, h_front_porch, v_front_porch, h_back_porch, v_back_porch): - - # Initialize blanking image - img_blank = np.zeros((v_total,h_total)) - - # Get the total blanking on vertical an horizontal axis - h_blank = h_total - h_active - v_blank = v_total - v_active - - # (C1,C0)=(0,0) region - img_blank[:v_front_porch,:h_front_porch] = 0b1101010100 - img_blank[:v_front_porch,h_blank-h_back_porch:] = 0b1101010100 - img_blank[v_blank-v_back_porch:v_blank,:h_front_porch] = 0b1101010100 - img_blank[v_blank-v_back_porch:v_blank,h_blank-h_back_porch:] = 0b1101010100 - img_blank[v_blank:,:h_blank] = 0b1101010100 - - # (C1,C0)=(0,1) region - img_blank[:v_front_porch,h_front_porch:h_blank-h_back_porch] = 0b0010101011 - img_blank[v_blank-v_back_porch:,h_front_porch:h_blank-h_back_porch] = 0b0010101011 - - # (C1,C0)=(1,0) region - img_blank[v_front_porch:v_blank-v_back_porch,:h_front_porch] = 0b0101010100 - img_blank[v_front_porch:v_blank-v_back_porch,h_blank-h_back_porch:] = 0b0101010100 - - # (C1,C0)=(1,1) region - img_blank[v_front_porch:v_blank-v_back_porch,v_front_porch:h_blank-h_back_porch] = 0b1010101011 - - return(img_blank) - -def TMDS_encoding (I, blanking = False): - """TMDS image coding - - Inputs: - - I: 3-D image array (v_size, h_size, channels) - - blanking: Boolean that specifies if horizontal and vertical blanking is applied or not - - 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 = np.repeat(I[:, :, np.newaxis], 3, axis=2).astype('uint8') - - chs = 3 - - # 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==900)*1000 + (v_in==720)*750 + (v_in==600)*628 + (v_in==480)*525 - h = (h_in==1920)*2200 + (h_in==1600)*1800 + (h_in==1280)*1650 + (h_in==800)*1056 + (h_in==640)*800 - - v_diff = v - v_in - h_diff = h - h_in - - v_front_porch = (v_in==1080)*4 + (v_in==900)*1 + (v_in==720)*5 + (v_in==600)*1 + (v_in==480)*2 - v_back_porch = (v_in==1080)*36 + (v_in==900)*96 + (v_in==720)*20 + (v_in==600)*23 + (v_in==480)*25 - - h_front_porch = (h_in==1920)*88 + (h_in==1600)*24 + (h_in==1280)*110 + (h_in==800)*40 + (h_in==640)*8 - h_back_porch = (h_in==1920)*148 + (h_in==1600)*96 + (h_in==1280)*220 + (h_in==800)*88 + (h_in==640)*40 - - # 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) 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, - 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: - v_diff = 0 - h_diff = 0 - I_c = 255*np.ones((v_in,h_in,chs)).astype('uint16') - - # Iterate over channels and pixels - for c in range(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, j + h_diff, c], cnt[c] = pixel_fastencoding (pix,cnt[c]) - - return I_c class TMDS_image_source(gr.sync_block): """ @@ -327,7 +41,7 @@ class TMDS_image_source(gr.sync_block): * 1920x1080 """ - def __init__(self, image_file, mode): + def __init__(self, image_file, mode, blanking): gr.sync_block.__init__(self, name="TMDS_image_source", in_sig=None, @@ -335,33 +49,45 @@ class TMDS_image_source(gr.sync_block): self.image_file = image_file self.mode = mode + self.blanking = blanking self.load_image() def load_image(self): - """Decode the image into a buffer and encode it (or not) TMDS with blanking""" - self.image_data = imread(self.image_file) + """Decode the image into a buffer and encode it (or not) TMDS""" + self.image_data = np.array(Image.open(self.image_file)) # Check if mode uses TMDS encoding if (self.mode==1 or self.mode==3): #Encode the image with TMDS - self.image_data = TMDS_encoding(self.image_data,blanking = True) + self.image_data = TMDS_encoding(self.image_data,blanking = self.blanking) print('TMDS encoding ready!!!') - else: - # Do not encode TMDS. Use blanking - v_in, h_in = self.image_data.shape[:2] - v = (v_in==1080)*1125 + (v_in==900)*1000 + (v_in==720)*750 + (v_in==600)*628 + (v_in==480)*525 - h = (h_in==1920)*2200 + (h_in==1600)*1800 + (h_in==1280)*1650 + (h_in==800)*1056 + (h_in==640)*800 - image_blank = 255*np.ones((v,h,3)) - # Create "ghost dimension" if image_data is gray-scale image (not RGB) + else: + + # Do not encode TMDS + # Create "ghost dimension" if I is gray-scale image (not RGB) if len(self.image_data.shape)!= 3: - self.image_data = np.repeat(self.image_data[:, :, np.newaxis], 3, axis=2).astype('uint8') - # image_blank = np.zeros((v,h,3)) - hdiff = (h-h_in)//2 - vdiff = (v-v_in)//2 - image_blank[vdiff:vdiff+v_in,hdiff:hdiff+h_in] = self.image_data[:,:,:3] - self.image_data = image_blank[:,:,:3] + # Gray-scale image + self.image_data = np.repeat(self.image_data[:, :, np.newaxis], 3, axis=2).astype('uint8') + chs = 1 + else: + # RGB image + chs = 3 + + if self.blanking: + # Use blanking + v_in, h_in = self.image_data.shape[:2] + v = (v_in==1080)*1125 + (v_in==900)*1000 + (v_in==720)*750 + (v_in==600)*628 + (v_in==480)*525 + h = (h_in==1920)*2200 + (h_in==1600)*1800 + (h_in==1280)*1650 + (h_in==800)*1056 + (h_in==640)*800 + image_blank = 255*np.ones((v,h,chs)) + + + hdiff = (h-h_in)//2 + vdiff = (v-v_in)//2 + image_blank[vdiff:vdiff+v_in,hdiff:hdiff+h_in] = self.image_data[:,:,:3] + + self.image_data = image_blank[:,:,:3] (self.image_height, self.image_width) = self.image_data.shape[:2]