精华内容
下载资源
问答
  • 一个tensorflow流程进行记录。 tensorflow官网上提供很详细python教程,也确实很好用。但是,应用软件产品大多用c/c++写。所以,大部分应用都是用python来训练模型,得到训练好模型后. 用c++调用...

    近期在进行模型设置过程中,遇到了加载双计算图时,模型运算出错的情况。

    在搜集相关资料过程中,发现网上冲浪时候,教程基本只有MNIST的预测。

    对一个tensorflow流程进行记录。

    tensorflow的官网上提供很详细的python教程,也确实很好用。但是,应用软件产品大多用c/c++写的。所以,大部分的应用都是用python来训练模型,得到训练好的模型后. 用c++调用tensorflow模型进行推理。通过收集资料,总结了方法.

    1. 使用python训练模型,并保存模型
    a.训练并保存模型

    sess = tf.Session(config=config) #session
    saver = tf.train.Saver()  #需要添加的代码,在初始化变量前。
    sess.run(tf.initialize_all_variables())
    #....训练过程....
    saver.save(sess, "xxx.ckpt") #保存模型在model目录下


    可以看到,ckpt保存路径下,有如下的内容:

    model.ckpt.data-00000-of-00001 包含所有变量的值,没有结构。
    model.ckpt.index 映射表,将data映射到meta的计算图中。
    model.ckpt.meta 包含元图,即计算图的结构,没有变量的值(基本上可以在tensorboard / graph中看到)。
    
    


    b.模型整合
    调用tensorflow自带的freeze_graph.py工具, 输入为格式.pb或.pbtxt的protobuf文件和.ckpt的参数文件,输出为一个新的同时包含图定义和参数的.pb文件;这个步骤的作用是把checkpoint中.ckpt文件中的参数转化为常量const operator后和之前的tensor定义绑定在一起。

    得到一个可以用于推理的、包含权重的计算图结构pb文件。

    python freeze_graph.py --input_checkpoint=../ckpt/InsightFace..._1000000.ckpt -- output_graph=../model/model_frozen.pb --output_node_names=output_node  


    现在,我们得到model_frozen.pb,这是在C++中常用的模型格式。

    2.使用c++加载模型
    a.头文件
    如下,可以看出,必须要包含"tensorflow/core/platform/env.h"和"tensorflow/core/public/session.h"等一系列的头文件才能编译。

    ...
    #ifndef CONVERT_FACE_H
    #define CONVERT_FACE_H
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include "tensorflow/cc/client/client_session.h"
    #include "tensorflow/cc/ops/standard_ops.h"
    #include "tensorflow/cc/ops/image_ops.h"
    #include "tensorflow/core/framework/tensor.h"
    #include "tensorflow/core/platform/env.h"
    #include "tensorflow/core/public/session.h"
    #include <opencv2/opencv.hpp>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include "common/constant.h"
    #include "common/face.h"
    ...
    
    

    b.tensorflow模型初始化

        // 建立graph
        tensorflow::GraphDef graph;
        // pb模型加载到graph里
        tensorflow::Status ret = tensorflow::ReadBinaryProto(tensorflow::Env::Default(), model_path, &graph);
        if (!ret.ok()) {
            log->error("fail to load pb model");
            return 1;
        }
    
        // 配置session
        tensorflow::Session *p_session;
        tensorflow::SessionOptions options;
        options.config.mutable_gpu_options()->set_allow_growth(true);
        options.config.mutable_gpu_options()->set_visible_device_list("0"); // single gpu 
        NewSession(options, &p_session);
    
        // graph 加载到 session
        ret = p_session->Create(graph);
        if (!ret.ok()) {
            log->error("fail to load graph");
            return 1;
        }
    
    
    
    c.模型预测(单张图片)
    相较于一些例子只是对MLP进行推理,在实际应用中并不常见。这里贴出一个实际应用中常见的例子 : 预测单张图片, 对其进行分类判别 :
    
        // 输入图像
        cv::Mat &face_data;
        // 输入tensor NHWC
        // 并定义输入张量,包括数据类型和大小。
        tensorflow::Tensor input_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, m_height, m_width, m_channel})); 
    
        // 根据输入数据face_data的情况,进行调整
        cv::Mat input_mat = cv::Mat(m_height, m_width, CV_32FC3);
        face_data.convertTo(input_mat, CV_32FC3);
        
        // Mat to Tensor    
        int data_size = m_height * m_width * m_channel * 1 * sizeof(float);
        std::copy_n((float*)input_mat, data_size, (input_tensor.flat<float>()).data());
        
        // 定义模型输入
        std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
                { "input", input_tensor}
            };
    
        // 定义模型输出
        std::vector<tensorflow::Tensor> outputs;
        tensorflow::Status status = p_session->Run(inputs, {"output"}, {}, &outputs);  //调用模型,
        // 输出节点名为softmax,结果保存在output中。
        if (!status.ok()) {
            cout << status.ToString() << "\n";
            return 1;
        }
        std::cout << "input"  << input_tensor.DebugString() << std::endl;
        std::cout << "output" << outputs[0].DebugString() << std::endl;
    


    3. 当模型有多个输入/输出时候的处理策略
    3.1 多个输入
    3.1.1 建图
    多层感知机MLP模型定义如下, 其中x和y都需要输入。

    with tf.Session() as sess:
        x = tf.placeholder(tf.float32, [None, 32], name="x")
        y = tf.placeholder(tf.float32, [None, 8], name="y")
    
        w1 = tf.Variable(tf.truncated_normal([32, 16], stddev=0.1))
        b1 = tf.Variable(tf.constant(0.0, shape=[16]))
    
        w2 = tf.Variable(tf.truncated_normal([16, 8], stddev=0.1))
        b2 = tf.Variable(tf.constant(0.0, shape=[8]))
    
        a = tf.nn.tanh(tf.nn.bias_add(tf.matmul(x, w1), b1))
        y_out = tf.nn.tanh(tf.nn.bias_add(tf.matmul(a, w2), b2), name="y_out")
        cost = tf.reduce_sum(tf.square(y-y_out), name="cost")
        optimizer = tf.train.AdamOptimizer().minimize(cost, name="train")
    
        init = tf.initialize_variables(tf.all_variables(), name='init_all_vars_op')
        tf.train.write_graph(sess.graph_def,
                             './',
                             'mlp.pb', as_text=False)
    


    3.1.2 c++中加载&使用模型

    下面是在C++中加载上一节定义的计算图并运行的代码,分为如下几个步骤:创建Session
    初始化计算图
    运行

    #include "tensorflow/core/public/session.h"
    #include "tensorflow/core/graph/default_device.h"
    using namespace tensorflow;
    
    int main(int argc, char* argv[]) {
    
        std::string graph_definition = "mlp.pb";
        Session* session;
        GraphDef graph_def;
        SessionOptions opts;
        std::vector<Tensor> outputs; // Store outputs
        TF_CHECK_OK(ReadBinaryProto(Env::Default(), graph_definition, &graph_def));
    
        // Set GPU options
        graph::SetDefaultDevice("/gpu:0", &graph_def);
        opts.config.mutable_gpu_options()->set_per_process_gpu_memory_fraction(0.5);
        opts.config.mutable_gpu_options()->set_allow_growth(true);
    
        // create a new session
        TF_CHECK_OK(NewSession(opts, &session));
    
        // Load graph into session
        TF_CHECK_OK(session->Create(graph_def));
    
        // Initialize our variables
        TF_CHECK_OK(session->Run({}, {}, {"init_all_vars_op"}, nullptr));
    
        Tensor x(DT_FLOAT, TensorShape({100, 32}));
        Tensor y(DT_FLOAT, TensorShape({100, 8}));
        auto _XTensor = x.matrix<float>();
        auto _YTensor = y.matrix<float>();
    
        _XTensor.setRandom();
        _YTensor.setRandom();
    
        for (int i = 0; i < 10; ++i) {
    
            TF_CHECK_OK(session->Run({{"x", x}, {"y", y}}, {"cost"}, {}, &outputs)); // Get cost
            float cost = outputs[0].scalar<float>()(0);
            std::cout << "Cost: " <<  cost << std::endl;
            TF_CHECK_OK(session->Run({{"x", x}, {"y", y}}, {}, {"train"}, nullptr)); // Train
            outputs.clear();
        }
    
    
        session->Close();
        delete session;
        return 0;
    }
    


    ps: 下面是代码的英文说明,不难搞懂,就不翻译了。

    If we examine the code we will see many similarities with running the Python code. Lines 15-17 set GPU options for the graph definition. We set the default device as “/gpu:0”, set the memory fraction as 0.5 and allow growth on the GPU memory usage. These settings are the same when initializing a session with a tf.config in python.

    Lines 20 create a new session with the options we specified. Line 23 loads the graph definition into the session so we can use it. Line 26 we initialize all our variables like we would in any tensorflow session.

    Lines 28-31 we initialize a tensor as our inputs and outputs. TensorFlow’s tensors are underlying Eigen tensors. Essentially, we call x.matrix() to get a pointer to Eigen’s tensor and hence the underlying data. We can similarly call x.vector, x.scalar and so on… See Eigen’s Tensor Documentation and TensorFlow’s Tensor Documentation for more details. Lines 33-34 generates some random data.

    Lines 36-43 is where the real computation happens. Recall in our graph definition we explicitly named some variables and operations. Here we reference them by name as a string and providing the necessary inputs. The output is obtained by passing a vector that is populated when the graph is run (Lines 38-40). Lastly, Line 41 performs the training for the neural network. The remaining lines close the session and clean up our pointer.

    可以看出,输入为session --> Run({{"x", x}, {"y", y}, ... {}}, ...),即对每个输入占位符,都要设置一个形如{"x", x}的格式。3.2 多个输出

    // 2018, Patrick Wieschollek <mail@patwie.com>
    #include <tensorflow/core/protobuf/meta_graph.pb.h>
    #include <tensorflow/core/public/session.h>
    #include <tensorflow/core/public/session_options.h>
    #include <iostream>
    #include <string>
    
    typedef std::vector<std::pair<std::string, tensorflow::Tensor>> tensor_dict;
    
    /**
     * @brief load a previous store model
     * @details [long description]
     *
     * in Python run:
     *
     *    saver = tf.train.Saver(tf.global_variables())
     *    saver.save(sess, './exported/my_model')
     *    tf.train.write_graph(sess.graph, '.', './exported/graph.pb, as_text=False)
     *
     * this relies on a graph which has an operation called `init` responsible to
     * initialize all variables, eg.
     *
     *    sess.run(tf.global_variables_initializer())  # somewhere in the python
     * file
     *
     * @param sess active tensorflow session
     * @param graph_fn path to graph file (eg. "./exported/graph.pb")
     * @param checkpoint_fn path to checkpoint file (eg. "./exported/my_model",
     * optional)
     * @return status of reloading
     */
    tensorflow::Status LoadModel(tensorflow::Session *sess, std::string graph_fn,
                                 std::string checkpoint_fn = "") {
      tensorflow::Status status;
    
      // Read in the protobuf graph we exported
      tensorflow::MetaGraphDef graph_def;
      status = ReadBinaryProto(tensorflow::Env::Default(), graph_fn, &graph_def);
      if (status != tensorflow::Status::OK()) return status;
    
      // create the graph in the current session
      status = sess->Create(graph_def.graph_def());
      if (status != tensorflow::Status::OK()) return status;
    
      // restore model from checkpoint, iff checkpoint is given
      if (checkpoint_fn != "") {
        const std::string restore_op_name = graph_def.saver_def().restore_op_name();
        const std::string filename_tensor_name =
            graph_def.saver_def().filename_tensor_name();
    
        tensorflow::Tensor filename_tensor(tensorflow::DT_STRING,
                                           tensorflow::TensorShape());
        filename_tensor.scalar<std::string>()() = checkpoint_fn;
    
        tensor_dict feed_dict = {{filename_tensor_name, filename_tensor}};
        status = sess->Run(feed_dict, {}, {restore_op_name}, nullptr);
        if (status != tensorflow::Status::OK()) return status;
      } else {
        // virtual Status Run(const std::vector<std::pair<string, Tensor> >& inputs,
        //                  const std::vector<string>& output_tensor_names,
        //                  const std::vector<string>& target_node_names,
        //                  std::vector<Tensor>* outputs) = 0;
        status = sess->Run({}, {}, {"init"}, nullptr);
        if (status != tensorflow::Status::OK()) return status;
      }
    
      return tensorflow::Status::OK();
    }
    
    int main(int argc, char const *argv[]) {
      const std::string graph_fn = "./exported/my_model.meta";
      const std::string checkpoint_fn = "./exported/my_model";
    
      // prepare session
      tensorflow::Session *sess;
      tensorflow::SessionOptions options;
      TF_CHECK_OK(tensorflow::NewSession(options, &sess));
      TF_CHECK_OK(LoadModel(sess, graph_fn, checkpoint_fn));
    
      // prepare inputs
      tensorflow::TensorShape data_shape({1, 2});
      tensorflow::Tensor data(tensorflow::DT_FLOAT, data_shape);
    
      // same as in python file
      auto data_ = data.flat<float>().data();
      for (int i = 0; i < 2; ++i) data_[i] = 1;
    
      tensor_dict feed_dict = {
          {"input", data},
      };
    
      std::vector<tensorflow::Tensor> outputs;
      TF_CHECK_OK(sess->Run(feed_dict, {"output", "dense/kernel:0", "dense/bias:0"},
                            {}, &outputs));
    
      std::cout << "input           " << data.DebugString() << std::endl;
      std::cout << "output          " << outputs[0].DebugString() << std::endl;
      std::cout << "dense/kernel:0  " << outputs[1].DebugString() << std::endl;
      std::cout << "dense/bias:0    " << outputs[2].DebugString() << std::endl;
    
      return 0;
    }



    同样地,可以看到在sess->Run(xx, {"output", "dense/kernel:0", "dense/bias:0"}, {}, &outputs)将输出的节点放在sess->Run()的第2个参数的位置,并把经过计算图计算出来的结果放在第4个参数的位置处outputs处,对应进行解析就可以了。

    参考资料:
    Tensorflow C++ 编译和调用图模型 :即讲了安装tensorflow c++过程,又讲了使用过程。
    Tensorflow CPP API demo :第一个参考资料的源码,很详细。
    Loading a TensorFlow graph with the C++ API :一个非常好的加载tensorflow模型教程,常被引用。
    tensorflow c++ 加载模型 总结的很细
     

    展开全文
  • SDL系列教程():显示图象的一部分 作者:akinggw 欢迎进入今天课程,今天我们要讲解如何显示图象的一部分。也许你要问,这有什么用呢? 它用处大很,比如,我们在玩“大话西游”时,我们窗口是不是...
     
    

    SDL系列教程(八):显示图象的一部分

    作者:akinggw

    欢迎进入今天的课程,今天我们要讲解如何显示图象的一部分。也许你要问,这有什么用呢?

    它的用处大的很,比如,我们在玩“大话西游”时,我们的窗口是不是只显示了程序的一部分。

    同样还有精灵的显示,这个我们在后面会慢慢讲解。

    在这篇教程中,我会简单地讲解如何在有限的窗口中显示一幅巨大的图片。

    先看下面这张图片:

     

    它的高宽分别是1000 X 1000。而我们的屏幕分辨率只有640X480,显然不能全部显示这幅图片。

    打开我们前面的代码,在apply_surface函数中,你会找到下面这个函数:

    SDL_BlitSurface( source, NULL, destination, &offset );

     

    我们函数的第二个参数使用的是空,意思是不传值,但现在我们要使用它。

    这个参数的结构是SDL_Rect,我们在前面使用了它来显示图片。

     

    这个结构如下:

    SDL_Rect{

    Int x;               //X坐标

    Int y;               //Y坐标

    Int w;              //宽度值

    Int h;               //高度值

    }

     

    SDL_BlitSurface函数的第一个参数表示要显示的图片;第二个参数表示从要显示的图片的X,Y坐标开始到W,H之内的范围;第三个参数表示将图片显示到哪里,可以是空的屏幕,也可以是一张图片上;第四个参数表示屏幕的X,Y坐标到W,H之内的范围。

     

    现在,我们将apply_surface函数修改成下面的形式:

     

    /*显示函数*/

     

    void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination ,SDL_Rect* clip = NULL  )

    {

        //建立一个暂时的矩形存储源图象的一部分

    SDL_Rect offset;

      //得到矩形的坐标

     offset.x = x;

     offset.y = y;

    //显示图象

     SDL_BlitSurface( source, clip, destination, &offset );

    }

    在这个函数中,我们主要就是添加了参数SDL_Rect* clip。

    在主函数中,添加一个新的参数:

    SDL_Rect clip;

    然后在游戏循环中设置这个新的参数,如下:

    clip.x=x;

     clip.y=y;

     clip.w=640;

     clip.h=480;

     

    我们在这里将图象的显示坐标设置成变量X,Y,主要是我们想通过前面学习的键盘操作来控制图象的显示位置。图象显示的范围为从X,Y到W,H。

    然后就可以显示它了:

    apply_surface(0,0,demo,screen,&clip);

    最后得到的显示结果如下图:

     

     

     

    关于更多内容请访问金桥科普网站( http://popul.jqcq.com  )游戏开发栏目,如你需要游戏开发方面的书籍请参考金桥书城游戏频道(http://book.jqcq.com/category/1_70_740.html )。 如果你在阅读本篇文章时有什么好的建议请来信给我,我的E_mail: akinggw@126.com. 如果你在使用SDL时有什么问题,请到金桥科普网站(http://popul.jqcq.com  )游戏开发栏目,我将详细地为你解答。

     

    接下来,我可能会讲解一点我学习网络引擎Raknet的一些心得,谢谢关注!

    展开全文
  • 图片处理时,有时需要为图片加一些边框,下面介绍种为图片添加简单边框方法。 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上...也可以将八张图片组合在一起...

    图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法。

    基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下角,右边,右上角,上边,其中左右上下只需要一个有效长度,就像重写水平进度条一样,只需要一个有效的长度,然后平铺,就达到了最后想要的效果,不错,左右上下边采用的也是这样的思路。也可以将八张图片组合在一起,然后读取整张图片,用代码裁剪,下面会给出相应的代码。下面的代码主要是给出第一种方法,后一种给出代码,有兴趣的可以自己试试。注意图片不要放到drawable目录下面,因为屏幕分辨率会影响图片的大小,所以最好是放在assets目录里面。下面代码为了简便所以没有那样做。后面一篇还会讲到另一种添加边框图片的方法。

    下面贴图片:

    原图片:


    处理后:


    代码(res参数为上面所说的八个边框组合图片资源):

     

    1. /** 
    2.      * 图片与边框组合 
    3.      * @param bm 原图片 
    4.      * @param res 边框资源 
    5.      * @return 
    6.      */  
    7.     private Bitmap combinateFrame(Bitmap bm, int[] res)  
    8.     {  
    9.         Bitmap bmp = decodeBitmap(res[0]);  
    10.         // 边框的宽高  
    11.         final int smallW = bmp.getWidth();  
    12.         final int smallH = bmp.getHeight();  
    13.           
    14.         // 原图片的宽高  
    15.         final int bigW = bm.getWidth();  
    16.         final int bigH = bm.getHeight();  
    17.           
    18.         int wCount = (int) Math.ceil(bigW * 1.0 / smallW);  
    19.         int hCount = (int) Math.ceil(bigH  * 1.0 / smallH);  
    20.           
    21.         // 组合后图片的宽高  
    22.         int newW = (wCount + 2) * smallW;  
    23.         int newH = (hCount + 2) * smallH;  
    24.           
    25.         // 重新定义大小  
    26.         Bitmap newBitmap = Bitmap.createBitmap(newW, newH, Config.ARGB_8888);  
    27.         Canvas canvas = new Canvas(newBitmap);  
    28.         Paint p = new Paint();  
    29.         p.setColor(Color.TRANSPARENT);  
    30.         canvas.drawRect(new Rect(0, 0, newW, newH), p);  
    31.           
    32.         Rect rect = new Rect(smallW, smallH, newW - smallW, newH - smallH);  
    33.         Paint paint = new Paint();  
    34.         paint.setColor(Color.WHITE);  
    35.         canvas.drawRect(rect, paint);  
    36.           
    37.         // 绘原图  
    38.         canvas.drawBitmap(bm, (newW - bigW - 2 * smallW) / 2 + smallW, (newH - bigH - 2 * smallH) / 2 + smallH, null);  
    39.         // 绘边框  
    40.         // 绘四个角  
    41.         int startW = newW - smallW;  
    42.         int startH = newH - smallH;  
    43.         Bitmap leftTopBm = decodeBitmap(res[0]); // 左上角  
    44.         Bitmap leftBottomBm = decodeBitmap(res[2]); // 左下角  
    45.         Bitmap rightBottomBm = decodeBitmap(res[4]); // 右下角  
    46.         Bitmap rightTopBm = decodeBitmap(res[6]); // 右上角  
    47.           
    48.         canvas.drawBitmap(leftTopBm, 0, 0, null);  
    49.         canvas.drawBitmap(leftBottomBm, 0, startH, null);  
    50.         canvas.drawBitmap(rightBottomBm, startW, startH, null);  
    51.         canvas.drawBitmap(rightTopBm, startW, 0, null);  
    52.           
    53.         leftTopBm.recycle();  
    54.         leftTopBm = null;  
    55.         leftBottomBm.recycle();  
    56.         leftBottomBm = null;  
    57.         rightBottomBm.recycle();  
    58.         rightBottomBm = null;  
    59.         rightTopBm.recycle();  
    60.         rightTopBm = null;  
    61.           
    62.         // 绘左右边框  
    63.         Bitmap leftBm = decodeBitmap(res[1]);  
    64.         Bitmap rightBm = decodeBitmap(res[5]);  
    65.         for (int i = 0, length = hCount; i < length; i++)  
    66.         {  
    67.             int h = smallH * (i + 1);  
    68.             canvas.drawBitmap(leftBm, 0, h, null);  
    69.             canvas.drawBitmap(rightBm, startW, h, null);  
    70.         }  
    71.           
    72.         leftBm.recycle();  
    73.         leftBm = null;  
    74.         rightBm.recycle();  
    75.         rightBm = null;  
    76.           
    77.         // 绘上下边框  
    78.         Bitmap bottomBm = decodeBitmap(res[3]);  
    79.         Bitmap topBm = decodeBitmap(res[7]);  
    80.         for (int i = 0, length = wCount; i < length; i++)  
    81.         {  
    82.             int w = smallW * (i + 1);  
    83.             canvas.drawBitmap(bottomBm, w, startH, null);  
    84.             canvas.drawBitmap(topBm, w, 0, null);  
    85.         }  
    86.           
    87.         bottomBm.recycle();  
    88.         bottomBm = null;  
    89.         topBm.recycle();  
    90.         topBm = null;  
    91.           
    92.         canvas.save(Canvas.ALL_SAVE_FLAG);  
    93.         canvas.restore();  
    94.           
    95.         return newBitmap;  
    96.     }  


    如果边框是在一张图片里面,下面给出从一张图片取中间200X200的区域。如何类似边框过多,可以将裁剪的信息写入到指定的文件,裁剪时可先将边框图片信息读取出来,然后再裁剪出边框。如果处理的原图片太大,可能会出内存溢出。可先将图片缩小到一定尺寸再处理,具体缩小方法,参见android图像处理系列之二--图片旋转、缩放、反转的图片缩放。

    1. /** 
    2.  * 截取图片的中间的200X200的区域 
    3.  * @param bm 
    4.  * @return 
    5.  */  
    6. private Bitmap cropCenter(Bitmap bm)  
    7. {  
    8.     int dstWidth = 200;  
    9.        int dstHeight = 200;  
    10.        int startWidth = (bm.getWidth() - dstWidth)/2;  
    11.        int startHeight = ((bm.getHeight() - dstHeight) / 2);  
    12.        Rect src = new Rect(startWidth, startHeight, startWidth + dstWidth, startHeight + dstHeight);  
    13.        return dividePart(bm, src);  
    14. }  
    15.   
    16. /** 
    17.  * 剪切图片 
    18.  * @param bmp 被剪切的图片 
    19.  * @param src 剪切的位置 
    20.  * @return 剪切后的图片 
    21.  */  
    22. private Bitmap dividePart(Bitmap bmp, Rect src)  
    23. {  
    24.     int width = src.width();  
    25.     int height = src.height();  
    26.     Rect des = new Rect(0, 0, width, height);  
    27.     Bitmap croppedImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);  
    28.     Canvas canvas = new Canvas(croppedImage);  
    29.     canvas.drawBitmap(bmp, src, des, null);  
    30.     return croppedImage;  
    31. }  


    处理后图片(原图片还是上面的图片):

    展开全文
  • 图片处理时,有时需要为图片加一些边框,下面介绍种为图片添加简单边框方法。 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上...也可以将八张图片组合在一起...

    图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法。

    基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下角,右边,右上角,上边,其中左右上下只需要一个有效长度,就像重写水平进度条一样,只需要一个有效的长度,然后平铺,就达到了最后想要的效果,不错,左右上下边采用的也是这样的思路。也可以将八张图片组合在一起,然后读取整张图片,用代码裁剪,下面会给出相应的代码。下面的代码主要是给出第一种方法,后一种给出代码,有兴趣的可以自己试试。注意图片不要放到drawable目录下面,因为屏幕分辨率会影响图片的大小,所以最好是放在assets目录里面。下面代码为了简便所以没有那样做。后面一篇还会讲到另一种添加边框图片的方法。

    下面贴图片:

    原图片:


    处理后:


    代码(res参数为上面所说的八个边框组合图片资源):

     

    1. /** 
    2.      * 图片与边框组合 
    3.      * @param bm 原图片 
    4.      * @param res 边框资源 
    5.      * @return 
    6.      */  
    7.     private Bitmap combinateFrame(Bitmap bm, int[] res)  
    8.     {  
    9.         Bitmap bmp = decodeBitmap(res[0]);  
    10.         // 边框的宽高  
    11.         final int smallW = bmp.getWidth();  
    12.         final int smallH = bmp.getHeight();  
    13.           
    14.         // 原图片的宽高  
    15.         final int bigW = bm.getWidth();  
    16.         final int bigH = bm.getHeight();  
    17.           
    18.         int wCount = (int) Math.ceil(bigW * 1.0 / smallW);  
    19.         int hCount = (int) Math.ceil(bigH  * 1.0 / smallH);  
    20.           
    21.         // 组合后图片的宽高  
    22.         int newW = (wCount + 2) * smallW;  
    23.         int newH = (hCount + 2) * smallH;  
    24.           
    25.         // 重新定义大小  
    26.         Bitmap newBitmap = Bitmap.createBitmap(newW, newH, Config.ARGB_8888);  
    27.         Canvas canvas = new Canvas(newBitmap);  
    28.         Paint p = new Paint();  
    29.         p.setColor(Color.TRANSPARENT);  
    30.         canvas.drawRect(new Rect(0, 0, newW, newH), p);  
    31.           
    32.         Rect rect = new Rect(smallW, smallH, newW - smallW, newH - smallH);  
    33.         Paint paint = new Paint();  
    34.         paint.setColor(Color.WHITE);  
    35.         canvas.drawRect(rect, paint);  
    36.           
    37.         // 绘原图  
    38.         canvas.drawBitmap(bm, (newW - bigW - 2 * smallW) / 2 + smallW, (newH - bigH - 2 * smallH) / 2 + smallH, null);  
    39.         // 绘边框  
    40.         // 绘四个角  
    41.         int startW = newW - smallW;  
    42.         int startH = newH - smallH;  
    43.         Bitmap leftTopBm = decodeBitmap(res[0]); // 左上角  
    44.         Bitmap leftBottomBm = decodeBitmap(res[2]); // 左下角  
    45.         Bitmap rightBottomBm = decodeBitmap(res[4]); // 右下角  
    46.         Bitmap rightTopBm = decodeBitmap(res[6]); // 右上角  
    47.           
    48.         canvas.drawBitmap(leftTopBm, 0, 0, null);  
    49.         canvas.drawBitmap(leftBottomBm, 0, startH, null);  
    50.         canvas.drawBitmap(rightBottomBm, startW, startH, null);  
    51.         canvas.drawBitmap(rightTopBm, startW, 0, null);  
    52.           
    53.         leftTopBm.recycle();  
    54.         leftTopBm = null;  
    55.         leftBottomBm.recycle();  
    56.         leftBottomBm = null;  
    57.         rightBottomBm.recycle();  
    58.         rightBottomBm = null;  
    59.         rightTopBm.recycle();  
    60.         rightTopBm = null;  
    61.           
    62.         // 绘左右边框  
    63.         Bitmap leftBm = decodeBitmap(res[1]);  
    64.         Bitmap rightBm = decodeBitmap(res[5]);  
    65.         for (int i = 0, length = hCount; i < length; i++)  
    66.         {  
    67.             int h = smallH * (i + 1);  
    68.             canvas.drawBitmap(leftBm, 0, h, null);  
    69.             canvas.drawBitmap(rightBm, startW, h, null);  
    70.         }  
    71.           
    72.         leftBm.recycle();  
    73.         leftBm = null;  
    74.         rightBm.recycle();  
    75.         rightBm = null;  
    76.           
    77.         // 绘上下边框  
    78.         Bitmap bottomBm = decodeBitmap(res[3]);  
    79.         Bitmap topBm = decodeBitmap(res[7]);  
    80.         for (int i = 0, length = wCount; i < length; i++)  
    81.         {  
    82.             int w = smallW * (i + 1);  
    83.             canvas.drawBitmap(bottomBm, w, startH, null);  
    84.             canvas.drawBitmap(topBm, w, 0, null);  
    85.         }  
    86.           
    87.         bottomBm.recycle();  
    88.         bottomBm = null;  
    89.         topBm.recycle();  
    90.         topBm = null;  
    91.           
    92.         canvas.save(Canvas.ALL_SAVE_FLAG);  
    93.         canvas.restore();  
    94.           
    95.         return newBitmap;  
    96.     }  


    如果边框是在一张图片里面,下面给出从一张图片取中间200X200的区域。如何类似边框过多,可以将裁剪的信息写入到指定的文件,裁剪时可先将边框图片信息读取出来,然后再裁剪出边框。如果处理的原图片太大,可能会出内存溢出。可先将图片缩小到一定尺寸再处理,具体缩小方法,参见android图像处理系列之二--图片旋转、缩放、反转的图片缩放。

    1. /** 
    2.  * 截取图片的中间的200X200的区域 
    3.  * @param bm 
    4.  * @return 
    5.  */  
    6. private Bitmap cropCenter(Bitmap bm)  
    7. {  
    8.     int dstWidth = 200;  
    9.        int dstHeight = 200;  
    10.        int startWidth = (bm.getWidth() - dstWidth)/2;  
    11.        int startHeight = ((bm.getHeight() - dstHeight) / 2);  
    12.        Rect src = new Rect(startWidth, startHeight, startWidth + dstWidth, startHeight + dstHeight);  
    13.        return dividePart(bm, src);  
    14. }  
    15.   
    16. /** 
    17.  * 剪切图片 
    18.  * @param bmp 被剪切的图片 
    19.  * @param src 剪切的位置 
    20.  * @return 剪切后的图片 
    21.  */  
    22. private Bitmap dividePart(Bitmap bmp, Rect src)  
    23. {  
    24.     int width = src.width();  
    25.     int height = src.height();  
    26.     Rect des = new Rect(0, 0, width, height);  
    27.     Bitmap croppedImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);  
    28.     Canvas canvas = new Canvas(croppedImage);  
    29.     canvas.drawBitmap(bmp, src, des, null);  
    30.     return croppedImage;  
    31. }  


    处理后图片(原图片还是上面的图片):

    展开全文
  • 一次偶然,误触到一个快捷键,界面就变大了。变大部分包括编辑器所有内容,就像一张图片,被放大一样,但功能都能使用。搜了很久,才发现是在这儿设置~ (细心你,一定发现编辑窗口目前是6个文件) 还是...
  • 引言 目录 引言 目录 用Python接口学习LeNet ...用一个小技巧来平铺前八张图片 迭代求解器 写一个常规训练循环 架构实验和优化 上一篇:Caffe实战系列(一)Classification_Visualization ...
  • Java爬虫爬取网易汽车车型库

    千次阅读 2017-04-15 23:21:55
    写了一个爬虫,主要用于爬取网易汽车车型库(http://product.auto.163.com/)上不同品牌/车标(共175个车标)下不同车系(共1650个系列的的图片(各八张) 代码下载代码如下: 共CarBrand.java,CarCrawe
  • Activity Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,是一个负责与用户交互组件 SSH 为 Struts+Spring+Hibernate的一个集成框架,是目前较流行一种Web应用程序开源框架。...
  • 需要先思考一个问题就是什么样网页是流畅? 这个问题我总结了一句话:在网页与用户产生交互过程中,让用户感觉流畅。 <p><img alt="图片" src=...
  • 自学编程的八大误区!克服它! 我的本科回忆录:从迷茫自卑到保送华科 聊聊加班多该如何有效地自我提升 到底要不要考研? 昨夜,我梦回武汉 . . . 转行的路,走起来其实有点累... 来B站一年,我活成了人的模样! ...
  • 通常我们说排序是大排序,事实上应该是九大排序,因为有一个排序,我们其实是不讨论。我这里使用一张别人的图片,来解释他们之间关系。 声明:该系列排序是在此博主未完成基础上进行二次加工 我们来介绍...
  • 分层立体图像制作,极为简单,只要在本软件中设置好参数,预览一下,就可以在一个特定文件夹中生成不同角度照片,而且数量可调,然后合成即可。 全仿真立体效果真实可人,而又极为简单方便。图像在Photoshop...
  • Apache Pig: 是一个基于Hadoop大规模数据分析工具,它提供SQL-LIKE语言叫Pig Latin,该语言编译器会把类SQL数据分析请求转换为一系列经过优化处理MapReduce运算 【不准备学,计划Hive代替Pig】 ...
  • 如何预测行人下一个动作、怎么去跟踪这个行人,也有一系列问题。 里面用到是马尔可夫链解决方案,这个技术叫做MDP,跟踪一个人,随时跟踪其下一个动作,预测其下一个动作。 以上其实都是一些传统感知方法,...
  • 会计理论考试题

    2012-03-07 21:04:40
    23.如果要把C盘某个文件夹中一些文件复制到C盘另外一个文件央中,在选定文件后,若采用拖放操作,可以用___B___目标方法。 A、直接拖至 B、Ctrl十拖至 C、Alt十拖至 D、单击 24.Windows98中磁盘根文件夹是...
  • Excel新增工具集

    2011-12-20 09:30:27
    3、抽取多工作簿中同名工作表到同一新建簿中:从指定文件夹所有工作簿中抽取与当前工作簿名称相同工作表到同一个工作簿,这个工作簿各工作表名称为所抽取工作簿名称,抽取后工作表内容不变。...
  • DevEco Studio是基于Intellij IDEA工具定制化深度研发的一个工具,所以对于那些熟悉IDEA,或Android Studio或WebStorm这些IDE开发工具朋友们来说应该是很熟悉,不熟悉朋友们也可以很快就上手。 另外正是由于是...
  • 因此,这个节日中既有祭扫新坟生离死别悲酸泪,又有踏青游玩欢笑声,是一个富有特色节日。 清明节食物 很多地方在完成祭祀仪式后,将祭祀食品分吃。晋南人过清明时,习惯用白面蒸大馍,中间夹有核桃、...
  • ASP.NET精品课程+源代码

    千次下载 热门讨论 2009-01-05 20:15:51
    我们在多年专业技能课教学中,探讨出在教学中首先要将与职业岗位密切联系实训课题引入课堂,如一个大型网站建设,使学生有目的学习,引导学生学习兴趣,用任务训练岗位能力,提高学生再学习能力、解决问题...
  • PowerPoint.2007宝典 8/10

    2012-04-01 18:39:23
    10.4.4 到另一个程序 198 10.4.5 使用Office剪贴板 198 10.5 了解对象格式 199 10.6 调整对象大小 200 10.7 排列对象 201 10.7.1 旋转和翻转对象 201 10.7.2 使对象与网格对齐 202 10.7.3 微移对象 ...
  • 3、以ARCCIS9功能结构主线,学习利用ARCCIS进行数据输入,数据编辑,地图配标,投影转换,数据转换,数据显示(包括三维显示)与制图,数据查询与分析,数据输出等一系列操作。(以ARCCIS在农业中应为例介绍) ...

空空如也

空空如也

1 2
收藏数 31
精华内容 12
关键字:

一个系列的八张图片