【48】Android通过libjpeg-turbo库实现图片压缩
(1)公司为节约图片占用服务器存储资源成本,需要对Android手机客户端所传递到云存储服务器中的图片进行压缩,在不影响图片失真程度的情况下,最大限度的压缩图片以节省图片所占用的存储空间。
(2)本文即实战了通过jni调用libjpeg-gurbo库实现图片压缩的目的。
(3)实战结果:可以将一张4.9M的图片压缩至600kb大小,而且图片并不失真。
(4)本文细节尚存疏漏之处,请各位看观多多谅解,但能搞定这件事情已经实属不易,或许可以给你一些解决问题的思路供你参考。
Android通过libjpeg-turbo库实现图片压缩
文章目录
- Android通过libjpeg-turbo库实现图片压缩
- 1.Android CPU基础知识
- 1.1安卓CPU类型的说明
- 1.2安卓CPU类型的兼容性说明
- 1.3其他说明
- 1.4abiFilters 'armeabi-v7a'
- 2.使用AndroidStudio编译生成libjpeg-turbo动态链接库so文件
- 2.1libjpeg-turbo git clone地址
- 2.2使用AndroidStudio编译生成so动态链接库文件
- 3.将libjpeg_turbo的so库文件拷贝到其他项目使用,以zipphoto项目为例
- 3.1将自己编写的cpp源文件生成so动态链接库
- 3.2在Activity中调用图片压缩的本地方法
- 3.2.1图片压缩工具源码
- 3.2.2Activity调用源码
- 3.2.3Activity布局文件
- 4.总结
- 4.1注意:
- 5.参考文档

1.Android CPU基础知识
1.1安卓CPU类型的说明
(1)arm64-v8a: 第8代、64位ARM处理器,目前手机大多数是此架构.
(2)armeabiv-v7a: 第七代及以上的 ARM 处理器。2011年5月以后生产的大部分安卓设备都使用它
(3)armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多,缺乏对浮点数计算的硬件支持,在须要大量计算时有性能瓶颈。
(4)x86: 平板、模拟器用得比较多。x86 架构的手机都会包含由 Intel 提供的称为 Houdini 的指令集动态转码工具,实现对arm .so 的兼容。考虑 x86不到1% 的市场占有率,x86 相关的两个 .so 也是可以忽略的。
(5)x86_64: 64位的平板
(6)mips/mips64 极少用于手机可忽略。
1.2安卓CPU类型的兼容性说明
(1)armeabi设备只兼容armeabi,不支持硬件辅助浮点运算,支持所有的 ARM* 设备;
(2)armeabi-v7a设备兼容armeabi-v7a、armeabi;
(3)arm64-v8a设备兼容arm64-v8a、armeabi-v7a、armeabi;
(4)x86设备兼容x86、armeabi;
(5)x86_64设备兼容x86_64、x86、armeabi;
(6)mips64设备兼容mips64、mips;
(7)mips只兼容mips;
1.3其他说明
(1)以arm64-v8a设备为例,该Android设备优先寻找libs目录下的arm64-v8a文件夹。如果有文件夹,但是没有so库,则会报错。如果没有arm64-v8a文件夹,则会去找armeabi-v7a文件夹。如果找不到armeabi-v7a文件夹,则寻找armeabi文件夹,兼容运行该文件夹下的so,
(2)从上面解释就可以大概知道下载哪种APK了。普通手机用户,建议下载arm64-v8a(第8代、64位ARM处理器)版本,能够发挥手机最佳性能(只要本型号手机支持8G运存或8G以上就是64位处理器)。如果是很老的手机,也有可能不是64位处理器,那么就选择armeabi-v7a,几乎通用所有手机,而且也兼容64位处理器。
1.4abiFilters ‘armeabi-v7a’
(1)指的是以armeabi-v7a指令环境运行
(2)默认会编译出4个平台,arm64-v8a、armeabi-v7a、x86、x86_64
2.使用AndroidStudio编译生成libjpeg-turbo动态链接库so文件
2.1libjpeg-turbo git clone地址
git clone https://github.com/libjpeg-turbo/libjpeg-turbo
2.2使用AndroidStudio编译生成so动态链接库文件
(1)新建Android Native C++项目
(2)将libjpeg-turbo的源代码(注意是所有文件)复制到 native c++项目的cpp目录中
(3)配置app目录下的build.gradle文件,主要检查两个配置项是否已经配置了
a.android的defaultConfig配置中是否存在ndk关于CPU类型的配置
ndk{abiFilters 'arm64-v8a'
}
b.android下是否存在externalNativeBuild的配置,即
externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')version '3.22.1'}
}
(4)运行项目生成so库
(1)生成CPU类型为arm64-v8a与armeabiv-v7a的so库需要在Android真机上运行项目,项目运行完之后,到项目app目录下的.cxx目录里面去找生成的so库文件。
(2)生成CPU类型为x86与x86_64的so库需要在Android模拟器上运行项目如雷电或夜神或其他Android模拟器上运行,项目运行完之后,同样是在app目录下的.cxx目录里面去找生成的so库文件。
(3)以下是多次设置CPU类型运行项目后生成so库文件的列表
3.将libjpeg_turbo的so库文件拷贝到其他项目使用,以zipphoto项目为例
(1)新建Android项目,取名zipphoto。
(2)新建jni目录
(3)把libjpeg-turbo关于图片压缩相关的头文件拷贝到jni目录。
注意:到libjpeg-turbo的源代码中去搜索头文件所在的目录,然后拷贝过来,主要是以下几个头文件。
(4)编写图片压缩zipimg.cpp源文件
#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include <android/log.h>
#include <malloc.h>
#define TAG "image "
#define LOGE(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
/*** 导入jpeg的头文件*/
extern "C"{#include "jpeglib.h"
}typedef uint8_t BYTE;/** (1)extern "C":声明下面的代码,采用C的编译方式。* (2)JNIEnv *env:是整个JNI的所有API的一个桥梁,只需要将这个里面的所有函数学完了,那么JNI就学完了。即所有的操作都需要通过它来。* (3)JNIEXPORT:JNI重要标记关键字,不能少(VS编译能通过,运行会报错)或(AS运行不会报错),规则(标记为该方法可以被外部调用),Windows内部规则。* (4)JNICALL:也是一个关键字,(可以少的) jni call(约束函数入栈顺序和堆栈内存清理的规则)。* (5)jclass clazz:如果java本地方法声明成static native,jclass就是指的这个类,如果java本地方法未声明成static,就会变成jobject thiz,即指的是这个类的实例* (6)如果当前是native-lib.c,(*env)->xxx函数,如果是C语言,JNIEnv *env是二级指针,C是没有对象的,想持有env环境,就必须传递进去,(*env).AllocObject(clazz);* (7)如果当前是native-lib.cpp,env->xxx函数,如果是C++语言,JNIEnv *env是一级指针,evn可以直接调用一级指针下的函数,C++是有对象的,本身就会持有this,所以不需要传,env->AllocObject(clazz);*/
/*
extern "C"
JNIEXPORT jstring JNICALL
Java_com_gdc_imagezip_ImageZipUtil_compressBitmap(JNIEnv *env, jclass clazz, jobject bitmap, jint width, jint height, jint quality, jbyteArray file_name, jboolean optimize) {}*//*** 1.写数据,将b、g、r通过jpeg的方式写入进去* 2.APP需要通过jpeg_compress_struct去调用* @param data* @param path* @param w* @param h*/void writeJpg(BYTE *data, const char *path, int w, int h) {struct jpeg_compress_struct jpeg_struct;//1.初始化//()设置错误信息struct jpeg_error_mgr jerr;//()错误信息初始化,设置错误的处理信息jpeg_struct.err = jpeg_std_error(&jerr);//()设置缓冲区,创建开始压缩任务jpeg_create_compress(&jpeg_struct);//()因为我们要输出,所以打开一个文件,即输出到文件中去,FILE *file = fopen(path,"wb");//()设置输出路径jpeg_stdio_dest(&jpeg_struct,file);//()设置图片宽高jpeg_struct.image_width = w;jpeg_struct.image_height = h;//()设置使用哈夫曼算法进行压缩,为false的时候采用哈夫曼算法进行压缩jpeg_struct.arith_code = FALSE;//()设置对结构进行优化jpeg_struct.optimize_coding = TRUE;//()初始化位深jpeg_struct.in_color_space = JCS_RGB;//()初始化组成,R、G、B三个为一组,所以组成为3.jpeg_struct.input_components = 3;//()设置其他的参数函数,设置成默认的jpeg_set_defaults(&jpeg_struct);//()设置压缩质量,范围是0~100,一般20优化比是最好的。jpeg_set_quality(&jpeg_struct,20,true);//2.开始压缩jpeg_start_compress(&jpeg_struct,TRUE);//()写入数据JSAMPROW row_pointer[1];//()行的rgbint row_stride = w*3;while(jpeg_struct.next_scanline < h){row_pointer[0] = reinterpret_cast<JSAMPROW>(&data[jpeg_struct.next_scanline * w * 3]);jpeg_write_scanlines(&jpeg_struct,row_pointer,1);//让next_scanline++自动加1}//()结束压缩//()释放结构体jpeg_finish_compress(&jpeg_struct);//()jpeg_destroy_compress(&jpeg_struct);//()关闭文件fclose(file);
}/**1.jpeg压缩* (1)条件:压缩程度比较低,采用的是哈夫曼的算法进行压缩,所有的压缩数据必须是元数据,即不能够被分割的数据。* (2)bitmap不是元数据,像素也不是元数据,因为像素还可以分为a r g b。* (3)一个像素由多少位去表示呢?由4个字节表示。* 一个像素是一个int类型,一个像素由高8位,第二个8位,第三个8位,第四个8位,每个8位分别表示A、R、G、B.* 4个字节就由4个8位组成。* (4)A、R、G、B整张力片取出来之后,放到一个数组里面去。* (5)将一张bitmap取出来之后,我肯定要取出它的像素数据,怎么取出像素数据呢?** 2.取出Bitmap的像素数据* int AndroidBitmap_lockPixels(_JNIEnv *env, jobject jbitmap, void **addrPtr)* (1)_JNIEnv *env:结构体* (2)jobject jbitmap:bitmap* (3)void **addrPtr:入参与出参对象,它是一个数组,将这个数组传进来之后,经过这个方法,它就将bitmap像素数据全部转化到数组里面去.** 3.在NDK里面去取出图片的宽和高* static int AndroidBitmap_getInfo(_JNIEnv *env, jobject jbitmap, struct__anonymous *info)* (1)_JNIEnv *env:结构体* (2)jobject jbitmap:bitmap* (3)struct__anonymous *info:入参与出参结构体** 4.整体需求就是* (1)将一张图片全部解压成元数据,并且将它放到一个datas数组里面去。*/
extern "C"
JNIEXPORT void JNICALL
Java_com_gdc_zipphoto_util_ImageZipUtil_compressBitmap(JNIEnv *env, jobject thiz, jobject bitmap, jstring path_) {//()获取图片存储路径const char *path = env->GetStringUTFChars(path_, 0);LOGE("==================1.进入方法==================");//()定义入参出参对象AndroidBitmapInfo bitmapInfo,在AndroidBitmapInfo结构体中就有了图片的宽、高属性。AndroidBitmapInfo bitmapInfo;//()在NDK里面去取出图片的宽和高,调用此函数之后,bitmapInfo中会有bitmap的具体宽高值。AndroidBitmap_getInfo(env,bitmap,&bitmapInfo);//()取出Bitmap的像素数据,放到pixels数组里面去BYTE *pixels;//()将bitmap中的数据转换到pixels里面AndroidBitmap_lockPixels(env,bitmap,reinterpret_cast<void **>(&pixels));//()遍历pixels里面的内容,它的内容是4个字节的int数据。遍历图片的宽、高。int h = bitmapInfo.height;int w = bitmapInfo.width;//()定义像素数据,用于存放取出的像素数据int color;//()定义R、G、B分别存放红、绿、蓝三种颜色BYTE r,g,b;//()定义新的数组用于存放取出来的元数据,tmpDatas是防止丢失的数组。BYTE *datas, *tmpDatas;//()这个数组有多大呢?它应该是w*h的3倍datas = static_cast<BYTE *>(malloc(w * h * 3));tmpDatas = datas;for(int i = 0; i < h ; ++i){for(int j = 0 ; j < w ; ++j){//()将数组元素像素数据取出来color = *((int*)pixels);//()取出A、R、G、B,但本例中只取R、G、B,要取R、G、B就需要用到左移与右移r = ((color & 0x00FF0000) >> 16);g = ((color & 0x0000FF00) >> 8);b = (color & 0x000000FF);//()将元数据放到一个新的数组里面去,如何放呢?是按b、g、r、a的方式放的,即是倒着放的,*datas = b;*(datas+1) = g;*(datas+2) = r;datas += 3;//()取下一个像素数据需要+4,因为每个像素是由4个字节的32位int数据表示的。pixels += 4;}}LOGE("==================2.压缩完毕==================");//()释放图片所占用的资源AndroidBitmap_unlockPixels(env,bitmap);LOGE("==================3.写入SD卡==================");//()写数据,将b、g、r通过jpeg的方式写入进去writeJpg(tmpDatas,path,w,h);env->ReleaseStringUTFChars(path_,path);
}
3.1将自己编写的cpp源文件生成so动态链接库
(1)在jni目录新建Android.mk文件与Application.mk文件
(2)Android.mk文件主要用于说明我自己的so库需要哪些so动态库的支持,有哪些cpp源文件,本例的内容如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := libjpeg
LOCAL_SRC_FILES := libjpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)LOCAL_MODULE := zipimg
LOCAL_SRC_FILES := zipimg.cpp
LOCAL_SHARED_LIBRARIES := libjpeg
LOCAL_LDLIBS := -ljnigraphics -llog
LOCAL_C_INCLUDES := $(LOCAL_PATH)include $(BUILD_SHARED_LIBRARY)
(3)Application.mk文件主要用于说明我自己的so库支持哪些CPU类型 ,以及最高支持到哪一个Android平台使用,内容如下:
APP_ABI :=arm64-v8a
APP_PLATFORM := android-33
(3)为zipphoto配置ndk编译环境
File----- > Project Structure----->设置自己下载的ndk路径
(4)配置项目app目录下的build.gradle文件
a.在android节点下,添加sourceSets节点配置,用于指定生成自己的so动态链接库成功后,存放输出so库到哪个目录,本例是放到libs目录下
sourceSets{main{jni.srcDirs = []//设置禁止gradle生成Android.mkjniLibs.srcDirs = ['libs']}}
b.在android节点下,添加一个task构建任务,配置内容如下
task ndkBuild(type:Exec){commandLine "D:\\android-ndk-r27c\\ndk-build.cmd",'NDK_PROJECT_PATH=build/intermediates/ndk','NDK_LIBS_OUT=libs','APP_BUILD_SCRIPT=jni/Android.mk','NDK_APPLICATION_MK=jni/Application.mk'}
(5)通过ndk构建生成so动态链接库
我所采用的是通过命令行的方式生成动态链接库。
a.将自己下载的ndk设置到windows系统环境变量中。过程略(自己网上查)
b.打开命令提示符
win+R cmd 回车
c.进入到zipphoto的jni目录,即执行命令:
cd F:\cxzworkspace\zipphoto\app\jni
d.执行ndk-build命令
通过以上步骤就已经完成了自己编写的c++程序的so动态链接库的生成。
3.2在Activity中调用图片压缩的本地方法
3.2.1图片压缩工具源码
public class ImageZipUtil {//1.引入动态链接库static {//(1)zipimg动态链接库用于调用图片压缩jpeg.so动态链接库中所提供的api函数实现图片的压缩System.loadLibrary("zipimg");//(2)引入图片压缩jpeg.so动态链接库System.loadLibrary("jpeg");}//2.图片压缩本地方法,在zipphoto.cpp的c++源文件中具体实现public native void compressBitmap(Bitmap bitmap, String path);
}
3.2.2Activity调用源码
package com.gdc.zipphoto;import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;import com.gdc.zipphoto.permission.PermissionsUtil;
import com.gdc.zipphoto.util.FilePathManager;
import com.gdc.zipphoto.util.ImageZipUtil;import java.io.File;public class MainActivity extends Activity implements View.OnClickListener , PermissionsUtil.IPermissionsResult {private Button mCompressBtn;private ImageView mImage;/*** 图片存放根目录*/private final String mImageRootDir = FilePathManager.getFileDirectory().getAbsolutePath();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//(1)批量申请多个权限PermissionsUtil.getInstance().checkPermissions(MainActivity.this,PermissionsUtil.getInstance().getPermissionList(),this);//(2)压缩后保存临时文件目录File tempFile = new File(mImageRootDir);if (!tempFile.exists()) {tempFile.mkdirs();}//(3)mCompressBtn = (Button) findViewById(R.id.compress_btn);mImage = (ImageView) findViewById(R.id.image);//(4)mCompressBtn.setOnClickListener(this);}@Overridepublic void onClick(View v) {if(v == mCompressBtn){new Thread(new Runnable() {@Overridepublic void run() {//(1)压缩后的图片存储路径final File afterCompressImgFile = new File(mImageRootDir + "/temp.jpg");//(2)需要压缩的图片路径String tempCompressImgPath = mImageRootDir + File.separator + "temp.jpg";//(3)直接使用jni libjpeg压缩Bitmap bitmap = BitmapFactory.decodeFile(tempCompressImgPath);new ImageZipUtil().compressBitmap(bitmap,tempCompressImgPath);MainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {mImage.setImageBitmap(BitmapFactory.decodeFile(afterCompressImgFile.getPath()));}});}}).start();}}@Overridepublic void permitPermissions() {}@Overridepublic void refusePermissions() {}
}
3.2.3Activity布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><Buttonandroid:id="@+id/compress_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:layout_marginLeft="15dp"android:text="终极压缩" /><ImageViewandroid:id="@+id/image"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="15dp"android:layout_marginLeft="15dp"android:contentDescription="@null"android:scaleType="centerInside" />
</LinearLayout>
4.总结
(1)libjpeg-turbo库的so库的编译生成是通过AndroidStudio的native c++项目生成
(2)目前用的是cmake方式,即借助libjpeg-turbo本身存在的CMakeLists.txt文件去生成。
(3)只需要在app目录下的build.gradle文件中去指定CMakeLists.txt文件所在位置即可
(4)运行项目在生成so动态链接库的时候,会去读取CMakeLists.txt中的文件信息生成libjpeg-turbo的so库文件
(5)我们自己的项目需要用到libjpeg-turbo的so库时,只需要将其添加到jni目录,并且复制图片压缩相关的.h头文件,并在自己编写的扩展名为.cpp的文件中去引入这些头文件,以达到调用libjpeg-turbo函数库实现图片压缩的目的。
(6)自己写的.cpp文件(C或C++)函数希望能在Android Activity或其他java程序中调用,需要将其生成so动态链接库,然后通过JNI去调用。
(7)生成so动态链接库的过程,可以ndk来实现,主要是在JNI目录中添加Android.mk与Application.mk文件中去写清楚编译so库需要的依赖库有哪些,比如图片压缩so库,即前述生成的jpeg.so库,还要写清楚编译的c++源文件是什么,so库最高支持到Android哪个版本,等等,也就是要告诉ndk,编译的内容有哪些,编译成哪种CPU类型的指令集。
(8)要运行Android.mk和Application.mk文件需要用到ndk-build命令。
前提是ndk必须配置系统环境变量。
首先是要打开命令提示符,并进入Android.mk文件所处的目录,然后再执行ndk-build命令。
4.1注意:
(1)在执行ndk-build命令之前,需要将不同CPU型号指令库,即jpeg.so库复制到jni目录下,去生成对应的CPU型号的so库,不然可能在生成so库的过程中会出问题。
(2)这句话的意思就是
arm-v8a的jpeg.so库复制到jni目录生成arm-v8a的zipimg.so库。
armeabi-v7a的jpeg.so库复制到jni目录生成armeabi-v7a的zipimg.so库。
x86的jpeg.so库复制到jni目录生成x86的zipimg.so库。
x86_64的jpeg.so库复制到jni目录生成x86_64的zipimg.so库。
(3)主要目的就是为了避免不同CPU生成的指令不一样,导致程序无法运行的问题。因此编译之前一定要在Application.mk中设置编译的CPU类型名称,arm64-v8a、armeabi-v7a、x86、x86_64,可以一个一个的设置,然后编译。
5.参考文档
参考文档
相关文章:

【48】Android通过libjpeg-turbo库实现图片压缩
(1)公司为节约图片占用服务器存储资源成本,需要对Android手机客户端所传递到云存储服务器中的图片进行压缩,在不影响图片失真程度的情况下,最大限度的压缩图片以节省图片所占用的存储空间。 (2)…...

Linux输入设备应用编程
本章学习输入设备的应用编程,首先要知道什么是输入设备?输入设备其实就是能够产生输入事件的设备就称为输入设备,常见的输入设备包括鼠标、键盘、触摸屏、按钮等等,它们都能够产生输入事件,产生输入数据给计算机系统。…...
【Vulkan入门】03-创建Device
目录 先叨叨git信息关键代码VulkanEnv::CreateDevice() 编译并运行程序题外话 先叨叨 在上篇已经选择了一个合适的PhysicalDevice。 本篇要为这个PhysicalDevice创将一个Device。Device可以理解为APP与PhysicalDevice之间的代理。 所有APP与PhysicalDevice之间交互的资源都通过…...
【jvm】C2编译器
目录 1. 说明2. 编译流程3. 使用与配置4. 性能优化与监控5. 局限性 1. 说明 1.JVM(Java虚拟机)C2编译器是Java编译过程中的重要环节,专门用于将Java字节码编译成高效的本地机器代码,以提升Java程序的执行效率。2.特点:…...
使用 Acme.sh 自动生成和续签免费 SSL 证书(含通配符支持)
Acme.sh 是一个开源的脚本,能够从 ZeroSSL、Let’s Encrypt 等证书颁发机构(CA)获取免费的 HTTPS 证书。该脚本特别简单易用,并且支持多种验证方式。下面将详细介绍使用 Acme.sh 生成、安装和更新证书的各个步骤。 Github地址 使用…...

Android 图形系统之四:Choreographer
Choreographer 是 Android 系统中负责帧同步的核心组件,它协调输入事件、动画和绘制任务,以确保界面以固定频率(通常是每 16ms,一帧)流畅渲染。通过管理 VSYNC 信号和调度任务,Choreographer 是实现流畅 UI…...
CAP定理和BASE理论
CAP定理 CAP定理,也称为布鲁尔定理(Brewer’s Theorem),是分布式系统设计中的一个基本原理。它指出在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容…...
笔记软件:我来、思源笔记、Obsidian、OneNote
最近wolai的会员到期了,促使我更新了一下笔记软件。 首先,wolai作为一个笔记软件,我觉得有很多做得不错的方面(否则我也不会为它付费2年了),各种功能集成得很全(公式识别这个功能我写论文的时候…...

试探互联网如何工作?
Reading: How_does_the_Internet_workhow-does-internet-work Watching:How the Internet Works in 5 Minutes Outline: 互联网通过全球互联的计算机和服务器网络工作,通过标准化协议进行通信。数据被分解成数据包,并使用互联…...
【c++笔试强训】(第三十篇)
目录 爱丽丝的⼈偶(贪⼼构造) 题目解析 讲解算法原理 编写代码 集合(排序) 题目解析 讲解算法原理 编写代码 爱丽丝的⼈偶(贪⼼构造) 题目解析 1.题目链接:登录—专业IT笔试面试备考平…...

微信小程序购物车全选反选功能以及合计
微信小程序基于Vant Weapp的购物车功能实现 1、单选 使用微信小程序原生表单组件checkbox和checkbox-group 注意:checkbox原生不支持bind:change事件,checkbox-group支持 <checkbox-group bindchange"handleCheck"><checkbox val…...

vue-qr在线生成二维码组件(vue2版本)
在对于二维码生成中有许多组件,下面介绍关于自定义比较高的vue-qr组件,能自定义设置背景颜色、背景图片、背景Gif图、实点和空白区的颜色、中心Logo的图片和边距。 依赖下载 注意: 直接npm下载最新版 有些项目可能运行会抱错 这时候你可以降…...

大语言模型技术相关知识-笔记整理
系列文章目录 这个系列攒了很久。主要是前段之间面试大语言模型方面的实习(被拷打太多次了),然后每天根据面试官的问题进行扩展和补充的这个笔记。内容来源主要来自视频、个人理解以及官方文档中的记录。方便后面的回顾。 文章目录 系列文章…...
SCP命令实现Linux中的文件传输
CP命令的主要作用是实现Linux与Linux系统之间的文件传输。 SCP命令时基于SSH协议,所以两台服务器的sshd服务必须处于开启状态,否则无法完成上传与下载操作。 #1.上传文件 scp linux本地文件路径 远程用户名@linux主机地址:远程路径 #2.下载文件 scp 远程用户名@linux主机地址…...

linux环境中后台运行java程序
在生产环境,我们通常需要让java进程后台运行,并且即使会话关闭,进程也依然存在。 使用的命令: nohup java -jar xxx.jar -> aaa.log 2>&1 & 详细介绍下上面这条命令 (1)nohup:…...

Go学习:变量
目录 1. 变量的命名 2. 变量的声明 3. 变量声明时注意事项 4. 变量的初始化 5. 简单例子 变量主要用来存储数据信息,变量的值可以通过变量名进行访问。 1. 变量的命名 在Go语言中,变量名的命名规则 与其他编程语言一样,都是由字母、数…...

在Unity编辑模式下运行Mono中的方法
[ExecuteAlways] 最简单的方法当然是直接给Mono加上[ExecuteAlways]修饰,这样Mono中的Awake,Update等等都可以在编辑模式下按照原本的时机运行。 [ExecuteAlways] public class TestScript : MonoBehaviour {void TestMethod(){Debug.Log("TestMe…...

Y20030028 JAVA+SSM+MYSQL+LW+基于JAVA的考研监督互助系统的设计与实现 源代码 配置 文档
基于JAVA的考研监督互助系统 1.项目描述2. 课题开发背景及意义3.项目功能4.界面展示5.源码获取 1.项目描述 随着高等教育的普及和就业竞争的加剧,越来越多的学生选择继续深造,参加研究生入学考试。考研人数的不断增加,使得考研过程中的学习监…...

MATLAB期末复习笔记(下)
目录 五、数据和函数的可视化 1.MATLAB的可视化对象 2.二维图形的绘制 3.图形标识 4.多子图绘图 5.直方图的绘制 (1)分类 (2)垂直累计式 (3)垂直分组式 (4)水平分组式 &…...

「Mac畅玩鸿蒙与硬件37」UI互动应用篇14 - 随机颜色变化器
本篇将带你实现一个随机颜色变化器应用。用户点击“随机颜色”按钮后,界面背景会随机变化为淡色系颜色,同时显示当前的颜色代码,页面还会展示一只猫咪图片作为装饰,提升趣味性。 关键词 UI互动应用随机颜色生成状态管理用户交互…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...