Download stc_ml_c.cpp from Yassine/Stego: direct link, hf CLI and curl.
- Browser
- Download file 37.7 kB
-
https://huggingface.co/spaces/Yassine/Stego/resolve/main/stc_ml_c.cpp
- Command line
-
hf download hf://spaces/Yassine/Stego/stc_ml_c.cpp
-
curl -L -o stc_ml_c.cpp https://huggingface.co/spaces/Yassine/Stego/resolve/main/stc_ml_c.cpp
37.7 kB
| // {{{ write_vector_to_file() | |
| template< class T > void write_vector_to_file( uint n, T *ptr, const char* file_name ) { | |
| std::ofstream f( file_name ); | |
| for ( uint i = 0; i < n; i++ ) | |
| f << std::left << std::setw( 20 ) << i << std::left << std::setw( 20 ) << ptr[i] << std::endl; | |
| f.close(); | |
| } | |
| // }}} | |
| // {{{ write_matrix_to_file() | |
| // write column-ordered matrix into file | |
| template< class T > void write_matrix_to_file( uint rows, uint columns, T *ptr, const char* file_name ) { | |
| std::ofstream f( file_name ); | |
| for ( uint i = 0; i < rows; i++ ) { | |
| f << std::left << std::setw( 20 ) << i; | |
| for ( uint j = 0; j < columns; j++ ) | |
| f << std::left << std::setw( 20 ) << ptr[j * rows + i]; | |
| f << std::endl; | |
| } | |
| f.close(); | |
| } | |
| // }}} | |
| // {{{ align_*() | |
| // Templates to handle aligned version of new and delete operators. | |
| // These functions are necessary for creating arrays aligned address of certain multiples, such as 16. | |
| template< class T > T* align_new( unsigned int n, unsigned int align_size ) { | |
| char *ptr, *ptr2, *aligned_ptr; | |
| int align_mask = align_size - 1; | |
| ptr = new char[n * sizeof(T) + align_size + sizeof(int)]; | |
| if ( ptr == 0 ) return 0; | |
| ptr2 = ptr + sizeof(int); | |
| aligned_ptr = ptr2 + (align_size - ((size_t) ptr2 & align_mask)); | |
| ptr2 = aligned_ptr - sizeof(int); | |
| *((int*) ptr2) = (int) (aligned_ptr - ptr); | |
| return (T*) aligned_ptr; | |
| } | |
| template< class T > void align_delete( T *ptr ) { | |
| int *ptr2 = (int*) ptr - 1; | |
| char *p; | |
| p = (char*) ptr; | |
| p -= *ptr2; | |
| delete[] p; | |
| } | |
| // }}} | |
| // {{{ randperm() | |
| /* Generates random permutation of length n based on the MT random number generator with seed 'seed'. */ | |
| void randperm( uint n, uint seed, uint* perm ) { | |
| boost::mt19937 *generator = new boost::mt19937( seed ); | |
| boost::variate_generator< boost::mt19937, boost::uniform_int< > > *randi = new boost::variate_generator< boost::mt19937, | |
| boost::uniform_int< > >( *generator, boost::uniform_int< >( 0, INT_MAX ) ); | |
| // generate random permutation - this is used to shuffle cover pixels to randomize the effect of different neighboring pixels | |
| for ( uint i = 0; i < n; i++ ) | |
| perm[i] = i; | |
| for ( uint i = 0; i < n; i++ ) { | |
| uint j = (*randi)() % (n - i); | |
| uint tmp = perm[i]; | |
| perm[i] = perm[i + j]; | |
| perm[i + j] = tmp; | |
| } | |
| delete generator; | |
| delete randi; | |
| } | |
| // }}} | |
| // {{{ sum_inplace() | |
| inline float sum_inplace( __m128 x ) { | |
| float y; | |
| // add all 4 terms from x together | |
| x = _mm_add_ps( x, _mm_shuffle_ps(x,x,_MM_SHUFFLE(1,0,3,2)) ); | |
| x = _mm_add_ps( x, _mm_shuffle_ps(x,x,_MM_SHUFFLE(2,3,0,1)) ); | |
| _mm_store_ss( &y, x ); | |
| return y; | |
| } | |
| // }}} | |
| // {{{ calc_entropy() | |
| float calc_entropy( uint n, uint k, float* costs, float lambda ) { | |
| float const LOG2 = log( 2.0 ); | |
| __m128 inf = _mm_set1_ps( F_INF ); | |
| __m128 v_lambda = _mm_set1_ps( -lambda ); | |
| __m128 z, d, rho, p, entr, mask; | |
| entr = _mm_setzero_ps(); | |
| for ( uint i = 0; i < n / 4; i++ ) { | |
| z = _mm_setzero_ps(); | |
| d = _mm_setzero_ps(); | |
| for ( uint j = 0; j < k; j++ ) { | |
| rho = _mm_load_ps( costs + j * n + 4 * i ); // costs array must be aligned in memory | |
| p = exp_ps( _mm_mul_ps( v_lambda, rho ) ); | |
| z = _mm_add_ps( z, p ); | |
| mask = _mm_cmpeq_ps( rho, inf ); // if p<eps, then do not accumulate it to d since x*exp(-x) tends to zero | |
| p = _mm_mul_ps( rho, p ); | |
| p = _mm_andnot_ps( mask, p ); // apply mask | |
| d = _mm_add_ps( d, p ); | |
| } | |
| entr = _mm_sub_ps( entr, _mm_div_ps( _mm_mul_ps( v_lambda, d ), z ) ); | |
| entr = _mm_add_ps( entr, log_ps( z ) ); | |
| } | |
| return sum_inplace( entr ) / LOG2; | |
| } | |
| // }}} | |
| // {{{ get_lambda_entropy() | |
| float get_lambda_entropy( uint n, uint k, float *costs, float payload, float initial_lambda = 10 ) { | |
| float p1, p2, p3, lambda1, lambda2, lambda3; | |
| int j = 0; | |
| uint iterations = 0; | |
| lambda1 = 0; | |
| p1 = n * log( (float)k ) / log( 2.0f ); | |
| lambda3 = initial_lambda; | |
| p3 = payload + 1; // this is just an initial value | |
| lambda2 = initial_lambda; | |
| while ( p3 > payload ) { | |
| lambda3 *= 2; | |
| p3 = calc_entropy( n, k, costs, lambda3 ); | |
| j++; | |
| iterations++; | |
| // beta is probably unbounded => it seems that we cannot find beta such that | |
| // relative payload will be smaller than requested. Binary search does not make sence here. | |
| if ( j > 10 ) { | |
| return lambda3; | |
| } | |
| } | |
| while ( (p1 - p3) / n > payload / n * 1e-2 ) { // binary search for parameter lambda | |
| lambda2 = lambda1 + (lambda3 - lambda1) / 2; | |
| p2 = calc_entropy( n, k, costs, lambda2 ); | |
| if ( p2 < payload ) { | |
| lambda3 = lambda2; | |
| p3 = p2; | |
| } else { | |
| lambda1 = lambda2; | |
| p1 = p2; | |
| } | |
| iterations++; // this is for monitoring the number of iterations | |
| } | |
| return lambda1 + (lambda3 - lambda1) / 2; | |
| } | |
| // }}} | |
| // {{{ calc_distortion() | |
| float calc_distortion( uint n, uint k, float* costs, float lambda ) { | |
| __m128 eps = _mm_set1_ps( std::numeric_limits< float >::epsilon() ); | |
| __m128 v_lambda = _mm_set1_ps( -lambda ); | |
| __m128 z, d, rho, p, dist, mask; | |
| dist = _mm_setzero_ps(); | |
| for ( uint i = 0; i < n / 4; i++ ) { // n must be multiple of 4 | |
| z = _mm_setzero_ps(); | |
| d = _mm_setzero_ps(); | |
| for ( uint j = 0; j < k; j++ ) { | |
| rho = _mm_load_ps( costs + j * n + 4 * i ); // costs array must be aligned in memory | |
| p = exp_ps( _mm_mul_ps( v_lambda, rho ) ); | |
| z = _mm_add_ps( z, p ); | |
| mask = _mm_cmplt_ps( p, eps ); // if p<eps, then do not accumulate it to d since x*exp(-x) tends to zero | |
| p = _mm_mul_ps( rho, p ); | |
| p = _mm_andnot_ps( mask, p ); | |
| d = _mm_add_ps( d, p ); | |
| } | |
| dist = _mm_add_ps( dist, _mm_div_ps( d, z ) ); | |
| } | |
| return sum_inplace( dist ); | |
| } | |
| // }}} | |
| // {{{ get_lambda_distortion() | |
| float get_lambda_distortion( uint n, uint k, float *costs, float distortion, float initial_lambda = 10, float precision = 1e-3, | |
| uint iter_limit = 30 ) { | |
| float dist1, dist2, dist3, lambda1, lambda2, lambda3; | |
| int j = 0; | |
| uint iterations = 0; | |
| lambda1 = 0; | |
| dist1 = calc_distortion( n, k, costs, lambda1 ); | |
| lambda3 = initial_lambda; | |
| dist2 = F_INF; // this is just an initial value | |
| lambda2 = initial_lambda; | |
| dist3 = distortion + 1; | |
| while ( dist3 > distortion ) { | |
| lambda3 *= 2; | |
| dist3 = calc_distortion( n, k, costs, lambda3 ); | |
| j++; | |
| iterations++; | |
| // beta is probably unbounded => it seems that we cannot find beta such that | |
| // relative payload will be smaller than requested. Binary search cannot converge. | |
| if ( j > 10 ) { | |
| return lambda3; | |
| } | |
| } | |
| while ( (fabs( dist2 - distortion ) / n > precision) && (iterations < iter_limit) ) { // binary search for parameter lambda | |
| lambda2 = lambda1 + (lambda3 - lambda1) / 2; | |
| dist2 = calc_distortion( n, k, costs, lambda2 ); | |
| if ( dist2 < distortion ) { | |
| lambda3 = lambda2; | |
| dist3 = dist2; | |
| } else { | |
| lambda1 = lambda2; | |
| dist1 = dist2; | |
| } | |
| iterations++; // this is for monitoring the number of iterations | |
| } | |
| return lambda1 + (lambda3 - lambda1) / 2; | |
| } | |
| // }}} | |
| // {{{ binary_entropy_array() | |
| float binary_entropy_array( uint n, float *prob ) { | |
| float h = 0; | |
| float const LOG2 = log( 2.0 ); | |
| float const EPS = std::numeric_limits< float >::epsilon(); | |
| for ( uint i = 0; i < n; i++ ) | |
| if ( (prob[i] > EPS) && (1 - prob[i] > EPS) ) h -= prob[i] * log( prob[i] ) + (1 - prob[i]) * log( 1 - prob[i] ); | |
| return h / LOG2; | |
| } | |
| // }}} | |
| // {{{ entropy_array() | |
| float entropy_array( uint n, float* prob ) { | |
| double h = 0; | |
| double const LOG2 = log( 2.0 ); | |
| double const EPS = std::numeric_limits< double >::epsilon(); | |
| for ( uint i = 0; i < n; i++ ) | |
| if ( prob[i] > EPS ) h -= prob[i] * log( prob[i] ); | |
| return h / LOG2; | |
| } | |
| // }}} | |
| // {{{ mod() | |
| inline uint mod( int x, int m ) { | |
| int tmp = x - (x / m) * m + m; | |
| return tmp % m; | |
| } | |
| // }}} | |
| /* EMBEDDING ALGORITHMS */ | |
| // {{{ stc_embed_trial() | |
| void stc_embed_trial( uint n, float* cover_bit_prob0, u8* message, uint stc_constraint_height, uint &num_msg_bits, uint* perm, u8* stego, | |
| uint &trial, uint max_trials, const char* debugging_file = "cost.txt" ) { | |
| bool success = false; | |
| u8* cover = new u8[n]; | |
| double* cost = new double[n]; | |
| while ( !success ) { | |
| randperm( n, num_msg_bits, perm ); | |
| for ( uint i = 0; i < n; i++ ) { | |
| cover[perm[i]] = (cover_bit_prob0[i] < 0.5) ? 1 : 0; | |
| cost[perm[i]] = -log( (1 / std::max( cover_bit_prob0[i], 1 - cover_bit_prob0[i] )) - 1 ); | |
| if ( cost[perm[i]] != cost[perm[i]] ) // if p20[i]>1 due to numerical error (this is possible due to float data type) | |
| cost[perm[i]] = D_INF; // then cost2[i] is NaN, it should be Inf | |
| } | |
| memcpy( stego, cover, n ); // initialize stego array by cover array | |
| // debugging | |
| // write_vector_to_file<double>(n, cost, debugging_file); | |
| try { | |
| if ( num_msg_bits != 0 ) stc_embed( cover, n, message, num_msg_bits, (void*) cost, true, stego, stc_constraint_height ); | |
| success = true; | |
| } catch ( stc_exception& e ) { | |
| if ( e.error_id != 4 ) { // error_id=4 means No solution exists, thus we try to embed with different permutation. | |
| delete[] cost; | |
| delete[] cover; | |
| throw e; | |
| } | |
| num_msg_bits--; // by decreasing the number of bits, we change the permutation used to shuffle the bits | |
| trial++; | |
| if ( trial > max_trials ) { | |
| delete[] cost; | |
| delete[] cover; | |
| throw stc_exception( "Maximum number of trials in layered construction exceeded (2).", 6 ); | |
| } | |
| } | |
| } | |
| delete[] cost; | |
| delete[] cover; | |
| } | |
| // }}} | |
| // {{{ check_costs() | |
| // SANITY CHECKS for cost arrays | |
| void check_costs( uint n, uint k, float *costs ) { | |
| bool test_nan, test_non_inf, test_minus_inf; | |
| for ( uint i = 0; i < n; i++ ) { | |
| test_nan = false; // Is any element NaN? Should be FALSE | |
| test_non_inf = false; // Is any element finite? Should be TRUE | |
| test_minus_inf = false; // Is any element minus Inf? should be FALSE | |
| for ( uint j = 0; j < k; j++ ) { | |
| test_nan |= (costs[k * i + j] != costs[k * i + j]); | |
| test_non_inf |= ((costs[k * i + j] != -F_INF) & (costs[k * i + j] != F_INF)); | |
| test_minus_inf |= (costs[k * i + j] == -F_INF); | |
| } | |
| if ( test_nan ) { | |
| std::stringstream ss; | |
| ss << "Incorrect cost array." << i << "-th element contains NaN value. This is not a valid cost."; | |
| throw stc_exception( ss.str(), 6 ); | |
| } | |
| if ( !test_non_inf ) { | |
| std::stringstream ss; | |
| ss << "Incorrect cost array." << i << "-th element does not contain any finite cost value. This is not a valid cost."; | |
| throw stc_exception( ss.str(), 6 ); | |
| } | |
| if ( test_minus_inf ) { | |
| std::stringstream ss; | |
| ss << "Incorrect cost array." << i << "-th element contains -Inf value. This is not a valid cost."; | |
| throw stc_exception( ss.str(), 6 ); | |
| } | |
| } | |
| } | |
| // }}} | |
| // {{{ stc_pm1_pls_embed() | |
| // MULTI-LAYERED EMBEDDING for plus/minus one changes | |
| // payload limited case - returns distortion | |
| float stc_pm1_pls_embed( uint cover_length, int* cover, float* costs, uint message_length, u8* message, // input variables | |
| uint stc_constraint_height, float wet_cost, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output variables | |
| return stc_pm1_dls_embed( cover_length, cover, costs, message_length, message, F_INF, stc_constraint_height, 0, wet_cost, stego, | |
| num_msg_bits, max_trials, coding_loss ); | |
| } | |
| // }}} | |
| // {{{ stc_pm1_dls_embed() | |
| // distortion limited case - returns distortion | |
| float stc_pm1_dls_embed( uint cover_length, int* cover, float* costs, uint message_length, u8* message, float target_distortion, // input variables | |
| uint stc_constraint_height, float expected_coding_loss, float wet_cost, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output variables | |
| check_costs( cover_length, 3, costs ); | |
| float dist = 0; | |
| int *stego_values = new int[4 * cover_length]; | |
| float *costs_ml2 = new float[4 * cover_length]; | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| costs_ml2[4 * i + mod( (cover[i] - 1 + 4), 4 )] = costs[3 * i + 0]; // set cost of changing by -1 | |
| stego_values[4 * i + mod( (cover[i] - 1 + 4), 4 )] = cover[i] - 1; | |
| costs_ml2[4 * i + mod( (cover[i] + 0 + 4), 4 )] = costs[3 * i + 1]; // set cost of changing by 0 | |
| stego_values[4 * i + mod( (cover[i] + 0 + 4), 4 )] = cover[i]; | |
| costs_ml2[4 * i + mod( (cover[i] + 1 + 4), 4 )] = costs[3 * i + 2]; // set cost of changing by +1 | |
| stego_values[4 * i + mod( (cover[i] + 1 + 4), 4 )] = cover[i] + 1; | |
| costs_ml2[4 * i + mod( (cover[i] + 2 + 4), 4 )] = wet_cost; // set cost of changing by +2 | |
| stego_values[4 * i + mod( (cover[i] + 2 + 4), 4 )] = cover[i] + 2; | |
| } | |
| // run general 2 layered embedding in distortion limited regime | |
| dist = stc_ml2_embed( cover_length, costs_ml2, stego_values, message_length, message, target_distortion, stc_constraint_height, | |
| expected_coding_loss, stego, num_msg_bits, max_trials, coding_loss ); | |
| delete[] costs_ml2; | |
| delete[] stego_values; | |
| return dist; | |
| } | |
| // }}} | |
| // {{{ stc_pm2_dls_embed() | |
| // MULTI-LAYERED EMBEDDING for plus/minus one and two changes | |
| // payload limited case - returns distortion | |
| float stc_pm2_pls_embed( uint cover_length, int* cover, float* costs, uint message_length, u8* message, // input variables | |
| uint stc_constraint_height, float wet_cost, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output variables | |
| return stc_pm2_dls_embed( cover_length, cover, costs, message_length, message, F_INF, stc_constraint_height, 0, wet_cost, stego, | |
| num_msg_bits, max_trials, coding_loss ); | |
| } | |
| // }}} | |
| // {{{ stc_pm2_dls_embed() | |
| // distortion limited case - returns distortion | |
| float stc_pm2_dls_embed( uint cover_length, int* cover, float* costs, uint message_length, u8* message, float target_distortion, // input variables | |
| uint stc_constraint_height, float expected_coding_loss, float wet_cost, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output variables | |
| check_costs( cover_length, 5, costs ); | |
| int *stego_values = new int[8 * cover_length]; | |
| float* costs_ml3 = new float[8 * cover_length]; | |
| std::fill_n( costs_ml3, 8 * cover_length, wet_cost ); // initialize new cost array | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| costs_ml3[8 * i + mod( (cover[i] - 2 + 8), 8 )] = costs[5 * i + 0]; // set cost of changing by -2 | |
| stego_values[8 * i + mod( (cover[i] - 2 + 8), 8 )] = cover[i] - 2; | |
| costs_ml3[8 * i + mod( (cover[i] - 1 + 8), 8 )] = costs[5 * i + 1]; // set cost of changing by -1 | |
| stego_values[8 * i + mod( (cover[i] - 1 + 8), 8 )] = cover[i] - 1; | |
| costs_ml3[8 * i + mod( (cover[i] + 0 + 8), 8 )] = costs[5 * i + 2]; // set cost of changing by 0 | |
| stego_values[8 * i + mod( (cover[i] + 0 + 8), 8 )] = cover[i] + 0; | |
| costs_ml3[8 * i + mod( (cover[i] + 1 + 8), 8 )] = costs[5 * i + 3]; // set cost of changing by +1 | |
| stego_values[8 * i + mod( (cover[i] + 1 + 8), 8 )] = cover[i] + 1; | |
| costs_ml3[8 * i + mod( (cover[i] + 2 + 8), 8 )] = costs[5 * i + 4]; // set cost of changing by +2 | |
| stego_values[8 * i + mod( (cover[i] + 2 + 8), 8 )] = cover[i] + 2; | |
| stego_values[8 * i + mod( (cover[i] + 3 + 8), 8 )] = cover[i] + 3; // these values are not used and are defined | |
| stego_values[8 * i + mod( (cover[i] + 4 + 8), 8 )] = cover[i] + 4; // just to have the array complete | |
| stego_values[8 * i + mod( (cover[i] + 5 + 8), 8 )] = cover[i] + 5; // | |
| } | |
| // run general 3 layered embedding in distortion limited regime | |
| float dist = stc_ml3_embed( cover_length, costs_ml3, stego_values, message_length, message, target_distortion, stc_constraint_height, | |
| expected_coding_loss, stego, num_msg_bits, max_trials, coding_loss ); | |
| delete[] costs_ml3; | |
| delete[] stego_values; | |
| return dist; | |
| } | |
| // }}} | |
| // GENERAL MULTI-LAYERED EMBEDDING | |
| // {{{ stc_ml1_embed() | |
| // algorithm for embedding into 1 layer, both payload- and distortion-limited case | |
| float stc_ml1_embed( uint cover_length, int* cover, short* direction, float* costs, uint message_length, u8* message, | |
| float target_distortion,// input variables | |
| uint stc_constraint_height, float expected_coding_loss, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output variables | |
| float distortion, lambda = 0, m_max = 0; | |
| bool success = false; | |
| uint m_actual = 0; | |
| uint n = cover_length + 4 - (cover_length % 4); // cover length rounded to multiple of 4 | |
| uint *perm1 = new uint[n]; | |
| float* c = align_new< float > ( 2 * n, 16 ); | |
| std::fill_n( c, 2 * n, F_INF ); | |
| std::fill_n( c, n, 0 ); | |
| for ( uint i = 0; i < cover_length; i++ ) { // copy and transpose data for better reading via SSE instructions | |
| c[mod( cover[i], 2 ) * n + i] = 0; // cost of not changing the element | |
| c[mod( (cover[i] + 1), 2 ) * n + i] = costs[i]; // cost of changing the element | |
| } | |
| if ( target_distortion != F_INF ) { // distortion-limited sender | |
| lambda = get_lambda_distortion( n, 2, c, target_distortion, 2 ); // | |
| m_max = (1 - expected_coding_loss) * calc_entropy( n, 2, c, lambda ); // | |
| m_actual = std::min( message_length, (uint) floor( m_max ) ); // | |
| } | |
| if ( (target_distortion == F_INF) || (m_actual < floor( m_max )) ) { // payload-limited sender | |
| m_actual = std::min( cover_length, message_length ); // or distortion-limited sender with | |
| } | |
| /* SINGLE LAYER OF 1ST LSBs */ | |
| num_msg_bits[0] = m_actual; | |
| uint trial = 0; | |
| u8* cover1 = new u8[cover_length]; | |
| double* cost1 = new double[cover_length]; | |
| u8* stego1 = new u8[cover_length]; | |
| while ( !success ) { | |
| randperm( cover_length, num_msg_bits[0], perm1 ); | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| cover1[perm1[i]] = mod( cover[i], 2 ); | |
| cost1[perm1[i]] = costs[i]; | |
| if ( cost1[perm1[i]] != cost1[perm1[i]] ) cost1[perm1[i]] = D_INF; | |
| } | |
| memcpy( stego1, cover1, cover_length ); // initialize stego array by cover array | |
| // debugging | |
| // write_vector_to_file<double>(n, cost, debugging_file); | |
| try { | |
| if ( num_msg_bits[0] != 0 ) stc_embed( cover1, cover_length, message, num_msg_bits[0], (void*) cost1, true, stego1, | |
| stc_constraint_height ); | |
| success = true; | |
| } catch ( stc_exception& e ) { | |
| if ( e.error_id != 4 ) { // error_id=4 means No solution exists, thus we try to embed with different permutation. | |
| delete[] cost1; | |
| delete[] cover1; | |
| delete[] stego1; | |
| delete[] perm1; | |
| delete[] c; | |
| throw e; | |
| } | |
| num_msg_bits[0]--; // by decreasing the number of bits, we change the permutation used to shuffle the bits | |
| trial++; | |
| if ( trial > max_trials ) { | |
| delete[] cost1; | |
| delete[] cover1; | |
| delete[] stego1; | |
| delete[] perm1; | |
| delete[] c; | |
| throw stc_exception( "Maximum number of trials in layered construction exceeded (1).", 6 ); | |
| } | |
| } | |
| } | |
| /* FINAL CALCULATIONS */ | |
| distortion = 0; | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| stego[i] = (stego1[perm1[i]] == cover1[perm1[i]]) ? cover[i] : cover[i] + direction[i]; | |
| distortion += (stego1[perm1[i]] == cover1[perm1[i]]) ? 0 : costs[i]; | |
| } | |
| if ( coding_loss != 0 ) { | |
| float lambda_dist = get_lambda_distortion( n, 2, c, distortion, lambda, 0, 20 ); // use 20 iterations to make lambda_dist precise | |
| float max_payload = calc_entropy( n, 2, c, lambda_dist ); | |
| (*coding_loss) = (max_payload - m_actual) / max_payload; // fraction of max_payload lost due to practical coding scheme | |
| } | |
| max_trials = trial; | |
| delete[] cost1; | |
| delete[] cover1; | |
| delete[] stego1; | |
| delete[] perm1; | |
| align_delete< float > ( c ); | |
| return distortion; | |
| } | |
| // }}} | |
| // {{{ stc_ml2_embed() | |
| // algorithm for embedding into 2 layers with possibility to use only 1 layer, both payload- and distortion-limited cases | |
| float stc_ml2_embed( uint cover_length, float* costs, int* stego_values, uint message_length, u8* message, float target_distortion, // input variables | |
| uint stc_constraint_height, float expected_coding_loss, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output and optional variables | |
| float distortion, dist_coding_loss, lambda = 0, m_max = 0; | |
| uint m_actual = 0; | |
| uint n = cover_length + 4 - (cover_length % 4); // cover length rounded to multiple of 4 | |
| check_costs( cover_length, 4, costs ); | |
| // if only binary embedding is sufficient, then use only 1st LSB layer | |
| bool lsb1_only = true; | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| uint n_finite_costs = 0; // number of finite cost values | |
| uint lsb_xor = 0; | |
| for ( uint k = 0; k < 4; k++ ) | |
| if ( costs[4 * i + k] != F_INF ) { | |
| n_finite_costs++; | |
| lsb_xor ^= (k % 2); | |
| } | |
| lsb1_only &= ((n_finite_costs <= 2) & (lsb_xor == 1)); | |
| } | |
| if ( lsb1_only ) { // use stc_ml1_embed method | |
| distortion = 0; | |
| int *cover = new int[cover_length]; | |
| short *direction = new short[cover_length]; | |
| float *costs_ml1 = new float[cover_length]; | |
| for ( uint i = 0; i < cover_length; i++ ) { // normalize such that minimal element is 0 - this helps numerical stability | |
| uint min_id = 0; | |
| float f_min = F_INF; | |
| for ( uint j = 0; j < 4; j++ ) | |
| if ( f_min > costs[4 * i + j] ) { | |
| f_min = costs[4 * i + j]; // minimum value | |
| min_id = j; // index of the minimal entry | |
| } | |
| costs_ml1[i] = F_INF; | |
| cover[i] = stego_values[4 * i + min_id]; | |
| for ( uint j = 0; j < 4; j++ ) | |
| if ( (costs[4 * i + j] != F_INF) && (min_id != j) ) { | |
| distortion += f_min; | |
| costs_ml1[i] = costs[4 * i + j] - f_min; | |
| direction[i] = stego_values[4 * i + j] - cover[i]; | |
| } | |
| } | |
| distortion += stc_ml1_embed( cover_length, cover, direction, costs_ml1, message_length, message, target_distortion, | |
| stc_constraint_height, expected_coding_loss, stego, num_msg_bits, max_trials, coding_loss ); | |
| delete[] direction; | |
| delete[] costs_ml1; | |
| delete[] cover; | |
| return distortion; | |
| } | |
| // copy and transpose data for faster reading via SSE instructions | |
| float* c = align_new< float > ( 4 * n, 16 ); | |
| std::fill_n( c, 4 * n, F_INF ); | |
| std::fill_n( c, n, 0 ); | |
| for ( uint i = 0; i < 4 * cover_length; i++ ) | |
| c[n * (i % 4) + i / 4] = costs[i]; | |
| // write_matrix_to_file<float>(n, 4, c, "cost_ml2.txt"); | |
| for ( uint i = 0; i < n; i++ ) { // normalize such that minimal element is 0 - this helps numerical stability | |
| float f_min = F_INF; | |
| for ( uint j = 0; j < 4; j++ ) | |
| f_min = std::min( f_min, c[j * n + i] ); | |
| for ( uint j = 0; j < 4; j++ ) | |
| c[j * n + i] -= f_min; | |
| } | |
| if ( target_distortion != F_INF ) { | |
| lambda = get_lambda_distortion( n, 4, c, target_distortion, 2 ); | |
| m_max = (1 - expected_coding_loss) * calc_entropy( n, 4, c, lambda ); | |
| m_actual = std::min( message_length, (uint) floor( m_max ) ); | |
| } | |
| if ( (target_distortion == F_INF) || (m_actual < floor( m_max )) ) { | |
| m_actual = std::min( 2 * cover_length, message_length ); | |
| lambda = get_lambda_entropy( n, 4, c, m_actual, 2 ); | |
| } | |
| /* | |
| p = exp(-lambda*costs); | |
| p = p./(ones(4,1)*sum(p)); | |
| */ | |
| float* p = align_new< float > ( 4 * n, 16 ); | |
| __m128 v_lambda = _mm_set1_ps( -lambda ); | |
| for ( uint i = 0; i < n / 4; i++ ) { | |
| __m128 sum = _mm_setzero_ps(); | |
| for ( uint j = 0; j < 4; j++ ) { | |
| __m128 x = _mm_load_ps( c + j * n + 4 * i ); | |
| x = exp_ps( _mm_mul_ps( v_lambda, x ) ); | |
| _mm_store_ps( p + j * n + 4 * i, x ); | |
| sum = _mm_add_ps( sum, x ); | |
| } | |
| for ( uint j = 0; j < 4; j++ ) { | |
| __m128 x = _mm_load_ps( p + j * n + 4 * i ); | |
| x = _mm_div_ps( x, sum ); | |
| _mm_store_ps( p + j * n + 4 * i, x ); | |
| } | |
| } | |
| // this is for debugging purposes | |
| // float payload_dbg = entropy_array(4*n, p); | |
| uint trial = 0; | |
| float* p10 = new float[cover_length]; | |
| float* p20 = new float[cover_length]; | |
| u8* stego1 = new u8[cover_length]; | |
| u8* stego2 = new u8[cover_length]; | |
| uint *perm1 = new uint[cover_length]; | |
| uint *perm2 = new uint[cover_length]; | |
| /* LAYER OF 2ND LSBs */ | |
| for ( uint i = 0; i < cover_length; i++ ) | |
| p20[i] = p[i] + p[i + n]; // p20 = p(1,:)+p(2,:); % probability of 2nd LSB of stego equal 0 | |
| //num_msg_bits[1] = (uint) floor( binary_entropy_array( cover_length, p20 ) ); // msg_bits(2) = floor(sum(binary_entropy(p20))); % number of msg bits embedded into 2nd LSBs | |
| num_msg_bits[1] = (uint) (message_length/2 /*+ message_length%2*/ ); // XXX | |
| try { | |
| stc_embed_trial( cover_length, p20, message, stc_constraint_height, num_msg_bits[1], perm2, stego2, trial, max_trials, "cost2.txt" ); | |
| } catch ( stc_exception& e ) { | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] stego1; | |
| delete[] stego2; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| throw e; | |
| } | |
| /* LAYER OF 1ST LSBs */ | |
| for ( uint i = 0; i < cover_length; i++ ) // | |
| if ( stego2[perm2[i]] == 0 ) // % conditional probability of 1st LSB of stego equal 0 given LSB2=0 | |
| p10[i] = p[i] / (p[i] + p[i + n]); // p10(i) = p(1,i)/(p(1,i)+p(2,i)); | |
| else // % conditional probability of 1st LSB of stego equal 0 given LSB2=1 | |
| p10[i] = p[i + 2 * n] / (p[i + 2 * n] + p[i + 3 * n]); // p10(i) = p(3,i)/(p(3,i)+p(4,i)); | |
| num_msg_bits[0] = m_actual - num_msg_bits[1]; // msg_bits(1) = m_actual-msg_bits(2); % number of msg bits embedded into 1st LSBs | |
| try { | |
| stc_embed_trial( cover_length, p10, message + num_msg_bits[1], stc_constraint_height, num_msg_bits[0], perm1, stego1, trial, | |
| max_trials, "cost1.txt" ); | |
| } catch ( stc_exception& e ) { | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] stego1; | |
| delete[] stego2; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| throw e; | |
| } | |
| delete[] p10; | |
| delete[] p20; | |
| /* FINAL CALCULATIONS */ | |
| distortion = 0; | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| stego[i] = stego_values[4 * i + 2 * stego2[perm2[i]] + stego1[perm1[i]]]; | |
| distortion += costs[4 * i + 2 * stego2[perm2[i]] + stego1[perm1[i]]]; | |
| } | |
| if ( coding_loss != 0 ) { | |
| dist_coding_loss = 0; | |
| for ( uint i = 0; i < cover_length; i++ ) | |
| dist_coding_loss += c[i + n * (2 * stego2[perm2[i]] + stego1[perm1[i]])]; | |
| float lambda_dist = get_lambda_distortion( n, 4, c, dist_coding_loss, lambda, 0, 20 ); // use 20 iterations to make lambda_dist precise | |
| float max_payload = calc_entropy( n, 4, c, lambda_dist ); | |
| (*coding_loss) = (max_payload - m_actual) / max_payload; // fraction of max_payload lost due to practical coding scheme | |
| } | |
| max_trials = trial; | |
| delete[] stego1; | |
| delete[] stego2; | |
| delete[] perm1; | |
| delete[] perm2; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| return distortion; | |
| } | |
| // }}} | |
| // {{{ stc_ml3_embed() | |
| // algorithm for embedding into 3 layers, both payload- and distortion-limited case | |
| float stc_ml3_embed( uint cover_length, float* costs, int* stego_values, uint message_length, u8* message, float target_distortion, // input variables | |
| uint stc_constraint_height, float expected_coding_loss, // other input parameters | |
| int* stego, uint* num_msg_bits, uint &max_trials, float* coding_loss ) { // output and optional variables | |
| float distortion, dist_coding_loss, lambda = 0, m_max = 0; | |
| uint m_actual = 0; | |
| uint n = cover_length + 4 - (cover_length % 4); // cover length rounded to multiple of 4 | |
| check_costs( cover_length, 8, costs ); | |
| float* c = align_new< float > ( 8 * n, 16 ); | |
| std::fill_n( c, 8 * n, F_INF ); | |
| std::fill_n( c, n, 0 ); | |
| for ( uint i = 0; i < 8 * cover_length; i++ ) | |
| c[n * (i % 8) + i / 8] = costs[i]; // copy and transpose data for better reading via SSE instructions | |
| // write_matrix_to_file<float>(n, 8, c, "cost_ml3.txt"); | |
| for ( uint i = 0; i < n; i++ ) { // normalize such that minimal element is 0 - this helps numerical stability | |
| float f_min = F_INF; | |
| for ( uint j = 0; j < 8; j++ ) | |
| f_min = std::min( f_min, c[j * n + i] ); | |
| for ( uint j = 0; j < 8; j++ ) | |
| c[j * n + i] -= f_min; | |
| } | |
| if ( target_distortion != F_INF ) { | |
| lambda = get_lambda_distortion( n, 8, c, target_distortion, 2.0 ); | |
| m_max = (1 - expected_coding_loss) * calc_entropy( n, 8, c, lambda ); | |
| m_actual = std::min( message_length, (uint) floor( m_max ) ); | |
| } | |
| if ( (target_distortion == F_INF) || (m_actual < floor( m_max )) ) { | |
| m_actual = std::min( 3 * cover_length, message_length ); | |
| lambda = get_lambda_entropy( n, 8, c, m_actual, 2.0 ); | |
| } | |
| /* | |
| p = exp(-lambda*costs); | |
| p = p./(ones(8,1)*sum(p)); | |
| */ | |
| float* p = align_new< float > ( 8 * n, 16 ); | |
| __m128 v_lambda = _mm_set1_ps( -lambda ); | |
| for ( uint i = 0; i < n / 4; i++ ) { | |
| __m128 sum = _mm_setzero_ps(); | |
| for ( uint j = 0; j < 8; j++ ) { | |
| __m128 x = _mm_load_ps( c + j * n + 4 * i ); | |
| x = exp_ps( _mm_mul_ps( v_lambda, x ) ); | |
| _mm_store_ps( p + j * n + 4 * i, x ); | |
| sum = _mm_add_ps( sum, x ); | |
| } | |
| for ( uint j = 0; j < 8; j++ ) { | |
| __m128 x = _mm_load_ps( p + j * n + 4 * i ); | |
| x = _mm_div_ps( x, sum ); | |
| _mm_store_ps( p + j * n + 4 * i, x ); | |
| } | |
| } | |
| // this is for debugging | |
| // float payload_dbg = entropy_array(8*n, p); | |
| uint trial = 0; | |
| float* p10 = new float[cover_length]; | |
| float* p20 = new float[cover_length]; | |
| float* p30 = new float[cover_length]; | |
| u8* stego1 = new u8[cover_length]; | |
| u8* stego2 = new u8[cover_length]; | |
| u8* stego3 = new u8[cover_length]; | |
| uint *perm1 = new uint[cover_length]; | |
| uint *perm2 = new uint[cover_length]; | |
| uint *perm3 = new uint[cover_length]; | |
| /* LAYER OF 3RD LSBs */ | |
| for ( uint i = 0; i < cover_length; i++ ) | |
| p30[i] = p[i] + p[i + n] + p[i + 2 * n] + p[i + 3 * n]; // | |
| num_msg_bits[2] = (uint) floor( binary_entropy_array( cover_length, p30 ) ); // | |
| try { | |
| stc_embed_trial( cover_length, p30, message, stc_constraint_height, num_msg_bits[2], perm3, stego3, trial, max_trials, "cost3.txt" ); | |
| } catch ( stc_exception& e ) { | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] p30; | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] perm3; | |
| delete[] stego1; | |
| delete[] stego2; | |
| delete[] stego3; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| throw e; | |
| } | |
| /* LAYER OF 2ND LSBs */ | |
| for ( uint i = 0; i < cover_length; i++ ) { // | |
| int s = 4 * stego3[perm3[i]]; // % conditional probability of 2nd LSB of stego equal 0 given LSB3 | |
| p20[i] = (p[i + s * n] + p[i + (s + 1) * n]) / (p[i + s * n] + p[i + (s + 1) * n] + p[i + (s + 2) * n] + p[i + (s + 3) * n]); | |
| } | |
| num_msg_bits[1] = (uint) floor( binary_entropy_array( cover_length, p20 ) );// msg_bits(2) = floor(sum(binary_entropy(p20))); % number of msg bits embedded into 2nd LSBs | |
| try { | |
| stc_embed_trial( cover_length, p20, message + num_msg_bits[2], stc_constraint_height, num_msg_bits[1], perm2, stego2, trial, | |
| max_trials, "cost2.txt" ); | |
| } catch ( stc_exception& e ) { | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] p30; | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] perm3; | |
| delete[] stego1; | |
| delete[] stego2; | |
| delete[] stego3; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| throw e; | |
| } | |
| /* LAYER OF 1ST LSBs */ | |
| for ( uint i = 0; i < cover_length; i++ ) { // | |
| int s = 4 * stego3[perm3[i]] + 2 * stego2[perm2[i]]; // % conditional probability of 1st LSB of stego equal 0 given LSB3 and LSB2 | |
| p10[i] = p[i + s * n] / (p[i + s * n] + p[i + (s + 1) * n]); | |
| } | |
| num_msg_bits[0] = m_actual - num_msg_bits[1] - num_msg_bits[2]; // msg_bits(1) = m_actual-msg_bits(2)-msg_bits(3); % number of msg bits embedded into 1st LSBs | |
| try { | |
| stc_embed_trial( cover_length, p10, message + num_msg_bits[1] + num_msg_bits[2], stc_constraint_height, num_msg_bits[0], perm1, | |
| stego1, trial, max_trials, "cost1.txt" ); | |
| } catch ( stc_exception& e ) { | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] p30; | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] perm3; | |
| delete[] stego1; | |
| delete[] stego2; | |
| delete[] stego3; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| throw e; | |
| } | |
| delete[] p10; | |
| delete[] p20; | |
| delete[] p30; | |
| max_trials = trial; | |
| /* FINAL CALCULATIONS */ | |
| distortion = 0; | |
| for ( uint i = 0; i < cover_length; i++ ) { | |
| stego[i] = stego_values[8 * i + 4 * stego3[perm3[i]] + 2 * stego2[perm2[i]] + stego1[perm1[i]]]; | |
| distortion += costs[8 * i + 4 * stego3[perm3[i]] + 2 * stego2[perm2[i]] + stego1[perm1[i]]]; | |
| } | |
| if ( coding_loss != 0 ) { | |
| dist_coding_loss = 0; | |
| for ( uint i = 0; i < cover_length; i++ ) | |
| dist_coding_loss += c[i + n * (4 * stego3[perm3[i]] + 2 * stego2[perm2[i]] + stego1[perm1[i]])]; | |
| float lambda_dist = get_lambda_distortion( n, 8, c, dist_coding_loss, lambda, 0, 20 ); // use 20 iterations to make lambda_dist precise | |
| float max_payload = calc_entropy( n, 8, c, lambda_dist ); | |
| (*coding_loss) = (max_payload - m_actual) / max_payload; // fraction of max_payload lost due to practical coding scheme | |
| } | |
| delete[] perm1; | |
| delete[] perm2; | |
| delete[] perm3; | |
| delete[] stego1; | |
| delete[] stego2; | |
| delete[] stego3; | |
| align_delete< float > ( c ); | |
| align_delete< float > ( p ); | |
| return distortion; | |
| } | |
| // }}} | |
| /* EXTRACTION ALGORITHMS */ | |
| // {{{ stc_ml_extract() | |
| /** Extraction algorithm for any l-layered construction. | |
| @param stego_length - ... | |
| @param stego - ... | |
| @param msg_bits - ... | |
| @param stc_constraint_height - ... | |
| @param message - ... | |
| */ | |
| void stc_ml_extract( uint stego_length, int* stego, uint num_of_layers, uint* num_msg_bits, // input variables | |
| uint stc_constraint_height, // other input parameters | |
| u8* message ) { // output variables | |
| u8* stego_bits = new u8[stego_length]; | |
| u8* msg_ptr = message; | |
| uint *perm = new uint[stego_length]; | |
| for ( uint l = num_of_layers; l > 0; l-- ) { // extract message from every layer starting from most significant ones | |
| // extract bits from l-th LSB plane | |
| if ( num_msg_bits[l - 1] > 0 ) { | |
| randperm( stego_length, num_msg_bits[l - 1], perm ); | |
| for ( uint i = 0; i < stego_length; i++ ) | |
| stego_bits[perm[i]] = mod( stego[i], (1 << l) ) >> (l - 1); | |
| stc_extract( stego_bits, stego_length, msg_ptr, num_msg_bits[l - 1], stc_constraint_height ); | |
| msg_ptr += num_msg_bits[l - 1]; | |
| } | |
| } | |
| delete[] stego_bits; | |
| delete[] perm; | |
| } | |
| // }}} | |