Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

Python3与OpenCV3.3 图像处理(四)--色彩空间

$
0
0

一、本节简述

本节讲解图像色彩空间的处理和色彩空间的基础知识

二、色彩空间基础知识

什么是色彩空间,人们建立了多种色彩模型,以一维、二维、三维甚至四维空间坐标来表示某一色彩,这种坐标系统所能定义的色彩范围即色彩空间

色彩空间有很多,但是常用的色彩空间一共5种:RGB、HSV、HSI、YCrCb、YUV,简单讲一下这5个色彩空间。

  • RGB就不用多说了,RGB是我门经常用到的;
  • HSV也称六角锥体模型,是根据颜色的直观特性创建的一种颜色空间,这个颜色空间是本节课讲解的一个重点。
  • HSI是从人的视觉系统出发,用色调(  Hue  )、色饱和  度(  Saturation  或  Chroma  )和亮度(  Intensity  或  Brightness  )来描述颜色。  HSI  颜色空间可以用一个圆  锥空间模型来描述
  • YCrCb主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视,这个可以用来检测皮肤和检测人脸
  • YUV是被欧洲电视系统所采用的一种颜色编码方法(属于PAL),是PAL和SECAM模拟彩色电视制式采用的颜色空间。


三、色彩空间的转换

OpenCV提供多种将图像的色彩空间转换为另一个色彩空间的方法,转换方法的方法名一般为 “原色彩空间2需要转化的色彩空间”,下面我们以图像的RGB色彩转换为其他四种色彩空间和GRAY色彩空间。

def ColorSpace(image):
    """
    色彩空间转化
    RGB转换为其他色彩空间
    """
    gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    cv.imshow("gray",gray)
    hsv=cv.cvtColor(image,cv.COLOR_RGB2HSV)
    cv.imshow("hsv",hsv)
    yuv=cv.cvtColor(image,cv.COLOR_RGB2YUV)
    cv.imshow("yuv",yuv)
    ycrcb=cv.cvtColor(image,cv.COLOR_RGB2YCrCb)
    cv.imshow("ycrcb",ycrcb)


四、标记图像中的特定颜色

一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围,下面是网友通过实验计算的模糊范围(准确的范围在网上都没有给出)。

H:  0 180

S:  0 255

V:  0 255

以下是不同颜色的HSV最大最小的范围:

以下代码是标注出图像中的黑色部分,黑色部分将以白色显示,其他颜色部分将以黑色显示,颜色标注OpenCV 提供了一个方法,inRange()。该方法提供三个参数,第一个参数是图像色彩空间即hsv值,第二个参数是hsv的最小查找范围,第三个参数是hsv的最大查找范围。代码运行后,将会标注出图像的黑色部分。

capture=cv.VideoCapture("test.mp4")
    while(True):
        ret,frame=capture.read()
        if ret==False:
            break;
        hsv=cv.cvtColor(frame,cv.COLOR_BGR2HSV)
        lower_hsv=np.array([0,0,0])
        upperb_hsv = np.array([180, 255, 46])
        mask=cv.inRange(hsv,lowerb=lower_hsv,upperb=upperb_hsv)
        cv.imshow("video_mask", mask)
        cv.imshow("video",frame)
        c=cv.waitKey(40)
        if c==27:
            break;


作者:gangzhucoll 发表于2017/11/19 17:25:03 原文链接
阅读:146 评论:0 查看评论

LWC 59:729. My Calendar I

$
0
0

LWC 59:729. My Calendar I

传送门:729. My Calendar I

Problem:

Implement a MyCalendar class to store your events. A new event can be added if adding the event will not cause a double booking.

Your class will have the method, book(int start, int end). Formally, this represents a booking on the half open interval [start, end), the range of real numbers x such that start <= x < end.

A double booking happens when two events have some non-empty intersection (ie., there is some time that is common to both events.)

For each call to the method MyCalendar.book, return true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar.

Your class will be called like this: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

Example 1:

MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(15, 25); // returns false
MyCalendar.book(20, 30); // returns true
Explanation:
The first event can be booked. The second can’t because time 15 is already booked by another event.
The third event can be booked, as the first event takes every time less than 20, but not including 20.

Note:

  • The number of calls to MyCalendar.book per test case will be at most 1000.
  • In calls to MyCalendar.book(start, end), start and end are integers in the range [0, 10^9].

思路:
判断新加入的区间是否与原来的重叠,总共有三种重叠情况,左相交,右相交和包含,如下图:
alt text

当然这里还有一种情况,即黄色块完全包含于红色块,但这种情况已经包含在了A情况或者B情况中,所以不需要重复判断。

代码如下:

class MyCalendar {

    class Interval{
        int s;
        int e;

        Interval(int s, int e){
            this.s = s;
            this.e = e;
        }
    }

    List<Interval> mem;

    public MyCalendar() {
        mem = new ArrayList<>();
    }

    public boolean book(int start, int end) {
        Interval candicate = new Interval(start, end);
        if (overlap(candicate)) {
            return false;
        }
        else {
            mem.add(candicate);
            return true;
        }
    }

    public boolean overlap(Interval cand) {
        for (Interval tmp : mem) {
            if (overlap(tmp, cand)) return true;
        }
        return false;
    }

    boolean overlap(Interval a, Interval b) {
        if (b.s >= a.s && b.s < a.e || b.e <= a.e && b.e > a.s || b.s <= a.s && b.e >= a.e) {
            return true;
        }
        return false;
    }
}
作者:u014688145 发表于2017/11/19 17:34:44 原文链接
阅读:138 评论:0 查看评论

[bzoj1834][网络流]network 网络扩容

$
0
0

Description

给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流;
2、 将1到N的最大流增加K所需的最小扩容费用。

Input

输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。
接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。

Output

输出文件一行包含两个整数,分别表示问题1和问题2的答案。

Sample Input

5 8 2
1 2 5 8
2 5 9 9
5 1 6 2
5 1 1 8
1 2 8 7
2 5 4 9
1 2 1 1
1 4 2 1

Sample Output

13 19

HINT

30%的数据中,N<=100

100%的数据中,N<=1000,M<=5000,K<=10

题解

第一问不用说吧。。网络流模板大家都会我就不弄了
第二问,实际上就是在第一问的残余网络上重新建图,跑费用流
怎么建图??可以想到,如果最大流要+K,那么残余网络图中至少有一条权为K的增广路径。那么,我们在残留网络上建图。
ins(x,y,c,w)表示x~y有一条权为c,值为w的边。要不要删原图了??当然不用啦
跑spfa的时候,如果x->y原本有残余网络,那么一定会选择值较小的边走对吧。所以不能删原图。
至于x->y没有残余网络了,那就在新图上跑咯
顺带学了一下费用流模板

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{int x,y,c,d,next,other;}a[211000];int len,last[110000];
void ins(int x,int y,int c,int d)
{
    int k1,k2;
    k1=++len;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
    a[len].next=last[x];last[x]=len;

    k2=++len;
    a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;
    a[len].next=last[y];last[y]=len;

    a[k1].other=k2;a[k2].other=k1;
}
int list[11000];
int head,tail,st,ed;
int n,m,upd;
int h[11000];
bool bt_h()
{
    head=1;tail=2;
    memset(h,0,sizeof(h));
    h[st]=1;list[1]=st;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(h[y]==0 && a[k].c>0)
            {
                h[y]=h[x]+1;
                list[tail++]=y;
            }
        }
        head++;
    }
    if(h[ed]==0)return false;
    return true;
}
int find_flow(int x,int f)
{
    if(x==ed)return f;
    int s=0,t;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(h[y]==h[x]+1 && a[k].c>0 && s<f)
        {
            s+=(t=find_flow(y,min(a[k].c,f-s)));
            a[k].c-=t;a[a[k].other].c+=t;
        }
    }
    if(s==0)h[x]=0;
    return s;
}
int pos[21000],tmp[21000];
int d[21000],v[21000];
bool spfa()
{
    for(int i=1;i<=n+1;i++)d[i]=999999999;
    d[st]=0;
    memset(v,false,sizeof(v));v[st]=true;
    list[1]=st;head=1;tail=2;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[k].c>0 && d[y]>d[x]+a[k].d)
            {
                pos[y]=x;tmp[y]=k;
                d[y]=d[x]+a[k].d;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail++]=y;
                    if(tail==n+2)tail=1;
                }
            }
        }
        head++;
        if(head==n+2)head=1;
        v[x]=false;
    }
    if(d[n+1]==999999999)return false;
    return true;
}
int p[21000],q[21000],g[21000],co[21000];
int main()
{
    scanf("%d%d%d",&n,&m,&upd);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&p[i],&q[i],&g[i],&co[i]);
        ins(p[i],q[i],g[i],0);
    }
    st=1;ed=n;
    int ans=0;
    while(bt_h())ans+=find_flow(st,999999999);
    printf("%d ",ans);
    for(int i=1;i<=m;i++)ins(p[i],q[i],upd,co[i]);
    ins(n,n+1,upd,0);
    ans=0;
    while(spfa())
    {
        int tp=n+1,minn=999999999;
        while(tp!=st)
        {
            minn=min(minn,a[tmp[tp]].c);
            tp=pos[tp];
        }
        tp=n+1;
        while(tp!=st)
        {
            ans+=minn*a[tmp[tp]].d;
            a[tmp[tp]].c-=minn;a[a[tmp[tp]].other].c+=minn;
            tp=pos[tp];
        }
    }
    printf("%d\n",ans);
    return 0;
}
作者:Rose_max 发表于2017/11/19 18:13:18 原文链接
阅读:148 评论:0 查看评论

MyBatis的级联查询(两种方式)

$
0
0



与上次唯一不同的一下几个类

Department.java

package com.cn.zhu.bean;

public class Department {
	private 	Integer  id;
	private  String  departmentName;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDepartmentName() {
		return departmentName;
	}
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}
	@Override
	public String toString() {
		return "Department [departmentName=" + departmentName + ", id=" + id
				+ "]";
	}
	
}
Employee.java
package com.cn.zhu.bean;

import org.apache.ibatis.type.Alias;

@Alias("emp")
public class Employee {
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	private Department dept;
	
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public Employee(Integer id, String lastName, String email, String gender) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}

	
	public Department getDept() {
		return dept;
	}

	public void setDept(Department dept) {
		this.dept = dept;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	@Override
	public String toString() {
		return "Employee [email=" + email + ", gender=" + gender + ", id=" + id
		+ ", lastName=" + lastName + "]";
	}

}

EmployeeMapperPlus.java

package com.cn.mybatis.dao;

import java.util.List;
import java.util.Map;


import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;

import com.cn.zhu.bean.Employee;

public interface EmployeeMapperPlus {
	public Employee getEmpById(Integer id);
	public Employee getEmpAndDept(Integer id);
 }

EmployeeMapperPlus.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mybatis.dao.EmployeeMapperPlus">

	<!--
		自定义某个javaBean的封装规则 id 定义主键会底层有优化 type: 自定义规则的java类型 property :
		唯一id方便引用
	-->
	<!--
		<resultMap type="com.cn.zhu.bean.Employee" id="MySimpleEmp"> 指定主键的封装规则
		column 指定哪一列 property 指定对应的javaBean属性 <id column="id" property="id"/>
		定义普通列封装 <result column="last_name" property="lastName"/> 其他不指定的列会自动封装
		,我们只要写resultMap就把全部的映射规则都写上 <result column="email" property="email"/>
		<result column="gender" property="gender"/> </resultMap>
	--><!-- public Employee getEmpById(Integer id); -->
	<!-- resultMap: 自定义结果集映射规则 -->
	<!--
		<select id="getEmpById" resultMap="MyEmp"> select * from tal_employee
		where id=#{id} </select>
	-->
	<!--
		场景一 : 查询Employee的同时查询员工对应的部门 Employee ===department 一个员工有与之对应的部门信息
	-->

	<resultMap type="com.cn.zhu.bean.Employee" id="MyDifEmp">
		<!--
			指定主键的封装规则 column 指定哪一列 property 指定对应的javaBean属性
		-->
		<!-- 
                  联合查询 :级联属性封装结果
        -->
		<id column="id" property="id" />
		<!-- 定义普通列封装 -->
		<result column="last_name" property="lastName" />
		<!-- 其他不指定的列会自动封装 ,我们只要写resultMap就把全部的映射规则都写上 -->
		<result column="gender" property="gender" />
		<result column="did" property="dept.id" />
		<result column="dept_name" property="dept.departmentName" />
	</resultMap>

  <!--  使用association  定义单个对象的封装规则 -->
	<resultMap type="com.cn.zhu.bean.Employee" id="MyDifEmp2">
		<id column="id" property="id" />
		<!-- 定义普通列封装 -->
		<result column="last_name" property="lastName" />
		<!-- 其他不指定的列会自动封装 ,我们只要写resultMap就把全部的映射规则都写上 -->
		<result column="gender" property="gender" />
		<!--association  可以指定联合的javaBean对象
		  property 指定哪个是联合的对象
		  javaType: 指定这个属性对象的类型
		  
		  -->
		<association property="dept" javaType="com.cn.zhu.bean.Department">
		  <id column="did" property="id"/>
		   <result column="dept_name" property="departmentName"/>
		</association>
	</resultMap>


	<!-- public Employee getEmpAndDept(Integer id); -->
	<select id="getEmpAndDept" resultMap="MyDifEmp2">
		SELECT e.id id,e.last_name last_name ,e.gender gender,e.d_id d_id,
		d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d
		where e.d_id=d.id and e.id=#{id}
	</select>
</mapper>

以上是两种映射封装结果


增加EmployeeMapperPlus.xml 后,一定要在mybatis-config.xml里加上这个配置文件的声明,

<mappers>
		<mapper resource="mybatis/mapper/EmployeeMapperPlus.xml" />
	</mappers>
另外sql语句一定要正确,可以先在navict数据库图形化管理工具中,测试sql



下面编写测试类(这里直接写测试方法吧):详细请看映射一

@Test
	public void test05() throws IOException{
		SqlSessionFactory sqlsessionFactory=getSqlSessionFactory();
		// 1  获取到的sqlsession不会自动提交数据
		SqlSession openSession=sqlsessionFactory.openSession();

		try{
			
			EmployeeMapperPlus mapper=openSession.getMapper(EmployeeMapperPlus.class);
            Employee empAndDept=mapper.getEmpAndDept(1);
            System.out.println(empAndDept);
            System.out.println(empAndDept.getDept());
		}finally{
			openSession.commit();
		}
	}

测试完成



接下来,会写分布查询,请看下一篇文章

作者:zhupengqq 发表于2017/11/19 18:24:14 原文链接
阅读:114 评论:0 查看评论

LWC 59:731. My Calendar II

$
0
0

LWC 59:731. My Calendar II

传送门:729. My Calendar II

Problem:

Implement a MyCalendarTwo class to store your events. A new event can be added if adding the event will not cause a triple booking.

Your class will have one method, book(int start, int end). Formally, this represents a booking on the half open interval [start, end), the range of real numbers x such that start <= x < end.

A triple booking happens when three events have some non-empty intersection (ie., there is some time that is common to all 3 events.)

For each call to the method MyCalendar.book, return true if the event can be added to the calendar successfully without causing a triple booking. Otherwise, return false and do not add the event to the calendar.

Your class will be called like this: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

Example 1:

MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(50, 60); // returns true
MyCalendar.book(10, 40); // returns true
MyCalendar.book(5, 15); // returns false
MyCalendar.book(5, 10); // returns true
MyCalendar.book(25, 55); // returns true
Explanation:
The first two events can be booked. The third event can be double booked.
The fourth event (5, 15) can’t be booked, because it would result in a triple booking.
The fifth event (5, 10) can be booked, as it does not use time 10 which is already double booked.
The sixth event (25, 55) can be booked, as the time in [25, 40) will be double booked with the third event;
the time [40, 50) will be single booked, and the time [50, 55) will be double booked with the second event.

Note:

  • The number of calls to MyCalendar.book per test case will be at most 1000.
  • In calls to MyCalendar.book(start, end), start and end are integers in the range [0, 10^9].

思路:
水过,当新加入一个区间时,如果在该区间内,出现重复的区间时,则认为是a triple booking,关键在于如何求出两个区间的重叠区间,画个图就能明白,重叠区间为:

s = max(s1, s2), si = 区间i的开始坐标
e = min(e1, e2), ei = 区间i的结束坐标

首先排除一部分冗余区间,左侧的与候选区间的不重叠区间和右侧的不重叠区间均可以排除,而中间那一部分,则需要以O(n2)的时间复杂度来计算每两个区间的重叠区域,是否与候选区域发生重叠。

代码如下:

class MyCalendarTwo {

    class Interval implements Comparable<Interval>{
        int s;
        int e;
        Interval(int s, int e){
            this.s = s;
            this.e = e;
        }

        @Override
        public int compareTo(Interval o) {
            return this.s == o.s ? this.e - o.e : this.s - o.s;
        }

        @Override
        public String toString() {
            return "[" + s + ", " + e +"]";
        }
    }

    List<Interval> mem;

    public MyCalendarTwo() {
        mem = new ArrayList<>();
    }

    public boolean book(int start, int end) {
        Interval candicate = new Interval(start, end);
        if (tripleOverlap(candicate)) {
            return false;
        }
        else {
            mem.add(candicate);
            return true;
        }
    }

    boolean tripleOverlap(Interval candicate) {
        Collections.sort(mem);
        int n = mem.size();
        int left = 0;
        int right = n - 1;
        while (left < n && candicate.s >= mem.get(left).e) left ++;
        while (right >= 0 && candicate.e <= mem.get(right).s) right --;
        for (int i = left; i <= right; ++i) {
            for (int j = left; j < i; ++j){
                Interval ans = new Interval(-1, -1);
                if (overlap(mem.get(j), mem.get(i), ans)) {
                    if (overlap(ans, candicate, new Interval(-1, -1))) return true;
                }
            }
        }
        return false;
    }

    boolean overlap(Interval a, Interval b, Interval ans) {
        if (b.s >= a.s && b.s < a.e || b.e <= a.e && b.e > a.s || b.s <= a.s && b.e >= a.e) {
            ans.s = Math.max(b.s, a.s);
            ans.e = Math.min(a.e, b.e);
            return true;
        }
        return false;
    }
}

再来一种积分的思路,时间复杂度为O(n2),加入新的区间后,判断当前区间的累积值是否超过3,超过3则说明该区间不该被加入,重新去除。

具体查看imos 累积法

JAVA代码超时,C++则AC,代码如下:

class MyCalendarTwo {
public:
    map<int, int> m;
    MyCalendarTwo() {
    }

    bool book(int start, int end) {
        m[start] ++;
        m[end] --;
        int s = 0;
        for (auto it : m) {
            s += it.second;
            if (s >= 3) {
                m[end] ++;
                m[start] --;
                return false;
            }
        }
        return true;
    }
};
作者:u014688145 发表于2017/11/19 18:24:24 原文链接
阅读:140 评论:0 查看评论

Object Detection系列(五) R-FCN

$
0
0

这里写图片描述

Object Detection系列(一) R-CNN
Object Detection系列(二) SPP-Net
Object Detection系列(三) Fast R-CNN
Object Detection系列(四) Faster R-CNN
Object Detection系列(五) R-FCN

R-FCN简介:

上面这张图在这个系列文章中都会出现,可以看到,在时间轴上R-FCN并不应该出现在第五篇中,但是R-FCN在内容上是承接Faster R-CNN的,同样是何凯明团队提出,所以在这里把R-FCN移到了前面。

CNN的旧形态

用于图像分类的基础CNN模型,有一个旧形态与新形态的区分,基于旧形态的CNN结构如AlexNet,VGG,Network-in-Network,ZF-Net等等,它们都有一个特点是卷积之后保留了几层用于逻辑判断的全连接网络。何凯明和RBG团队的R-CNN系列在Faster R-CNN之前都是在这种旧形态的CNN模型上改出来了,又因为Faster R-CNN及其之前的网络一直在解决的问题就是如何充分的利用原有模型的卷积层作共享计算呢?所以才会有用卷积层完成整幅图像的特征提取,区域建议的生成等等工作。

CNN的新形态

我们都知道基础的CNN模型,一般情况下层数越深,特征图的厚度也就会越大,这样一来,为了适应第一层的全连接的维度,往往会在最后一层特征图上做全尺寸的卷积,这层卷积的参数量是非常巨大的,比如AlexNet一共只有60M个参数,但是这一层卷积的参数量就会占去一多半,所以新形态的CNN呈现全卷积化的趋势,比如ResNet,GoogleNet,DenseNet等等,而且最后一层一般采用全局池化而不是全尺寸卷积,这样一来可以在尽量减少参数的情况下增加网络的深度。

R-CNN系列在新形态CNN下的问题

Faster R-CNN及其之前的结构都是基于旧形态CNN设计的,如果把新形态的CNN迁移到Faster R-CNN中就会出现问题,对于这个问题,作者给出了如下解释:
图片分类任务与目标检测任务性质是有所差异的
分类任务想要的是对于变换的不变性(Translation invariance),也就是说不管这个类别的东西在图片的那个位置,对分类的结果不应该产生影响。
检测任务想要的是对于变换的敏感性(Translation variance),因为需要知道物体到底在哪里。
但是卷积的层数越深,不变性就越强,敏感性就会变弱。所以Faster R-CNN的结构并不适合新形态的CNN。
在Faster R-CNN的时候,作者是用了ResNet-101作为基础模型的,但是ResNet-101没有分成98层+3层,而是分为91层+10层,这个实验的结果相比于旧形态的CNN,mAP高了,但是时间也变长了,具体的值在最后的实验结果中可以看到。通过这个实验可以得出的结论是新形态的CNN模型,不适用于这种前几层共享卷积计算,后几层不共享的方式。为了解决这个问题,就有了R-FCN。

这里需要说明一点:这些观点大多来源于论文,其实在现在的很多工程实践中,Faster R-CNN就是在用ResNet,在这里之所以把论文的观点说出来主要是为了理解作者是如何一步一步的改进,最后完成R-CNN整个系列,个人认为这个思路的理解要比理解单个模型或者跑一遍代码更为重要一些。

R-FCN

R-FCN是为了适应全卷积化的CNN结构,首先R-FCN在共享所有的卷积层的,其次为了解决上面提到的问题,R-FCN提出了:
位置敏感分值图(Position-sensitive score maps)
它用来判断某一个框到底属于哪一个类别
位置敏感池化(Position-sensitive RoI pooling)
在位置敏感分值图的基础上提出的一种池化操作。

这里写图片描述
上面这张图就是R-FCN的结构,在绿色框里的内容就是ResNet-101模型,这部分卷积计算还是用来被RPN和Position-sensitive Net共享的,他就相当于Faster R-CNN里面的前五层卷积一样;蓝色的框内是RPN,它在R-FCN中的作用和在Faster R-CNN中是一样的,负责输出建议区域与边界框;最重要的部分,就是红色框内的位置敏感卷积与池化,实际上,这部分的结构在Faster R-CNN中是RoI pooling+全连接+多任务损失函数等等,在R-FCN中,这部分内容换成了对位置敏感卷积,并把RPN的建议框扣在在分值图上做位置敏感池化。

R-FCN之所以起这个名字,是因为图像分割任务中出现了一个FCN(全卷积网络),而这种全卷积的说法和R-FCN想要表达的意思很契合,或许这就是为啥这一版的名字没有叫Fastest R-CNN吧,哈哈。

位置敏感卷积:

这里写图片描述
上面图中,feature maps后面那根线就是位置敏感卷积层,它的卷积核个数是K^2(C+1),其中K是超参数,在论文用的比较多的是k=3,K^2是Grid的个数,这个个数与位置敏感池化操作后的尺寸相关联的。C是物体的类别数量,加1是因为还有一个背景类。
经过了这一层卷积之后的输出就是位置敏感分值图,分值图的宽高尺寸是与feature maps的宽高一致的,分值图的通道数就是K^2(C+1),即每一个类别都有K^2个通道。
虽然这一层卷积操作叫位置敏感卷积,并输出了位置敏感分值图,但是它本质上只是一个常规的卷积,不同的地方其实在于配合它一起使用的位置敏感池化。

位置敏感卷池化:

这里写图片描述
位置敏感池化是在分值图上的一种池化操作,它是RoI池化的变种,之前说分值图的通道是K^2(C+1),每一个类别有K^2个通道,换句话说,分值图上就有k^2个C+1个通道的组合。那么假设k=3的话,这种情况就像上面图示的那样,每一个颜色都有C+1个通道,RPN的区域建议扣在分值图上后,位置敏感池化会把这个区域在每一个通道上平均分为K^2份,然后在每一个bin内做Max pooling,但是关键在K*K格子的位置是和通道对应的,如上图中深黄色的通道数有C+1个,那么位置敏感池化操作的时候,只要深黄色通道为左上角的bin内的值,作为K*K格子的左上角位置的值,所以K*K格子的左上角位置也是深黄色的。
这种对应关系就是从左到右,从上到下。这样的话,会得到一个K*K的格子,厚度是C+1,也就是每一个通道代表一个类别。
下面这张图可以更直观的说明位置敏感池化:
这里写图片描述
红色的箭头就说明了这种对应关系。

得到K*K格子之后,再做一步全局平均池化,就得到了1*1*(C+1)的特征,刚好是C+1个,这样一来,维度固定了,同样实现了将不同的输入整理成相同维度的输出,同时维度刚好与分类数相等。

R-FCN损失函数

这里写图片描述
这个损失函数,和之前的没啥区别,同样是一个分类+回归的多任务损失,最后在一个batch加和计算loss。
用于分类的是还是负的概率log值;
用于回归的是smooth L1。

关键的地方时,R-FCN把什么特征送到了Bounding box回归模型里面,在Fast R-CNN里是conv5特征经过RoI pooling之后的特征,在RPN里是用一个卷积分支专门生产的特征,在R-FCN里面也是用一个单独的卷积分支生产的,特征的通道数是4K^2,方法和位置敏感卷积时一样的,只是C+1改成了4。

R-FCN训练

R-FCN训练的步骤与Faster R-CNN相同,同样是分步训练法,只是把Fast R-CNN换成了R-FCN。
此外,文章提出了一个叫做OHEM(Online Hard Example Mining)的训练技巧:
当一个图片生成N个区域建议后,会使用当前的网络一次计算所有N个区域的loss,并根据loss从大到小排序建议区域,并从这N个排序后的区域中取前Batch size个。
这是因为,如何某区域的loss更大,那么说明网络中的参数并没有照顾到这种特征,而这种特征应该是被学习到的,如何把本来loss就很小的特征在送入网络中参与训练,对参数的更新也没啥影响。

R-FCN性能评价

这里写图片描述
上面这张图说明了超参数k对最后的mAP的影响:
初始的Faster R-CNN RoI pooling的k选择为1时,mAP为61.7%,选择为7时,mAP为68.9%;
R-FCN RoI pooling的k选择为1时是没有位置敏感信息的,直接fail,k选择为3和7时,mAP分别为75.5%与76.6%。

这里写图片描述
上面这张图从多个角度对比了Faster R-CNN与R-FCN,其中Faster R-CNN用的是Resnet101,把前91层做共享卷积计算,后10层代替原来的3层全连接,所以这个Faster R-CNN的测试时间不是之前说的0.18s,而是mAP也不是66.9%。
关注中间一行,在使用OHEM的情况下,以ResNet-101为初始模型的两个结构,单张训练时间,R-FCN比Faster R-CNN快3倍多,单张测试时间R-FCN比Faster R-CNN快2.5倍左右。而0.17s这个时间,和Faster R-CNN使用旧形态的CNN模型时间是差不多的,但是mAP确实79.5%,优于原来的69.9%。

作者:chaipp0607 发表于2017/11/19 19:21:32 原文链接
阅读:113 评论:0 查看评论

安卓调用系统录像功能:1、启动录像返回视频,2、启动录像将视频存储在指定路径下

$
0
0

全栈工程师开发手册 (作者:栾鹏)

安卓教程全解

安卓调用系统录像功能,两种方式获取拍摄的视频。

1、启动系统录像intent,并直接返回视频数据

2、启动系统录像intent,录像后存储在指定的路径下,返回后app主动读取路径下的视频文件。


第一种方式:启动系统相机录像,返回视频数据

  private static final int RECORD_VIDEO = 0;
  private void takevideo() {
    //生成Intent.
    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    //启动摄像头应用程序
    startActivityForResult(intent, RECORD_VIDEO);
  }

第二种方式:启动系统录像,视频存储到指定路径下

 //使用一个intent请求录像,视频存储在指定位置
  private Uri outputFileUri;  
  private static final int RECORD_VIDEO_SAVE = 1;
  public void takevideo_save() {
    //创建输出文件
      File file = new File(Environment.getExternalStorageDirectory(),"test.mp4");  //存放在sd卡的根目录下
      outputFileUri = Uri.fromFile(file);

      //生成Intent.
      Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

      //启动摄像头应用程序
      startActivityForResult(intent, RECORD_VIDEO_SAVE);
  }

接收系统录像的事件的返回结果(1返回视频数据,2返回视频地址)

除了可以根据返回的intent,也可以根据请求码来区别。

对于包含视频数据的,直接将数据给VideoView播放,对于不包含视频数据的这判定为存储在了指定位置。通过uri获取视频路径,将地址给VideoView播放。

  //1、获取录制视频使用VideoView播放,2、获取视频存储地址
  @Override
  protected void onActivityResult(int requestCode,int resultCode, Intent data) {
      //读取直接返回的视频数据
    if (requestCode == RECORD_VIDEO) {
          VideoView videoView = (VideoView)findViewById(R.id.activity1_video1);
          Uri uri=data.getData();
          videoView.setVideoURI(uri); 
          videoView.start();
          Log.v("系统录像", "直接返回视频数据"+uri.getPath());
    }
    //读取指定路径的视频文件
    else if (requestCode == RECORD_VIDEO_SAVE) {
        VideoView videoView = (VideoView)findViewById(R.id.activity1_video1);
        videoView.setKeepScreenOn(true);
        String path = outputFileUri.getPath();
        String path1=Environment.getExternalStorageDirectory()+"/test.mp4";
        videoView.setVideoPath(path1);
        Log.v("系统录像", path+"读取"+path1+"下的视频文件");
    }
  } 
作者:luanpeng825485697 发表于2017/11/19 20:00:49 原文链接
阅读:170 评论:0 查看评论

福建第六届省赛 最长连续串(贪心)

$
0
0

ZB is playing a card game where the goal is to make straights. Each card in the deck has a number between 1 and M(including 1 and M). A straight is a sequence of cards with consecutive values. Values do not wrap around, so 1 does not come after M. In addition to regular cards, the deck also contains jokers. Each joker can be used as any valid number (between 1 and M, including 1 and M).

You will be given N integers card[1] .. card[n] referring to the cards in your hand. Jokers are represented by zeros, and other cards are represented by their values. ZB wants to know the number of cards in the longest straight that can be formed using one or more cards from his hand.

Input

The first line contains an integer T, meaning the number of the cases.

For each test case:

The first line there are two integers N and M in the first line (1 <= N, M <= 100000), and the second line contains N integers card[i] (0 <= card[i] <= M).

Output

For each test case, output a single integer in a line -- the longest straight ZB can get.

Sample Input
2
7 11
0 6 5 3 0 10 11
8 1000
100 100 100 101 100 99 97 103
Sample Output
5
3


【experience】:题意真爽!

先是看到样例以为是最长递增子序列,交了5发WA。

然后曲解题意,以为是找连续的子串,没调出来。

经队友提示,才真正明白这道题的意思是干啥!md...

【分析】:

题意是从手中的卡片中排出一个最长的连续序列,与输入的顺序无关!!0可以代表任意任意数字。


用数组c标记哪个数字存在,同时记下0的数量。

设两个下标,i和last,扫一遍,i在前,last在后。过程中保证last~i之间的空位可以用0补全。


【代码】:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int c[101010];
int n,m,x;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(c,0,sizeof(c));
        int r0=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            if(x==0)r0++;
            c[x]=1;
        }
        int ans=0;
        for(int i=1,c0=0,last=0;i<=m;i++)
        {
            if(!c[i])c0++;
            while(c0>r0)
            {
                last++;
                if(!c[last])c0--;
            }
            ans=max(ans,i-last);
        }
        cout<<ans<<endl;
    }
}


作者:winter2121 发表于2017/11/19 20:30:28 原文链接
阅读:108 评论:0 查看评论

nginx include conf

$
0
0

nginx 的配置很灵活,支持include配置文件,如果我们的域名都配置到nginx.conf. 这个文件就会比较乱, 也影响管理和阅读.所以直接拆分出来,分成不同的配置文件.

怎么实现呢
see
这里写图片描述

这里写图片描述

如果你想在vhost下放多个配置文件
我想都加载到nginx中

include /usr/local/nginx/conf/vhost.d/*.conf

然后nginx -s reload 就ok了

作者:kwy15732621629 发表于2017/11/19 20:31:24 原文链接
阅读:111 评论:0 查看评论

Unity Shader 学习笔记(14) 阴影

$
0
0

Unity Shader 学习笔记(14) 阴影

参考书籍:《Unity Shader 入门精要》

*版本:2017.1.1f1


阴影

实现原理

  使用Shadow Map技术。把摄像机与光源位置重合,光源的阴影部分就是摄像机看不到的地方。

  前向渲染路径中,最重要的平行光如果开启了阴影,Unity就会为光源计算阴影映射纹理(shadowmap),本质就是深度图,记录光源出发到最近表面位置。两种方法:
1. 摄像机放在光源位置,然后按正常渲染流程(调用Base Pass 和 Additional Pass)来更新深度信息,得到阴影映射纹理。
2. 摄像机放在光源位置,调用额外的Pass:LightMode = ShadowCaster,把顶点变换到光源空间,渲染目标不是帧缓存,而是阴影映射纹理。

阴影采样

  • 传统方法:
    • 正常渲染Pass,计算顶点的光源空间,用xy分量对纹理采样,如果顶点值大于该深度值,就说明在阴影区域。
  • Unity5及以后:
    • 屏幕空间的阴影映射技术(Screenapce Shadow Map)。是在延迟渲染中产生阴影的方法。不过需要显卡支持MRT。根据阴影映射纹理和深度纹理得到屏幕空间的阴影图。阴影图包含了屏幕空间所有阴影区域。

接收其他物体阴影

  在Shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,结果和最后的光照相乘即可。

向其他物体投射阴影

  把物体加入到光源的阴影映射纹理计算中(让其他物体可以得到该物体信息),即执行LightMode 为 ShadowCaster的Pass。


具体实现

使用AutoLight.cginc文件内的三个宏:
- SHADOW_COORDS:声明了一个名为_ShadowCoord的阴影纹理坐标变量。
- TRANSFER_SHADOW:根据不同平台实现。如果使用了屏幕空间的阴影映射技术,会使用内置ComputeScreenPos函数计算_ShadowCoord;否则就直接把顶点转换到光源空间存到_ShadowCoord
- SHADOW_ATTENUATION:对_ShadowCoord采样,得到阴影信息。

需要注意:内置宏用了一些变量名需要在自己定义的时候要匹配:a2f的顶点坐标为vertex,输出的v2f结构体名为v,v2f中顶点位置名为pos。

  1. 计算阴影深度,绘制深度纹理:
  2. 绘制阴影:
  3. 最后图形:

BasePass:

...

#include "AutoLight.cginc"          // 计算阴影的宏

...

struct v2f {
    ...
    // 对阴影纹理采样的坐标,参数为下一个可用插值寄存器的索引值(前面TEXCOORD0和1,所以这里是2)
    SHADOW_COORDS(2)
};

v2f vert(a2v v) {
    ... 
    // 计算阴影纹理坐标
    TRANSFER_SHADOW(o); 
    return o;
}

fixed4 frag(v2f i) : SV_Target {
    ...
    // 计算阴影值
    fixed shadow = SHADOW_ATTENUATION(i);
    return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
}

衰减和阴影统一管理

  使用AutoLight.cginc的内置宏UNITY_LIGHT_ATTENUATION,不再需要自己判断光源类型等,也不用在BasePass中单独处理阴影。如果Additional Pass需要添加阴影,用#pragma multi_compile_fwdadd_fullshadows命令。
  第一个参数变量名,宏会创建这个名字的变量;第二个参数为v2f结构体,用来计算阴影;第三个参数是世界空间的坐标,计算光照衰减。

fixed4 frag(v2f i) : SV_Target {
    ... // 不需要定义atten,下面宏会自己定义

    // 使用内置宏同时计算光照衰减和阴影。自动声明atten变量。
    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

    return fixed4(ambient + (diffuse + specular) * atten, 1.0);
}

部分源码如下。可以看到点光源、聚光灯、平行光的计算。后面还有部分代码是根据是否启用cookie的情况做不同版本的宏。


透明物体的阴影

透明测试与阴影

如果直接使用FallBack默认回调(VertexLit、Diffuse、Specular),往往无法得到正确的阴影,因为在透明测试中某些片元丢弃了,而默认回调没有进行这样的处理。可以使用回调Transparent/Cutout/VertexLit。可见源文件Alpha-VertexLit.shader。

第一个:FallBack “Transparent/VertexLit”;第二个:FallBack “Transparent/Cutout/VertexLit”;第三个:基于第二个同时在MeshRenderer的Cast Shadows选择Two Sided。

透明混合与阴影

由于需要关闭深度写入,阴影处理变得复杂,所以内置半透明的Shader都是没有阴影效果的。可以修改Fallback为VertexLit、Diffuse等不透明物体用的UnityShader。

默认不投射不接收阴影(FallBack “Transparent/VertexLit”):

强制接收阴影(FallBack “VertexLit”),但不能透过立方体看到后面墙壁的阴影:

作者:l773575310 发表于2017/11/19 21:13:01 原文链接
阅读:98 评论:0 查看评论

Java4Android笔记之Java中的对象的转型

$
0
0

对象的向上转型

什么是向上转型

向上转型——将子类的对象赋值给父类的引用:

Student s = new Student();
Person p = s;

class Student extends Person{
    String address;

    void introduce(){
        super.introduce();
        System.out.println("我的家在"+address);
    }

    void study(){
        System.out.println("我在学习");
    }
}


class Person{
    String name;
    int age;

    void introduce(){
        System.out.println("我的姓名是"+name+",我的年龄是"+age);
    }   
}

一个引用能够调用哪些成员(变量和函数),取决于这个引用的类型

一个引用调用的是哪一个方法,取决于这个引用所指向的对象

class Test{
    public static void main(String args[]){
        Student s = new Student();
        Person p = s;//要求为继承关系
        //或者这样写:
        //Person p = new Student();
        //一个引用能够调用哪些成员(变量和函数),取决于这个引用的类型
        p.name = "张三";
        p.age = 20;
        //p.address = "北京";//报错,提示找不到此符号
        p.introduce();//调用的Student的introduce()
        //p.study();//报错,提示找不到符号
    }
}   

对象的向下转型

什么是向下转型

向下转型——将父类的对象赋值给子类的引用

向下转型的前提是向上转型

Student s1 = new Student();
Person p = s1;
Student s2 = (Student)p;

class Test{
    public static void main(String args[]){
        Person p = new Student();
        Student s = (Student)p;

        //错误:
        //Person p = new Person();
        //Student s = (Student)p;
    }
}
作者:wudongjiang333 发表于2017/11/19 21:22:07 原文链接
阅读:136 评论:0 查看评论

LWC 59:730. Count Different Palindromic Subsequences

$
0
0

LWC 59:730. Count Different Palindromic Subsequences

传送门:730. Count Different Palindromic Subsequences

Problem:

Given a string S, find the number of different non-empty palindromic subsequences in S, and return that number modulo 10^9 + 7.

A subsequence of a string S is obtained by deleting 0 or more characters from S.

A sequence is palindromic if it is equal to the sequence reversed.

Two sequences A_1, A_2, … and B_1, B_2, … are different if there is some i for which A_i != B_i.

Example 1:

Input:
S = ‘bccb’
Output: 6
Explanation:
The 6 different non-empty palindromic subsequences are ‘b’, ‘c’, ‘bb’, ‘cc’, ‘bcb’, ‘bccb’.
Note that ‘bcb’ is counted only once, even though it occurs twice.

Example 2:

Input:
S = ‘abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba’
Output: 104860361
Explanation:
There are 3104860382 different non-empty palindromic subsequences, which is 104860361 modulo 10^9 + 7.

Note:

  • The length of S will be in the range [1, 1000].
  • Each character S[i] will be in the set {‘a’, ‘b’, ‘c’, ‘d’}.

思路:
难点在于如何划分子问题,才能保证更新dp时没有重复,其中需要解决重复元素子串的表达。为了保证每个子问题的回文在原问题中没有出现过,定义如下规则:子问题求出的回文串必须套上一层外壳,即子问题中的回文串集合Set = {s | s 为回文}, 有新的回文 s’ = “a” + s + “a” or “b” + s + “b”,….

定义函数如下f(i, j) 表示当前对应S[i,…j]的不重复回文串个数,于是有:

初始化: ans = 0
1. 子问题的回文串批层外衣,有 ans += f(i + 1, j - 1) , 其中S[i] == S[j]
2. 考虑"a_..._a", "_..._"表示子问题的回文串,抽出a'= a...a,其中"..."表示x个a,那么有新的回文串aa...a 和 aa...aa,有ans += 2

代码如下:

    public int countPalindromicSubsequences(String S) {
        int n = S.length();
        int[][] next = new int[4][1010];
        int[][] prev = new int[4][1010];

        char[] cs = S.toCharArray();

        for (int i = 0; i < 4; ++i) Arrays.fill(next[i], n);
        for (int i = n - 1; i >= 0; --i) {
            int c = cs[i] - 'a';
            for (int j = 0; j < 4; ++j) next[j][i] = i + 1 == n ? n : next[j][i + 1];
            next[c][i] = i;
        }

        for (int i = 0; i < 4; ++i) Arrays.fill(prev[i], -1);
        for (int i = 0; i < n; ++i) {
            int c = cs[i] - 'a';
            for (int j = 0; j < 4; ++j) prev[j][i] = i - 1 == -1 ? -1 : prev[j][i - 1];
            prev[c][i] = i;
        }
        dp = new int[1010][1010];
        return f(cs, next, prev, 0, n - 1);
    }

    int mod = 1000000000 + 7;
    int[][] dp;

    int f(char[] cs, int[][] next, int[][] prev, int s, int e) {
        if (s > e) return 0;
        if (dp[s][e] > 0) return dp[s][e];
        long ans = 0;
        for (int i = 0; i < 4; ++i) {
            int ns = next[i][s];
            int ne = prev[i][e];
            if (ns > ne) continue;
            if (ns != ne) ans += 1;
            ans ++;
            ans += f(cs, next, prev, ns + 1, ne - 1);
        }
        dp[s][e] = (int)(ans % mod);
        return dp[s][e];
    }
作者:u014688145 发表于2017/11/19 21:45:11 原文链接
阅读:102 评论:0 查看评论

Java4Android笔记之Java中的抽象类和抽象函数

$
0
0

抽象函数的语法特征

定义

只有函数的定义,没有函数体的函数被称为抽象函数:

abstract void fun();

例如:

class Person(){//编译报错,Person不是抽象的
    String name;
    int age;

    void introduce(){
        System.out.println("...");
    }

    abstract void eat();
}

抽象类的语法特征

定义

使用abstract定义的类被称之为抽象类;

  1. 抽象类不能够生成对象;
  2. 如果一个类当中包含有抽象函数,那么这个类必须被声明为抽象类;
  3. 如果一个类当中没有抽象函数,那么这个类也可以被声明为抽象类;

    abstract class Person(){//编译报错,Person不是抽象的
        String name;
        int age;
    
        Person(){
            System.out.println("Person()");
        }
    
        void introduce(){
            System.out.println("...");
        }
    
        abstract void eat();
    }
    
    
    
    class Chinese extends Person{
        Chinese(){
            System.out.println("Chinese()");
        }
        //覆写
        void eat(){
            System.out.println("用筷子吃饭");
        }
    }       
    
    
    class Test{
        public static void main(String[] args){
            Person p = new Chinese();
            p.eat();
        }
    }   
    

抽象类的作用

  1. 用作基类
  2. 抽象类不能生成对象,但是可以有构造函数!!因为在子类中可以通过super关键字调用父类的构造函数
作者:wudongjiang333 发表于2017/11/19 21:53:30 原文链接
阅读:148 评论:0 查看评论

SpringBoot特性之Actuator

$
0
0

SpringBoot自动配置的特性,很大程度上解放了我们繁琐的配置的工作,但是也向我们屏蔽了很多内部运行
的细节,好在SpringBoot为我们提供了Actuator,Actuator在应用程序里提供了众多的Web端点,通过它
们,我们可以了解应用程序运行时的内部状况。我们可以了解Bean的组装信息,获取环境配置信息,等等
Actuator为我们提供了如下的一些端口

HTTP方法 路径 描述 Sensitive Default
GET /autoconfig 自动配置报告,记录哪些自动配置条件通过了,哪些没通过 true
GET /configprops 自动配置的属性信息 true
GET /beans 应用程序上下文里全部的Bean,以及它们的关系 true
GET /dump 获取线程活动的快照 true
GET /env 获取全部环境属性,包含环境变量和配置属性信息 true
GET /env/{name} 根据名称获取特定的环境属性值 true
GET /health 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 false
GET /info 获取应用程序的定制信息,这些信息由info打头的属性提供 false
GET /mappings 全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 true
GET /metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 true
GET /metrics/{name} 报告指定名称的应用程序度量值 true
GET /shutdown 优雅的关闭应用程序(endpoints.shutdown.enabled设置为true才会生效) true
GET /trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) true
GET /loggers 展示应用程序中的日志配置信息 true
GET /heapdump 当访问这个请求的时候,会下载一个压缩的hprof的堆栈信息文件 true

我们如果要使用Actuator的话也很简单,我们首先在pom文件中引入Actuator的依赖就行了,如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

然后再添加一个配置项即可:

management:
  security:
    enabled: false

下面我们访问几个来看一下功能(端口号换成自己应用的端口号):
请求:http://localhost:8003/beans
效果如下:
Beans Info
在上面的图片中,Actuator为我们展示了一个Bean的信息,Bean的名字(bean)、别名(aliases)、
scope(singleton or prototype)、类型(type)、位置(resource绝对路径)、依赖(dependencies)
请求:http://localhost:8003/autoconfig
效果如下:
autoconfig
SpringBoot的另一个重要的特性是自动配置,当我们访问/autoconfig的时候,Actuator为我们输出了
autoconfig的一些信息,自动配置这个bean需要什么样的条件。
其他的一些Actuator端点也很有意思,例如:/configprops、/mappings、/heapdump等。当然我们也可以自定义Actuator来满足自己的功能需要,demo如下所示:

package com.zkn.springboot.exercise.endpoint;

import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.stereotype.Component;

import java.lang.management.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zkn
 * @date 2017/11/15 22:50
 */
@Component
public class ThreadInfoEndpoint extends AbstractEndpoint<Map<String, String>> {

    public ThreadInfoEndpoint() {
        //id
        super("threadInfo");
    }

    /**
     * Called to invoke the endpoint.
     *
     * @return the results of the invocation
     */
    @Override
    public Map<String, String> invoke() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        //获取所有的线程信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
        if (threadInfos != null && threadInfos.length > 0) {
            Map<String, String> map = new HashMap<>(threadInfos.length);
            for (ThreadInfo threadInfo : threadInfos) {
                map.put(threadInfo.getThreadName(), getThreadDumpString(threadInfo));
            }
            return map;
        }
        return null;
    }

    /**
     * 组装线程信息
     *
     * @param threadInfo
     */
    private String getThreadDumpString(ThreadInfo threadInfo) {

        StringBuilder sb = new StringBuilder("threadName:" + threadInfo.getThreadName() + ",threadId:" + threadInfo.getThreadId() + ",threadStatus:" + threadInfo.getThreadState());
        //锁的名字
        if (threadInfo.getLockName() != null) {
            sb.append(",lockName:" + threadInfo.getLockName());
        }
        //锁的持有者
        if (threadInfo.getLockOwnerName() != null) {
            sb.append(",lockOwnerName:" + threadInfo.getLockOwnerName());
        }
        //线程中断
        if (threadInfo.isSuspended()) {
            sb.append(",suspended:" + threadInfo.isSuspended());
        }
        if (threadInfo.isInNative()) {
            sb.append(",inNative:" + threadInfo.isInNative());
        }
        sb.append("\n");

        StackTraceElement[] stackTraceElementst = threadInfo.getStackTrace();
        MonitorInfo[] monitorInfos = threadInfo.getLockedMonitors();
        StackTraceElement stackTraceElement;
        if (stackTraceElementst != null) {
            int i;
            for (i = 0; i < stackTraceElementst.length; i++) {
                stackTraceElement = stackTraceElementst[i];
                sb.append(",stackTraceElement:" + i + ";" + stackTraceElement.toString());
                if (i == 0 && threadInfo.getLockInfo() != null) {
                    Thread.State ts = threadInfo.getThreadState();
                    switch (ts) {
                        case BLOCKED:
                            sb.append("\t-  blocked on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        case WAITING:
                            sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        case TIMED_WAITING:
                            sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        default:
                    }
                }
                for (MonitorInfo mi : monitorInfos) {
                    if (mi.getLockedStackDepth() == i) {
                        sb.append("\t-  locked " + mi);
                        sb.append('\n');
                    }
                }
            }
            if (i < stackTraceElementst.length) {
                sb.append("\t...");
                sb.append('\n');
            }

            LockInfo[] locks = threadInfo.getLockedSynchronizers();
            if (locks.length > 0) {
                sb.append("\n\tNumber of locked synchronizers = " + locks.length);
                sb.append('\n');
                for (LockInfo li : locks) {
                    sb.append("\t- " + li);
                    sb.append('\n');
                }
            }
            sb.append('\n');
        }
        return sb.toString();
    }
}

关键点是:一:继承AbstractEndpoint这个类(注意泛型类型),二:写一个无参的构造函数,调用父类的一
个有参构造函数,传入一个id描述(id描述会映射为响应的请求),三:重写invoke方法。在
AbstractEndpoint中注入Environment,所以你可以通过Environment获取系统环境变量中的值。

作者:zknxx 发表于2017/11/19 20:48:21 原文链接
阅读:22 评论:0 查看评论

SpringBoot源码分析之CommandLineRunner、ApplicationRunner

$
0
0

我们在之前的文章中简单的说过SpringBoot的CommandLineRunner和ApplicationRunner这两个接口
SpringBoot之CommandLineRunner接口和ApplicationRunner接口,这篇文章中我们从源码上简单的分析一下这两个
接口。在org.springframework.boot.SpringApplication#run()这个方法中有这样一段代码:

afterRefresh(context, applicationArguments);

方法内容如下:

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
        callRunners(context, args);
    }

SpringBoot的注释中说,在上下文刷新完之后调用这个方法。在调用这个方法的时候Spring容器已经启动完
成了。这里的context的真正对象是:AnnotationConfigEmbeddedWebApplicationContext,这个类贯
穿着SpringBoot的整个启动过程。我们看一下callRunners这个方法的内容:

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        //从Spring容器中查找类型为ApplicationRunner的Bean
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        //从Spring容器中查找类型为CommandLineRunner的Bean
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        //将上一步得到的Bean进行排序
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<Object>(runners)) {
            //如果是ApplicationRunner的实例
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            //如果是CommandLineRunner的实例
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

callRunner方法的内容就很简单了直接调用run方法。

    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
        try {
            (runner).run(args);
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
        }
    }

ApplicationRunner和CommandLineRunner的区别就是run方法参数不同,ApplicationRunner中run方法
的参数是ApplicationArguments,CommandLineRunner中run方法的参数是String类型的可变参数。。

作者:zknxx 发表于2017/11/19 23:12:32 原文链接
阅读:20 评论:0 查看评论

剑指offer 链表与指针

$
0
0

涉及到链表的面试题:

  • 如果有链表编号的参数,先明确链表的编号是从0开始还是从1开始
  • 如果输入有编号、个数这样的参数时,使用unsigned int ,这样最多只需要判断其不等于0即可
  • 两个指针可以做很多事,例如先把他们间隔固定,最后一个到结尾时,第一个指针就指向倒数第n个结点了
  • 输入的有效性必须进行判断,涉及到鲁棒性
  • 在代码中,有Node->Next_Node出现时,注意判断其是否为NULL(nullptr)
  • 当一个指针在链表上不能解决问题时,我们可以用两个指针(让一个走得快(两步),一个走一步),这可以解决找到链表中间节点的问题
#include <iostream>  
#include <string>  
#include <vector>  
#include <stack>
using namespace std;  

typedef int datatype;

struct Node
{
	datatype value;
	Node* Next_Node;
};

//从头到尾打印链表中的结点
/*面试官是否允许这个函数允许改变输入,也就是改变输入链表的顺序是一个交流点*/
bool Print_Node(Node **first)
{
	if (first == NULL || *first == NULL)
	{
		return false;
	}

	Node *Node_temp = *first;
	vector<Node*> My_Nodes;
	//利用栈的先进后出的特点也是比较好的
	stack <Node*> My_Nodes2;

 	while (Node_temp != NULL)
	{
		My_Nodes.push_back(Node_temp);
		My_Nodes2.push(Node_temp);//进
		Node_temp = Node_temp->Next_Node;
	}

	vector<Node*>::iterator it1 = My_Nodes.end();
	for (it1;it1 != My_Nodes.begin();it1--)
	{
		cout<<(*it1)->value<<endl;
	}

	while (!My_Nodes2.empty())
	{
		Node_temp = My_Nodes2.top();//出,栈顶元素赋值
		cout<<Node_temp->value<<endl;
		My_Nodes2.pop();
	}
}

//找到一个链表中倒数第K个结点(正数就比较简单了)
//与面试官交流,确认尾结点的编号(0或1皆可,这里取第一个结点的编号为1)(输入为unsigned比较重要“编号类的题目”)(或者int的话,就 <= 0)
//两种方法:1 先遍历找到结点个数,再遍历 2:两个指针,第一个指针先走k-1步,然后第二个指针指向首指针,最后第一个指针走到最后,第二个指针就指向倒数第k个结点
Node* Find_k(Node** first, unsigned int k)
{
	if (first == NULL || *first == NULL || k == 0)
	{
		return NULL;
	}

	Node* Node_temp = *first;
	for(int i = 0;i<k-1;i++)
	{
		if (Node_temp->Next_Node != NULL)
		{
			Node_temp = Node_temp->Next_Node;
		}
		else
		{
			return NULL;
		}	
	}

	Node* Node_temp2 = *first;
	while (Node_temp != NULL)
	{
		Node_temp = Node_temp->Next_Node;
		Node_temp2 = Node_temp2->Next_Node;
	}
	return Node_temp2;
}

void main()  
{     

	system("pause");
}  


作者:misayaaaaa 发表于2017/11/19 19:09:55 原文链接
阅读:0 评论:0 查看评论

前端框架-jQuery入门

$
0
0

前端框架-jQuery框架简介

1.jQuery是什么:

  • 用原生js封装好的非常多代码的框架
  • jq是用js封装的,能用jq实现的,用js都能实现
  • js能实现的,jq有些不能实现
  • jq的API只对自己开放 jq不能用js的API js也不能用jq的API
    jQuery官方库
    jQuery的API库

2.jQuery简单实用

jQuery常用$jQuery符号作为关键字

#函数调用
$(function () {
    alert(1)
})

#id调用
 $("#box").html("aaa")
jQuery("#box").html("555");

#class调用 
$(".box1").html("bbb")


#标签调用
$("p").html("111");

#append添加内容
var obj = $("<div><p>我是p标签</p></div>")
$("#box").append(obj);

#JS对象转jQuery
var box=document.getElementById("box")
box.innerHTML="123"
$(box).html("555") #转换成jQuery

#jQuery对象转JS
jq获取的元素类似于集合,获取的元素是一组
$("#box p").get(0).style.color="red"
$("#box p")[0].style.color="red" #单个元素
$("#box").get(0).innerHTML = "9999";

#jQuery修改样式
$("#box p").css("color","red");#所有元素
$("#box p").eq(2).css("color","red"); #指定单个元素

#jQuery标签遍历
$("#box p").each(function (n) {//n是序号
    $(this).html('我是'+n);#jQuery调用
    this.innerHTML="我是原生"+n  #原生调用
})
----------------------------------------------------------------
#jQuery 属性操作
#attr 设置属性/获取属性,也可以自定义标签属性
var $box = $("#box").attr("id");//读操作,id的名称
alert($box);
$("#box").attr("class","python");//设置class属性名称为python
$("#box").attr("test","java");//自定义test属性的名称为java
removeAttr()
$("#box").removeAttr("class");

#prop 设置属性/获取属性,只能处理系统自带的标签属性
$("#box").prop({
            "class":"xiaopo",
            "title": "hahah"
        });

removeProp()

#处理Class方法
addClass
removeClassclass 移除你传的那个
    没有  移除全部
$("#box").addClass("wrap box1 box2");#添加多个
$("#box").removeClass("");#删除所有
$("#box").removeClass("box2 box1"); #删除指定

toggleClass 有就删没有则加
$("#box").toggleClass("on");

操作class类名
jq  对应      js
html()    innerHTML
text()    innerText
val()     valuejq里面,设置某个值的时候,一般自带遍历
          获取某个值的时候,一般获取第一个

#CSS样式
.css()
.width()
.height()

innerWidth / innerHeight 算了padding
outerWidth / outerHeight 算了 padding+border

position()
    该对象有top /left 属性
    代表到定位父级的 top/left的值
    不算 marginpadding
offset()
   该对象有top /left 属性
   代表到浏览器窗口的 top/left的值
$("#box").css({
    "width":"200px",
    "background":"blue",
    "height":"200px"
});

   alert($("#box").css("width"));//200px
   alert($("#box").innerWidth());//600
   alert($("#box").outerWidth());//620
   alert($("#box").offset().left);
 alert($("#box").position().left);



作者:lianjiaokeji 发表于2017/11/20 9:27:03 原文链接
阅读:0 评论:0 查看评论

JAVA自带故障排查工具

$
0
0

jps

jinfo

jmap

jstack

打印线程dump,发现线程目前停留在哪行代码
-l
打印线程锁信息
-F

强制dump

jstat 统计信息
>

jconsole

jvisialvm

作者:io97704842 发表于2017/11/19 21:41:08 原文链接
阅读:25 评论:0 查看评论

JVM 微解2

$
0
0

JVM Trace跟踪参数

-verbose:gc
    打印GC日志信息
-XX:+PrintGCDetails
    打印GC日志信息
-Xloggc:d:/gc.log 
    GC日志目录
-XX:+PrintHeapAtGC 
    每次一次GC后,都打印堆信息
-XX:+TraceClassLoading
    类加载信息

JVM 内存分配参数详解

-Xmx 
    最大堆
–Xms
    最小堆
-Xmn 
    新生代大小 (eden+2s)
–XX:NewRatio 
    年轻代(eden+2s):老年代
–XX:SurvivorRatio  
    2s:eden 
-Xss
    决定方法调用的深度
    没个线程独有栈空间
    参数,局部变量分配在栈上
    一般几百K就够了,64位jvm默认1M
    对栈深度的影响

JVM 内存参数案例讲解

标记-清除算法
    标记阶段
    标记存活对象
    清除阶段
    统一回收所有未标记的对象
    缺点
    会产生内存碎片
    如果空间内存碎片太多,当程序产生大对象无法在堆中找到连续空间大小存放的时候,会强制发生GC

复制算法
    原理
        内存一分为二,每次只使用其中一块,当一块内存没有连续空间存储对象的时候,会把存活下来的对象复制到另外一块内存中,然后一次性清除之前的哪块空间
    优缺点
        没有内存碎片问题
        代价就是讲内存减少了一半,空间利用率不高
        不适用于存活对象较多的场景,比如老年代
        而实际上我们并不需要按照1:1的比例来划分,因为大部分对象从创建到结束这个生命周期很短
        HotSpot虚拟机默认Eden:Survivor=8:1

这里写图片描述

    标记-整理算法
        原理
            标记存活对象,然后把存活对象向一端移动
            清理掉存活对象这端以外的所有空间
        优缺点
            适合用于存活对象较多的场合,如老年代
            解决了空间碎片和效率问题:
            将所有的存活对象压缩到内存的一端,然后清理边界外所有的空间

这里写图片描述

    分代收集算法
        分代思想
            堆划分为新生代和老年代
            新生代中,能够存活的对象很少,可以使用复制算法
            老年代中,对象存活率高,而且没有额外的空间用来做老年代的担保,可以使用标记清除或者标记整理算法

JVM 内存回收
GC

Serial  新生代串行收集器 新(复制算法),老(标记整理)
ParNew 新生代并行收集器
Parallel Scavenge 新生代并行收集器
    目标:尽可能缩GC时用户线程的停顿时间
    在注重吞吐量或CPU资源敏感的场合,可以优先考虑Parallel Scavenge收集器 + Parallel Old收集器
Serial Old 老年代串行收集器
Parallel Old 老年代并行收集器
CMS 真正意义上的并发收集器(老年代收集器)
    目标:最短的GC停顿时间
G1

分代收集GC组合
这里写图片描述

并发:用户线程和GC线程可以同时执行,如果发生GC,用户线程依然可以执行

并行:用户线程和GC线程可以同时执行,如果发生GC 用户线程会暂停

作者:io97704842 发表于2017/11/20 0:13:04 原文链接
阅读:22 评论:0 查看评论

Kubernetes学习总结(2)——Kubernetes设计架构

$
0
0

Kubernetes集群包含有节点代理kubelet和Master组件(APIs, scheduler, etc),一切都基于分布式的存储系统。下面这张图是Kubernetes的架构图。


Kubernetes节点
在这张系统架构图中,我们把服务分为运行在工作节点上的服务和组成集群级别控制板的服务。
Kubernetes节点有运行应用容器必备的服务,而这些都是受Master的控制。
每次个节点上当然都要运行Docker。Docker来负责所有具体的映像下载和容器运行。
Kubernetes主要由以下几个核心组件组成:
etcd保存了整个集群的状态;
apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;
除了核心组件,还有一些推荐的Add-ons:
kube-dns负责为整个集群提供DNS服务
Ingress Controller为服务提供外网入口
Heapster提供资源监控
Dashboard提供GUI
Federation提供跨可用区的集群

Fluentd-elasticsearch提供集群日志采集、存储与查询



分层架构

Kubernetes设计理念和功能其实就是一个类似Linux的分层架构,如下图所示


核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
接口层:kubectl命令行工具、客户端SDK以及集群联邦
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
kubelet
kubelet负责管理pods和它们上面的容器,images镜像、volumes、etc。
kube-proxy
每一个节点也运行一个简单的网络代理和负载均衡(详见services FAQ )(PS:官方 英文)。 正如Kubernetes API里面定义的这些服务(详见the services doc)(PS:官方 英文)也可以在各种终端中以轮询的方式做一些简单的TCP和UDP传输。
服务端点目前是通过DNS或者环境变量( Docker-links-compatible 和 Kubernetes{FOO}_SERVICE_HOST 及 {FOO}_SERVICE_PORT 变量都支持)。这些变量由服务代理所管理的端口来解析。
Kubernetes控制面板
Kubernetes控制面板可以分为多个部分。目前它们都运行在一个master 节点,然而为了达到高可用性,这需要改变。不同部分一起协作提供一个统一的关于集群的视图。
etcd
所有master的持续状态都存在etcd的一个实例中。这可以很好地存储配置数据。因为有watch(观察者)的支持,各部件协调中的改变可以很快被察觉。
Kubernetes API Server
API服务提供Kubernetes API (PS:官方 英文)的服务。这个服务试图通过把所有或者大部分的业务逻辑放到不两只的部件中从而使其具有CRUD特性。它主要处理REST操作,在etcd中验证更新这些对象(并最终存储)。
Scheduler
调度器把未调度的pod通过binding api绑定到节点上。调度器是可插拔的,并且我们期待支持多集群的调度,未来甚至希望可以支持用户自定义的调度器。
Kubernetes控制管理服务器
所有其它的集群级别的功能目前都是由控制管理器所负责。例如,端点对象是被端点控制器来创建和更新。这些最终可以被分隔成不同的部件来让它们独自的可插拔。
replicationcontroller(PS:官方 英文)是一种建立于简单的 pod API之上的一种机制。一旦实现,我们最终计划把这变成一种通用的插件机制。
参考:
https://github.com/kubernetes/kubernetes/blob/release-1.2/docs/design/architecture.md
https://feisky.gitbooks.io/kubernetes/architecture/architecture.html
作者:u012562943 发表于2017/11/20 10:09:35 原文链接
阅读:17 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>