前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >springboot+jpa+shiro+layui实现权限管理

springboot+jpa+shiro+layui实现权限管理

作者头像
sucl
发布于 2019-08-07 06:47:26
发布于 2019-08-07 06:47:26
2.2K10
代码可运行
举报
文章被收录于专栏:企业平台构建企业平台构建
运行总次数:0
代码可运行

在上一篇文章中主要简单说明了如何使用springboot与shiro进行整合,同时简单的说明了下shiro相关核心模块,当然关于shiro的说明其实网上已经有了很多技术文章,同时springboot的作用其实就是spring,帮助我们提供java bean的运行环境与管理机制,将我们常说的对象,从生产、维护、管理、销毁等整个生命中期全部托管给spring容器,因此如何和springboot整合,其关键还是对shiro的理解。

由于很久没了解前端的知识了,上篇也说的,这次将使用layui来搭建整个系统,里面会涉及到如何取使用这个框架,当然更多的是查看API来帮助我们使用,但是作为一个UI框架,我们却缺少很多行为驱动的组件,也就是数据交互,如果有可能,将尽可能的扩展一些这些模块,让我们对前端的开发尽量的快,做到真正的精简。

关于该项目如何构建,这里不说太多,因为前几次已经做过了很多次,这也不可能成为我们的困惑。之前一直在强调如何搭建这样的环境,以及如何配合这些框架使用,但是具体为什么,如何使用,有哪些需要注意的点,可能存在哪些问题,一些地方实现是源于什么样的思考?等等这些问题都会涉及到,因此这次的内容会相对较多。

首先如果是通过idea+maven搭建的项目,会有如下结构:

首先我们可以将我们依赖的jar在pom配置,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.10.RELEASE</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

这个是干嘛的呢,我们知道springboot相比spring多了的就是预配置,想做到这点,我们必须先知道大家可能需要什么,那么才能在使用者需要的时候,能够简单的使用,该依赖就是管理我们在springboot对应版本那些约定的的jar的最优版本,而我们在真正配置那些依赖是,就不需要再添加<version>了,比如当我们需要引入freemaker时,只用这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
</dependency>

,其他的依赖都是按需添加的。

当然,在继续之前,我们先看下shiro的这个依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>${shiro.version}</version>
</dependency>

重点需要看下的是ShiroAutoConfiguration,与spring.factories,我们都知道,默认情况下,springboot会自动扫描Application及下级包中的bean,但是当我们引入其他的jar又没有通过@ComponentScan这样的组件扫描时,为什么新引入的jar中spring的注解也能够生效,那么原理当然是springboot的EnableAutoConfiguration,具体怎么实现则需要深入看下去,由于这不在本次探讨的范围内,所以不做深入讲解。打开spring.factories

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
  org.apache.shiro.spring.boot.autoconfigure.ShiroBeanAutoConfiguration,\
  org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration,\
  org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration

org.springframework.boot.diagnostics.FailureAnalyzer = \
  org.apache.shiro.spring.boot.autoconfigure.ShiroNoRealmConfiguredFailureAnalyzer

这些配置其实就会让那些通过@Configuration注解的类能够委托spring管理起来了,其实其他类似于xxx-spring-boot-starter都是这个原理,那么我们在进行shiro的整合时,也会参考这个配置的内容。

再来细看下项目的结构:

目前主要有三个模块,core(一些公共接口与模块)、security(安全认证)、system(系统管理功能)

其中core包括如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
config:主要是一些@Configuration,包含了spring的容器配置、web容器配置、freemaker配置、自定义过滤器等
method:主要是mvc中处理RequestMapping对应的方法参数处理,比如请求时将参数封装成自定义类型,以实现后续的功能扩展
orm:数据库关系映射用到的一些实体、接口、实现等,比如共有Dao的实现
rem:异常与消息处理,将返回不管是异常还是正常对象都封装成固定的对象,方便前端统一处理
service:服务层接口定义与实现,以及一些其他的共有接口
util:工具类
view:与视图相关的一些对象,比如树节点在后台的封装
web:抽象controller,提供了基础的请求,比如根据id插叙、根据条件查询、分页查询、保存、删除等常用的服务

然后看下security:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
annotation:自定注解
auth:这里面定义了认证与鉴权相关的接口
config:这里面就是配置的shiro相关模块,比如Realm、SecurityManager、SessionManager、ShiroFilterFactoryBean等
exception:这里是自定义异常,因为shiro已经针对不同认证错误内置了一些异常,我们进行了一些扩展
filter:我们知道,shiro内置了filter,比如anno、authc、user、perm等,这里也是进行了一定的扩展
permission:将权限相关的单独拿出来,以后方便扩展
realm:自定义realm
service:关于用户认证、token校验的一些服务
token:token作为认证的桥梁,当然框架内置了一些token,但是我们也可以按需扩展
util:工具类
web:有时候认证也会有一些与客户端的交互,比如之前说过的,通过请求认证而非过滤器

最后是system,其实这个是很普遍的一个模块,我们的业务模块也将以这样的结构进行扩展:

将项目的模块区分,并非是无事找事,将各个职责进行细分,无论是从功能结构上,还是从实际使用中,都会是项目有更好的可读性与可维护性,比如为什么dao、service,我们一般在dao会进行一些单表的操作,而service会加入一些业务与关联操作以及事务控制,而web中仅提供对外的访问入口,不要掺杂任何的业务逻辑,将业务逻辑放在service中也是可以进行复用的。

springboot项目启动其实很简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication();

        application.run(Application.class,args);
    }
}

不用加任何其他的配置,但是我们这是一个传统的web项目,首先数据库是必须要的,为了提升效率,pom引入数据库连接池相关的jar,比如成c3po、dbcp、druid等。

然后添加相关的配置,构建DataSource对象,能够在spring容器中被发现,由于spring内置相关的处理,所以我们经常只用在application.properties中添加如下配置即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#jdbc
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jsms?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=jsms
spring.datasource.password=jsms

但是数据源如何构建的呢,那就去看下之前说到的自动配置吧,spring.factories中可以找到与DataSource相关的DataSourceAutoConfiguration。

此次数据持久框架选择使用JPA,而不是mybatis,我觉得如果仅仅是对单表或者关联表查询,完全没必要使用mybatis,毕竟需要写sql也不是很方便,虽然很灵活,但是还是看自己有没这种灵活的必要,当然现在也有一些mybatis扩展的框架,减少了哪些烦人的sql。

说到JPA,这个其实是一种规范,我们大多数时候使用的起始是hibernate,因为这是JPA的一种实现。所以现在需要引入相关的jar。起始jpa如何使用,之前也简单的说到了,我们只用将dao继承一些Repository即可,当然JPA提供了几种Repository接口,而每一种都有相关的方法,只要你继承了,就能使用,就像下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Repository
public interface AgencyDao extends JpaRepository<Agency,Serializable>,JpaSpecificationExecutor<Agency>,
        org.springframework.data.repository.Repository<Agency,Serializable>{
}

而dao的实现,则是通过jpa内部实现,就像mybatis一样,只用定义接口,有xml通过代理方式实现。

人后看下service层,其实这里主要定义了一些公共方法,这些方法必须是抽象的,主要为了方便其他模块能够通过继承的方式进行复用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface BaseService<R ,T> {

    T getById(Serializable id);

    T getOne(String property,Object value);

    T getInitializeObject(Serializable id, String[] props);

    List<T> getAll2(Collection<Condition> conditions);

    List<T> getAll(T t);

    Pager<T> getPager(Pager pager,Collection<Condition> conditions,Collection<Order> orders);

    T save(T t);

    void saveBatch(Collection<T> ts);

    T updateById(T t);

    T saveOrUpdate(T t);

    void deleteById(Serializable id);

    void delete(String property,Object value);

    void delete(T t);

    void deleteAll(Collection<T> ts);

    boolean exist(Serializable id);

    boolean exist(T t);
}

如果之后需要为所有的service添加功能,只用对这个基类进行重构即可。

而实现层,可以看下部分代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class BaseServiceImpl<R extends Repository<T,Serializable>,T> implements BaseService<R,T>{

    protected R dao;
    /**
     * 通过examle查询
     */
    protected JpaRepository<T,Serializable> repository;
    /**
     * 通过Specification查询
     */
    protected JpaSpecificationExecutor<T> specificationExecutor;

    protected abstract Class<T> getDomainClazz();

    @Resource
    public void setRepository(R r) {
        this.dao = r;
        if(r instanceof JpaRepository){
            this.repository = (JpaRepository<T, Serializable>) r;
        }
        if(r instanceof JpaSpecificationExecutor){
            this.specificationExecutor = (JpaSpecificationExecutor<T>) r;
        }
    }

首先,通过泛型来实现抽象的概念,而里面添加dao、repository、specificationExecutor,主要是因为刚说到了,不同的repository有不同的功能,比如dao可以访问我们自定义的方法,specificationExecutor进行分页查询。

最后看下Controller的抽象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class BaseController<S extends BaseService<?,T>,T> {

    @Autowired
    protected S service;

    /**
     * 根据主键查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public T get(@PathVariable String id){
        return service.getById(id);
    }

    @GetMapping(value = "/{id}",params = {"initialize"})
    public T get(@PathVariable String id, @RequestParam String[] initialize){
        return service.getInitializeObject(id, initialize);
    }

    /**
     * 根据条件查询
     * @param conditions
     * @return
     */
    @GetMapping
    public List<T> getAll(@QueryCondition Collection<Condition> conditions){
        return  service.getAll2(conditions);
    }

    /**
     * 按道理查询条件需要加上类型约束,只对指定类型的属性进行封装,但是T是泛型,没办法获取class
     * @param pager
     * @param conditions
     * @param orders
     * @return
     */
    @GetMapping(params = {"pager:pageIndex","pager:pageSize"})
    public Pager<T> getPager(Pager pager, @QueryCondition Collection<Condition> conditions,@QueryOrder Collection<Order> orders){
        return service.getPager(pager,conditions,orders);
    }

    /**
     * 保存或更新
     * 原则上是:有主键则根据主键查找,没有数据异常;没主键则新增
     * @param t
     * @return
     */
    @PostMapping
    public T saveOrUpdate(T t){
        return service.saveOrUpdate(t);
    }

    /**
     * 根据主键删除
     * @param id
     */
    @DeleteMapping("/{id}")
    public void delete(@PathVariable String id){
        service.deleteById(id);
    }
}

可见,都是我们常用的请求。

其实真正实现这个功能也没那么简单,比如我们如何在JPA提供的一些方法的条件下,添加支持各种条件查询方法,而通过前台的参数如果构建成该条件?

目前可以看到用到的有两种方式进行条件查询,分别为Example和Specification,由于Example提供的查询比较局限,我们只用其做‘=’这样的查询,但是针对>、<、like、in等这样的条件怎么办,当然是使用Specification,只不过为了让前台传递过来的参数能够顺利封装成Specification,我们自定义了Condition这样一个借口,并通过CustomSpecification来完成Condition的转换。那么问题又来了,究竟请求如何转换成Condition,我们不妨先了解下spring提供的一个借口,HandlerMethodArgumentResolver,这个是干嘛用的,那再用spring的时候,像@RequestParam、@RequestBody、@PathVariable等这样的作用在方法参数上的注解应该是比较常见的,那么就是通过HandlerMethodArgumentResolver来实现的。同理,条件、分页、排序等请求方法参数的的封装,都是通过实现该接口进行处理的。

最后要说的是返回值的处理,为了方便前端对请求数据的统一处理,我们会将请求的返回进行封装,这个其实我们可以想到spring中比较常见的一个注解,@ResponseBody,则是通过HandlerMethodReturnValueHandler这样一个接口,来对所有请求的返回进行进一步处理。

到这里基本完成了正常业务的各个抽象功能的定义与实现。

现在说下shiro如何引入进来,前面说到的,我们会参考一个类,ShiroAutoConfiguration:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
@SuppressWarnings("SpringFacetCodeInspection")
@ConditionalOnProperty(name = "shiro.enabled", matchIfMissing = true)
public class ShiroAutoConfiguration extends AbstractShiroConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected AuthenticationStrategy authenticationStrategy() {
        return super.authenticationStrategy();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected Authenticator authenticator() {
        return super.authenticator();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected Authorizer authorizer() {
        return super.authorizer();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SubjectDAO subjectDAO() {
        return super.subjectDAO();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SessionStorageEvaluator sessionStorageEvaluator() {
        return super.sessionStorageEvaluator();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SubjectFactory subjectFactory() {
        return super.subjectFactory();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SessionFactory sessionFactory() {
        return super.sessionFactory();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SessionDAO sessionDAO() {
        return super.sessionDAO();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SessionManager sessionManager() {
        return super.sessionManager();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected SessionsSecurityManager securityManager(List<Realm> realms) {
        return super.securityManager(realms);
    }

    @Bean
    @ConditionalOnResource(resources = "classpath:shiro.ini")
    protected Realm iniClasspathRealm() {
        return iniRealmFromLocation("classpath:shiro.ini");
    }

    @Bean
    @ConditionalOnResource(resources = "classpath:META-INF/shiro.ini")
    protected Realm iniMetaInfClasspathRealm() {
        return iniRealmFromLocation("classpath:META-INF/shiro.ini");
    }

    @Bean
    @ConditionalOnMissingBean(Realm.class)
    protected Realm missingRealm() {
        throw new NoRealmBeanConfiguredException();
    }
}

其实这里已经帮助我们配置了大部分需要的模块,当然还有一些在ShiroAnnotationProcessorAutoConfiguration、ShiroBeanAutoConfiguration。

那为什么我们还需要配置,当然是默认的配置有些不能满足我们的需要了。相比shiro-spring包中的ShiroBeanAutoConfiguration,ShiroBeanAutoConfiguration提供了更多的可配置项,我们可以通过application.properties类进行配置化修改,而ShiroAutoConfiguration更多的是约定了配置。一把情况下是没有必要的。

在之前,先了解下shiro的工作原理,我们知道,shiro是基于filter进行权限过滤与身份认证的,可以看到在DefaultFilter中有很多内置过滤器,其中常用进行身份认证的就是authc,它的实现逻辑则是,用户通过登录页面输入用户名、密码,进入authc,系统根据参数生成UsernamePasswordToken,然后会将其作为参数通过调用Realm中doGetAuthenticationInfo,构建对应的AuthenticationInfo,当然,token中封装了一些信息,我们通过这些信息去找到一些信息来构建AuthenticationInfo,比如通过用户名去数据库中找,如果找到了则返回AuthenticationInfo,表示认证成功,否则返回null即可。

这一次我没有使用authc这个过滤器,而是自己定义了一个,当然还是继承于FormAuthenticationFilter,为什么这样做?因为FormAuthenticationFilter中只有用户名、密码等参数,如果我们需要验证码什么的,就没办法了。其次Realm我们只使用了一个,为了实现多表身份认证,当然shiro是支持多realm的,实现上其实差不多,都是通过token去构建AuthenticationInfo,而现在则是通过不同的token获取找到对应的用户服务来构建起AuthenticationInfo,所以多表的认证我们需要一套token、用户服务来支持,这块实现比较复杂,可以参考代码的实现。

  • 这里需要额外说几点,关于rememberMe,虽然我开启了,但是没起作用,可以对FormAuthc做如下修改:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    configSession(request,response);
    return super.isAccessAllowed(request, response, mappedValue) || getSubject(request, response).isRemembered();
}
  • 在我们系统之初,是没有用户的,怎么登入用户呢,如果你在数据库中新建一个用户,那么密码如果是用复杂的算法构成的,你难道还能自己构建?这里我们引入开发者用户 ,主要是方便在没有用户的时候,能够同样进入系统进行操作,比如新增用户。
  • 在开始自定义filter时,是通过spring来管理的,但是却一直认证出现异常,自定义的filter应该这样加入:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected ShiroFilterFactoryBean shiroFilterFactoryBean() {
    ShiroFilterFactoryBean shiroFilterFactoryBean = super.shiroFilterFactoryBean();
    Map<String, Filter> map = new LinkedHashMap<>();
    //加入自定义的Filter
    map.put("formAuthc", new DefaultFormAuthenticationFilter());
    shiroFilterFactoryBean.setFilters(map);
    return shiroFilterFactoryBean;
}
  • 引入了@Principal注解,可以在用户实体中对属性,那样在用户认证时就可以通过用户名、电话、邮箱等多途径登录。
  • 通过form表单提交与ajax进行登录处理时不同的,因为form提交后页面会重定向到成功页面,但是ajax提交则有所不同,如下在formAuthc处理:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    configSession(request,response);
    String loginType = getLoginType(request);
    if(LOGIN_TYPE_AJAX.equals(loginType)){
        try(ServletOutputStream out = response.getOutputStream()){
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.writeValue(out,new Message(Message.SUCCESS_CODE,""));
        }catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }else if(LOGIN_TYPE_TOKEN.equals(equals(loginType))){
        //创建token
        return false;
    }else{
        return super.onLoginSuccess(token, subject, request, response);
    }
}
  • 在用户认证时是通过token找到AuthenticationInfo,在这个对象中的身份没必要用username,可以使用任何对象,只要保证密码的一致,这样我们可以更好的构建权限对象AuthorizationInfo。

shiro大致这些东西,因为网上的资料太多,不想做些重复的事情。

关于前端,我们引入了freemaker,主要是参考了renren-security,因为shiro在前端进行权限控制依赖的是jsp tag,现在前端使用的是html,那么那些标签自然无用了,但是我们可以传递验证权限的对象由freemaker管理,并进行验证。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class ShiroFreemarkerVariable implements FreemarkerVariable {
    @Override
    public String getName() {
        return "shiro";
    }

    @Override
    public Object getVariables() {
        return new ShiroTag();
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ShiroTag {

    public static boolean hasPermission(String permissionStr){
        return SecurityUtils.getSubject().isPermitted(permissionStr);
    }
}

页面:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<#if shiro.hasPermission(perm)>
[<html>]
</#if>

其中perm为我们在relam中构建的AuthorizationInfo中的数据。

源码:https://github.com/suspring/springboot-jpa-shiro-cms

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
源码下载下来登录一直说密码错误
源码下载下来登录一直说密码错误
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
工业党福利:使用PaddleX高效实现指针型表计读取系列文章(1)
最近在做一个工业巡检的项目,主要涉及的内容是指针型表计的读取。本系列文章主要介绍实现表计读取的全流程开发(立个FLAG,想想真是肝...留下了不争气的眼泪),其中主要使用的工具为百度开发的PaddleX和Visual studio 2019。
用户7699929
2020/08/27
9650
工业党福利:使用PaddleX高效实现指针型表计读取系列文章(1)
工业党福利:使用PaddleX高效实现指针型表计读取系列文章(2)
最近在做一个工业巡检的项目,主要涉及的内容是指针型表计的读取。本系列文章主要介绍实现表计读取的全流程开发(立个FLAG,想想真是肝...留下了不争气的眼泪),其中主要使用的工具为百度开发的PaddleX和Visual studio 2019。
用户7699929
2020/08/27
1.6K0
工业党福利:使用PaddleX高效实现指针型表计读取系列文章(2)
又一神器面世:百度重磅发布「全流程开发套件」!
最近在做一个工业巡检的项目,主要涉及的内容是指针型表计的读取。本系列文章主要介绍实现表计读取的全流程开发。其中主要使用的工具为飞桨全流程开发工具 PaddleX 和 Visual Studio 2019。
GitHubDaily
2020/11/05
5990
又一神器面世:百度重磅发布「全流程开发套件」!
LabVIEW仪表盘识别
机器视觉系统中常需要从各类仪表的显示屏图像中提取其读数。这些仪表的显示屏可以分为模拟指针显示屏、LCD显示屏和LED显示屏等。
不脱发的程序猿
2021/08/12
1.6K0
opencv如何读取仪表中的指针刻度
向AI转型的程序员都关注了这个号👇👇👇 机器学习AI算法工程   公众号:datayx 最近遇到一个问题,如何读取仪表中的指针指向的刻度  解决方法有多种,比如,方案一:模板匹配+边缘检测+霍夫直线检测,方案二:神将网络(CNN)目标定位等, 其中CNN就有点麻烦了,需要一定数量的训练样本,太麻烦,而方案一太普通,最后我采用了方案三, 方案三:模板匹配+k-means+直线拟合 具体做法如下: 首先说一下模板匹配,它是OpenCV自带的一个算法,可以根据一个模板图到目标图上去寻找对应位置,如果模板找
机器学习AI算法工程
2022/04/11
1.9K0
opencv如何读取仪表中的指针刻度
OpenCV4系统化学习路线图与教程
OpenCV4.0发布以来,其依靠良好的接口代码、系统级别的优化、更加通用易学的函数调用,集成OpenVINO与tensorflow、caffe等模型加速推断、实现了从传统的图像处理到基于深度学习的视觉处理路线图的完整拓展。OpenCV4毫无疑问是一个OpenCV发展历史的一个重要里程碑之作。
磐创AI
2019/05/05
1.3K0
OpenCV4系统化学习路线图与教程
OpenCV图像处理笔记(三):霍夫变换、直方图、轮廓等综合应用
一、霍夫直线变换 1、霍夫直线变换 Hough Line Transform用来做直线检测 前提条件 – 边缘检测已经完成 平面空间到极坐标空间转换 2、霍夫直线变换介绍 对于任意一条直线上的所有点来说 变换到极坐标中,从[0~360]空间,可以得到r的大小 属于同一条直线上点在极坐标空(r, theta)必然在一个点上有最强的信号出现,根据此反算到平面坐标中就可以得到直线上各点的像素坐标。从而得到直线 3、相关API 标准的霍夫变换 cv::HoughLines从平面坐标转换到霍夫空间,最终输出是
MiChong
2020/09/24
3K0
OpenCV图像处理笔记(三):霍夫变换、直方图、轮廓等综合应用
halcon 算子功能查找大全中文版(可直接下载)
原文链接:https://www.cnblogs.com/DOMLX/p/11543364.html 下载后 可以直接ctrl+f查找 很方便
徐飞机
2019/09/19
5.1K0
万字长文告诉新手如何学习Python图像处理(上篇完结 四十四) | 「Python」有奖征文
期结合深度学习研究图像识别、图像分类应用。希望文章对您有所帮助,如果有不足之处,还请海涵~
全栈程序员站长
2022/11/04
2K0
万字长文告诉新手如何学习Python图像处理(上篇完结 四十四) | 「Python」有奖征文
OpenCV 系列教程5 | OpenCV 图像处理(中)
霍夫变换是一种特征提取技术,主要应用于检测图像中的直线或者圆。 OpenCV 中分为霍夫线变换和霍夫圆变换。
机器视觉CV
2019/11/12
1.6K0
OpenCV 系列教程5 | OpenCV 图像处理(中)
机器学习-09-图像处理02-PIL+numpy+OpenCV实践
开源地理空间基金会中文分会 Pillow (PIL Fork) 10.0.1 文档
用户2225445
2024/04/14
5220
机器学习-09-图像处理02-PIL+numpy+OpenCV实践
机器视觉工业缺陷检测(光源,相机,镜头,算法)
视觉工业检测大体分为工件尺寸测量与定位,和表面缺陷检测,及各种Logo标识的检测与识别等。
机器学习AI算法工程
2021/10/14
18K0
机器视觉工业缺陷检测(光源,相机,镜头,算法)
指纹识别系统概述
毕业设计完成于2012年,现阶段关于图像的东西都是走神经网络了,本文仅可以作为背景知识和简单的课程设计参考,本文另附一个MFC演示程序见文末下载链接
流川疯
2019/02/15
4.4K0
【人工智能】面试问题整理
为了消除数据特征之间的量纲影响,我们需要对特征进行归一化处理,使得不同指标之间具有可比性。例如,分析一个人的身高和体重对健康的影响,如果使用米(m)和千克(kg)作为单位,那么身高特征会在1.6~1.8m的数值范围内,体重特征会在50~100kg的范围内,分析出来的结果显然会倾向于数值差别比较大的体重特征。想要得到更为准确的结果,就需要进行特征归一化(Normalization)处理,使各指标处于同一数值量级,以便进行分析。
杨丝儿
2022/03/20
1.2K0
【人工智能】面试问题整理
树莓派计算机视觉编程:6~10
在上一章中,我们学习了如何对图像执行基本的数学和逻辑运算。 在本章中,我们将继续探索计算机视觉及其在现实世界中的应用领域中一些更有趣的概念。 就像本书前面的章节一样,我们将在 Python 3 上进行大量动手练习,并创建许多实际的应用。 我们将涵盖计算机视觉领域的许多高级主题。 我们将学习的主要主题与色彩空间,变换和阈值图像有关。 完成本章后,您将能够为一些基本的实际应用编写程序,例如跟踪特定颜色的对象。 您还可以将几何和透视变换应用于图像和实时 USB 网络摄像头。
ApacheCN_飞龙
2023/04/27
1.4K0
Python3 OpenCV4 计算机视觉学习手册:1~5
您已经读了这本书,因此您可能已经对 OpenCV 是什么有了个概念。 也许您听说过似乎来自科幻小说的功能,例如训练人工智能模型以识别通过相机看到的任何东西。 如果这是您的兴趣,您将不会感到失望! OpenCV 代表开源计算机视觉。 它是一个免费的计算机视觉库,可让您处理图像和视频以完成各种任务,从显示网络摄像头中的帧到教机器人识别现实中的物体。
ApacheCN_飞龙
2023/04/27
4.2K0
Python3 OpenCV4 计算机视觉学习手册:1~5
语义分割技术综述_语义分割模型
综述论文翻译:A Review on Deep Learning Techniques Applied to Semantic Segmentation
全栈程序员站长
2022/09/25
9730
语义分割技术综述_语义分割模型
三维重建技术综述
来源丨https://blog.csdn.net/qq_30815237/article/details/91897736
3D视觉工坊
2021/01/13
2.7K0
三维重建技术综述
机器视觉表面缺陷检测综述
中国是一个制造大国,每天都要生产大量的工业产品。用户和生产企业对产品质量的要求越来越高,除要求满足使用性能外,还要有良好的外观,即良好的表面质量。但是,在制造产品的过程中,表面缺陷的产生往往是不可避免的。不同产品的表面缺陷有着不同的定义和类型,一般而言表面缺陷是产品表面局部物理或化学性质不均匀的区域,如金属表面的划痕、斑点、孔洞,纸张表面的色差、压痕,玻璃等非金属表面的夹杂、破损、污点,等等。表面缺陷不仅影响产品的美观和舒适度,而且一般也会对其使用性能带来不良影响,所以生产企业对产品的表面缺陷检测非常重视,以便及时发现,从而有效控制产品质量,还可以根据检测结果分析生产工艺中存在的某些问题,从而杜绝或减少缺陷品的产生,同时防止潜在的贸易纠份,维护企业荣誉。
智能算法
2019/07/19
11K0
OpenCV 安卓编程示例:1~6 全
在本章中,我将逐步介绍如何开始使用 OpenCV 开发具有视觉感知的 Android 应用。
ApacheCN_飞龙
2023/04/27
5.8K0
OpenCV 安卓编程示例:1~6 全
推荐阅读
相关推荐
工业党福利:使用PaddleX高效实现指针型表计读取系列文章(1)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文