【Android 内存优化】Bitmap 图像尺寸缩小 ( 考虑像素密度、针对从不同像素密度资源中解码对应的 Bitmap 对象 | inDensity | inTargetDensity )

文章目录

  • 一、像素密度对解码图片的影响
  • 二、不考虑像素密度会导致图片缩小尺寸不准确
  • 三、DisplayMetrics 源码阅读、研究手机资源获取规则
  • 四、像素密度参数设置取值 ( inDensity | inTargetDensity | setDensity )
  • 五、inDensity 与 inTargetDensity 设置
  • 六、新的图片缩小工具类代码 ( 在原基础上添加了像素密度控制 )
  • 七、GitHub 地址





一、像素密度对解码图片的影响



在之前讲内存占用的博客中 【Android 内存优化】Bitmap 内存占用计算 ( Bitmap 图片内存占用分析 | Bitmap 内存占用计算 | Bitmap 不同像素密度间的转换 ) , 讲到从不同的像素密度资源中获取图片 , 其解码后的大小不同 ;


在上述博客最后从不同像素密度 , 加载 1990 x 1020 大小的图片 , 解码出来分别是如下结果 :

  • hdpi : 宽 3483 , 高 1785 , 占用内存 24868620 字节 ;
  • mdpi : 宽 5224 , 高 2678 , 占用内存 55959488 字节 ; 从 drawable 默认目录中读取也是这个配置 ;
  • xhdpi : 宽 2612 , 高 1339 , 占用内存 13989872 字节 ;
  • xxhdpi : 宽 1741 , 高 893 , 占用内存 6218852 字节 ;
  • xxxhdpi : 宽 1306 , 高 669 , 占用内存 3494856 字节 ;

详细的计算过程查看上述博客 , 这里不再详述 ;


Bitmap 解码尺寸计算公式如下 :

=×加载到内存中的宽或高像素值 = 实际宽或高像素值 \times \dfrac{本机像素密度}{图片存放的目录对应的像素密度}





二、不考虑像素密度会导致图片缩小尺寸不准确



目前 R.drawable.blog 图片在 drawable 目录中存放 , 其代表的像素密度前缀是 mdpi ;

从该 drawable 目录中读取的资源 densityDpi 值为 DENSITY_MEDIUM = 160, 当前的 Pixel 2 手机屏幕密度 density = 2.625 , 屏幕像素密度 densityDpi = 420 ;

在博客 【Android 内存优化】Bitmap 图像尺寸缩小 ( 设置 Options 参数 | inJustDecodeBounds | inSampleSize | 工具类实现 ) 中出现如下问题 :

明明在代码中设置了宽高最大值时 100 x 100 , 解码出来的图片居然是 , 程序解析错了 ?

        Bitmap reduceSizeBitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog,
                100, 100 , false , null);

解码结果 : 解码出来的宽度 163 像素 , 高度 81 像素 , 明显出现问题了 ;

2020-06-30 22:04:22.959 3766-3766/? I/Bitmap: blog : 5224 , 2678 , 55959488
2020-06-30 22:04:22.960 3766-3766/? W/BitmapSizeReduce: getResizedBitmap inSampleSize=32
2020-06-30 22:04:22.980 3766-3766/? I/Bitmap: reduceSizeBitmap : 163 , 81 , 26406

原因说明 :

在设置了 options.inJustDecodeBounds = true 选项后 , 使用 BitmapFactory.decodeResource(resources, iamgeResId, options) 解码出的图片参数 , 是图片的实际参数 , 即 1990 x 1020 , 此时按照该实际参数进行了图片解码 , 计算图片缩小值 inSampleSize = 32 , 此时是可以将图片宽高都缩小到 100 的 , 缩小后的图片宽高是 62 x 32 ;


如果从真实的图像解码 , 会将像素密度解码考虑进去 , 这里从 mdpi 资源中解码图片 , 实际的解码出来的大小是 5224 x 2678 , 如果将该值缩小 32 倍 , 肯定无法到达宽高都小于 100 像素 , 这里得到的图片大小事 163 x 81 ;





三、DisplayMetrics 源码阅读、研究手机资源获取规则



仔细阅读 DisplayMetrics 中的代码 , 可以看到不同像素密度的手机的资源来源 , 基本上是获取其向上取整屏幕密度的资源 , 如果当前手机 densityDpi = 420 , 其处于 DENSITY_XHIGH 与 DENSITY_XXHIGH 之间 , 那么就会优先读取 DENSITY_XXHIGH 对应 xxhdpi 中的资源 , 这也是为了保证图片清晰度设定的策略 ;


规则 : 当手机的屏幕像素密度处于两个标准量化值之间 , 那么会自动选取高的标准量化值对应的资源缩小后使用 ;


public class DisplayMetrics {
	// 低密度屏幕标准量化值 , 对应 ldpi , 现在基本不使用
    public static final int DENSITY_LOW = 120;
    
    // DENSITY_LOW 与 DENSITY_MEDIUM 之间的密度  
    // 应用程序中不用考虑为这些像素密度准备资源  
    // 该密度的手机由系统自动缩放 DENSITY_MEDIUM 对应的资源使用
    public static final int DENSITY_140 = 140;
	
	// 中等密度屏幕标准量化值 , 对应 mdpi 
    public static final int DENSITY_MEDIUM = 160;	
    
    // DENSITY_MEDIUM 与 DENSITY_HIGH 之间的密度  
    // 应用程序中不用考虑为这些像素密度准备资源   
    // 该密度的手机由系统自动缩放 DENSITY_HIGH 对应的资源使用
    public static final int DENSITY_180 = 180;		
    public static final int DENSITY_200 = 200;
    public static final int DENSITY_TV = 213;
    public static final int DENSITY_220 = 220;

	// 高密度屏幕标准量化值 , 对应 hdpi 
    public static final int DENSITY_HIGH = 240;

    // DENSITY_HIGH 与 DENSITY_XHIGH 之间的密度 
    // 应用程序中不用考虑为这些像素密度准备资源  
    // 该密度的手机由系统自动缩放 DENSITY_XHIGH  对应的资源使用
    public static final int DENSITY_260 = 260;
    public static final int DENSITY_280 = 280;
    public static final int DENSITY_300 = 300;

	// 超高密度屏幕标准量化值 , 对应 xhdpi 
    public static final int DENSITY_XHIGH = 320;

    // DENSITY_XHIGH 与 DENSITY_XXHIGH 之间的密度 
    // 应用程序中不用考虑为这些像素密度准备资源  
    // 该密度的手机由系统自动缩放 DENSITY_XXHIGH 对应的资源使用
    public static final int DENSITY_340 = 340;
    public static final int DENSITY_360 = 360;
    public static final int DENSITY_400 = 400;
    public static final int DENSITY_420 = 420;
    public static final int DENSITY_440 = 440;
    public static final int DENSITY_450 = 450;

	// 超超高密度屏幕标准量化值 , 对应 xxhdpi 
    public static final int DENSITY_XXHIGH = 480;

    // DENSITY_XXHIGH 与 DENSITY_XXXHIGH 之间的密度 
    // 应用程序中不用考虑为这些像素密度准备资源  
    // 该密度的手机由系统自动缩放 DENSITY_XXXHIGH 对应的资源使用
    public static final int DENSITY_560 = 560;
    public static final int DENSITY_600 = 600;

	// 超超超高密度屏幕标准量化值 , 对应 xxxhdpi 
    public static final int DENSITY_XXXHIGH = 640;

    public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
    public static final float DENSITY_DEFAULT_SCALE = 1.0f / DENSITY_DEFAULT;
    public static int DENSITY_DEVICE = getDeviceDensity();
    public static final int DENSITY_DEVICE_STABLE = getDeviceDensity();

	// 省略一万行代码 ... 
}




四、像素密度参数设置取值 ( inDensity | inTargetDensity | setDensity )



在 Bitmap 操作过程中 , 需要设置一系列与像素密度相关的取值 , 如 inDensity , inTargetDensity , setDensity 等值 ;

这些值设置的是 densityDpi 值 , 定义在 DisplayMetrics 中 ;

就是上述的 DENSITY_LOW 到 DENSITY_XXXHIGH 之间的一系列常量值 , 取值范围 120 ~ 640 ;





五、inDensity 与 inTargetDensity 设置



这两个值都是 BitmapFactory.Options 中设置的值 ;


① inDensity 像素密度值 : 设置该值会导致被返回的图像会被强制设置一个像素密度值 , 相当于设置了图片来自于哪个像素密度的资源 ;

② inTargetDensity 目标像素密度值 : 表示要缩放到的目标图像像素密度值 , 该值需要结合 inScaled 值使用 , 如果同时设置了 inScaled = true , 和 inDensity 像素密度值 , 在图像返回时 , 会自动将图像按照 inDensity 向 inTargetDensity 缩放 ;

        // 设置图片的来源方向的像素密度 , 如设置 
        options.inDensity = decodeDensityDpi;
        // 设置图片的目标方向的像素密度
        options.inTargetDensity = decodeDensityDpi;
        // 设置图片解码可缩放 , 该配置与上述两个配置结合使用
        options.inScaled = true;




六、新的图片缩小工具类代码 ( 在原基础上添加了像素密度控制 )



package kim.hsl.bm.utils;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * Bitmap 尺寸缩小
 */
public class BitmapSizeReduce {
    private static final String TAG = "BitmapSizeReduce";

    /**
     * 获取指定尺寸于鏊求的 Bitmap 对象
     * 该方法有缺陷 , 计算值的时候没有考虑像素密度
     * 如果从不同像素密度的资源文件中加载
     * 可能计算出的值与指定的 maxBitmapWidth maxBitmapHeight 略有出入
     *
     * @param context           上下文对象
     * @param iamgeResId        要解析的图片资源 id
     * @param maxBitmapWidth    Bitmap 的最大宽度
     * @param maxBitmapHeight   Bitmap 的最大高度
     * @param hasAlphaChannel   是否包含 ALPHA 通道, 即透明度信息
     * @param inBitmap          复用的 Bitmap, 将新的 Bitmap 对象解析到该 Bitmap 内存中
     * @return  返回新的 Bitmap 对象
     */
    public static Bitmap getResizedBitmap(Context context,
                                          int iamgeResId, int maxBitmapWidth, int maxBitmapHeight,
                                          boolean hasAlphaChannel, Bitmap inBitmap){

        // 0. 声明方法中使用的局部变量

        // 用于解析资源
        Resources resources = context.getResources();
        // 为图像指定解码的 像素密度
        int decodeDensityDpi = resources.getDisplayMetrics().densityDpi;
        // Bitmap 图片加载选项
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 图片宽度
        int imageWidth;
        // 图片高度
        int imageHeight;
        /*
            根据 图片宽度 imageWidth , 图片高度 imageHeight ,
            最大宽度 maxBitmapWidth , 最大高度 maxBitmapHeight ,
            计算出的图片缩放系数 , 该值最终要设置到 BitmapFactory.Options 对象中
         */
        int inSampleSize = 1;

        // 1. 解析图片参数 : 该阶段不解析所有的数据 , 否则会将实际的图片数据解析到内存中 , 这里只解析图片的宽高信息

        /*
            设置 inJustDecodeBounds 为 true , 解析器会返回 null
            但是 outXxx 字段会被设置对应的图片属性值 ,
            如 : outWidth 输出图像的 宽度 , outHeight 输出高度 , outMimeType 输出类型 ,
            outConfig 像素格式 , outColorSpace 输出颜色空间
         */
        options.inJustDecodeBounds = true;

        // 设置图片的来源方向的像素密度 , 如设置
        options.inDensity = decodeDensityDpi;
        // 设置图片的目标方向的像素密度
        options.inTargetDensity = decodeDensityDpi;
        // 设置图片解码可缩放 , 该配置与上述两个配置结合使用
        options.inScaled = true;

        /*
            由于设置了 inJustDecodeBounds = true , 该方法返回值为空 ;
            但是传入的 BitmapFactory.Options 对象中的 outXxx 字段都会被赋值 ;
            如 outWidth , outHeight , outConfig , outColorSpace 等 ;
            可以获取该图片的宽高 , 像素格式 , 颜色空间等信息
         */
        BitmapFactory.decodeResource(resources, iamgeResId, options);

        // 获取 iamgeResId 图片资源对应的图片宽度
        imageWidth = options.outWidth;
        // 获取 iamgeResId 图片资源对应的图片高度
        imageHeight = options.outHeight;
        // 打印解码后的宽高值
        Log.w(TAG, "getResizedBitmap options.outWidth=" + options.outWidth +
                " , options.outHeight=" + options.outHeight);



        // 2. 计算图片缩小比例

        /*
            计算缩小的比例
            宽度和高度只要存在一个大于限定的最大值时 , 就进行缩小操作
            要求指定的图片必须能放到 maxBitmapWidth 宽度 , maxBitmapHeight 高度的矩形框中
            最终要求就是 宽度必须小于 maxBitmapWidth, 同时高度也要小于 maxBitmapHeight
         */
        if(imageWidth > maxBitmapWidth || imageHeight > maxBitmapHeight){
            // 如果需要启动缩小功能 , 那么进入如下循环 , 试探最小的缩放比例是多少
            while ( imageWidth / inSampleSize > maxBitmapWidth ||
                    imageHeight / inSampleSize > maxBitmapHeight ){
                // 注意该值必须是 2 的幂次方值 , 1 , 2 , 4 , 8 , 16 , 32 , 64
                inSampleSize = inSampleSize * 2;
            }

            // 执行到此处 , 说明已经找到了最小的缩放比例 , 打印下最小比例
            Log.w(TAG, "getResizedBitmap inSampleSize=" + inSampleSize);
        }


        // 3. 设置图像解码参数

        /*
            inSampleSize 设置大于 1 : 如果值大于 1 , 那么就会缩小图片 ;
            解码器操作 : 此时解码器对原始的图片数据进行子采样 , 返回较小的 Bitmap 对象 ;

            样本个数 : 样本的大小是在两个维度计算的像素个数 , 每个像素对应一个解码后的图片中的单独的像素点 ;
            样本个数计算示例 :
            如果 inSampleSize 值为 2 , 那么宽度的像素个数会缩小 2 倍 , 高度也会缩小两倍 ;
            整体像素个数缩小 4 倍 , 内存也缩小了 4 倍 ;

            小于 1 取值 : 如果取值小于 1 , 那么就会被当做 1 , 1 相当于 2 的 0 次方 ;
            取值要求 : 该值必须是 2 的幂次方值 , 2 的次方值 , 如 1 , 2 , 4 , 8 , 16 , 32
            如果出现了不合法的值 , 就会就近四舍五入到最近的 2 的幂次方值
         */
        options.inSampleSize = inSampleSize;

        // 用户设置的是否保留透明度选项 , 如果不保留透明度选项 , 设置像素格式为 RGB_565
        // 每个像素占 2 字节内存
        if (!hasAlphaChannel){
            /*
                指定配置解码 : 如果配置为非空 , 解码器会将 Bitmap 的像素解码成该指定的非空像素格式 ;
                自动匹配配置解码 : 如果该配置为空 , 或者像素配置无法满足 , 解码器会尝试根据系统的屏幕深度 ,
                源图像的特点 , 选择合适的像素格式 ;
                如果源图像有透明度通道 , 那么自动匹配的默认配置也有对应通道 ;
                默认配置 : 默认使用 ARGB_8888 进行解码
             */
            options.inPreferredConfig = Bitmap.Config.RGB_565;
        }

        /*
            注意解码真实图像的时候 , 要将 inJustDecodeBounds 设置为 false
            否则将不会解码 Bitmap 数据 , 只会将
            outWidth , outHeight , outConfig , outColorSpace 等 outXxx 图片参数解码出来
         */
        options.inJustDecodeBounds = false;

        /*
            设置图片可以被复用
         */
        options.inMutable = true;

        /*
            如果设置了一个 Bitmap 对象给 inBitmap 参数
            解码方法会获取该 Bitmap 对象 , 当加载图片内容时 , 会尝试复用该 Bitmap 对象的内存

            如果解码方法无法复用该 Bitmap 对象 , 解码方法可能会抛出 IllegalArgumentException 异常 ;
            当前的实现是很有必要的 , 被复用的图片必须是可变的 , 解码后的 Bitmap 对象也是可变的 ,
            即使当解码一个资源图片时 , 经常会得到一个不可变的 Bitmap 对象 ;

            确保是否解码成功 :
            该解码方法返回的 Bitmap 对象是可以使用的 ,
            鉴于上述约束情况 和 可能发生的失败故障 , 不能假定该图片解码操作是成功的 ;

            检查解码返回的 Bitmap 对象是否与设置给 Options 对象的 inBitmap 相匹配 ,
            来判断该 inBitmap 是否被复用 ;

            不管有没有复用成功 , 你应该使用解码函数返回的 Bitmap 对象 , 保证程序的正常运行 ;

            与 BitmapFactory 配合使用 :

            在 KITKAT 以后的代码中 , 只要被解码生成的 Bitmap 对象的字节大小 ( 缩放后的 )
            小于等于 inBitmap 的字节大小 , 就可以复用成功 ;

            在 KITKAT 之前的代码中 , 被解码的图像必须是
            JPEG 或 PNG 格式 ,
            并且 图像大小必须是相等的 ,
            inssampleSize 设置为 1 ,
            才能复用成功 ;
            另外被复用的图像的 像素格式 Config ( 如 RGB_565 ) 会覆盖设置的 inPreferredConfig 参数
         */
        options.inBitmap = inBitmap;


        // 4. 解码图片 , 并返回被解码的图片

        return BitmapFactory.decodeResource(resources, iamgeResId, options);
    }

}

执行结果 :

2020-07-01 11:17:31.389 12350-12350/kim.hsl.bm I/Bitmap: blog : 5224 , 2678 , 55959488
2020-07-01 11:17:31.390 12350-12350/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap options.outWidth=1990 , options.outHeight=1020
2020-07-01 11:17:31.390 12350-12350/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap inSampleSize=32
2020-07-01 11:17:31.413 12350-12350/kim.hsl.bm I/Bitmap: reduceSizeBitmap : 62 , 31 , 3844

顺利将图片的宽高都缩小为 100 像素以下 ;





七、GitHub 地址



BitmapMemory

热门文章

暂无图片
编程学习 ·

蒙哥马利模乘

Montgomery乘法的数学表达式是A * B * R ^ (-1)mod M。A、B是同位长大数,R是2的M(位长)的次方,R ^ (-1)是指R相对于M的模逆,即R ^ (-1)满足以下条件的数:R * R ^ (-1) mod M = 1;这个条件成立的充要条件是R与M互素,这一点只需要M为奇数即可。 使用蒙哥马利乘法可以做…
暂无图片
编程学习 ·

【数据结构】-排序-快速排序

~快速排序在平均情况下是效果最好的排序算法~每趟子表的排序都是从两头向中间交替逼近,接下来举一个例子类别排序方法最好时间最坏时间平均时间空间复杂度稳定性序列特征适用于插入排序直接插入排序n(顺序)n2(逆序)n21稳定有序序列+待排序元素+无序序列基本有序/n很小折半插…
暂无图片
编程学习 ·

leetcode 124. 二叉树中的最大路径和

题目 给定一个非空二叉树,返回其最大路径和。 本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。 思路 简单题,dfs返回当前节点为一端的最大链。答案有两种情况,1 当前节点到子孙的一条链 2 当前节点为中端,…
暂无图片
编程学习 ·

Vue

常用指令文本插值 v-html:把文本解析为 HTML 代码。 绑定属性 v-bind:为 HTML 标签绑定属性值。 条件渲染v-if:条件性的渲染某元素,判定为真时渲染,否则不渲染。 v-else:条件性的渲染。 v-else-if:条件性的渲染。 v-show:根据条件展示某元素,区别在于切换的是 display …
暂无图片
编程学习 ·

Python之OpenCV的学习(二)

图像 1.OpenCV读取图像的格式是BGR 2.shape属性 # 返回一个[h,w,c]元组,即height,width,通道数 img_shape = img.shape img_shape1 = img_shape[0] img_shape2 = img_shape[1] img_shape3 = img_shape[2]3.彩色图像与灰度图像 # cv2.IMREAD_COLOR; 彩色图像 # cv2.IMREAD_GRAY…
暂无图片
中恒嘉业 ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
中恒嘉业 ·

学习笔记六——循环神经网络

一、RNN 前馈神经网络:信息往一个方向流动。包括MLP和CNN 循环神经网络:信息循环流动,网络隐含层输出又作为自身输入,包括RNN、LSTM、GAN等。 RNN模型结构如下图所示: 展开之后相当于堆叠多个共享隐含层参数的前馈…
暂无图片
郑州普通话 ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
郑州普通话 ·

[Redux/Mobx] Redux和vuex有什么区别?

[Redux/Mobx] Redux和vuex有什么区别? 1:vuex的异步数据不需要使用到中件间,redux需要 2:vuex可以直接commit,action不是必须的,redux是必须的 个人简介 我是歌谣,欢迎和大家一起交流前后端知…
暂无图片
代理记账 ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
代理记账 ·

[Redux/Mobx] Redux和vuex有什么区别?

[Redux/Mobx] Redux和vuex有什么区别? 1:vuex的异步数据不需要使用到中件间,redux需要 2:vuex可以直接commit,action不是必须的,redux是必须的 个人简介 我是歌谣,欢迎和大家一起交流前后端知…
暂无图片
cgfy ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
cgfy ·

学习笔记六——循环神经网络

一、RNN 前馈神经网络:信息往一个方向流动。包括MLP和CNN 循环神经网络:信息循环流动,网络隐含层输出又作为自身输入,包括RNN、LSTM、GAN等。 RNN模型结构如下图所示: 展开之后相当于堆叠多个共享隐含层参数的前馈…
暂无图片
coreui ·

[Redux/Mobx] Redux和vuex有什么区别?

[Redux/Mobx] Redux和vuex有什么区别? 1:vuex的异步数据不需要使用到中件间,redux需要 2:vuex可以直接commit,action不是必须的,redux是必须的 个人简介 我是歌谣,欢迎和大家一起交流前后端知…
暂无图片
未来博客 ·

[Redux/Mobx] Redux和vuex有什么区别?

[Redux/Mobx] Redux和vuex有什么区别? 1:vuex的异步数据不需要使用到中件间,redux需要 2:vuex可以直接commit,action不是必须的,redux是必须的 个人简介 我是歌谣,欢迎和大家一起交流前后端知…
暂无图片
建站日记 ·

[Redux/Mobx] Redux和vuex有什么区别?

[Redux/Mobx] Redux和vuex有什么区别? 1:vuex的异步数据不需要使用到中件间,redux需要 2:vuex可以直接commit,action不是必须的,redux是必须的 个人简介 我是歌谣,欢迎和大家一起交流前后端知…
暂无图片
mfbz ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
mfbz ·

学习笔记六——循环神经网络

一、RNN 前馈神经网络:信息往一个方向流动。包括MLP和CNN 循环神经网络:信息循环流动,网络隐含层输出又作为自身输入,包括RNN、LSTM、GAN等。 RNN模型结构如下图所示: 展开之后相当于堆叠多个共享隐含层参数的前馈…
暂无图片
珊珊日记 ·

[Redux/Mobx] 在Redux中怎么发起网络请求?

[Redux/Mobx] 在Redux中怎么发起网络请求? 如果单纯的使用Redux,因为redux的actionCreator返回一个plain object,所以不能在actionCreator中发起网络请求;reducer是一个纯函数,不能有副作用,所以也不能有异…
暂无图片
珊珊日记 ·

学习笔记六——循环神经网络

一、RNN 前馈神经网络:信息往一个方向流动。包括MLP和CNN 循环神经网络:信息循环流动,网络隐含层输出又作为自身输入,包括RNN、LSTM、GAN等。 RNN模型结构如下图所示: 展开之后相当于堆叠多个共享隐含层参数的前馈…