图像处理之高斯金字塔

article/2025/10/22 4:52:42

一:图像金字塔基本操作

对一张图像不断的模糊之后向下采样,得到不同分辨率的图像,同时每次得到的

新的图像宽与高是原来图像的1/2, 最常见就是基于高斯的模糊之后采样,得到的

一系列图像称为高斯金字塔。




高斯金字塔不同(DoG)又称为拉普拉斯金字塔,其计算公式如下:

L(i) = G(i) – expand(G(i+1))

第i层拉普拉斯金字塔是由第i层高斯金字塔减去第i+1层高斯金字塔expand之后得到。

本文得到的DoG(Difference of Gaussian)结果如下:


二:关键代码解析

金字塔reduce操作实现代码如下:

	private BufferedImage pyramidReduce(BufferedImage src) {int width = src.getWidth();int height = src.getHeight();BufferedImage dest = createSubCompatibleDestImage(src, null);int[] inPixels = new int[width*height];int ow = width/2;int oh = height/2;int[] outPixels = new int[ow*oh];getRGB(src, 0, 0, width, height, inPixels );int inRow=0, inCol = 0, index = 0, oudex =0, ta = 0;float[][] keneralData = this.getHVGaussianKeneral();for(int row=0; row<oh; row++) {for(int col=0; col<ow; col++) {inRow = 2* row;inCol = 2* col;if(inRow >= height) {inRow = 0;}if(inCol >= width) {inCol = 0;}float sumRed = 0, sumGreen = 0, sumBlue = 0;for(int subRow = -2; subRow <= 2; subRow++) {int inRowOff = inRow + subRow;if(inRowOff >= height || inRowOff < 0) {inRowOff = 0;}for(int subCol = -2; subCol <= 2; subCol++) {int inColOff = inCol + subCol;if(inColOff >= width || inColOff < 0) {inColOff = 0;}index = inRowOff * width + inColOff;ta = (inPixels[index] >> 24) & 0xff;int red = (inPixels[index] >> 16) & 0xff;int green = (inPixels[index] >> 8) & 0xff;int blue = inPixels[index] & 0xff;sumRed += keneralData[subRow + 2][subCol + 2] * red;sumGreen += keneralData[subRow + 2][subCol + 2] * green;sumBlue += keneralData[subRow + 2][subCol + 2] * blue;}}oudex = row * ow + col;outPixels[oudex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);}}setRGB( dest, 0, 0, ow, oh, outPixels );return dest;}
金字塔expand实现代码如下:

public BufferedImage pyramidExpand(BufferedImage src) {int width = src.getWidth();int height = src.getHeight();int[] inPixels = new int[width*height];getRGB(src, 0, 0, width, height, inPixels );int ow = 2*width;int oh =2*height;int[] outPixels = new int[ow * oh];int index = 0, outdex = 0, ta = 0;float[][] keneralData = this.getHVGaussianKeneral();BufferedImage dest = createTwiceCompatibleDestImage(src, null);for(int row=0; row<oh; row++) {for(int col=0; col<ow; col++) {float sumRed = 0, sumGreen = 0, sumBlue = 0;for(int subRow = -2; subRow <= 2; subRow++) {double srcRow = (row + subRow)/2.0;double j = Math.floor(srcRow);double t = srcRow - j; if(t > 0) {continue;}if(srcRow >= height || srcRow < 0) {srcRow = 0;}for(int subCol = -2; subCol <= 2; subCol++) {double srcColOff = (col + subCol)/2.0;j = Math.floor(srcColOff);t = srcColOff - j;if(t > 0) {continue;}if(srcColOff >= width || srcColOff < 0) {srcColOff = 0;}index = (int)(srcRow * width + srcColOff);ta = (inPixels[index] >> 24) & 0xff;int red = (inPixels[index] >> 16) & 0xff;int green = (inPixels[index] >> 8) & 0xff;int blue = inPixels[index] & 0xff;sumRed += keneralData[subRow + 2][subCol + 2] * red;sumGreen += keneralData[subRow + 2][subCol + 2] * green;sumBlue += keneralData[subRow + 2][subCol + 2] * blue;}}outdex = row * ow + col;outPixels[outdex] = (ta << 24) | (clamp(4.0f * sumRed) << 16) | (clamp(4.0f * sumGreen) << 8) | clamp(4.0f * sumBlue);// outPixels[outdex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);}}setRGB( dest, 0, 0, ow, oh, outPixels );return dest;
}
图像金字塔的reduce与expand过程都是卷积采样实现。特别注意的是 expand

操作不是reduce的可逆操作。

关于什么是卷积,高斯滤波请参见博客上的其它相关文章。

高斯金字塔全部算法源代码如下:

package com.gloomyfish.image.pyramid;import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;public class PyramidAlgorithm extends GaussianFilter {private float a;public PyramidAlgorithm() {a = 0.4f;}public void setParameter(float p) {this.a = p;}public BufferedImage[] pyramidDown(BufferedImage src, int level) {BufferedImage[] imagePyramids = new BufferedImage[level + 1];imagePyramids[0] = src;for(int i=1; i<imagePyramids.length; i++) {imagePyramids[i] = pyramidReduce(imagePyramids[i-1]);}return imagePyramids;}public BufferedImage[] pyramidUp(BufferedImage[] srcImage) {BufferedImage[] imagePyramids = new BufferedImage[srcImage.length];for(int i=0; i<srcImage.length; i++) {imagePyramids[i] = pyramidExpand(srcImage[i]);}return imagePyramids;}/**** l1 = g1 - expand(g2)* l2 = g2 - expand(g3)* l0 = g0 - expand(g1)* @param reduceImages* @param expandImages* @return*/public BufferedImage[] getLaplacianPyramid(BufferedImage[] reduceImages) {BufferedImage[] laplaciImages = new BufferedImage[reduceImages.length -1];for(int i=1; i<reduceImages.length; i++) {BufferedImage expandImage = pyramidExpand(reduceImages[i]);laplaciImages[i-1] = createCompatibleDestImage(expandImage, null);int width = reduceImages[i-1].getWidth();int height = reduceImages[i-1].getHeight();int ewidth = expandImage.getWidth();width = (width > ewidth) ? ewidth : width;height = (height > expandImage.getHeight()) ? expandImage.getHeight():height;System.out.println(" width = " + width + " expand width = " + ewidth);int[] reducePixels = new int[width*height];int[] expandPixels = new int[width*height];int[] laPixels = new int[width*height];getRGB( reduceImages[i-1], 0, 0, width, height, reducePixels);getRGB( expandImage, 0, 0, width, height, expandPixels );int index = 0;int er = 0, eg = 0, eb = 0;for(int row=0; row<height; row++) {int ta = 0, tr = 0, tg = 0, tb = 0;for(int col=0; col<width; col++) {index = row * width + col;ta = (reducePixels[index] >> 24) & 0xff;tr = (reducePixels[index] >> 16) & 0xff;tg = (reducePixels[index] >> 8) & 0xff;tb = reducePixels[index] & 0xff;ta = (expandPixels[index] >> 24) & 0xff;er = (expandPixels[index] >> 16) & 0xff;eg = (expandPixels[index] >> 8) & 0xff;eb = expandPixels[index] & 0xff;tr = tr - er;tg = tg - eg;tb = tb - eb;laPixels[index] = (ta << 24) | (clamp(tr) << 16) | (clamp(tg) << 8) | clamp(tb);}}setRGB( laplaciImages[i-1], 0, 0, width, height, laPixels );}return laplaciImages;}private BufferedImage pyramidReduce(BufferedImage src) {int width = src.getWidth();int height = src.getHeight();BufferedImage dest = createSubCompatibleDestImage(src, null);int[] inPixels = new int[width*height];int ow = width/2;int oh = height/2;int[] outPixels = new int[ow*oh];getRGB(src, 0, 0, width, height, inPixels );int inRow=0, inCol = 0, index = 0, oudex =0, ta = 0;float[][] keneralData = this.getHVGaussianKeneral();for(int row=0; row<oh; row++) {for(int col=0; col<ow; col++) {inRow = 2* row;inCol = 2* col;if(inRow >= height) {inRow = 0;}if(inCol >= width) {inCol = 0;}float sumRed = 0, sumGreen = 0, sumBlue = 0;for(int subRow = -2; subRow <= 2; subRow++) {int inRowOff = inRow + subRow;if(inRowOff >= height || inRowOff < 0) {inRowOff = 0;}for(int subCol = -2; subCol <= 2; subCol++) {int inColOff = inCol + subCol;if(inColOff >= width || inColOff < 0) {inColOff = 0;}index = inRowOff * width + inColOff;ta = (inPixels[index] >> 24) & 0xff;int red = (inPixels[index] >> 16) & 0xff;int green = (inPixels[index] >> 8) & 0xff;int blue = inPixels[index] & 0xff;sumRed += keneralData[subRow + 2][subCol + 2] * red;sumGreen += keneralData[subRow + 2][subCol + 2] * green;sumBlue += keneralData[subRow + 2][subCol + 2] * blue;}}oudex = row * ow + col;outPixels[oudex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);}}setRGB( dest, 0, 0, ow, oh, outPixels );return dest;}public BufferedImage createSubCompatibleDestImage(BufferedImage src, ColorModel dstCM) {if ( dstCM == null )dstCM = src.getColorModel();return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth()/2, src.getHeight()/2), dstCM.isAlphaPremultiplied(), null);}public BufferedImage createTwiceCompatibleDestImage(BufferedImage src, ColorModel dstCM) {if ( dstCM == null )dstCM = src.getColorModel();return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth()*2, src.getHeight()*2), dstCM.isAlphaPremultiplied(), null);}public BufferedImage pyramidExpand(BufferedImage src) {int width = src.getWidth();int height = src.getHeight();int[] inPixels = new int[width*height];getRGB(src, 0, 0, width, height, inPixels );int ow = 2*width;int oh =2*height;int[] outPixels = new int[ow * oh];int index = 0, outdex = 0, ta = 0;float[][] keneralData = this.getHVGaussianKeneral();BufferedImage dest = createTwiceCompatibleDestImage(src, null);for(int row=0; row<oh; row++) {for(int col=0; col<ow; col++) {float sumRed = 0, sumGreen = 0, sumBlue = 0;for(int subRow = -2; subRow <= 2; subRow++) {double srcRow = (row + subRow)/2.0;double j = Math.floor(srcRow);double t = srcRow - j; if(t > 0) {continue;}if(srcRow >= height || srcRow < 0) {srcRow = 0;}for(int subCol = -2; subCol <= 2; subCol++) {double srcColOff = (col + subCol)/2.0;j = Math.floor(srcColOff);t = srcColOff - j;if(t > 0) {continue;}if(srcColOff >= width || srcColOff < 0) {srcColOff = 0;}index = (int)(srcRow * width + srcColOff);ta = (inPixels[index] >> 24) & 0xff;int red = (inPixels[index] >> 16) & 0xff;int green = (inPixels[index] >> 8) & 0xff;int blue = inPixels[index] & 0xff;sumRed += keneralData[subRow + 2][subCol + 2] * red;sumGreen += keneralData[subRow + 2][subCol + 2] * green;sumBlue += keneralData[subRow + 2][subCol + 2] * blue;}}outdex = row * ow + col;outPixels[outdex] = (ta << 24) | (clamp(4.0f * sumRed) << 16) | (clamp(4.0f * sumGreen) << 8) | clamp(4.0f * sumBlue);// outPixels[outdex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);}}setRGB( dest, 0, 0, ow, oh, outPixels );return dest;}}
特别注意:我没有处理像素的宽与高,如果宽与高不是偶数可能

会有问题,使用时请自己处理吧。

UI实现源代码如下:

package com.gloomyfish.image.pyramid;import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.MediaTracker;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;public class PyramidDemoUI extends JComponent implements ActionListener {/*** */private static final long serialVersionUID = 1L;private JButton upButton;private JButton downButton;private BufferedImage[] reduceImages;private BufferedImage[] expandImages;private BufferedImage sourceImage;private Dimension mySize;private MediaTracker tracker;public PyramidDemoUI(File f){initComponents(f);}private void initComponents(File f){// TODO Auto-generated method stubtry {  sourceImage = ImageIO.read(f);  } catch (IOException e1) {  e1.printStackTrace();  }  tracker = new MediaTracker(this);  tracker.addImage(sourceImage, 1);  // blocked 10 seconds to load the image data  try {  if (!tracker.waitForID(1, 10000)) {  System.out.println("Load error.");  System.exit(1);  }// end if  } catch (InterruptedException e) {  e.printStackTrace();  System.exit(1);  }// end catch  JPanel btnPanel = new JPanel();btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));upButton = new JButton("Laplacian Pyramid");downButton = new JButton("Pyramid Down");upButton.addActionListener(this);downButton.addActionListener(this);btnPanel.add(upButton);btnPanel.add(downButton);mySize = new Dimension(800, 800);   JFrame mainFrame = new JFrame("Pyramid Demo - Gloomyfish");mainFrame.getContentPane().setLayout(new BorderLayout());mainFrame.getContentPane().add(this, BorderLayout.CENTER);mainFrame.getContentPane().add(btnPanel, BorderLayout.SOUTH);mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  mainFrame.pack();  mainFrame.setVisible(true);  }@Overridepublic Dimension getPreferredSize() {return mySize;}@Overrideprotected void paintComponent(Graphics g) {
//		g.drawImage(sourceImage, 10, 10, sourceImage.getWidth(), sourceImage.getHeight(), null);int width = 10;
//		if(reduceImages != null) {
//			for(int i=1; i<reduceImages.length; i++) {
//				width += (10 + reduceImages[i-1].getWidth());
//				g.drawImage(reduceImages[i], width, 10, reduceImages[i].getWidth(), reduceImages[i].getHeight(), null);
//			}
//		}width = 10;if(expandImages != null) {for(int i=0; i<expandImages.length; i++) {g.drawImage(expandImages[i], width, 15, expandImages[i].getWidth(), expandImages[i].getHeight(), null);// g.drawImage(expandImages[i], width, 15 + sourceImage.getHeight(), expandImages[i].getWidth(), expandImages[i].getHeight(), null);width += (10 + expandImages[i].getWidth());}}super.paintComponent(g);}public static void main(String[] args) {  JFileChooser chooser = new JFileChooser();  chooser.showOpenDialog(null);  File f = chooser.getSelectedFile();  new PyramidDemoUI(f);  }@Overridepublic void actionPerformed(ActionEvent event) {if(event.getActionCommand().equals("Laplacian Pyramid")) {if(reduceImages != null) {// int size = reduceImages.length;PyramidAlgorithm pyramid = new PyramidAlgorithm();expandImages = pyramid.getLaplacianPyramid(reduceImages);// expandImages = pyramid.pyramidUp(reduceImages);repaint();} else {}} else if(event.getActionCommand().equals("Pyramid Down")) {// a.Smooth the image with Gaussian filter 5×5(1/4-a/2, 1/4, a, 1/4, 1/4-a/2) a = [0.3,0.6]// b.Sub sample the image by half - 选择偶数行与列// c.If reached desired size stop, else send the result to step 1PyramidAlgorithm pyramid = new PyramidAlgorithm();reduceImages = pyramid.pyramidDown(sourceImage, 3);repaint();} else {// do nothing}}  }
转载请务必注明出处-GloomyFish


http://chatgpt.dhexx.cn/article/p0ff6iV9.shtml

相关文章

【OpenCV学习笔记】之图像金字塔(Image Pyramid)

一、尺度调整 顾名思义&#xff0c;即对源图像的尺寸进行放大或者缩小变换。在opencv里面可以用resize函数&#xff0c;将源图像精准地转化为指定尺寸的目标图像。要缩小图像&#xff0c;一般推荐使用CV_INETR_AREA(区域插值)来插值&#xff1b;若要放大图像&#xff0c;推荐使…

图像金字塔总结

本文转载自&#xff1a; http://blog.csdn.net/dcrmg/article/details/52561656 一、 图像金字塔 图像金字塔是一种以多分辨率来解释图像的结构&#xff0c;通过对原始图像进行多尺度像素采样的方式&#xff0c;生成N个不同分辨率的图像。把具有最高级别分辨率的图像放在底…

数字图像处理(21): 图像金字塔(高斯金字塔 与 拉普拉斯金字塔)

目录 1 图像金字塔简介 2 向下取样——pyrDown() 2.1 基础理论 2.2 代码示例 3 向上取样——pyrUp() 3.1 基础理论 3.2 代码示例 4 Laplacian 金字塔 4.1 基础理论 4.2 代码示例 参考资料 1 图像金字塔简介 以多个分辨率来表示图像的一种有效且概念简单的结构是图像…

图像处理(二):图像金字塔

图像金字塔是图像多尺度表达的一种&#xff0c;是一种以多分辨率来解释图像的有效且概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低&#xff0c;且来源于同一张原始图的图像集合。 高斯金字塔是常见的图像金字塔&#xff0c;如下所示&#xff1a;…

什么是图像金字塔

有些情况下&#xff0c;我们需要处理源自同一张图像的不同分辨率的图像集合。这些不同分辨率的图像组成的集合称为图像金字塔。 图像金字塔的主要类型可分为低通和带通 有两种常用图像金字塔&#xff1a; Gaussian Pyramid &#xff08;低通&#xff09;Laplacian Pyramids &…

OpenCV图像处理学习十三,图像金字塔——高斯金字塔和拉普拉斯金字塔

一.图像金字塔概念 我们在图像处理中常常会调整图像大小&#xff0c;最常见的就是放大(zoom in)和缩小&#xff08;zoom out&#xff09;&#xff0c;尽管几何变换也可以实现图像放大和缩小&#xff0c;但是这里我们介绍图像金字塔 。 一个图像金字塔是由一系列的图像组成&…

图像金字塔

图像金字塔是图像处理和计算机视觉中的概念&#xff0c;常常用于多尺度处理领域&#xff08;multiscale processing)&#xff0c;尤其早年的图像匹配、识别等算法中都用到了图像金字塔 高斯金字塔&#xff08;Gaussian pyramid&#xff09; 金字塔的底层为原始图像&#xff0…

【OpenCV 学习笔记】第十一章: 图像金字塔

第十一章: 图像金字塔 一、什么是图像金字塔 同一张图片不同分辨率的子图的集合。 图像金字塔底部是待处理的高分辨率图像&#xff0c;也就是原始图像&#xff0c;顶部是低分辨率的近似图像。一般情况下&#xff0c;都是每向上移动一级&#xff0c;图像的宽和高都降低为原来的…

openCV——图像金字塔

图像金字塔 理论基础 通常情况下&#xff0c;图像金字塔的底部是待处理的高分辨率图像&#xff08;原始图像&#xff09;&#xff0c;而顶部则为其低 分辨率的近似图像。向金字塔的顶部移动时&#xff0c;图像的尺寸和分辨率都不断地降低。通常情况下&#xff0c; 每向上移动一…

第11章:图像金字塔

第11章&#xff1a;图像金字塔 一、理论基础&#xff1a;1. 向下采样&#xff1a;2. 向上采样&#xff1a; 二、pyrDown函数使用&#xff1a;三、pyrUp函数及使用&#xff1a;四、采样可逆性研究五、拉普拉斯金字塔1. 定义&#xff1a;2. 应用&#xff1a; 什么是图像金子塔&am…

C语言for语句简单打印心形。

C语言简单for语句打印心形 #include<stdio.h> int main() {int i;int j;int k;int n;int m;printf("\n\n\n\n\n\n\n\n表白朋友圈:\n\n\n\n");for (i 1; i < 3; i){if (i 1){printf(" ");printf(&quo…

C语言暴力解法:围圈报数(解析与思考)

围圈报数 题目 解题步骤 一.整体逻辑 1.从第&#xff11;个人开始报数&#xff0c; 2.数到第&#xff4d;个人出列&#xff0c; 3.然后从出列的下一个人开始报数&#xff0c; 4.数到第&#xff4d;个人又出列&#xff0c;…&#xff0c;如此反复到所有的人全部出列为止。 二…

html 一个圆圈一个c,如何用c语言程序画一个圆?

以一个空心圆来举例。 /* 判断是是否在圆上 */ int isAtCircle(int x, int y, int r) { /* 将(x, y)转换为相当圆心(r, r)的坐标 */ int rx x - r; int ry y - r; double d sqrt(rx*rx ry*ry) - r; /*计算到圆心的距离*/ if(fabs(d) < 0.5)""> return 1; e…

在HTML 页面中如何显示带圈圈的数字

数字外面有个圈圈&#xff0c; 或者圈圈里有反底色显示&#xff0c;效果类似&#xff1a; 带圈圈的数字示例&#xff1a; ① 反色圈圈数字示例&#xff1a; ❶ 要实现上面的效果&#xff0c;使用以下技术都可以达成&#xff1a; 方式1&#xff0c;. 使用图片 <image&…

C语言打印出心形表白,初学C语言也能看懂~(3)

例2&#xff1a;C语言实现打印出心形&#xff0c;初学者的表白神器。 解题思路&#xff1a;这道例题可以分成4部分&#xff0c;前3行一部分&#xff0c;4-6行一部分&#xff0c;7-13行一部分&#xff0c;最后一行一部分&#xff0c;读者请仔细阅读注释&#xff0c;小林写的很详…

C语言-报数出圈问题(链表实现)

问题描述&#xff1a;n个人围成一圈&#xff0c;顺序编号。从第一个人开始从1到m报数&#xff0c;凡报到m的人退出圈子&#xff0c;编程求解最后留下的人的初始编号。 程序运行示例&#xff1a; 6 3&#xff08;两个输入数据之间有空格&#xff09; 1输入格式&#xff1a;scanf…

蓝桥杯 小朋友崇拜圈 C语言

标题&#xff1a;小朋友崇拜圈 班里N个小朋友&#xff0c;每个人都有自己最崇拜的一个小朋友&#xff08;也可以是自己&#xff09;。 在一个游戏中&#xff0c;需要小朋友坐一个圈&#xff0c; 每个小朋友都有自己最崇拜的小朋友在他的右手边。 求满足条件的圈最大多少人&…

java 圈复杂度_详解圈复杂度

详解圈复杂度 圈复杂度概念 圈复杂度(Cyclomatic complexity,简写CC)也称为条件复杂度,是一种代码复杂度的衡量标准。由托马斯J麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度,其符号为VG或是M。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为…

控制台图形化打印二叉树(c/c++)

二叉树作为一种常见的数据结构,我们在学习的过程中会经常用到. 当我们做课设的时候,能把二叉树图形化的打印出来无疑是一个加分项,所以今天我们就来探讨一下如何图形化的打印出二叉树来. 目录 分析阶段 如何打印 如何将数据存入打印数组 代码实现 存储结构定义 二叉树的…

C语言:围圈报数游戏

游戏规则&#xff1a;有N个人围成一圈&#xff0c;顺序排号&#xff0c;从第一个人开始1到D报数&#xff0c;&#xff0c;凡报到D的人退出圈子&#xff08;下场&#xff09;&#xff0c;问最后留下来的是原来的第几号&#xff1f; 逻辑思想&#xff1a;用布尔数组记下每个人的…