本文目錄一覽:
- 1、java 實現人臉的動漫化 照片用數組存還是image對象?提取人臉的什麼特徵?人臉處理常用哪些特徵?
- 2、java裡面獲得圖像像素後怎麼做dct變換
- 3、JAVA與圖像壓縮
- 4、什麼是dct 變換?它有什麼特點?它被廣泛應用在什麼編碼標準中
- 5、關於離散餘弦變換(DCT)
java 實現人臉的動漫化 照片用數組存還是image對象?提取人臉的什麼特徵?人臉處理常用哪些特徵?
YOGUAI為保險柜,自動門,考勤等行業提供人臉識別方案。
隨著計算機網路和通信技術的發展,信息安全、知識產權保護和身份認證等問題成了一個重要而緊迫的研究課題。身份認證是保證系統安全的必要前提,在多種不同的安全領域都需要準確的身份認證。傳統的身份證、智能卡、密碼等身份認證方法存在攜帶不便、容易遺失、不可讀或密碼易被破解等諸多問題。基於人臉識別技術的身份認證方法與傳統的方法相比,具有更好的安全性、可靠性和有效性,因此正越來越受到人們的重視,並逐漸進入社會生活的各個領域。
人臉識別技術具有廣泛的應用前景,可以應用到多種不同的安全領域,因其識別特徵的獨特性、惟一性和相對穩定性,逐漸成為一非常熱門的研究課題。許多典型的人臉識別演算法和應用系統都是針對標準或特定的人臉資料庫,利用庫內人臉進行訓練,並在相同的庫中實現人臉識別。但在軟體保護、計算機安全等特殊應用中,身份認證僅針對單個對象進行人臉識別,現有的人臉識別方法並不能勝任這樣的識別任務。為此,本文針對單對象人臉識別的特點,討論了單對象人臉檢測和識別的關鍵技術,在此基礎上提出了一種單對象人臉識別演算法,實驗結果證明了該方法的有效性。
2單對象人臉識別的特點
與典型的人臉識別相比,單對象人臉識別有以下4個方面的特點:
應用領域人臉識別的應用領域很廣,如刑偵破案、證件核對、保安監控等,而單對象人臉識別主要應用在軟體保護、計算機安全鎖、特定對象追蹤等領域。
識別系統的目標單對象人臉識別的最終目標是系統必須具有高度的安全性和可靠性,即識別錯誤率趨於0。雖然降低識別錯誤率的同時識別率也會降低,但可以通過提示用戶調整姿態(如注視攝像頭等)加以改善。
膚色模型由於單對象人臉識別僅針對特定的對象,所以人臉檢測的膚色模型可採用自適應的方法調整膚色範圍。
分類方法單對象人臉識別不存在人臉資料庫,常用的最小距離分類法不能夠正確識別特定的對象,只能用閾值作為判據。因此,閾值的選取十分重要,閾值過大則容易出現錯判,存在安全隱患;而閾值過小又會影響識別效率。
3人臉的檢測和歸一化
人臉檢測是人臉識別的前提。對於給定的圖像,人臉檢測的目的在於判斷圖像中是否存在人臉,如果存在,則返回其位置和空間分布。利用人臉膚色和面部特徵,將人臉檢測分為兩個階段:外臉檢測和內臉定位。外臉檢測主要利用人臉膚色進行初步的臉區檢測,分割出膚色區域;內臉檢測是在外臉區域中利用面部幾何特徵進行驗證和定位。
3.1外臉檢測
外臉檢測的任務是將待檢圖像中可能的人臉區域找出來並加以標記,其步驟如下:
(1)根據人類膚色在色彩空間中存在區域性的特點,將可能為人臉的像素檢測出來。為更好地利用膚色特徵,同時選用HSI和YcbCr兩種色彩空間對圖像進行二值化處理,膚色範圍限定在H∈[0,46],S∈[0.10,0.72],Cb∈[98,130],Cr∈[128,170]內。將滿足條件的像素標記為膚色像素,其餘的均為非膚色像素。
(2)去噪處理。在以每一個膚色點為中心的5×5鄰域內統計膚色像素的個數,超過半數時中心點保留為膚色,否則認為是非膚色。
(3)將二值圖像中的膚色塊作區域歸併,並對目標區域進行比例、結構分析,過濾掉不可能的人臉區域。目標區域的高度/寬度比例限定在0.8~2.0。
3.2內臉檢測和定位
將包含眼、眉、鼻和嘴的區域稱為內臉區域。內臉區域能夠很好地表達人臉特徵,且不易受背景、頭髮等因素的干擾,因此內臉區域的檢測和定位對後續的特徵提取和識別至關重要。
在外臉區域的上半部,對二值圖像進行水平方向和垂直方向的投影,確定兩個包含黑點的矩形區域作為雙眼的大致區域。在確定的兩個區域中,對黑點進行區域膨脹,可以得到眼睛的基本輪廓和左石眼角,黑點坐標的平均值作為瞳孔的位置。
設左右瞳孔的坐標分別為(Lx,Ly)和(Rx,Ry),兩個瞳孔之間的距離為d,根據人臉的幾何特徵,我們將內臉區域定義為:寬度=-d×1.6,高度=-d×1.8,左上角坐標為(Lx-d×0.3,(Ly Ry)/2-(-d)×0.3)。實驗表明,該區域能夠很好地表達人臉特徵。
3.3內臉區域的歸一化
由於各待測圖像中的人臉大小具有很大的隨機性,因此,有必要對內臉區域進行歸一化操作。人臉歸一化是指對內臉區域的圖像進行縮放變換,得到統一大小的標準圖像,實驗中,我們規定標準圖像的大小為128×128。歸一化處理,保證了人臉大小的一致性,體現了人臉在圖像平面內的尺寸不變性。
對歸一化的人臉圖像,採用小波變換與DCT相結合的方法提取人臉特徵。首先對人臉圖像進行3層小波分解,取低頻子圖像LL3作為人臉特徵提取的對象,從而獲得每幅訓練樣本或測試樣本的低頻子圖像;然後對低頻子圖像進行離散餘弦變換(DCT),DCT係數個數與子圖像的大小相等(即256),由於圖像DCT變換,能量集中在低頻部分,因此只取其中的136個低頻係數作為特徵向量。
5人臉的識別
完成訓練過程並獲得待測樣本的特徵後,即可進行人臉識別,本文採用歐氏距離進行分類。
5.1計算樣本與平均臉的歐氏距離
用m和x表示平均臉和樣本的特徵向量,則樣本與平均臉的歐氏距離為:
其中mk表示平均臉的第k個特徵向量,xk表示待測樣本的第k個特徵向量。身份認證時,計算待測樣本與平均臉的歐氏距離,並與特定對象的自適應閾值進行比較,將小於閾值的樣本判為該對象的人臉,即認證通過。
5.2自適應閾值的選取
與典型的人臉識別方法不同,單對象人臉認識沒有人臉資料庫,不能用距離最小作為判據,只能用閾值作為判別依據。閾值的選取應兼顧識別率和識別的準確性,實驗中我們取訓練樣本與平均臉的歐氏距離平均值作為分類閾值,即:
其中,N為訓練樣本數,此值不宜太小;di為第i個樣本與平均臉之間的歐氏距離。
莫士特科技有限公司提供模式識別主板及解決方案。
希望採納
java裡面獲得圖像像素後怎麼做dct變換
private void setAlpha(ByteArrayOutputStream os) {
try {
ImageIcon imageIcon = new ImageIcon(os.toByteArray());
BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(),imageIcon.getIconHeight()
, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
g2D.drawImage(imageIcon.getImage(), 0, 0,
imageIcon.getImageObserver());
//循環每一個像素點,改變像素點的Alpha值
int alpha = 100;
for (int j1 = bufferedImage.getMinY(); j1 bufferedImage.getHeight(); j1++) {
for (int j2 = bufferedImage.getMinX(); j2 bufferedImage.getWidth(); j2++) {
int rgb = bufferedImage.getRGB(j2, j1);
rgb = ( (alpha + 1) 24) | (rgb 0x00ffffff);
bufferedImage.setRGB(j2, j1, rgb);
}
}
g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
//生成圖片為PNG
ImageIO.write(bufferedImage, “png”, new File(圖片路徑));
}
catch (Exception e) {
e.printStackTrace();
}
}
使用JAVA對圖片進行效果變換(第二幅圖覆蓋第一幅圖的像素並顯示)
一提到JAVA,談論最多的就是JSP/SERVLET/J2EE之類的,但對於用JAVA對圖片進行效果變換,到論壇里看了看,關於這方面的話題並不多,網上關於圖像效果處理的文章也並不豐富,於是在自己摸索了幾天,並且參考了AnfyJava(專業級的JAVA效果生成器)後,我用輕量級控制項寫了一個(AnfyJava繼承的是Applet,Applet是java.awt包中的,屬於重量級控制項,SUN現在推薦使用swing來編寫圖形程序,因此,我用的是JApplet)。
其實,用JAVA做圖像效果和其它語言在本質上並沒有什麼區別,只不過在實現起來有所不同罷了,下面我就把我在項目中處理的經驗與大家分享一下吧。
圖像的變換,實際上就是把兩幅圖片的內容進行某些運算,生成新的圖像,然後顯示出來,最終實現從一幅圖片到另一幅圖片的過度效果。變換的具體過程如下:
在上面的過程中,圖片A和B的尺寸最好保持一致,如果不一致的話,可能要做一些額外的處理,在此,我選用的圖片A和B的尺寸是一致的。
首先,我們將其當作一個Applet來寫,由於Applet的局限性,不可以直接使用File類來讀取圖像文件,因此,我們只能通過如下方法來獲取圖像文件。
URLClassLoader urlLoader = (URLClassLoader)this.getClass().getClassLoader();
URL url = urlLoader.findResource(“imagea.gif”);
Image image = Toolkit.getDefaultToolkit().getImage(url);
當我們獲得了圖像後,可以通過java.awt.image.PixelGrabber包中的PixelGrabber方法來將圖像中的像素信息完全讀取出來,其用法如下:
PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, int off, int scansize)
其中img是要讀取的圖像,x/y是要讀取圖像中的左上角坐標,w/h分別是從x/y開始起的距離,其實x,y,w,h就是一個矩形,pix是保存像素的數組,off是讀取圖像時開始的位置,scansize是指掃描的寬度,一般來說和w相等。
int width = image.getWidth();
int height = image.getHeight();
int size = width * height;
int[] pixels = new int[size];
pixelgrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
try {
pixelgrabber.grabPixels(); //讀取像素入數組
}
catch (InterruptedException _ex) {}
由於像素信息是由alpha,red,green,blue組成的,其格式為
因此,我們可以將顏色分解成單獨的RGB信息
int alpha = (pixel 24) 0xff;
int red = (pixel 16) 0xff;
int green = (pixel 8) 0xff;
int blue = (pixel) 0xff;
假如要實現顯示圖片A後,圖片B由上至下展開,則可以每次將圖片A中的一行像素替換為B中的相應行,然後生成新的像素信息:
圖像A的像素數組 圖像B的像素數組
old = pixelA; //保存圖片A的像素信息
oldR = redA; //保存圖片A的R信息
oldG = greenA; //保存圖片A的G信息
oldB = blueA; //保存圖片A的B信息
for (int i = 0; i width; i++) {//line為行數
oldR[line * width + i] = redA [line * width + i];
oldG[line * width + i] = greenA [line * width + i];
oldB[line * width + i] = blueA [line * width + i];
old[line * width + i] = oldR[line * width + i] 16 + oldG[line * width + i] 8 + oldB[line * width + i];
}
當生成新的像素信息後,可以通過java.awt.image.MemoryImageSource包中的MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, int scan)方法將像素數組對應到圖像,並且可以用newPixels()方法來生成新的圖像(具體用法可以參考JAVA API DOC)。
memoryimagesource = new MemoryImageSource(imageWidth, imageHeight,
new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff), blocks, 0, imageWidth);
//檢查java版本
String javaVersion;
try {
javaVersion = System.getProperty(“java.version”);
}
catch (SecurityException _ex) {
javaVersion = “unk”;
}
if (!javaVersion.startsWith(“1.0”)) { //jdk1.1以上的版本才支持此方法
try {
memoryimagesource.setAnimated(true);
memoryimagesource.setFullBufferUpdates(true);
imageBuf = createImage(memoryimagesource);
memoryimagesource.newPixels();//生成新的圖像
}
catch (NoSuchMethodError _ex) {
System.out.println(“unknow java version!”);
}
}
到此,新的圖像已經產生,只需要輸出到屏幕即可。
在此,需要注意以下幾個問題:
1、由於Applet讀取的圖像文件可以比較大,對於速度較慢的網路,可能會造成圖像未讀取完全就開始進行變換,因此,建議使用MediaTracker方法來保證圖像能被順利載入。
2、 在顯示的時候,為了避免閃爍,可以採用雙緩衝的方法。
3、 由於在某此高速計算機上,生成新的像素可以非常快,為了避免速度過快而造成效果並不明顯,可以加以適當的延時處理。
4、 為了保證效果的平滑,我們特地開闢了非常大的數組來保存像素/RGB信息,這是以空間換時間的做法。
完整的源程序附下(在jdk1.4/2k Server/RedHat9下運行通過,所用機器為P4 2.4G/512M)
package pic;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class effect
extends JApplet
implements Runnable {
//定義變數
Toolkit toolkit;
int totalBlocks = 0; //圖像被分解成的塊數,默認為 寬X高
int[] blocks; //保存塊數的信息
Image[] bufferImage = new Image[2]; //屏幕上的圖形緩衝
VolatileImage offScreenImage;
Image imageBuf; //保存圖片緩衝區內容
Graphics2D offScreenGraphics;
Thread thread;
MediaTracker mediaTracker;
boolean[] isImageReady; //圖片是否已經裝載
MemoryImageSource memoryimagesource;
int imageWidth, imageHeight; //圖像的寬及高
int[] pixelA, pixelB;
int[] redA, greenA, blueA, redB, greenB, blueB;
public effect() throws HeadlessException {
bufferImage[0] = getImage(“a.jpg”);
bufferImage[1] = getImage(“b.jpg”);
if ( (bufferImage[0].getWidth(this) != bufferImage[1].getWidth(this)) ||
(bufferImage[0].getHeight(this) != bufferImage[1].getHeight(this))) {
System.out.println(“圖像尺寸不一致!”);
return;
}
toolkit = getToolkit();
imageWidth = bufferImage[0].getWidth(this);
imageHeight = bufferImage[0].getHeight(this);
totalBlocks = imageWidth * imageHeight; //計算分解的塊數
blocks = new int[totalBlocks];
pixelA = new int[totalBlocks];
pixelB = new int[totalBlocks];
redA = new int[totalBlocks];
greenA = new int[totalBlocks];
blueA = new int[totalBlocks];
redB = new int[totalBlocks];
greenB = new int[totalBlocks];
blueB = new int[totalBlocks];
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
offScreenImage = gc.createCompatibleVolatileImage(imageWidth, imageHeight); //創建圖像緩衝
offScreenGraphics = offScreenImage.createGraphics(); //取得緩衝的graphics對象
}
public void init() {
getImagePixels(bufferImage[0], pixelA);
getImagePixels(bufferImage[1], pixelB);
for (int i = 0; i totalBlocks; i++) {
blocks[i] = pixelA[i]; //保存圖像A的像素信息
redA[i] = pixelA[i] 0xff0000; //保存圖像B的red值
greenA[i] = pixelA[i] 0x00ff00; //保存圖像B的green值
blueA[i] = pixelA[i] 0x0000ff; //保存圖像B的blue值
redB[i] = pixelB[i] 0xff0000; //保存圖像B的red值
greenB[i] = pixelB[i] 0x00ff00; //保存圖像B的green值
blueB[i] = pixelB[i] 0x0000ff; //保存圖像B的blue值
}
prepareImagePixels(); //將像素信息還原為圖像
}
public void run() {
//檢查java版本
String javaVersion;
try {
javaVersion = System.getProperty(“java.version”);
}
catch (SecurityException _ex) {
javaVersion = “unk”;
}
if (javaVersion.startsWith(“1.0”)) {
System.out.println(“require java 1.1 or later version!”);
return;
}
try { //暫停3秒鐘後等待效果開始
thread.sleep(3000l);
}
catch (InterruptedException ex1) {
}
int line = 0;
Thread currentThread = Thread.currentThread();
while (line imageHeight thread == currentThread) {
for (int i = 0; i imageWidth; i++) {
int offset = line * imageWidth + i;
blocks[offset] = pixelB[offset]; //與下一被注釋的語句作用相同
//blocks[offset] = redB[offset] | greenB[offset] | blueB[offset];
}
memoryimagesource.newPixels(); //生成新的圖像
line++;
repaint();
//適當延時
try {
thread.sleep(20l);
}
catch (InterruptedException ex) {
}
}
}
public void paint(Graphics g) {
if (offScreenGraphics != null) { //保證在destory()時不引發異常
offScreenGraphics.drawImage(imageBuf, 0, 0, this);
g.drawImage(offScreenImage, 0, 0, this);
}
}
public void start() {
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void stop() {
thread = null;
}
public final void update(Graphics g) {
paint(g);
}
public void destroy() {
if (offScreenImage != null) {
offScreenImage.flush();
}
offScreenImage = null;
if (offScreenGraphics != null) {
offScreenGraphics.dispose();
}
offScreenGraphics = null;
System.gc();
}
Image getImage(String filename) {
URLClassLoader urlLoader = (URLClassLoader)this.getClass().getClassLoader();
URL url = null;
Image image = null;
url = urlLoader.findResource(filename);
image = Toolkit.getDefaultToolkit().getImage(url);
MediaTracker mediatracker = new MediaTracker(this);
try {
mediatracker.addImage(image, 0);
mediatracker.waitForID(0);
}
catch (InterruptedException _ex) {
image = null;
}
if (mediatracker.isErrorID(0)) {
image = null;
}
return image;
}
private boolean getImagePixels(Image image, int pixels[]) {
PixelGrabber pixelgrabber = new PixelGrabber(image, 0, 0, imageWidth,
imageHeight, pixels,
0, imageWidth);
try {
pixelgrabber.grabPixels();
}
catch (InterruptedException _ex) {
return false;
}
return true;
}
void prepareImagePixels() {
memoryimagesource = new MemoryImageSource(imageWidth, imageHeight,
new DirectColorModel(24, 0xff0000,
0x00ff00, 0x0000ff), blocks, 0, imageWidth);
try {
memoryimagesource.setAnimated(true);
memoryimagesource.setFullBufferUpdates(true);
imageBuf = createImage(memoryimagesource);
memoryimagesource.newPixels(); //生成新的圖像
}
catch (NoSuchMethodError _ex) {
}
}
public int getWidth() {
return imageWidth;
}
public int getHeight() {
return imageHeight;
}
public static void main(String args[]) {
JFrame frame = new JFrame(“Demo”);
effect e = new effect();
e.init();
e.start();
frame.getContentPane().setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.getContentPane().add(e);
frame.setSize(new Dimension(e.getWidth() + 6, e.getHeight() + 20));
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
frame.setLocation( (screenSize.width – frameSize.width) / 2,
(screenSize.height – frameSize.height) / 2);
frame.show();
}
}
新建一個HTML文件,加入以下語句可作為Applet(其中width/height分別是圖片的高和寬):
作為應用程序運行的時候,在命令行輸入:
java pic.effect
JAVA與圖像壓縮
此篇轉載。
———————————————————————-
下面是一段給圖片加上網站logo的代碼,注意第12,13,14行設置了圖片的壓縮比。本例為不壓縮原圖片。
java 代碼
BufferedImage image = ImageIO.read(new FileInputStream(“c:\\base.jpg”));
//讀取圖標
BufferedImage image_biao = ImageIO.read(new FileInputStream(
“c:\\logo.gif”));
Graphics2D g = image.createGraphics();
g.drawImage(image_biao, 10, 10, image_biao.getWidth(null),
image_biao.getHeight(null), null);
g.dispose();
FileOutputStream out = new FileOutputStream(“c:\\out.jpg”);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
param.setQuality(1f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(image);
out.close();
setQuality
public void setQuality(float quality, boolean forceBaseline)
quality取值在 1.0 到 0.0 之間
Some guidelines: 0.75 high quality 0.5 medium quality 0.25 low quality
另外要得到一張指定高度和寬度的圖片的話可以用以下代碼:(接上面代碼)
java 代碼
BufferedImage tag = new BufferedImage(500, 300,image.getType());
tag.getGraphics().drawImage(image, 0, 0, 500, 300, null); //繪製縮小後的圖
FileOutputStream out2 = new FileOutputStream(“c:\\out2.jpg”);
JPEGImageEncoder encoder2 = JPEGCodec.createJPEGEncoder(out2);
JPEGEncodeParam param2 = encoder.getDefaultJPEGEncodeParam(tag);
param2.setQuality(1f, false);
encoder2.setJPEGEncodeParam(param2);
encoder2.encode(tag);
out2.close();
注意第一行新建 BufferedImage 的時候要使用原圖片的type,這樣可以保證輸出與原圖片相同質量的圖片。
什麼是dct 變換?它有什麼特點?它被廣泛應用在什麼編碼標準中
DCT變換,全稱是離散餘弦變換(Discrete Cosine Transform),主要用於將數據或圖像的壓縮,能夠將空域的信號轉換到頻域上,具有良好的去相關性的性能。DCT變換本身是無損的,但是在圖像編碼等領域給接下來的量化、哈弗曼編碼等創造了良好的條件。同時由於DCT變換是對稱的,所以,我們可以在量化編碼後利用DCT反變換,在接收端恢復原始圖像,DCT變換在當前的圖像分析和圖像壓縮領域有著極廣的用途,我們常見的JPEG靜態圖像編碼以及MJPEG、MPEG動態編碼等標準中都使用了DCT變換。
特點:除實數變換、確定的變換矩陣、准最佳變換性能外,二維DCT還是一種可分離的變換,可以用兩次一維變換得到二維變換結果。
應用編碼標準:常見的JPEG靜態圖像編碼以及MJPEG、MPEG動態編碼等標準。
關於離散餘弦變換(DCT)
1.轉自: 離散餘弦變換(DCT)的定義_小火車_新浪博客
已知離散傅里葉變換( DFT)為:
由於許多要處理的信號都是實信號,在使用DFT時由於傅里葉變換時由於實信號傅立葉變換的共軛對稱性導致DFT後在頻域中有一半的數據冗餘。
離散餘弦變換(DCT)是對實信號定義的一種變換,變換後在頻域中得到的也是一個實信號,相比DFT而言,DCT可以減少一半以上的計算。DCT還有一個很重要的性質(能量集中特性):大多書自然信號(聲音、圖像)的能量都集中在離散餘弦變換後的低頻部分,因而DCT在(聲音、圖像)數據壓縮中得到了廣泛的使用。由於DCT是從DFT推導出來的另一種變換,因此許多DFT的屬性在DCT中仍然是保留下來的。
推導N點長實序列的DCT,首先來定義一個新的長度為2N的序列:
可看作是將周期為N的序列x[m]做一個周期延拓成一個周期為2N的序列。如圖1中第一張圖。
再來看圖1中第一張圖是關於x = -1/2對稱的,要讓他關於x = 0對稱需要將其向右平移1/2個單位,得到x』[m] = x』[m – 1/2]就是關於x = 0對稱的周期序列了(如圖1中第二張圖)。
然後求這個2N序列的DFT:
就是DCT-2型離散餘弦變換.從上面的過程也可以直接看出,離散餘弦變換相當於一個長度大概是它兩倍的離散傅里葉變換.
變換後的x[n]是以2N為周期,偶對稱的序列: X[N+n] = X[N+n-2N] = X[n-N] = x[N-n]
定義變換矩陣C[n,m]:
用計算機計算DCT-2 (用的是O(n^2)樸素演算法,用於驗證正交特性以及觀察其頻域數據):
DCT的結果:
對相同序列FFT的結果:
比較DFT和FFT的結果可以觀察出DCT變換隻有實部,而DFT變換後有虛部。在這個例子中DCT在頻域中只用3個點就可以表示這個信號,而DFT變換後在頻域中需要5個點來表示信號。
參考:
2.轉自: 二維DCT變換 – Wuyuan’s Blog
寫這篇文章的目的主要是為了給x264打好基礎,x264用的是整數DCT變換,所以就先來說說DCT變換吧。
DCT(Discrete Cosine Transform),又叫離散餘弦變換,它的第二種類型,經常用於信號和圖像數據的壓縮。經過DCT變換後的數據能量非常集中,一般只有左上角的數值是非零的,也就是能量都集中在離散餘弦變換後的直流和低頻部分,下面我會用matlab來演示整個過程。
1.一維DCT變換
我們首先來看看一維的DCT變換,這是二維的基礎。一維的DCT變換共有8種,其中最實用的是第二種形式,公式如下:
其中c(u)是加上去一個係數,為了能使DCT變換矩陣成為正交矩陣,在後面二維變換將看到他的作用。N是f(x)的總數。相比其他幾種形式,他的運算還是比較簡單的,因此也用的比較廣。
2.二維DCT變換
二維DCT變換是在一維的基礎上再進行一次DCT變換,這個比較好理解,直接看公式:
這裡我只討論兩個N相等的情況,也就是數據是方陣的形式,在實際應用中對不是方陣的數據都是先補齊再進行變換的。為了matlab模擬方便點,寫成矩陣形式:
下面就用matlab來模擬一下,使用隨機生成的4×4矩陣作為輸入,程序如下:
Y是使用上面的公式進行變換,YY是用matlab自帶的dct2函數變換,結果是是:
可以看出Y和YY的結果是一樣的,這也進一步驗證了上面的公式是正確的。由於X是我隨機生成的,相關性很小,變換後的結果比較亂;如果是信號或圖像這樣相關性比較大的數據的話,數值會集中在左上角,右下角一般都是零,再使用「之」字型掃描得到數據流會包含很多連續的零,編碼後數據量會非常小,這就是DCT變換帶來的好處。
3.二維DCT反變換
DCT逆變換的公式如下:
矩陣形式可以由正變換的公式直接推出來,因為在A中加了c(i)這個係數,使得A成為了正交矩陣,所以我們就可以這樣做:
在用matlab來驗證是否能反變換出原來的數據:
X使用的是上面正變換用的數據,運行後得到的X1為:
X1=
61.000019.000050.000020.0000
82.000026.000061.000045.0000
89.000090.000082.000043.0000
93.000059.000053.000097.0000
和X完全相等。在實際進行編碼的時候,比如JPEG壓縮的時候,只會對Y左上角的數據進行傳輸,所以解碼出來的內容不會完全和原來的相同。
4.整數DCT變換
說道DCT就順便提一下x264中的整數DCT變換,整數DCT變換是以DCT變換為基礎的,為了減少計算量做的一些調整,下面我寫一下整數DCT變換公式的大致推導過程:
然後根據A是正交矩陣,把c=bd帶入A中,使行向量為單位向量可以得到d=0.4142。令d=0.5,得到b*b=0.4,代入上面的式子中,把0.5提取出來放到右邊的點乘中就得到了:
這樣在對大括弧部分進行計算時就都是加法和減法了,而且在精度上沒有太大降低。在x264實際編碼中,變換和量化是一起進行的,使得編碼速度有了很大的提高。
原創文章,作者:簡單一點,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/127769.html