精华内容
下载资源
问答
  • 基于CSS类选择器重新定义的网页信息隐藏算法。
  • 封装和信息隐藏

    千次阅读 2018-11-08 19:45:24
    文章目录3.1 信息隐藏原则3.1.1 封装与信息隐藏3.1.2 接口扮演的角色3.2 创建对象的基本模式3.2.1 门户大开型对象3.2.2 用命名规范区别私用成员3.2.3 作用域、嵌套函数和闭包3.2.4 用闭包实现私用成员3.3 更多高级...

    为对象创建私用成员是任何面向对象语言中最基本和有用的特性之一。通过将一个方法或属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束。在代码有许多人参与设计的情况下,这也可以使代码更可靠、更易于调试。简而言之,封装是面向对象的设计的基石。
    尽管JavaScript是一种面向对象的语言,它并不具备用以将成员声明为公用或私用的任何内置机制。目前有几种办法可以用来创建具有公用、私用和特权方法的对象,它们各有优缺点。

    3.1 信息隐藏原则

    3.1.1 封装与信息隐藏

    可以把封装和信息隐藏视为同一个概念的两种表述。信息隐藏是目的,封装是达到这个目的的技术。
    封装可以被定义为对对象的内部数据表现形式和实现细节进行隐藏。要想访问封装过的对象中的数据,只有使用已定义的操作这一种办法。通过封装可以强制实施信息隐藏。许多面向对象语言都使用关键字来说明某些方法和属性应被隐藏。但在JavaScript中没有这样的关键字,我们将使用闭包的概念来创建只允许从对象内部访问的方法和属性。这比使用关键字的办法更复杂。

    3.1.2 接口扮演的角色

    在向其他对象隐藏信息的过程中接口是如何发挥作用的呢?
    接口提供了一份记载着可供公众访问的方法的契约。它定义了两个对象间可以具有的关系。只要接口不变,这个关系的双方都是可替换的。大多数情况下,你将发现对可以使用的方法加以记载会很有好处。不是有了接口就万事大吉,你应该避免公开未定义于接口中的方法。否则其他对象可能会对那些并不属于接口的方法产生依赖,这是不安全的。因为这些方法随时都可能发生改变或被删除,从而导致整个系统失灵。
    一个理想的软件系统应该为所有类定义接口。这些类只向外界提供它们实现的接口中规定的方法,任何别的方法都留作自用。其所有属性都是私有的,外界只能通过接口中定义的存取操作与之打交道。但实际的系统很少能真正达到这样的境界。优质的代码应尽量向这个目标靠拢,但又不能过于刻板,把那些并不需要这些特性的简单项目复杂化。

    3.2 创建对象的基本模式

    JavaScript创建对象的基本模式有3种

    1. 门户大开型,这是最简单的一种,但它只能提供公用成员。
    2. 使用下划线表示私用方法或属性。
    3. 使用闭包来创建真正的私用成员,这些成员只能通过一些特权方法访问。

    以Book为例,该类满足这样的需求:存储关于一本书的数据,并实现一个以HTML形式显示这些数据的方法。
    你只负责创建这个Book类,别人会创建并使用其实例。它会被这样使用:

    // Book (isbn, title, author)
    var theHobbit = new Book('0-395-07122-4', 'The Hobbit', 'J. R. R. Tolkien');
    theHobbit.display();//通过创建HTML element显示数据
    

    3.2.1 门户大开型对象

    用一个函数来做其构造器,它的所有属性和方法都是公开的、可访问的。这些公用属性需要使用this关键字来创建。

    var Book = function(isbn, title, author) {
    	if(isbn == undefined) throw new Error('Book constructor requires an isbn.');
    	this.isbn = isbn;
    	this.title = title || 'No title specified';
    	this.author = author || 'No author specified';
    };
    Book.prototype.display = function () {
    	...
    }
    

    好像提供了ISBN就可以查到书籍了,可是这里有一个最大的问题,你无法检验ISBN数据的完整性,而不完整的ISBN数据有可能导致display方法失灵。如果Book对象在创建的时候没有什么问题,那么在display时也能正常工作才对,但是由于没有进行完整性检查,就不一定了。下面的版本强化了对ISBN的检查。

    var Book = function(isbn, title, author) {
    	if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
    	this.isbn = isbn;
    	this.title = title || 'No title specified';
    	this.author = author || 'No author specified';
    }
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		if(isbn == undefined || typeof isbn != 'string'){
    			return false;
    		}
    		isbn = isbn.replace(/-/,'');
    		if(isbn.length != 10 && isbn.length !=13) {
    			return false;
    		}
    		var sum = 0;
    		if(isbn.length === 10) {//10位的ISBN
    			if(!isbn.match(/^\d{9}/)) {
    				return false;
    			}
    			for(var i = 0; i < 9; i++) {
    				sum += isbn.charAt(i) * (10-i);
    			}
    			var checksum = sum % 11;
    			if(checksum === 10) checksum = 'X';
    			if(isbn.charAt(9) != checksum) {
    				return false;
    			}
    		}
    		else {//13位的ISBN
    			if(!isbn.match(/^\d{12}/)) {
    				return false;
    			}
    			for(var i = 0; i < 12; i++) {
    				sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
    			}
    			var checksum = sum % 10;
    			if(isbn.charAt(12) != checksum) {
    				return false;
    			}		
    		}
    		return true;
    	},
    	display: function() {
    		...
    	}
    };
    

    checkIsbn保证ISBN是一个具有正确的位数和校验和的字符串。这样在创建对象的时候可以对ISBN的有效性进行检查,这可以确保display方法能正常工作。

    但问题又来了,即使在构造的时候能对ISBN进行检验,如果后续其他程序员把其他值赋给isbn,这时就检验不了了,

    theHobbit.isbn = '978-0261103283';
    theHobbit.display();
    

    为了保护内部数据,为每一个属性都提供了取值器和赋值器方法。

    • 取值器方法用于获取属性值,通常以getAttributeName这种形式命名;
    • 赋值器方法用于设置属性值,通常以setAttributeName这种形式命名。

    通过使用赋值器,你可以在把一个新值真正赋给属性之前进行各种检验。

    var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);
    
    var Book = function(isbn, title, author) {
    	this.setIsbn(isbn);
    	this.setTitle(title);
    	this.setAuthor(author);
    }
    
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		...
    	},
    	getIsbn: function() {
    		return this.isbn;
    	},
    	setIsbn: function(isbn) {
    		if(!this.checkIsbn()) throw new Error('Book: Invalid ISBN.');
    		this.isbn = isbn;
    	},
    	getTitle: function() {
    		return this.title;
    	},
    	setTitle: function(title) {
    		this.title = title || 'No title specified.';
    	},
    	getAuthor: function() {
    		return this.author;
    	},
    	setAuthor: function(author) {
    		this.author = author || 'No author specified.';
    	},
    	display: function() {
    		...
    	}
    };
    

    这是使用门户大开型对象创建方式所能得到的最好结果。

    这里明确定义了接口、一些对数据具有保护作用的取值器和赋值器方法,以及一些有效性检验方法。

    这里还是有一个漏洞,虽然我们为设置属性提供了赋值器方法,但那些属性仍然是公开的,可以被直接设置的,而在这种方案中却无法阻止这种行为。

    不过这种方法易于使用,创建这样的对象不要求你深入理解作用域或调用链的概念。由于所有方法和属性都是公开的,派生子类和进行单元测试也很容易。唯一的弊端在于无法保护内部数据,而且取值器和赋值器也引入了额外的代码。

    3.2.2 用命名规范区别私用成员

    从本质上来说,这种模式与门户大开型对象创建模式如出一辙,只不过在一些方法和属性的名称前加了下划线以示其私用性而已。这种方法可以解决上一种方法带来的问题:无法阻止其他程序员无意中绕过的所有检验步骤。

    var Book = function(isbn, title, author) {
    	this.setIsbn(isbn);
    	this.setTitle(title);
    	this.setAuthor(author);
    }
    
    Book.prototype = {
    	checkIsbn: function(isbn) {
    		...
    	},
    	getIsbn: function() {
    		return this._isbn;
    	},
    	setIsbn: function(isbn) {
    		if(!this.checkIsbn()) throw new Error('Book: Invalid ISBN.');
    		this._isbn = isbn;
    	},
    	getTitle: function() {
    		return this._title;
    	},
    	setTitle: function(title) {
    		this._title = title || 'No title specified.';
    	},
    	getAuthor: function() {
    		return this._author;
    	},
    	setAuthor: function(author) {
    		this._author = author || 'No author specified.';
    	},
    	display: function() {
    		...
    	}
    };
    

    这种命名规范也可以应用于方法,例如checkIson方法应该是类私有的方法:

    	_checkIsbn: function(isbn) {
    		...
    	},
    

    下划线的这种用法是一个众所周知的命名规范,它表明一个属性或方法仅供对象内部使用,直接访问它或设置它可能会导致意想不到的后果。这有助于防止程序员对它的无意使用,却不能防止对它的有意使用。

    这并不是真正可以用来隐藏对象内部数据的解决方法,它主要适用于非敏感性的内部方法和属性。

    3.2.3 作用域、嵌套函数和闭包

    在讨论真正的私用性方法和属性的实现技术之前,我们先花点时间解释一下这种技术背后的原理。
    在JavaScript中,只有函数具有作用域。也就是说,在一个函数内部声明的变量在函数外部无法访问。私用属性就其本质而言就是希望在对象外部无法访问的变量,所以为实现这种拒访性而求助于作用域这个概念是合乎情理的。

    定义在一个函数中的变量在该函数的内嵌函数中是可以访问的:

    function foo() {
    	var a = 10;
    	function bar() {
    		a *= 2;
    	}
    	bar();
    	return a;
    }
    foo();//20
    

    一个简单的闭包例子:

    function foo() {
    	var a = 10;
    	function bar() {
    		a *= 2;
    		return a;
    	}
    	return bar;
    }
    var baz = foo();
    baz(); // 20
    baz(); // 40
    baz(); // 80
    

    可以看到,函数是在foo外部被调用,但它依然能够访问a,这是因为JavaScript中的作用域是词法性的。函数是运行在定义它们的作用域中,而不是运行在调用它们的作用域中。所以,bar被定义在foo的作用域中,就能访问foo中定义的变量,即使foo的执行已经结束。

    3.2.4 用闭包实现私用成员

    var Book = function (newIsbn, newTitle, newAuthor) {
    	// Private attributes.
    	var isbn, title, author;
    
    	// Private method.
    	function checkIsbn(isbn) {
    		...
    	}
    
    	// Privileged methods.
    	this.getIsbn = function () {
    		return isbn;
    	};
    	this.setIsbn = function (newIsbn) {
    		if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
    		isbn = newIsbn;
    	};
    
    	this.getTitle = function () {
    		return title;
    	};
    	this.setTitle = function (newTitle) {
    		title = newTitle || 'No title specified';
    	};
    
    	this.getAuthor = function () {
    		return author;
    	};
    	this.setAuthor = function (newAuthor) {
    		author = newAuthor || 'No author specified';
    	};
    
    	// Constructor code.
    	this.setIsbn(newIsbn);
    	this.setTitle(newTitle);
    	this.setAuthor(newAuthor);
    };
    
    // Public, non-privileged methods.
    Book.prototype = {
    	display: function() {
    		...
    	}
    }
    

    代码解读:

    • 使用var声明的变量或方法是私有的,因为没有用this关键字,这意味着它们只存在于Book构造器中
    • 要访问这些私有的变量和方法,需在Book中使用this关键字声明方法,这些方法被称为特权方法(privileged method),它们是公用的方法,因为声明在Book内部,所以在对象外部可以访问到私用属性和方法。
    • 任何不需要直接访问私用属性的方法都可以在Book.prototype中声明。
    • 每生成一个新的对象实例都将为每一个私用方法和特权方法生成一个新的副本。这会比其他做法更耗内存。
    • 不利于派生子类,因为所派生出的子类不能访问超类的任何私用属性或方法。相比之下,在大多数语言中,子类都能访问超类的所有私用属性和方法。故在JavaScript中用闭包实现私用成员导致的派生问题被称为“继承破坏封装”

    3.3 更多高级对象创建模式

    前面学习了创建对象的3种基本模式,下面再对一些高级一点的模式做一个简介。

    3.3.1 静态方法和属性

    静态成员是在类的层次上操作的,而不是在实例的层次上操作。每个静态成员都只有一份。
    下面是添加了静态属性和方法的Book类:

    var Book = (function() {
    	// Private static attributes.
    	var numOfBooks = 0;
    
    	// Private static method.
    	function checkIsbn(isbn) {
    		...
    	}
    
    	// Return the constructor.
    	return function (newIsbn, newTitle, newAuthor) {
    		// Private attributes.
    		var isbn, title, author;
    	
    		// Private method.
    		function checkIsbn(isbn) {
    			...
    		}
    	
    		// Privileged methods.
    		this.getIsbn = function () {
    			return isbn;
    		};
    		this.setIsbn = function (newIsbn) {
    			if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
    			isbn = newIsbn;
    		};
    	
    		this.getTitle = function () {
    			return title;
    		};
    		this.setTitle = function (newTitle) {
    			title = newTitle || 'No title specified';
    		};
    	
    		this.getAuthor = function () {
    			return author;
    		};
    		this.setAuthor = function (newAuthor) {
    			author = newAuthor || 'No author specified';
    		};
    	
    		// Constructor code.
    		numOfBooks++;
    		if(numOfBooks > 50) throw new Error('Book: Only 50 instances of Book can be created.');
    		this.setIsbn(newIsbn);
    		this.setTitle(newTitle);
    		this.setAuthor(newAuthor);	
    	}
    })();
    
    // 创建Public static method.
    Book.convertToTitleCase = function(inputString) {
    	...
    };
    
    // Public, non-privileged methods.
    Book.prototype = {
    	display: function() {
    		...
    	}
    }
    

    这里创建了一个闭包,将构造器从原来的普通函数变成了一个内嵌函数,并且作为返回值赋给变量Book。外层的函数只是用于创建一个可以用来存放静态私用成员的闭包,这些静态私用成员不会为Book的每一个实例都创建一个新的副本。
    如何判断一个私用方法是否应该被设计为静态方法?
    看它是否需要访问任何实例数据。如果它不需要,则设计为静态方法会更有效率,因为它只会被创建一份。

    3.3.2 常量

    常量是一些不能被修改的变量。在JavaScript中,可以通过创建只有取值器而没有赋值器的私有变量来模仿常量。

    var Class = (function() {
    	// Constants (created as private static attributes.).
    	var UPPER_BOUND	= 100;
    	// Constructor.
    	var ctor = function (constructorArgument) {
    		...
    	};
    	// Privileged static method.
    	ctor.getUPPER_BOUND = function() {
    		return UPPER_BOUND;
    	};
    	...
    	// Return the constructor
    	return ctor;
    })();
    

    如果需要使用许多常量,但你不想为每个常量都创建一个取值器方法,那么可以创建一个通用的取值器方法,如下:

    var Class = (function() {
    	// Constants (created as private static attributes.).
    	var constants ={
    		UPPER_BOUND: 100,
    		LOWER_BOUND: -100
    	};
    	// Constructor.
    	var ctor = function (constructorArgument) {
    		...
    	};
    	// Privileged static method.
    	ctor.getConstant = function(name) {
    		return constants[name];
    	};
    	...
    	// Return the constructor
    	return ctor;
    })();
    Class.getConstant('UPPER_BOUND');
    

    3.3.3 单体和对象工厂

    单体模式和工厂模式也使用闭包来创建受保护的变量空间,后面部分会详细讨论这两种模式。在此简单介绍一下:

    • 单体模式使用一个由外层函数返回的对象字面量来公开特权成员,而私用成员则被保护性地封装在外层函数的作用域中。它使用的技术是:外层函数在定义之后立即执行,其结果被赋给一个变量。本章的例子中外层函数返回的都是一个函数,而单体模式返回的则是一个对象字面量。
    • 对象工厂也可以使用闭包来创建具有私用成员的对象。其最简形式就是一个类构造。

    3.4 封装之利

    要是在创建对象时不用操心闭包和特权方法,事情就会简单得多。那么,不厌其烦的隐藏实现细节究竟有什么好处?
    封装保护了内部数据的完整性。通过将数据的访问途径设置为取值器和赋值器这两个方法,可以获得对取值和赋值的完全控制。这可以减少其他函数所需的错误检查代码的数量,并确保数据不会处于无效状态。
    封装提高了对象的可重用性,使其在必要的时候可以被替换。使用私用变量也有助于避免命名空间冲突。
    封装还使你可以大幅改动对象的内部细节,而不会影响到其他部分的代码。总的来说,代码的修改变得更轻松,如果对象的内部数据都是公开的话,你不可能完全清楚代码的修改会带来什么结果。

    3.5 封装之弊

    私用方法很难进行单元测试。因为他们及其内部变量都是私用的,在对象外部无法访问到它们。要么通过使用公用方法来提供访问途径(这样一来就葬送了使用私用方法所带来的大多数好处),要么设法在对象内部定义并执行所有测试单元。最好的解决办法是只对公用方法进行单元测试。这是一种广为接受的处理方式。
    使用封装意味着不得不与复杂的作用域链打交道,而这会使错误调试变得更加困难。有时候会很难区分来自不同作用域的大批同名变量。这个问题不是经过封装的对象所特有的,但实现私用方法和属性所需的闭包会让它变得更复杂。
    过度封装也是一个潜在的问题。
    最大的问题在于JavaScript中实现封装的困难。JavaScript本来就是一门与多数面向对象语言大相径庭的语言,而封装技术设计的调用链和定义后立即执行的匿名函数等概念更是加大了学习难度。此外,封装技术的应用还使不熟悉特定模式的人难以理解既有代码。注释和程序文档可以提供一些帮助,但并不能完全解决这个问题。

    3.6 小结

    本章讨论了信息隐藏的概念以及如何用封装这种手段来实现它。因为JavaScript没有对封装提供内置的支持,所以其实现必须依赖于一些其他技术。本书后面的多数章节都依赖于这些基本技术,因此你得好好品味一下本节的内容。只要理解了JavaScript中作用域的特点,你就能模仿出各种面向对象的技术。

    展开全文
  • 利用MATLAB实现WAV音频的LSB信息隐藏

    千次阅读 2020-12-10 23:35:36
    近年来,虽然数字音频信息隐藏技术的研究工作发展很快,出现了一些代表算法,但是与图像信息隐藏算法相比,数字音频信息隐藏技术面临着更大的挑战,因为人类听觉系统对随机噪声十分敏感,使得可以嵌入的数据量非常...

    0. 背景

    音频信息隐藏技术就是将秘密数据直接嵌入音频载体中,但不影响原载体的使用价值,也不容易被人的知觉系统觉察或注意到。

    音频信息隐藏技术作为信息隐藏技术的一个重要分支,受到越来越多的关注。

    近年来,虽然数字音频信息隐藏技术的研究工作发展很快,出现了一些代表算法,但是与图像信息隐藏算法相比,数字音频信息隐藏技术面临着更大的挑战,因为人类听觉系统对随机噪声十分敏感,使得可以嵌入的数据量非常有限。

    为了能够简明扼要地对音频信息隐藏技术进行说明,本文对WAV音频的LSB这种时域信息隐藏算法进行了研究。

    1. LSB算法原理简介

    LSB全称为:Least Significant Bit,即最不重要位。LSB方法是一种最简单的数据嵌入方法。任何秘密数据都可以看做是一串二进制位流,而音频文件的每一个采样数据也是用二进制数来表示的。这样,可以将每一个采样值的最不重要位(大多数情况下为最低位),用代表秘密数据的二进制位替换,以达到在音频信号中编码进秘密数据的目的。
    下图即为利用LSB算法进行信息隐藏的过程。

    在这里插入图片描述

    提取隐藏的信息时的过程图如下图所示。
    在这里插入图片描述
    由图可知,如果有人截获了含有隐藏信息的音频采样数据,并且此人知道这段音频里藏有信息,且他知道被替换位为最低位,那么这个人就破解了这个秘密数据了。
    因此对于LSB算法来说,密钥就是秘密数据的替换位的位置信息,如果想加大对秘密数据攻击的难度,可以用一段伪随机序列来控制嵌入秘密二进制位的位置。那么这段伪随机序列就可以作为密钥了。

    2. 信息隐藏代码实现(随机序列)

    下面,为了简化代码,易于读者理解,秘密数据采取的是一串随机生成的序列,并且采用的是二进制的最低位替换方法。

    clc;
    clear;
    fid = fopen('music_new.wav', 'r');
    oa = fread(fid, inf, 'uint8');
    fclose(fid);
    
    n = length(oa) - 44;
    d = randsrc(1, n, [1 0]);
    M = oa;
    for i = 45 : 45 + n - 1  
        M(i) = bitset(M(i), 1, d(i - 44));
    end
    
    fid = fopen('music_new_secret.wav', 'wb');
    fwrite(fid, M, 'uint8');
    fclose(fid);
    
    figure;
    [y1,Fs] = audioread('music_new.wav');
    x1 = (0:length(y1) - 1)/Fs;
    subplot(2, 1, 1);
    plot(x1, y1);
    axis([0 max(x1) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('original audio');
    
    [y2,Fs] = audioread('music_new_secret.wav');
    x2 = (0:length(y2)-1)/Fs;
    subplot(2, 1, 2);
    plot(x2, y2);
    axis([0 max(x2) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('information hiding audio');
    
    
    figure;
    subplot(2, 1, 1); plot(x1, y1); axis([0 0.6 -0.05 0.05]);
    xlabel('Time / (s)');ylabel('Amplitude');title('original audio');
    
    subplot(2, 1, 2); plot(x2, y2); axis([0 0.6 -0.05 0.05]);
    xlabel('Time / (s)');ylabel('Amplitude');title('information hiding audio');
    
    • 这里解释一下第7行代码,为什么要减去44,同时也是解释了第10行代码,为什么要从45开始。
      因为我们是在wav格式的音频文件中进行信息隐藏,而wav格式的音频文件中,从文件头到数据前面一共有44字节的文件格式说明,因此在进行信息隐藏时,要跳过这44位。

    • 再解释一下第8行代码。第8行代码采用了MATLAB的生成指定范围的随机数函数,生成的随机数是1行n列的行向量,这个行向量的内容只有0和1。即,随机生成的这个向量d就是用来模拟一段秘密数据。

    • 第7行到第12行代码就是LSB算法的实现。首先第7行定义秘密数据的长度(这里取了能加密的最长的长度)。第8行生成了秘密数据d。第9行将原始音频的采样数据oa赋值给M,利用M存储含有隐藏信息的音频采样数据。第10行到第12行的代码,利用bitset函数,将M从第45个元素开始的每一位的最低位用秘密数据d的对应位替换掉,完成秘密数据的隐藏。

    在给出程序运行图之前,需要说明的是,这个音频是一首歌曲的前奏,并且,我们给的秘密信息的长度遍布了整个时域。对于这个时长为29秒的音频,它最多能够存储的秘密信息长度是2557800bit,换算后为319725Byte,约等于312KB。
    在这里插入图片描述
    下图是程序运行后输出的图片,第一张图是原始音频时域波形图,第二张图是含有隐藏信息的音频时域波形图。

    在这里插入图片描述
    从上面的对比图中难以看出差异,我们将其放大,将横轴的显示范围更改为0-0.6秒。这样便能够明显地看出差异。
    在这里插入图片描述

    3. 提取隐藏信息代码实现(随机序列)

    fid = fopen('music_new_secret.wav', 'r');
    oa = fread(fid, inf, 'uint8');
    
    m = oa;
    n = length(oa) - 44;
    for i = 1 : n
        w(i) = bitget(m(44 + i), 1);
    end
    fclose(fid);
    
    rate = length(find(w ~= d)) / length(d);
    
    • 提取隐藏信息的实现部分是在程序的第4至第8行。第4行代码将含有隐藏信息的音频采样数据赋值给m。第5行代码定义了待提取的秘密数据的长度(这个长度要和秘密数据d的长度保持一致,如果不一致,最终将会得到有缺失的秘密数据或者是多余的乱码,至少这个长度不能大于音频采样数据的长度-44)。第6行至第8行利用bitget函数,将m从第45个元素开始的每一位的最低位提取出来,存储到w中,最终,w就是秘密数据。
    • 第11行代码计算了一下误码率,因为本程序在提取秘密数据前没有对含有隐藏信息的音频做任何处理,因此这里得到的误码率rate为0。

    4. 二值图像作为秘密数据

    从第2节和第3节中,应该已经知道了信息隐藏以及信息提取的原理了。
    接下来,我们把一副二值图像当做秘密数据来进行隐藏。

    4.1 图像信息隐藏的代码实现

    首先给出待隐藏的图片,如下图所示。
    在这里插入图片描述
    下面为图像信息隐藏的代码。

    clc;
    clear;
    fid = fopen('music_new.wav', 'rb');
    oa = fread(fid, inf, 'uint8');
    fclose(fid);
    
    n = length(oa) - 44;
    io = imread('school.bmp');
    [row col] = size(io);
    wi = io(:);
    if row * col > n
        error('音频载体太小,请更换载体');
    end
    info_hd_audio = oa;
    for k = 1 : row * col
        info_hd_audio(44 + k) = bitset(info_hd_audio(44 + k), 1, wi(k));
    end
    
    fid = fopen('music_new_secret.wav', 'wb');
    fwrite(fid, info_hd_audio, 'uint8');
    fclose(fid);
    
    figure;
    [y1, Fs] = audioread('music_new.wav');
    x1 = (0:length(y1) - 1) / Fs;
    subplot(2, 1, 1);
    plot(x1, y1);
    axis([0 max(x1) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('original audio');
    
    [y2, Fs] = audioread('music_new_secret.wav');
    x2 = (0:length(y2) - 1) / Fs;
    subplot(2, 1, 2);
    plot(x2, y2);
    axis([0 max(x2) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('information hiding audio');
    
    
    figure;
    subplot(2, 1, 1); plot(x1, y1); axis([0 0.6 -0.05 0.05]);
    xlabel('Time / (s)');ylabel('Amplitude');title('original audio');
    
    subplot(2, 1, 2); plot(x2, y2); axis([0 0.6 -0.05 0.05]);
    xlabel('Time / (s)');ylabel('Amplitude');title('information hiding audio');
    

    代码的第11行到第13行是用来判断音频载体的大小是否足够用来隐藏待隐藏的图片。原理是:将原始音频采样数据oa减去44(wav文件的格式说明)后赋值给n,n即为这个音频最多能隐藏的信息的大小(单位为bit),再将二值图像的总的像素个数(row * col)与n作比较,如果像素个数小于等于n,那么程序继续执行,否则,载体不足以隐藏这个二值图像。

    除此之外,此代码的核心原理与隐藏信息为随机序列的代码(第2节)一致,因此不做过多介绍。

    下图是程序运行后输出的图片,第一张图是原始音频时域波形图,第二张图是含有隐藏信息的音频时域波形图。

    在这里插入图片描述
    从上面的对比图中难以看出差异,我们将其放大,将横轴的显示范围更改为0-0.6秒。这样便能够明显地看出差异。
    在这里插入图片描述

    4.2 对含有图像的音频进行攻击(压缩)的代码实现

    clc;
    clear;
    [y,Fs] = audioread('music_new_secret.wav');
    x = (0:length(y) - 1) / Fs;
    y1 = y * 0.8;
    
    subplot(2, 1, 1);
    plot(x, y);
    axis([0 max(x) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('information hiding audio');
    
    subplot(2, 1, 2);
    plot(x, y1);
    axis([0 max(x) -1 1]);
    xlabel('Time / (s)');ylabel('Amplitude');title('attacked audio');
    
    audiowrite('music_new_secret_attacked.wav', y1, Fs);
    

    对嵌入图像的音频进行压缩攻击测试。代码的第5行即是对音频的幅值进行压缩,压缩的倍数为0.8。

    下图是程序运行后输出的图片,第一张图是含有图像的音频时域波形图,第二张图是幅值压缩0.8倍后的含有图像的音频时域波形图。
    在这里插入图片描述

    4.3 提取隐藏图像的代码实现

    clc;
    clear;
    orig_pic = imread('school.bmp');
    
    
    fid = fopen('music_new_secret.wav', 'r');
    oa1 = fread(fid, inf, 'uint8');
    fclose(fid);
    n1 = 40000;
    for i = 1 : n1
        w1(i) = bitget(oa1(44 + i), 1);
    end
    w1 = w1';
    m1 = reshape(w1, 200, 200);
    figure;
    subplot(1, 2, 1);imshow(orig_pic);title('original picture');
    subplot(1, 2, 2);imshow(m1);title('not attacked extracted picture');
    imwrite(m1, 'school_extract.bmp', 'bmp');
    
    
    fid = fopen('music_new_secret_attacked.wav', 'r');
    oa2 = fread(fid, inf, 'uint8');
    fclose(fid)
    n2 = 40000;
    for i = 1 : n2
        w2(i) = bitget(oa2(44 + i), 1);
    end
    w2 = w2';
    m2 = reshape(w2, 200, 200);
    figure;
    subplot(1, 2, 1);imshow(orig_pic);title('original picture');
    subplot(1, 2, 2);imshow(m2);title('attacked extracted picture');
    imwrite(m2, 'school_extract_attacked.bmp', 'bmp');
    
    
    

    代码运行后输出两张图片,第一张图片是原图片由未被攻击的含有图像的音频中提取到的图片的对比图,如下图所示。
    在这里插入图片描述
    第二张图片是原图片由被攻击的含有图像的音频中提取到的图片的对比图,如下图所示。
    在这里插入图片描述
    由此可见,LSB算法的鲁棒性很差。

    4.4 更改载体音频重做一次实验

    上面做过的所有实验,采用的载体音频都是同一段音频,即一段长达29秒的歌曲前奏。

    接下来,我将载体音频改为本人录制的一段录音。录音的环境为空旷环境,内容为口播“一、二、三”。

    用到的所有代码与上面的代码都是一样的,仅仅是读取的音频文件不同。因此直接给出结果图。

    下图是图像信息隐藏的代码运行后输出的图片,第一张图是原始音频时域波形图,第二张图是含有隐藏信息的音频时域波形图。
    在这里插入图片描述
    从上面的对比图中难以看出差异,我们将其放大,将横轴的显示范围更改为0-0.6秒。这样便能够明显地看出差异。
    在这里插入图片描述
    下图是对含有图像的音频进行攻击(压缩)的代码运行后输出的图片,第一张图是含有图像的音频时域波形图,第二张图是幅值压缩0.8倍后的含有图像的音频时域波形图。
    在这里插入图片描述
    最后,提取隐藏图像的代码运行后输出两张图片,第一张图片是原图片由未被攻击的含有图像的音频中提取到的图片的对比图,如下图所示。
    在这里插入图片描述
    第二张图片是原图片由被攻击的含有图像的音频中提取到的图片的对比图,如下图所示。
    在这里插入图片描述
    由此可见,LSB算法的鲁棒性很差。

    5. LSB算法的特点

    LSB算法的特点是:

    • 本身简单易实现,音频信号里可编码的数据量大 。
    • 采用流加密方式分别对数据本身和嵌入过程进行加密,其安全性完全依赖于密钥,如果选择伪随机性能好的密钥产生机制,则可以做到“一次一密”。
    • 信息嵌入和提取算法简单,速度快。
    • 对信道干扰及数据操作的抵抗力很差,事实上,信道干扰、数据压缩、滤波、重采样、时域缩放等都会破坏编码信息。

    6. 其他音频信息隐藏算法

    • 回声隐藏算法
    • 相位隐藏算法
    • 傅氏变换域算法
    • 离散余弦变换域算法
    • 小波变换域算法
    展开全文
  • 我们在上一章节中已经学习了DataTables在BootStrap框架中的使用方式和初始化。 Bootstrap框架—-DataTables列表示例 最终效果如图: Bootstrap是自动适配移动端的,在...就是在移动端情况下隐藏部分列,点击展...

    我们在上一章节中已经学习了DataTables在BootStrap框架中的使用方式和初始化。
    Bootstrap框架—-DataTables列表示例

    最终效果如图:

    Bootstrap是自动适配移动端的,在手机上查看效果如图:

    我们发现当数据有很多列时,会存在超出屏幕的可能。这时候表格表现为在屏幕范围内可左右滑动。

    我们还有另一种解决方案。

    就是在移动端情况下隐藏部分列,点击展开按钮再展开。

    最终效果如图:

    可参考官网示例:
    显示行的附加信息
    http://datatables.club/example/api/row_details.html

    环境准备

    DataTables列表移动端适配定义隐藏列的使用建立在上篇文件的基础上
    Bootstrap框架—-DataTables列表示例

    主要需要的引用

    <link rel="stylesheet" href="/plugins/DataTables-1.10.16/extensions/Responsive/css/responsive.dataTables.css"></link>
    
    <script src="/plugins/DataTables-1.10.16/extensions/Responsive/js/dataTables.responsive.js"></script>

    注意事项

    需要注意的是 js的引用有顺序,否则会报找不到方法的各种错误。
    顺序是jQuery相关的js,bootstrap相关的js,datatables的js以及responsive的js,最后是datetimepicker的js。

    实现方案

    实现DataTables列表移动端适配定义隐藏列主要是通过dataTables.responsive.js实现的。
    我们查看dataTables.responsive.js文件里配置如下。

    Responsive.breakpoints = [
        { name: 'desktop',  width: Infinity },
        { name: 'tablet-l', width: 1024 },
        { name: 'tablet-p', width: 768 },
        { name: 'mobile-l', width: 480 },
        { name: 'mobile-p', width: 320 }
    ];


    分别表示在什么屏幕下显示该列。
    比如desktop表示在PC版大屏幕时才显示该列,否则隐藏。
    tablet-l表示1024的大小才显示该列,否则隐藏。
    以此类推。

    所以修改的代码只需要修改列头th的class名即可,如下:

      <thead>
                                        <tr>
                                            <th width="8%" class="mobile-l">姓名</th>
                                            <th width="10%" class="mobile-l">电话</th>
                                            <th width="10%" class="mobile-l">性别</th>
                                            <th width="10%" class="desktop">时间</th>
                                            <th width="8%" class="desktop">操作</th> 
                                        </tr>
                                    </thead>

    完整版JSP代码

    <%@ include file="./include/header.jsp"%>  
    <!-- jquery.dataTables.css -->
    <link href="/plugins/DataTables-1.10.16/media/css/dataTables.bootstrap.css" rel="stylesheet"
              type="text/css"></link>
    <link rel="stylesheet" href="/plugins/DataTables-1.10.16/extensions/Responsive/css/responsive.dataTables.css"></link>
      <!-- DATE STYLES-->
    <link rel="stylesheet" href="/plugins/bootstrap-datetimepicker-master/css/bootstrap-datetimepicker.min.css"></link>
    
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
    <%@ taglib prefix="cf" uri="com.data.web.view.function" %>  
            <div id="page-wrapper">  
                <div id="page-inner">  
    
    
            <div class="row">  
                        <div class="col-md-12">  
                            <h1 class="page-header">  
                  DataTables<small>示例</small>  
                            </h1>  
                        </div>  
           </div>  
    
    
           <div class="row">
                <form class="form-inline col-sm-11" id="search_form">
                    <div class="form-group ">
                        <input class="form-control" placeholder="名称或其他" name="name">
                    </div>
                    <div class="form-group">
                        <select class="form-control" id="gender" name="gender">
                            <option value="">--</option>
                            <option value=1></option>
                            <option value=0></option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="beginTime">开始</label> <input type="text"
                            class="datepicker form-control"
                            data-date-format="yyyy-mm-dd 00:00:00" name="beginTime"
                            id="beginTime" />
                    </div>
                    <div class="form-group">
                        <label for="endTime">截止</label> <input type="text"
                            class="datepicker form-control"
                            data-date-format="yyyy-mm-dd 23:59:59" name="endTime" id="endTime" />
                    </div>
                    <button type="button" id="query" class="btn btn-success query mt-5">查询</button>
                    <button type="button" id="add" class="btn btn-info add mt-5">新建</button>
                </form>
            </div>
            <!-- /. ROW  -->
    
    
          <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">示例列表</div>
                        <div class="panel-body">
                            <div class="table-responsive">
                                <table class="table table-striped table-bordered responsive table-hover" id="table" cellspacing="0" width="100%">
                                    <thead>
                                        <tr>
                                            <th width="8%" class="mobile-l">姓名</th>
                                            <th width="10%" class="mobile-l">电话</th>
                                            <th width="10%" class="mobile-l">性别</th>
                                            <th width="10%" class="desktop">时间</th>
                                            <th width="8%" class="desktop">操作</th> 
                                        </tr>
                                    </thead>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- /. ROW  -->
    
            </div>  
                <!-- /. PAGE INNER  -->  
            </div>  
            <!-- /. PAGE WRAPPER  -->  
     <%@ include file="./include/footer.jsp"%>  
     <!-- DATA TABLE SCRIPTS -->
    <script type="text/javascript" src="/plugins/DataTables-1.10.16/media/js/jquery.dataTables.min.js"></script>
    <script type="text/javascript" src="/plugins/DataTables-1.10.16/media/js/dataTables.bootstrap.min.js"></script>
    <script src="/plugins/DataTables-1.10.16/extensions/Responsive/js/dataTables.responsive.js"></script>
    <script src="/plugins/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.min.js"></script>
    <script src="/plugins/bootstrap-datetimepicker-master/js/locales/bootstrap-datetimepicker.zh-CN.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {
    
        $('.datepicker').datetimepicker({
            language : 'zh-CN',
            autoclose : true
        });
    
        refreshTable();//刷新列表数据内容
        clickEvent();//绑定表格中的按钮事件
    
        //点击查询  
        $('button.query').on("click", function() {
            refreshTable();
            clickEvent();
        });
    
        $('button.add').on('click',function(){
            location.href = '/create';
        });
    
    });
    function clickEvent(){
        $('#table').on('click','.js-delete',function(){
            if(confirm('确认删除该条数据?')){
                var id = $(this).attr('data-id');
                $.get('/delete?id='+id+'', function(result){
                console.log(result);
                    if(result==0){
                        alert('删除成功');
                        window.location.reload();
                    }
                })
            }
        }).on('click','.js-edit',function(){
            var id = $(this).attr('data-id');
            location.href = '/edit?id='+id+'';
        })
    }
    
    function refreshTable(){
    
        var $searchForm = $('#search_form').on('submit', function () {
            $dt.DataTable().searchEx({}).draw();
            return false;
        });
    
        var $dt = $('#table').on('preXhr.dt', function (e, settings, data) {
            //data.search.value = $searchForm.formGet();
        }).dataTable({
            "columns" : [
                {
                    "data" : "name",
                    "class" : "text-center"
                },
                {
                    "data" : "tel",
                    "class" : "text-center",
                    "render" : function(data, type, row) {
                        if(data){
                        var ms="00-";
                        ms=row.name+"的电话"+ms+data;
                        return ms;
                        }
                        return "";
                    }
                },
                {
                    "data" : "gender",
                    "class" : "text-center",
                    "render" : function(data, type, row) {
                        if(data==0){
                        return "女";
                        }else if(data==1){
                        return "男";
                        }
                        return "";
                    }
                },
                {
                    "data" : "creatTime",
                    "class" : "text-center",
                    "render" : function(data, type, row) {
                        return new Date(data).Format("yyyy-MM-dd hh:mm:ss");  
                    }
                },
                {
                    "data" : "id",
                    "class" : "text-center",
                    "render" : function(data, type, row) {
                        return '<span class="btn btn-primary btn-xs ml-5 js-edit" data-id="'+data+'">编辑</span> <span class="btn btn-danger btn-xs ml-5 js-delete" data-id="'+data+'">删除</span>';
                    }
                }
              ],
            "ajax" : {//类似jquery的ajax参数,基本都可以用。
                type : "post",//后台指定了方式,默认get,外加datatable默认构造的参数很长,有可能超过get的最大长度。
                url : "/listData",
                dataSrc : "data",//默认data,也可以写其他的,格式化table的时候取里面的数据
                data : function(d) {//d 是原始的发送给服务器的数据,默认很长。
                    var param = {};//因为服务端排序,可以新建一个参数对象
                    param.start = d.start;//开始的序号
                    param.length = d.length;//要取的数据的
                    var formData = $(
                            "#search_form")
                            .serializeArray();//把form里面的数据序列化成数组
                    formData
                            .forEach(function(e) {
                                param[e.name] = e.value;
                            });
                    return param;//自定义需要传递的参数。
                },
            },
            //"ajax": $.fn.dataTable.pagerAjax({url: "/listData"}),
            "destroy":true,
            lengthChange : false,
            serverSide : true,//分页,取数据等等的都放到服务端去
            searching : false,
            processing : true,//载入数据的时候是否显示“载入中”
            bDestroy : true,
            pageLength : 20,//首次加载的数据条数
            ordering : false,//排序操作在服务端进行,所以可以关了。
            language : {
                processing : "载入中",//处理页面数据的时候的显示
                paginate : {//分页的样式文本内容。
                    previous : "上一页",
                    next : "下一页",
                    first : "第一页",
                    last : "最后一页"
                },
                zeroRecords : "没有内容",//table tbody内容为空时,tbody的内容。
                //下面三者构成了总体的左下角的内容。
                info : "第 _PAGE_/_PAGES_页 共 _TOTAL_条记录",//左下角的信息显示,大写的词为关键字。
                infoEmpty : "第 _PAGE_/_PAGES_页 共 _TOTAL_条记录",//筛选为空时左下角的显示。
                infoFiltered : ""//筛选之后的左下角筛选提示(另一个是分页信息显示,在上面的info中已经设置,所以可以不显示),
            },
            "columnDefs": [{
                "defaultContent": "",
                "targets": "_all"
              }]
        }).on('click', 'a[row-index]', function () {
        }); 
    }
    
    
    //对Date的扩展,将 Date 转化为指定格式的String
    //月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 
    //年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
    //例子: 
    //(new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
    //(new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18 
    Date.prototype.Format = function (fmt) { //author: meizz 
     var o = {
         "M+": this.getMonth() + 1, //月份 
         "d+": this.getDate(), //日 
         "h+": this.getHours(), //小时 
         "m+": this.getMinutes(), //分 
         "s+": this.getSeconds(), //秒 
         "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
         "S": this.getMilliseconds() //毫秒 
     };
     if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
     for (var k in o)
     if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
     return fmt;
    }
    </script>
    </body>  
    </html>  
    展开全文
  • 信息安全之加密域可逆信息隐藏

    千次阅读 2020-04-30 09:34:04
    前面四篇文章主要介绍了图像置乱加密的安全性问题,本篇文章介绍一种图像加密算法的应用算法,图像加密域可逆信息隐藏。以文献1为例,介绍一种《基于预测误差分类置乱的图像加密域可逆信息隐藏》算法[1]。该文章是...

    前面四篇文章主要介绍了图像置乱加密的安全性问题,本篇文章介绍一种图像加密算法的应用算法,图像加密域可逆信息隐藏。以文献1为例,介绍一种《基于预测误差分类置乱的图像加密域可逆信息隐藏》算法[1]。该文章是文献2中算法(以下简称XU算法)的改进。

    前言:
    加密图像可逆信息隐藏(reversible data hiding in encrypted image, RDH-EI)是一种加密原始图像后,在密文图像中可逆地隐藏附加数据,并且在数据提取后,原始图像可以被无损重建的技术。可逆信息隐藏技术和传统的信息隐藏技术最大的区别就是所隐藏的秘密信息和原始载体都可以从含密图像中恢复出来。可逆信息隐藏技术在很多领域都有应用,比如医学图像、多媒体档案管理、图像转码以及视频错误隐藏编码等。
    2016年XU将预测误差技术应用于加密域中, Xu的算法首先对图像进行预测,采用传统预测方法并用预测误差替代原始像素得到预处理图像。接着对预处理图像加密,加密方法如下:(1)对采样像素进行异或加密。(2)对非采样像素:阈值之内的像素不加密,阈值之外的非采样像素采用Mod加密。Xu的加 密方法优点是加密后的像素值仍能保持在规定的范围内,缺点是对非采样阈值之内的像素不加密,以及采样像素仅异或加密会导致信息泄露[14]。Xu算法信息嵌入采用的是直方图平移和差值扩展技术,这种方法一个像素仅能嵌入1比特信息,限制了嵌入容量。XU的算法没有考虑到对于加密域信息隐藏不用顾及图像不可见性问题,因此在阈值之内的预测误差高位平面均可嵌入信息。同时因为算法嵌入数据与像素的位置无关,故加密方式可采用异或加密和置乱相结合的方法来提高加密图像的安全性。

    算法简介:
    第一步:预测误差,过程如下图:
    预测误差
    先通过黑色块中的像素值 预测 圈1 中的像素,再利用圈1和上下黑色块像素值预测 圈2 中的像素值。最后用 预测值-实际值 得到预测误差。

    第二步:生成Map(由0和1组成的矩阵) 记录像素值原始坐标,并将第一部中的预测误差重排
    在这里插入图片描述
    这一步设定了个阈值,[-1,2]。 即预测误差在[-1,2]之内的表示为黑点,,预测误差在[-1,2]之外的表示为白圈。需要注意Map的生成过程为:扫描原始预测误差图像,遇到预测误差值在[-1,2]范围内标记1,遇到阈值之外的差值标记为0.

    第三步:阈值之内的差值编码。 这一步对-1,0,1,2这四个差值进行编码,将这四个阈值表示为: 0,1,2,3。仅用两位比特 :00 01 10 11即可,那么高6位 既可用于隐藏信息。对于阈值之外的差值用最高位表示符号位。这样可以表示[-127,+127] 范围的差值。[-127,+127]之外的差值(极少数)用另一个Map2 进行记录。
    第四步,分类置乱加密。分别对第二步中右图的黑点、白圈、黑块像素值异或置乱加密。注意这里是分别置乱异或,并不是全局置乱异或。
    这样就得到了加密图像,隐藏信息者可以在密文图像中位于前方的黑点像素的高六位隐藏信息。
    所取得的隐藏容量相比于XU算法:
    在这里插入图片描述
    [1] 屈凌峰, 和红杰, 陈帆. 基于预测误差分类置乱的图像加密域可逆信息隐藏[J]. 光电子·激光, 2019, 30(02):62-68.
    [2] Dawen Xu, and Rangding Wang, “Separable and error-free reversible data hiding in encrypted images,” Signal Processing, June 2016,v 123, p 9-21;

    展开全文
  • Java实现 LeetCode 831 隐藏个人信息(暴力)

    千次阅读 多人点赞 2020-05-10 22:40:27
    831. 隐藏个人信息 给你一条个人信息字符串 S,它可能是一个 邮箱地址 ,也可能是一串 电话号码 。 我们将隐藏它的隐私信息,通过如下规则: 电子邮箱 定义名称 name 是长度大于等于 2 (length ≥ 2),并且只包含...
  • 以本人浅显的经验,把信息隐藏按照程度的不同分成(1)不可见不可用(2)可见不可用(3)可见可用。1 不可见不可用就是说模块内部的变量、结构体、类定义对外部而已完全隐藏,外部对此一无所知。常用的实现方法就是...
  • 现在尝试着在模型里面添加一层隐藏层吧!数据还是手写字体的数据,先看看拥有一层隐藏层的神经网络(多层感知机也叫)示意图吧!  Boom!博主懒得网上找图了,直接动手……原谅我笨拙的字体和手法……接下去我们看看...
  • 但是,模块的用户并不需要知道模块实现的任何细节,而且除了那些定义好的接口之外,用户不能以任何方式访问模块,这个定义觉得是不是和面向对象语言的类的定义类似呢,c没提供类这种结构,c实现抽象数据类型的武器...
  • 信息隐藏(Information Hiding)原则

    千次阅读 2012-07-20 14:34:12
    信息隐藏是结构化设计与面向对象设计的基础。在结构化中函数的概念和面向对象的封装思想都来源于信息隐藏。软件业对这个原则的认同也是最近十年的事情。  David Parnas在1972年最早提出信息隐藏的观点。他在其论文...
  • 831. 隐藏个人信息

    千次阅读 2019-06-17 08:15:25
    我们将隐藏它的隐私信息,通过如下规则: <u>1. 电子邮箱</u> 定义名称 <name> 是长度大于等于 2 (length ≥ 2),并且只包含小写字母 a-z 和大写字母 A-Z 的字符串。 电子邮箱地址由名称 &...
  • 项目中用js隐藏身份证信息

    千次阅读 2015-05-11 16:21:32
    在项目中通过js来隐藏身份证的某些字符,电脑版的实现是在页面截取,现在做手机版,想把截取字符串放在后台来操作,但为了保证手机版和电脑版显示一致,所以希望截取规则也能一致,ftl页面的js写法如下: ...
  • 对于上述问题,传统上通过加密技术和信息隐藏技术来缓解。加密技术由于乱码的存在容易吸引拦截者的注意,从而想方设法对其破解。而传统的信息隐藏技术经常导致载体不可逆转的失真,意味着载体在提取秘密信息之后将不能...
  • 基于LSB的信息隐藏实现

    千次阅读 2006-11-09 07:50:00
    LSB就是最不重要位信息隐藏算法,读取出图片中每个点的象素值,然后把信息的bit序列填充到这些象素的最低上.然后在分析检测的时候提取出最低位,就可以恢复原始信息.因为是在最低位上进行隐藏,所以图像的失真是比较小的...
  • person.c实现了对这个结构struct person分配内存,释放内存,和获取对象信息的函数。其中person_private.h只被person.c包含。 person.h // person.h #ifndef __PERSON_H__ #define __PERSON_H__ ...
  • 当表单被提交时,隐藏域就会将信息用你设置时定义的名称和值发送到服务器上。 代码格式:<input type="hidden" name="..." value="..."> 属性解释: type=”hidden”定义隐藏域; name属性定义隐藏域的名称...
  • 有关封装和信息隐藏的误区

    万次阅读 热门讨论 2010-08-29 00:10:00
     可以通过定义接口类(Java中的interface、C++中的纯虚类)来实现信息隐藏。具体实现如下:  定义一个接口类,仅包含一些公有的成员函数的 声明 (Java的abstract函数,C++的纯虚函数),没有任何函数实现...
  • 设计的核心任务之二:信息隐藏

    千次阅读 2012-04-17 23:59:45
    的时候,实际上只需要类的两个属性,但却把整个类定义成了这个方法的参数,这也是违背信息隐藏原则的。 还有一种比较有意思的情况是,目的是信息隐藏,但实际上却违背了信息隐藏原则。 比如说:类的方法中常常会使用...
  • 主要就是实现了数字水印的嵌入提取和在不同攻击如旋转剪切噪声等下的提取效果差异的比对1 DCT变换的原理2 DCT变换的特点 在基于DCT的变换编码中,图像是先经分块(8×8或16×16)后...,只反映了图像某一部分的信息...
  • 定义详解

    万次阅读 多人点赞 2017-08-29 15:32:09
    定义的黑魔法 - 宏菜鸟起飞手册 宏定义在C系开发中可以说占有举足轻重的作用。底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行。而在更高层级进行...
  • php隐藏

    千次阅读 2012-11-24 21:13:32
    隐藏定义隐藏域是用来收集或发送信息的不可见元素,对于网页的访问者来说,隐藏域是看不见的。当表单被提交时,隐藏域就会将信息用你设置时定义的名称和值发送到服务器上。 aaa.html文件: //跳转到bbb.php...
  • 如何查看和隐藏 BIND 的版本信息

    千次阅读 2015-01-21 14:40:58
    如何隐藏 BIND 版本信息 修改/etc/named.conf ,在named.conf文件里添加 options { directory "/var/named"; version "[ cuisir ]"; }; 重起named服务,再查看 c:>nslookup -q=txt -class=...
  • http://blog.csdn.net/jiangwei0910410003/article/details/52836241一、前言前文已经介绍了Xposed框架的基本使用规则,本文主要来介绍一个实际案例就是如何通过这个框架来修改系统的地理位置信息来实现隐藏功能,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 388,181
精华内容 155,272
关键字:

信息隐藏的定义