Android OpenGLES 360全景图片渲染(球体内部)

概述
360度全景图是一种虚拟现实技术,它通过对现实场景进行多角度拍摄后,利用计算机软件将这些照片拼接成一个完整的全景图像。这种技术能够让观看者在虚拟环境中以交互的方式查看整个周围环境,就好像他们真的站在那个位置一样。在Android设备上查看360度全景图片,可以使用一些专门的app, 不如Google相册, Google 街景, 第三方的全景图片查看应用。这些应用程序能够识别并以交互方式展示360度全景图像,让用户可以旋转、缩放和平移来探索整个场景。
360全景图片渲染可以使用openGLES来轻松实现.
实现


使用OpenGL 创建一个球体, 并将图片纹理按一定的规则贴到球体的内部. 如上图, 可以考虑将图片等比例铺满球面展开的面积即可, 方法有很多, 可以按经度, 也可以按维度, 不同顺序对结果没影响.
参考代码:
主界面
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import com.android.apitester.SphereView;public class SphereViewer extends Activity {SphereView sphereView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Bitmap bm = BitmapFactory.decodeFile("/sdcard/sphere.jpg");sphereView = new SphereView(this);sphereView.setBitmap(bm);setContentView(sphereView);}
}
自定义GLSurfaceView
import android.content.Context;
import android.graphics.Bitmap;
import android.hardware.SensorManager;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;import com.ansondroider.acore.Logger;
import com.ansondroider.acore.opengl.EGLHelper;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;public class SphereView extends GLSurfaceView implements GLSurfaceView.Renderer {final String TAG = "SphereView";private String vertextShaderSource = "attribute vec4 aPosition;" +"precision mediump float;" +"uniform mat4 uMatrix;" +"attribute vec2 aCoordinate;" +"varying vec2 vCoordinate;" +"attribute float alpha;" +"varying float inAlpha;" +"void main(){" +" gl_Position = uMatrix*aPosition;\n" +//" gl_Position = aPosition;" +" gl_PointSize = 10.0;" +" vCoordinate = aCoordinate;" +" inAlpha = alpha;" +"}";private String fragmentShaderSource = "#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;" +"varying vec2 vCoordinate;" +"varying float inAlpha;" +//"uniform samplerExternalOES uTexture;" +"uniform sampler2D uTexture;" +"uniform bool isPoint;" +"void main() {" +" vec4 color = texture2D(uTexture, vCoordinate);" +" if(isPoint){" +" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);" +" }else{" +" gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +" }" +"}";//纹理IDprivate int mTextureId = -1;// 定义OpenGL 程序IDprivate int mProgram = -1;//矩阵变换接受者(shader中)private int mVertexMatrixHandler = -1;//顶点坐标接收者private int mVertexPosHandler = -1;//纹理坐标接受者private int mTexturePosHandler = -1;//纹理接受者private int mTextureHandler = -1;//半透明值接受者private int mAlphaHandler = -1;private int mPointHandler = -1;//矩阵private float[] mMatrix = new float[16];private float[] projectionMatrix = new float[16];//透明度private float mAlpha = 1f;public SphereView(Context context) {super(context);//A #16 pc 000000000000d348 [anon:dalvik-classes2.dex extracted in memory from /data/app/~~Kkld4fhSS0RUjHOLDPgB7w==/com.ansondroider.apitester-9-2nRYL85FvddCKlwbFEFw==/base.apk!classes2.dex] (com.ansondroider.apitester.sphere.SphereView.createGLPrg+36)//A #18 pc 000000000000d74a [anon:dalvik-classes2.dex extracted in memory from /data/app/~~Kkld4fhSS0RUjHOLDPgB7w==/com.ansondroider.apitester-9-2nRYL85FvddCKlwbFEFw==/base.apk!classes2.dex] (com.ansondroider.apitester.sphere.SphereView.onDrawFrame+138)setEGLContextClientVersion(2);setRenderer(this);setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {updateRotate(event);return true;}});}//if touching, stop sensor rotate.boolean touching;float dx, dy;float angStartX, angStartY;float angelTouchX, angelTouchY;public void updateRotate(MotionEvent e){if(e.getAction() == MotionEvent.ACTION_DOWN){touching = true;dx = e.getX();dy = e.getY();//float[] rot = {0, 0, 0};//provider.getAngle(rot);angStartX = angelTouchY;//rot[0];angStartY = angelTouchX;//rot[1];}else if(e.getAction() == MotionEvent.ACTION_MOVE){angelTouchY = angStartX + 180f * (e.getX() - dx) / getWidth();angelTouchX = angStartY + 180f * (e.getY() - dy) / getHeight();}else{touching = false;}}Bitmap tmpBm = null;boolean rebindTexture = false;public void setBitmap(Bitmap bm){//check mTextureId is -1, save bm to tmpBm;if(bm == null || bm.isRecycled())return;else tmpBm = bm;if(mTextureId >= 0){rebindTexture = true;}}float ratio = 1;//float frustumSize = 2.5f;float[] NEAR_FAR = {1.7f, 0.5f, 20f, 17f};float[] EYE_Z = {-0.06f, -3f, 3};float[] SCALE = {5f, 0.5f, 10};float[] FRUSTUM = {1, 1, 3};float[] RADIUS = {2.5f, 2, 3};/*** 初始化矩阵变换,主要是防止视频拉伸变形*/private void initDefMatrix() {//Log.d(TAG, "initDefMatrix");//设置相机位置float[] viewMatrix = new float[16];Matrix.setLookAtM(viewMatrix, 0,0f, 0f, EYE_Z[0],0f, 0f, 0,0f, 1.0f, 0f);float[] rotSensor = new float[16];Matrix.setIdentityM(rotSensor, 0);float[] rotTouch = new float[16];Matrix.setIdentityM(rotTouch, 0);Matrix.rotateM(rotTouch, 0, -angelTouchX, 1, 0, 0);float rotY = -angelTouchY + (vtSphere != null ? vtSphere.getStartRotateY() : 0);Matrix.rotateM(rotTouch, 0, rotY, 0, 1, 0);Matrix.multiplyMM(viewMatrix, 0, rotSensor, 0, rotTouch, 0);// 参数解释://result: 存储乘法结果的矩阵数组。//resultOffset: 存储结果矩阵的数组起始偏移量。//lhs: 左操作数矩阵(left-hand side)的数组。//lhsOffset: 左操作数矩阵的数组起始偏移量。//rhs: 右操作数矩阵(right-hand side)的数组。//rhsOffset: 右操作数矩阵的数组起始偏移量。//Matrix.multiplyMM(mMatrix, 0, projectionMatrix, 0, viewMatrix, 0);Matrix.multiplyMM(mMatrix, 0, projectionMatrix, 0, viewMatrix, 0);Matrix.scaleM(mMatrix, 0, SCALE[0], SCALE[0], SCALE[0]);}@Overridepublic void setAlpha(float alpha) {super.setAlpha(alpha);mAlpha = alpha;}/*** 创建并使用opengles程序*/private void createGLPrg() {//Logger.d(TAG, "createGLPrg");if (mProgram == -1) {int vertexShader = EGLHelper.compileVertexShader(vertextShaderSource);int fragmentShader = EGLHelper.compileFragmentShader(fragmentShaderSource);//创建programe陈谷mProgram = GLES20.glCreateProgram();//将顶点着色器加入程序GLES20.glAttachShader(mProgram, vertexShader);//将片元着色器加入程序GLES20.glAttachShader(mProgram, fragmentShader);GLES20.glLinkProgram(mProgram);//从程序中获取句柄mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture");mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate");mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha");mPointHandler = GLES20.glGetUniformLocation(mProgram, "isPoint");}//使用opengl程序if (mProgram != -1) GLES20.glUseProgram(mProgram);}@Overrideprotected void onDetachedFromWindow() {Logger.d(TAG, "onDetachedFromWindow");super.onDetachedFromWindow();GLES20.glDisableVertexAttribArray(mVertexPosHandler);GLES20.glDisableVertexAttribArray(mTexturePosHandler);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);GLES20.glDeleteProgram(mProgram);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {Logger.d(TAG, "onSurfaceCreated");}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {Logger.d(TAG, "onSurfaceChanged");GLES20.glViewport(0, 0, width, height);ratio = (float) width / height;//m: 存储结果矩阵的数组。//offset: 存储结果矩阵的数组起始偏移量。//left : 近平面左边界的 X 坐标值。//right : 近平面右边界的 X 坐标值。//bottom: 近平面下边界的 Y 坐标值。//top : 近平面上边界的 Y 坐标值。//near : 近平面的 Z 坐标值(必须为正数)。//far : 远平面的 Z 坐标值(必须为正数)。Matrix.frustumM(projectionMatrix, 0,-ratio*FRUSTUM[0], ratio*FRUSTUM[0],-FRUSTUM[0], FRUSTUM[0],NEAR_FAR[0], NEAR_FAR[3]);}void prepareTexture(){if((mTextureId < 0 || rebindTexture) && tmpBm != null){Logger.d(TAG, "setBitmap bind bitmap to texture " + mTextureId);if(mTextureId >= 0) {//check texture is bind, unbind.GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);}//create texture and bind bitmap to itint[] textures = new int[1];GLES20.glGenTextures(1, textures, 0);mTextureId = textures[0];rebindTexture = false;//mTextureId is not -1, then bind Bitmap to mTextureId// 将 Bitmap 加载到纹理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);// 设置纹理参数GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, tmpBm, 0);tmpBm.recycle(); // 释放 Bitmap 资源}}@Overridepublic void onDrawFrame(GL10 gl) {//Logger.d(TAG, "onDrawFrame");GLES20.glClearColor(bgRGBA[0], bgRGBA[1], bgRGBA[2], bgRGBA[3]);long ct = SystemClock.uptimeMillis();if(ct - last_fps > 1000) {last_fps = ct;fps = 0;}fps ++;prepareTexture();//drawBitmap_initBuffer();//清除颜色缓冲和深度缓冲GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);///GLES20.glEnable(GLES20.GL_DEPTH_TEST);if (mTextureId != -1) {initDefMatrix();//2/创建、编译、启动opengles着色器createGLPrg();//3.激活并绑定纹理单元///activateTexture();//5.开始绘制渲染doDraw();}}float[] bgRGBA = {0, 0, 0, 1};int fps = 0;long last_fps = SystemClock.uptimeMillis();/*** 开始绘制渲染*/SphereTexture vtSphere;public void doDraw() {//Logger.d(TAG, "doDraw");if(mMatrix == null)return;if(vtSphere == null){vtSphere = new SphereTexture(RADIUS[0], 32, 32,false);}// 绑定纹理GLES20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);GLES20.glUniform1i(mTextureHandler, 0);GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0);GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha);GLES20.glUniform1i(mPointHandler, vtSphere.isPoint() ? 1 : 0);vtSphere.draw(mVertexPosHandler, mTexturePosHandler);}}//Class SphereView end
球面顶点和纹理坐标的生成与渲染
import android.graphics.RectF;
import android.opengl.GLES20;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;public class SphereTexture extends VertexTexture{private float mRadius;private int mLat, mLong;private boolean mIs180;private float[] degreeLongitude;private float[] degreeLatitude;public SphereTexture(float radius, int latitudeBands, int longitudeBands,boolean is180) {mRadius = radius;mLat = latitudeBands;mLong = longitudeBands;mIs180 = is180;init();}@Overridepublic float getStartRotateY() {return 0;//mIs180 ? 180f: -90f;}@Overrideprotected void initVertexTextureCoordinate(){if(degreeLongitude == null) {degreeLongitude = new float[]{-_PI, _PI, _PI * 2};degreeLatitude = new float[]{-0, _PI, _PI};}mVertexCoors = new float[(mLat + 1) * (mLong + 1) * 3];mTextureCoors = new float[(mLat + 1) * (mLong + 1) * 2];mIndices = new short[mLat * mLong * 6];int vIndex = 0;int tIndex = 0;int iIndex = 0;for (int lat = 0; lat <= mLat; lat++) {float theta = degreeLatitude[0] + (float) lat / mLat * degreeLatitude[2];float sinTheta = (float) Math.sin(theta);float cosTheta = (float) Math.cos(theta);for (int lon = 0; lon <= mLong; lon++) {float phi = degreeLongitude[0] + (float) lon / mLong * degreeLongitude[2];//float phi = (float) lon / longitudeBands * 2 * (MAX_DEGREE_X);float sinPhi = (float) Math.sin(phi);float cosPhi = (float) Math.cos(phi);float x = cosPhi * sinTheta;float y = cosTheta;float z = sinPhi * sinTheta;mVertexCoors[vIndex] = mRadius * x;mVertexCoors[vIndex + 1] = mRadius * y;mVertexCoors[vIndex + 2] = mRadius * z;// 生成纹理坐标float u = (float) lon / mLong;float v = (float) lat / mLat;// 生成纹理坐标/*mTextureCoors[tIndex] = u;//xmTextureCoors[tIndex + 1] = v;//y*/RectF r = getTextureArea();mTextureCoors[tIndex] = r.left + r.width() * u;//xmTextureCoors[tIndex + 1] = r.top + r.height() * v;//yif (lat < mLat && lon < mLong) {int first = lat * (mLong + 1) + lon;int second = first + mLong + 1;mIndices[iIndex++] = (short) first;mIndices[iIndex++] = (short) second;mIndices[iIndex++] = (short) (first + 1);mIndices[iIndex++] = (short) second;mIndices[iIndex++] = (short) (second + 1);mIndices[iIndex++] = (short) (first + 1);/*first first + 1 ...|----------|-------------|| | || | |second|----------|-------------|| second + 1 || | || | ||----------|-------------|*/}vIndex += 3;tIndex += 2;}}}@Overridepublic void updateZ(float z) {super.updateZ(z);mRadius = z;initVertexTextureCoordinate();}@Overridepublic void draw(int mVertexPosHandler, int mTexturePosHandler) {// 绑定顶点缓冲区GLES20.glEnableVertexAttribArray(mVertexPosHandler);mVertexBuffer.position(0);GLES20.glVertexAttribPointer(mVertexPosHandler, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);// 绑定纹理坐标缓冲区GLES20.glEnableVertexAttribArray(mTexturePosHandler);mTextureBuffer.position(0);GLES20.glVertexAttribPointer(mTexturePosHandler, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);// 绑定纹理//if(textureId > -1)GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);// 绘制球体if(!isPoint()) {GLES20.glDrawElements(GLES20.GL_TRIANGLES, getIndexCount(),GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);}elseGLES20.glDrawArrays(GLES20.GL_POINTS, 0, getVertexCount());GLES20.glDisableVertexAttribArray(mVertexPosHandler);GLES20.glDisableVertexAttribArray(mTexturePosHandler);}}
最终效果:

参考
- Android OpenGLES3绘图:球形视频播放器
- Android 使用 OpenGL ES 绘制球面
相关文章:
Android OpenGLES 360全景图片渲染(球体内部)
概述 360度全景图是一种虚拟现实技术,它通过对现实场景进行多角度拍摄后,利用计算机软件将这些照片拼接成一个完整的全景图像。这种技术能够让观看者在虚拟环境中以交互的方式查看整个周围环境,就好像他们真的站在那个位置一样。在Android设备…...
LETTERS(DFS)
【题目描述】 给出一个rowcolrowcol的大写字母矩阵,一开始的位置为左上角,你可以向上下左右四个方向移动,并且不能移向曾经经过的字母。问最多可以经过几个字母。 【输入】 第一行,输入字母矩阵行数RR和列数SS,1≤R,S≤…...
嵌入式海思Hi3861连接华为物联网平台操作方法
1.1 实验目的 快速演示 1、认识轻量级HarmonyOS——LiteOS-M 2、初步掌握华为云物联网平台的使用 3、快速驱动海思Hi3861 WIFI芯片,连接互联网并登录物联网平台...
CMDB平台(进阶篇):3D机房大屏全景解析
在数字化转型的浪潮中,数据中心作为企业信息架构的核心,其高效、智能的管理成为企业竞争力的关键因素之一,其运维管理方式也正经历着革命性的变革。传统基于二维平面图表的机房监控方式已难以满足现代企业对运维可视化、智能化的需求。乐维CM…...
NVM 多版本Node.js 管理全指南(Windows系统)
🧑 博主简介:CSDN博客专家、全栈领域优质创作者、高级开发工程师、高级信息系统项目管理师、系统架构师,数学与应用数学专业,10年以上多种混合语言开发经验,从事DICOM医学影像开发领域多年,熟悉DICOM协议及…...
C,C++语言缓冲区溢出的产生和预防
缓冲区溢出的定义 缓冲区是内存中用于存储数据的一块连续区域,在 C 和 C 里,常使用数组、指针等方式来操作缓冲区。而缓冲区溢出指的是当程序向缓冲区写入的数据量超出了该缓冲区本身能够容纳的最大数据量时,额外的数据就会覆盖相邻的内存区…...
《Linux内存管理:实验驱动的深度探索》【附录】【实验环境搭建 2】【vscode搭建调试内核环境】
1. 如何调试我们的内核 1. GDB调试 安装gdb sudo apt-get install gdb-multiarchgdb-multiarch是多架构版本,可以通过set architecture aarch64指定架构 QEMU参数修改添加-s -S #!/usr/bin/shqemu-7.2.0-rc1/build/aarch64-softmmu/qemu-system-aarch64 \-nogr…...
Flutter项目之登录注册功能实现
目录: 1、页面效果2、登录两种状态界面3、中间按钮部分4、广告区域5、最新资讯6、登录注册页联调6.1、网络请求工具类6.2、注册页联调6.3、登录问题分析6.4、本地缓存6.5、共享token6.6、登录页联调6.7、退出登录 1、页面效果 import package:flutter/material.dart…...
mybatis 自带的几个插入接口的区别
研究这个的原由是应为需求对一张表新增了一个有默认值的字段,然后调用插入接口的时候发现这个字段没有传默认值但是还是以null值入库了,数据库中设置的默认值没有生效。 通过排查之后发现是使用了insertUseGeneratedKeys 方法进行插入,此方法…...
ctfshow VIP题目限免 源码泄露
根据题目提示是源代码泄露,右键查看页面源代码发现了 flag...
移动神器RAX3000M路由器变身家庭云之七:增加打印服务,电脑手机无线打印
系列文章目录: 移动神器RAX3000M路由器变身家庭云之一:开通SSH,安装新软件包 移动神器RAX3000M路由器变身家庭云之二:安装vsftpd 移动神器RAX3000M路由器变身家庭云之三:外网访问家庭云 移动神器RAX3000M路由器不刷固…...
《函数基础与内存机制深度剖析:从 return 语句到各类经典编程题详解》
一、问答题 (1)使用函数的好处是什么? 1.提升代码的复用性 2.提升代码的可维护性 3.增强代码的可读性 4.提高代码的灵活性 5.方便进行单元测试 (2)如何定义一个函数?如何调用一个函数? 在Pytho…...
Python | 使用Matplotlib绘制Swarm Plot(蜂群图)
Swarm Plot(蜂群图)是一种数据可视化图表,它用于展示分类数据的分布情况。这种图表通过将数据点沿着一个或多个分类变量轻微地分散,以避免它们之间的重叠,从而更好地显示数据的分布密度和分布趋势。Swarm Plot特别适用…...
风云可测:华为AI天气大模型将暴雨预测误差缩至3公里内
华为云正式发布全球首个气象专用人工智能大模型"盘古气象",实现台风路径24小时预测误差<30公里、暴雨落区72小时精度91%,较传统数值预报效率提升10000倍。本文基于对西北太平洋10个台风回溯测试、全国2360个气象站验证数据,解析…...
JavaScript基础-window.sessionStorage
在Web开发中,数据存储是一个非常重要的环节。它不仅关系到用户体验的提升,还影响着应用的状态管理与性能优化。window.sessionStorage 是一种轻量级的数据存储机制,允许网页在同一会话期间内保存数据。本文将详细介绍 sessionStorage 的基本概…...
新版本Xmind结合DeepSeek快速生成美丽的思维导图
前言 我的上一篇博客(https://quickrubber.blog.csdn.net/article/details/146518898)中讲到采用Python编程可以实现和Xmind的互动,并让DeepSeek来生成相应的代码从而实现对内容的任意修改。但是,那篇博客中提到的Xmind有版本的限…...
lodash库介绍(一个现代JavaScript实用工具库,提供模块化、性能优化和额外功能)JavaScript库(防抖、节流、函数柯里化)JS库
文章目录 Lodash库全解析简介核心优势一致性API模块化设计性能优化 常用功能分类数组操作对象操作函数增强 高级应用场景数据转换链函数组合 性能考量大数据集处理 最佳实践按需引入利用FP模块 结语 Lodash库全解析 简介 Lodash是一个现代JavaScript实用工具库,提…...
禾赛科技社招面经
下面面经内容是禾赛科技社招面经 Linux bsp软件工程师 一面: 1、自我介绍 2、中断里用什么锁 答:自旋锁 3、自旋锁和互斥锁的区别 答:自旋锁用在中断上下文中,适合于极短的临界区,CPU开销小,不可以阻塞 互斥锁用在进程上下文中,适用于较长的临界区,CPU开销大,可以阻塞…...
set和map封装
目录 set和map区别 set和map的插入 set和map的实现 修改红黑树的模板参数 修改比较时使用的变量 迭代器的实现 迭代器的定义 *解引用重载 ->成员访问重载 自增重载 重载 封装迭代器 RBTree迭代器封装 封装set迭代器 对set迭代器进行修改 封装map迭代器 修改…...
【Linux】Orin NX + Ubuntu22.04配置国内源
1、获取源 清华源 arm 系统的源,可以在如下地址获取到 https://mirror.tuna.tsinghua.edu.cn/help/ubuntu-ports/ 选择HTTPS,否则可能报错: 明文签署文件不可用,结果为‘NOSPLIT’(您的网络需要认证吗?)查看Orin NX系统版本 选择jammy的源 2、更新源 1)备份原配…...
Bazel中的Symbol, Rule, Macro, Target, Provider, Aspect 等概念
学习Bazel ,就要学习Bazel 的规则定义, 弄清各个概念是重要的一个步骤。 在 Bazel 规则定义中,Symbol、Rule 和 Macro 是常见的概念。除此之外,Bazel 还有 Target、Provider、Aspect Repository、Package、 Workspace、 Configura…...
Open-Sora:开源AI视频生成的新星
一.引言 近年来,AI视频生成技术快速发展,从文本生成图像(如Stable Diffusion、DALLE)到文本生成视频(如Runway、Pika),AI在多媒体创作领域的应用日益广泛。近期,Open-Sora作为一款开…...
【堆】《深入剖析优先级队列(堆):数据结构与算法的高效搭档》
文章目录 前言例题一、最后一块石头的重量二、数据流中的第 K 大元素三、前K个高频单词四、数据流的中位数 结语 前言 什么是优先级队列算法呢?它的算法原理又该怎么解释? 优先级队列(堆)算法是一种特殊的数据结构和算法…...
【CMOS输出缓冲器驱动强度】
一 、学习笔记 原始资料:https://www.ti.com.cn/cn/lit/an/zhcae18/zhcae18.pdf?ts1743589394832 Q1、电平转换芯片的其中一个关键指标是转换速率,转换速率跟什么因素有关系呢? 1、瞬态驱动强度 上升或下降时间用于评估瞬态驱动强度。需要…...
【C++】Cplusplus进阶
模板的进阶: 非类型模板参数 是C模板中允许使用具体值(而非类型)作为模板参数的特性。它们必须是编译时常量,且类型仅限于整型、枚举、指针、引用。(char也行) STL标准库里面也使用了非类型的模板参数。 …...
透明的卡组收费模式IC++
IC是信用卡处理商用来计算每笔交易相关费用的定价模型。与统一或混合定价相比,IC提供了额外的透明度。 作为企业主,了解IC定价的来龙去脉至关重要,以确定它是否对您的运营有意义。 什么是IC? IC或interchange plus是一种流行的定…...
虚拟试衣间微信小程序解决方案
目录 项目名称: 云尚衣橱 核心功能模块: 技术栈选型: 架构设计概览: 详细功能点实现思路: 数据库设计 (MongoDB 示例): 开发步骤建议: 关键注意事项和挑战: 项目名称: 云尚衣橱 核心功能模块: 用户系统 (User System) 我的衣柜 (My Wardrobe) 虚拟试衣间 (Vir…...
吾爱置顶软件,吊打电脑自带功能!
今天我给大家带来一款超棒的软件,它来自吾爱论坛的精选推荐,每一款都经过精心挑选,绝对好用! S_Clock 桌面计时软件 这款软件的界面设计特别漂亮,简洁又大方。它是一款功能齐全的时钟计时倒计时软件,既能正…...
使用MFC ActiveX开发KingScada控件(OCX)
最近有个需求,要在KingScada上面开发一个控件。 原来是用的WinCC,WinCC本身是支持调用.net控件,就是winform控件的,winform控件开发简单,相对功能也更丰富。奈何WinCC不是国产的。 话说KingScada,国产组态软…...
应用安全系列之四十五:日志伪造(Log_Forging)之二
日志伪造(Log Forging)是一种常见的安全威胁,攻击者通过注入恶意内容破坏日志完整性。不同编程语言的防御方式有所不同,本文主要介绍Java、C#、Node.js、Rails(Ruby)和Go语言中的防护方法。 1、Java 在另外一篇博客里已经描述的比较详细,可…...
